封面 AI 助手 开发文档
这个项目是什么
封面 AI 助手(cover-ai)是给封面/配图场景用的 AI 生图与修图服务:文字描述出图、上传参考图改图、本地素材库管理。实现上刻意做到极简——零依赖单文件 Node 服务(server.js)+ 一个 HTML 单页(ui.html),Docker 一条命令跑起来,镜像只有几十 MB。它还能被「米图设计」(快图设计/kuaitu-editor)的素材面板直接调用,作为编辑器的 AI 配图后端。
关键设计:AI 接口不在服务端配置。使用者在页面顶部「API 设置」里填自己的接口地址 + API Key + 模型,Key 只存浏览器 localStorage,服务端只做转发(不写日志、不落盘、不进镜像)。
技术栈与架构
| 层 | 实现 |
|---|---|
| 运行时 | Node 20(node:20-alpine 镜像),零 npm 依赖——只用内置 http、fetch,不装 express、不带 node_modules |
| 服务端 | server.js 单文件:HTTP 路由 + 静态页 + AI 请求转发 + 素材库文件存储 |
| 前端 | ui.html 单页(内联样式与脚本),三个页签:AI 生图 / AI 修图 / 素材库 |
| 存储 | 宿主目录挂载 /data:gallery/ 存生成图、gallery.json 存元数据、hub.json 存机器码 |
| 授权中心 | 复用自建 CardAuth 的「免费工具遥测」三接口:版本推送、公告、安装/在线统计 |
数据流:浏览器把 x-ai-base / x-ai-key / x-ai-model 放在请求头发给本服务 → 服务端转发到使用者配置的 AI 接口(OpenAI 兼容:/images/generations、/chat/completions、/images/edits)→ 生成图写入 /data/gallery/ → 前端从 /img/:name 取图。同源问题的解法:米图设计(8126 端口)通过 /ai-img/* 反代本服务的 /img/*,避免画布 canvas 被跨域污染。
仓库结构
├── server.js # 零依赖 Node 服务(全部后端逻辑)
├── ui.html # 单页前端(生图/修图/素材库 + API 设置 + 授权中心 UI)
├── Dockerfile # node:20-alpine,COPY server.js ui.html,EXPOSE 3000
├── restart.sh # 生产重建脚本(从 CardAuth 库动态读授权 KEY 注入 env)
├── README.md
└── .gitignore
接口一览
GET / UI(单页)
GET /api/ai-config 默认接口地址/模型/注册链接(服务端 env 下发)
GET /api/health 健康检查(含 version 与 hub 状态)
GET /api/models 拉模型清单 要求头 x-ai-base + x-ai-key
POST /api/gen {prompt, size, model} → 生成 要求 x-ai-key
POST /api/redraw {image, prompt, size, model} → 重画 要求 x-ai-key
POST /api/edit {image, prompt} → 真·修图(images/edits)要求 x-ai-key
GET /api/gallery 历史图列表(基础功能,不要 Key)
DELETE /api/gallery/:name 删图
GET /img/:name 取图(供编辑器跨端口反代)
POST /api/hub/refresh 绕过节流强刷一次授权中心
Key 的处理约定:没带 Key 的 AI 请求一律 403 {needKey:true} 并附注册链接;未配置时 /api/models、/api/gen 等全部拒绝,只有素材库/健康检查可用。
部署与配置
docker pull gitea.88531.cn/jinghui/cover-ai:latest # 免登录拉已发布镜像
# 或本地构建
docker build -t cover-ai:1.0 .
docker run -d --name cover-ai --restart always --memory 384m -p 8128:3000 \
-v $PWD:/data \
-e PUBLIC_BASE=http://<本机地址>:8128 \
-e AI_BASE=https://ai.88531.cn/v1 \
-e AI_MODEL=mimo-v2.6-flash \
-e REGISTER_URL=https://ai.88531.cn/register \
-e VERSION=1.0.0 \
-e HUB_PROJECT_ID=<授权中心项目id> \
-e HUB_API_KEY=<项目 api_key> \
cover-ai:1.0
环境变量(server.js 中读取的真实键):
| 键 | 含义 | 默认/说明 |
|---|---|---|
PORT |
监听端口 | 3000 |
DATA |
数据目录 | /data(gallery/、hub.json 落这里,宿主挂载持久化) |
PUBLIC_BASE |
对外访问地址 | 用于下发给前端/链接拼接 |
AI_BASE / AI_MODEL |
前端「API 设置」表单的默认接口地址与默认模型 | 只是默认值,使用者可改 |
REGISTER_URL |
无 Key 时的引导注册链接 | — |
VERSION |
当前版本号(右上角徽章) | — |
HUB_HOST |
授权中心地址(CardAuth) | 不配 = unconfigured |
HUB_PROJECT_ID / HUB_API_KEY |
授权中心项目 id 与 API Key | 不配也能跑,只是没有更新提示和公告 |
HUB_DISABLED |
设 1 彻底关掉授权中心上报 |
— |
没有 OMNI_KEY 这类服务端密钥——Key 由每个使用者在页面里自己填。HUB_* 是授权中心对接(版本推送/公告/安装与在线统计),授权中心不可达不影响任何功能(失败开放)。
构建与发布
改了 server.js / ui.html 必须重新 docker build(两个文件是 COPY 进镜像的)。发布镜像到 Gitea 镜像仓库 gitea.88531.cn/jinghui/cover-ai:latest(同步推 1.0 标签)。发新版的顺序:先把新镜像推出去 → 再在 CardAuth 写 ca_project_versions(新版本 is_latest=1)→ 客户端 6 小时内(或重启后)看到提示;公告写 ca_announcements 且 status 必须 = 1(默认 0 是静默隐身)。
二次开发要点
- 刻意零依赖:加 npm 包前先想想——依赖会拖慢构建、撑大镜像;
http+fetch够用。 - 修图是「重画」不是像素级修:默认走「看图(
/chat/completions)→ 出描述 → 文生图」,看图失败自动降级纯文字重画。真·像素修图走/api/edit(images/edits),可用性取决于使用者填的接口/模型——通道不支持会明确报错(如旧 omniroute 的Image edit is not supported for built-in provider),额度不足报 402 并翻译成中文。 - 裁切在前端做:模型返回尺寸是近似的(要 720×1280 实际可能回 864×1152),用 canvas 居中裁切拿平台精确规格,不额外消耗算力。
- 中文提示词兜底:服务端固定追加「不要出现文字/水印」「主体占画面主要面积」等约束,减少脏图——改 prompt 拼接逻辑时保留这些约束。
- KEY 不能走明文通道:
restart.sh从 CardAuth 库按 HEX 读 KEY 注入 env(明文密钥经终端/文本通道会被自动打码成掩码,表现为 403「API密钥无效」),不要手工把ak_...写进命令。 - 机器码 13 位数字只存服务端
/data/hub.json,不下发浏览器、不在 UI 展示;hub.json已进.gitignore。 - 节流参数:更新检查 6 小时(失败 30 分钟重试)、心跳 30 分钟、公告 60 秒。
相关链接
- Gitea 仓库:http://127.0.0.1:3200/jinghui/cover-ai
- 在线体验(经米图设计编辑器右下角 ✨ 按钮打开):https://mitu.88531.cn
- 镜像仓库:
gitea.88531.cn/jinghui/cover-ai:latest