diff --git a/docs/实现说明.md b/docs/实现说明.md index 752313a..04e0813 100644 --- a/docs/实现说明.md +++ b/docs/实现说明.md @@ -99,6 +99,7 @@ - **怎么实现**:复用接口2 的 502 点渲染管线,三档分别用 `hairline_texture[/_high|/_low]` 同名贴图渲染叠图;生发同接口2(ComfyUI inpaint),**黑模板固定取 `hairline_texture_black/`(middle)**,每发型 1 张生发图。`best_hairline_center_point`=眉心 x × 首个选中发型 middle 档发际线 y。 ### 接口52 发际线新规则 `/api/v1/hairline/generate-52`(网关透传) +- **客户端鉴权**:请求头`X-API-Token`,默认`yonghe1999`,可由`gateway/config.json`的`interface52_token`覆盖;不匹配返回HTTP401/code1009且不进入队列。客户端Token不传给worker,内部Token仍由网关添加。 - `main`新增网关路由和`static/test_interface52.html`。GPU worker负责中下庭均值定位、扶正裁切及生发;主备worker均需部署52号实现,网关只透传表单并附加内部Token。 - 非空图片使用原有`*_base64`→`*_url`落盘改写;空图片保留`*_base64: null`。metadata和坐标原样返回,middle叠加到`input_image_url`。旧接口、5号队列优先级及网关IP策略保持原样。 - 参数、空字段与部署步骤见[接口52网关接入](接口52网关接入.md)。worker分支的IP限制与算法不合入本次网关改动。 diff --git a/docs/接口52网关接入.md b/docs/接口52网关接入.md index f83b78d..c46d3e4 100644 --- a/docs/接口52网关接入.md +++ b/docs/接口52网关接入.md @@ -7,12 +7,17 @@ 客户端 → `POST /api/v1/hairline/generate-52` → 网关现有队列和worker池 → worker同路径 → 网关落盘非空图片并返回公网URL。 - 请求体原样转发,网关附加配置中的`X-Internal-Token`;客户端和公开测试页不携带内部密码。 +- 客户端须通过`X-API-Token`传入52号静态Token,默认`yonghe1999`。网关校验后才进入队列;该Token不转发给worker,与内部密码独立。 - 沿用现有主备派发、隔离期、重试、日志和全局并发限制。原有5号插队优先级保持原样,52号走默认队列。 - 5号`/api/v1/hairline/generate`和其他接口保持原样;网关不新增IP限制。 - worker的中下庭规则、扶正、3:4裁切和生发结果由同一次worker响应提供,网关不重新计算坐标。 ## 请求 +请求头必填:`X-API-Token: yonghe1999`。缺少、为空或不匹配时返回HTTP401,响应为`{"code":1009,"message":"未授权:缺少或错误的 X-API-Token","request_id":"gw-…","data":null}`,不读取转发表单、不占用GPU队列。其他接口不增加这项校验。 + +可在`gateway/config.json`设置`"interface52_token": "自定义值"`;旧配置省略该项时自动使用`yonghe1999`。显式配置空值或非字符串会导致启动校验失败,不能用来关闭鉴权。配置修改后重启网关生效,测试页输入框改为相同值。 + 使用`multipart/form-data`;图片三选一:`image_file`、`image_url`、`image_base64`。必填`gender=male/female`和`hair_style=1..7`(支持逗号分隔多选)。 可选参数全部透传: @@ -51,6 +56,7 @@ worker始终返回`*_base64`。网关沿用原有公共改写,不添加52号 ```bash curl -X POST https://hair.xiangsilian.com/api/v1/hairline/generate-52 \ + -H 'X-API-Token: yonghe1999' \ -F 'image_file=@/path/to/photo.jpg' \ -F 'gender=male' -F 'hair_style=1' \ -F 'normalize_image=true' -F 'generate_grow_image=false' @@ -58,8 +64,14 @@ curl -X POST https://hair.xiangsilian.com/api/v1/hairline/generate-52 \ 成功时`code=0`,检查`input_image_url`和`hairline_images[].image_middle_url`可打开、尺寸一致;high/low仍为`*_base64: null`。访问测试页时无需输入内部Token。 +测试页新增“客户端Token”输入框,默认`yonghe1999`,只作为`X-API-Token`请求头发送;自定义后端Token时应同步填写该值。 + ## 验证 `tests/test_gateway_interface52.py`覆盖5/52路由分离、完整表单透传、由网关注入内部Token、真实公共转发函数的图片落盘与URL可访问、空字段保持原样和队列槽释放。现有pool、serialization、monitor、reqlog测试用于防止回归。 +静态Token测试覆盖默认值、自定义覆盖、缺失/错误/空Token、内部Token不能代替客户端Token、未通过时不派发、无效配置启动失败以及客户端Token不透传至worker。 + +加入客户端Token后的验证:57项网关测试通过;临时网关实测缺失/错误Token返回401,测试页携带默认Token成功调用真实worker并显示7型图片,旧5号页面正常。测试关闭生发,未改动正在运行的GPU worker。 + 2026-11-04验证结果:上述网关测试共42项通过。另起本机临时网关连接当前真实GPU worker,浏览器验证旧5号仍返回三档,52号女性7型均返回可访问的图片URL,长图输出1062×1416,矩形叠加与空字段正常,页面请求不携带内部Token,无脚本错误。本轮关闭生发以验证网关链路,未重新运行ComfyUI生发;临时网关验证后关闭。 diff --git a/docs/接口文档.md b/docs/接口文档.md index 4605ccb..a5ba891 100644 --- a/docs/接口文档.md +++ b/docs/接口文档.md @@ -87,7 +87,7 @@ | 1006 | 文件超出大小限制 | 单文件超过 1 MB | | 1007 | 图片参数错误 | file / url / base64 未传,或同时传了多个(三者严格互斥) | | 1008 | 图片格式不支持 | 非 JPG / PNG | -| 1009 | 未授权 | 缺少或错误的 `X-Internal-Token`(`/api/*` 路径鉴权) | +| 1009 | 未授权 | worker内部`X-Internal-Token`错误;网关52号缺少或错误的客户端`X-API-Token`。HTTP401 | --- @@ -526,6 +526,8 @@ **请求**:`POST /api/v1/hairline/generate-52`(网关透传至同路径GPU worker) +**客户端鉴权**:请求头必填`X-API-Token: yonghe1999`,缺少、为空或不匹配返回HTTP401/code1009,不进入worker队列。默认值可通过网关`gateway/config.json`的`interface52_token`覆盖,修改后重启网关;内部`X-Internal-Token`仍由网关注入,客户端无需提供。该校验仅用于网关52号。 + **职责**:GPU worker返回base64并完成算法处理,网关负责透传参数、附加内部Token、保存非空图片及返回公网URL。网关不增加IP限制;网关出口IP须已在worker白名单内。部署与完整字段约定见[接口52网关接入](接口52网关接入.md)。 ### 图片扶正与标准构图 diff --git a/docs/网关待改动.md b/docs/网关待改动.md index 12f6341..822597f 100644 --- a/docs/网关待改动.md +++ b/docs/网关待改动.md @@ -7,6 +7,8 @@ ## 接口52(2026-11-04 已接入main) +客户端新增请求头`X-API-Token`校验,默认`yonghe1999`,可在网关配置`interface52_token`覆盖;缺少或不匹配返回HTTP401/code1009,不进入队列。其他接口不增加该校验。 + 新增`POST /api/v1/hairline/generate-52`,由网关转发到GPU worker同路径。5号保持原样,52号的`normalize_image`等参数原样透传;非空图片沿用公共base64→URL改写,空图片仍保留`*_base64: null`。不在网关增加IP限制,不合入worker算法。 测试页`/static/test_interface52.html`不包含内部密码。部署前确认主/备worker均支持52号,并检查生发请求的上游超时。详见[接口52网关接入](接口52网关接入.md)。 diff --git a/gateway/app.py b/gateway/app.py index 450a3e9..b317daa 100644 --- a/gateway/app.py +++ b/gateway/app.py @@ -6,13 +6,14 @@ import asyncio import base64 +import hmac import json import logging from contextlib import asynccontextmanager from pathlib import Path from typing import Optional -from fastapi import FastAPI, File, Form, Request, UploadFile +from fastapi import FastAPI, File, Form, Header, Request, UploadFile from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles @@ -674,12 +675,27 @@ async def hairline_generate(request: Request): @app.post("/api/v1/hairline/generate-52", tags=["人脸分析"], summary="接口52:发际线新规则与人像裁切") -async def hairline_generate_52(request: Request): +async def hairline_generate_52( + request: Request, + api_token: Optional[str] = Header(default=None, alias="X-API-Token", + description="52号接口客户端Token,默认yonghe1999"), +): """透传图片三选一、gender、hair_style及normalize_image等参数至GPU worker。 worker负责中下庭均值定位、扶正裁切和生发;网关沿用原有派发、鉴权与图片URL改写。 空图片保持原有 *_base64: null 字段。接口5保持原样。 + 客户端须传X-API-Token;缺少或不匹配时返回HTTP401/code1009,不进入派发队列。 """ + from uuid import uuid4 + from gateway.config import DEFAULTS, get_config + + expected = get_config().get("interface52_token", DEFAULTS["interface52_token"]) + if (not isinstance(expected, str) or not expected or not api_token + or not hmac.compare_digest(api_token.encode("utf-8"), expected.encode("utf-8"))): + return JSONResponse(status_code=401, content={ + "code": 1009, "message": "未授权:缺少或错误的 X-API-Token", + "request_id": f"gw-{uuid4().hex[:8]}", "data": None, + }) return await _proxy(request, "/api/v1/hairline/generate-52") diff --git a/gateway/config.example.json b/gateway/config.example.json index aa757af..1782edf 100644 --- a/gateway/config.example.json +++ b/gateway/config.example.json @@ -1,4 +1,5 @@ { + "interface52_token": "yonghe1999", "workers": [ "http://127.0.0.1:28187", "http://127.0.0.1:28188" diff --git a/gateway/config.py b/gateway/config.py index c533b08..7751d4a 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -13,6 +13,7 @@ logger = logging.getLogger("gateway.config") CONFIG_PATH = Path(__file__).resolve().parent / "config.json" DEFAULTS = { + "interface52_token": "yonghe1999", "public_base_url": "https://hair.xiangsilian.com", "static_dir": "static/annotations", "primary_workers": [], # 主 worker 地址列表(须为 workers 的子集);空 = 全部视为主 worker @@ -76,6 +77,10 @@ def load_config() -> dict: cfg = _deep_merge(DEFAULTS, raw) # --- 校验 --- + client_token = cfg["interface52_token"] + if not isinstance(client_token, str) or not client_token: + raise ValueError("配置错误: interface52_token 必须是非空字符串") + workers: List[str] = cfg.get("workers", []) if not workers: raise ValueError("配置错误: workers 列表不能为空,至少需要一个 worker 地址") diff --git a/static/integration.html b/static/integration.html index 49fd429..7b929b3 100644 --- a/static/integration.html +++ b/static/integration.html @@ -267,6 +267,7 @@ console.log(features.gene_style); // "自然型"
/api/v1/hairline/generate-52客户端请求头必填 X-API-Token: yonghe1999。缺少或不匹配返回HTTP401、code=1009。可在网关配置interface52_token覆盖默认值;内部Token由网关添加。
网关透传至GPU worker执行。中下庭均值定位 middle,支持9→152扶正、人像裁切和矩形发际线预览。接口5保留原有三档实现;新功能使用此独立接口。
基础图片参数、gender、hair_style、use_mask、prompt、generate_grow_image沿用接口5。新增可选normalize_image,默认false,传true开启扶正与裁切;测试页显式开启。
非空图片返回*_url,空图片沿用*_base64: null。另有height_rule、framing、guide_landmarks、原图及处理后底图。middle和坐标使用当前底图,high/low字段为null。标准裁切高约3.3333L、宽2.5L;长图在保留人脸的前提下裁为3:4。