# 圆码生成 API — 54／72 自动选择版

## 启动与范围

版本：API `4.0.0`，启动器 `4.0.0-portable.20261104.auto72`。本次交付源码，先按根目录DEPLOY.md安装Python依赖，再运行 `python src/serve.py`。将来重新冻结为EXE后可按原方式直接启动；旧3.2 EXE不会自动升级。默认在服务成功监听后打开本地调用页一次；启动失败或明确未授权时不打开。

- 默认监听 `http://127.0.0.1:8998`，仅本机可访问。
- 调用页面：`http://127.0.0.1:8998/`，HTML、CSS、JS 均内置，不依赖 CDN。
- 健康检查：`GET /health`。
- 机器可读接口定义：`GET /openapi.json`。
- 本文下载：`GET /api-docs.md`。
- 不提供 Swagger/ReDoc 外部资源页面，也不包含论坛、用户登录、计费、历史和短链服务。

本版保留原54射线D35/E17，并参考Go生产实现增加固定72射线D46/E20配置。自动优先54（280bit），超出后尝试72（368bit），两者都超限返回422；无需新增参数，不截断、不转短链。528bit是72配置全部码字，不是网址净容量。允许2048字符不代表可编码这么长；放大图片不会扩容。

## 生成接口

`POST /api/generate`

请求为 `multipart/form-data`；没有文件时也支持 URL 编码的表单。**不是 JSON 请求体。** 不需要 JWT/API Key，此服务默认仅供本机受信任调用。

| 参数 | 类型 | 必填 | 默认值 / 约束 |
| --- | --- | --- | --- |
| `url` | string | 是 | 完整 HTTP/HTTPS 域名网址；拒绝 IP、localhost、用户名密码、空白、控制字符和无效百分号编码；不抓取目标网页 |
| `avatar` | file | 否 | PNG/JPEG/WebP，最多 8 MiB、1600 万像素；不传、空字段、零字节均不加头像 |
| `size` | integer | 否 | 2048；范围 280–4096，表示码图区边长和最终图片宽度 |
| `style` | string | 否 | `lines`，可选 `lines` / `dots` |
| `avatar_ratio` | number | 否 | 0.44；范围 0.10–0.45，相对于码图区边长 |
| `format` | string | 否 | `png`，仅支持 PNG |

最终图片高 = `size + round(size × 0.12)`，默认 2048×2294；4096 对应 4096×4588。显式430仍为430×482，显式1280仍为1280×1434。固定附加“扫码打开网页链接”，右下角原绿色S徽标保留。上传头像仅在本次请求中处理，不保存上传或生成历史。

默认尺寸由1280提升至2048，不再承诺省略size的结果与旧EXE默认像素相同。需要保留旧HTTP图片尺寸时显式传入原size；旧HTTP范围280～1280的像素/布局保持兼容。码区和说明文字带的采样宽度均限制为4096，旧CLI更大尺寸的文字带采样倍数也按此上限收敛，不承诺逐像素相同。尺寸增加不扩充网址容量，也不替代实机扫码验证。

API每进程最多并发渲染2张；渲染槽占满时立即HTTP503，使用相同 `code/data/msg` 结构和中文繁忙提示，不无限排队。客户端可有限退避重试，不应立即再次并发请求；该保护不能替代网关认证或全站限流。

### 最小请求

Windows PowerShell 使用 `curl.exe`，避免与别名混淆：

```powershell
curl.exe --fail-with-body http://127.0.0.1:8998/api/generate -F "url=https://example.com"
```

### 上传头像、指定参数

```powershell
curl.exe --fail-with-body http://127.0.0.1:8998/api/generate -F "url=https://example.com" -F "avatar=@C:/images/avatar.png" -F "size=430" -F "style=lines" -F "avatar_ratio=0.44" -F "format=png" -o result.json
```

不要把 multipart boundary 写死，由 HTTP 客户端生成。HTML 页使用 `FormData`，不手动填写 Content-Type。

## 成功响应

HTTP 200，`Content-Type: application/json`：

```json
{
  "code": 200,
  "data": {
    "url": "https://baidu.com",
    "size": 2048,
    "width": 2048,
    "height": 2294,
    "caption": "扫码打开网页链接",
    "style": "lines",
    "format": "png",
    "has_avatar": false,
    "avatar_ratio": 0.44,
    "avatar_size": 0,
    "profile": "v54-d35-e17",
    "lattice_rays": 54,
    "max_capacity_bits": 280,
    "payload_bits": 123,
    "base64": "data:image/png;base64,..."
  },
  "msg": "生成成功"
}
```

`data.base64` 是完整 Data URL，不是公网图片链接；可直接赋给 `<img>.src`。二进制 PNG 从第一个逗号后的内容解码。服务不提供持久化图片 URL、下载鉴权、数据库和失败历史。

新增 `profile`（v54-d35-e17 / v72-d46-e20-sample）、`lattice_rays`（54/72）、`max_capacity_bits`（280/368）、`payload_bits`（实际占用，含模式/长度头）。上述百度网址实际占123bit，其他网址以对应响应为准。

响应头包含 `Cache-Control: no-store`、`X-Code-Mode`（编码模式）、`X-Code-Style`、`X-Image-Size`，以及 `X-Code-Profile`、`X-Code-Rays`、`X-Code-Capacity-Bits`、`X-Code-Payload-Bits`。客户端同时判断 HTTP 状态和 JSON `code`，不能仅看 HTTP 是否可连接。

## 失败响应

```json
{"code":40000,"data":null,"msg":"具体错误原因"}
```

| HTTP 状态 | 说明 |
| --- | --- |
| 400 | 头像格式不支持、损坏或像素过大 |
| 413 | 上传头像超过 8 MiB |
| 422 | 缺参数、参数无效、目标网址不符合原校验，或编码容量不足 |
| 404 / 405 | 不存在的路由 / 不支持的方法 |
| 500 | 内部处理失败；详情查看服务控制台 |
| 503 | 当前进程的渲染槽已满，请稍后重试 |

## 健康检查

`GET /health` 返回：

```json
{"code":200,"data":{"status":"ok","profile":"v54","default_size":2048,"default_avatar_ratio":0.44,"supported_profiles":["v54-d35-e17","v72-d46-e20-sample"],"selection":"auto","max_capacity_bits":368},"msg":"服务正常"}
```

## HTML 页面

服务成功启动后自动在默认浏览器打开 `/`。页面只填写网址和可选头像，已删除高级参数区域；请求只携带 `url` 和非空 `avatar`，由服务端使用当前默认值 lines、0.44、size2048、PNG。上表可选参数仍供其他API客户端使用。

不要双击 `index.html` 后在 `file://` 来源调用 API。该页面会提示正确打开方式；服务没有为 `Origin: null` 放开 CORS。所有图片预览为本地上传或 API 返回的 PNG，不在页面中打开目标网址。浏览器剪贴板不可用时使用手动复制。

网址超出容量时，HTTP422保持不变，`msg` 例如：“网址超出编码容量：当前需要 431 比特，最大支持 368 比特，请缩短网址。”数值由本次编码模式实际计算，不能简单按字符数乘8。55字符样例 `https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` 为360bit，可以使用72；不保证任意55字符都能容纳。

## 部署配置与安全

```powershell
.\sunflower-api.exe --host 127.0.0.1 --port 8998
.\sunflower-api.exe --host 0.0.0.0 --port 8998
.\sunflower-api.exe --version
.\sunflower-api.exe --no-browser
```

也可使用 `HOST` / `PORT` 环境变量。默认打开当前监听地址的页面，默认配置即 `http://127.0.0.1:8998/`；修改端口时相应使用该端口。单进程运行，不支持 reload 或多 worker。关闭控制台或按 Ctrl+C 停止服务。

## 启动授权策略

程序在启动 API 前使用内置卡密，向固定 HTTPS 授权接口发送一次JSON POST；不把卡密暴露给页面，不要求用户重复输入。`--help` / `--version` 不进行授权检查。

- HTTP请求成功且返回明确未授权：仅提示“授权已过期”，退出码1；不会监听端口或自动打开浏览器。
- 授权有效：无授权提示，正常启动。
- 网络、DNS、TLS、超时、非2xx、重定向、损坏或未知响应：按指定策略静默允许启动；最多等待4秒，无自动重试，不跟随重定向，不关闭TLS校验。

该策略仅在启动时检查，不逐次生成请求检查，不依据本机时间自行解释远端expire_time，也不在运行中因迟到响应关闭服务。因为网络失败允许启动，不能把它视为不可绕过的联网授权机制。API本身仍不接收JWT或卡密参数。

仅在确有需要时监听 0.0.0.0；公网部署应另加反向代理鉴权、TLS、请求体限制、限流及防火墙。此包没有费用扣除或身份校验，不能把它误当 Go 论坛的鉴权接口。网页使用同源请求，不开放任意跨域来源。

本次为跨平台源码包，原EXE构建信息仅供追溯；需要在相应系统重新打包才获得新版独立可执行文件，不能直接把旧Windows EXE复制到Linux。单文件运行需可写TEMP；冻结不代表加密或不可反编译。本版没有重新交付EXE或Linux二进制。
