Files
writer-work-flow/DEPLOYMENT.md

24 KiB
Raw Blame History

部署指南DEPLOYMENT

本文是把「AI 辅助中文网文写作工作流」部署到服务器的实操指南。 所有命令/端口/服务名均以仓库当前 docker-compose.yml、两份 Dockerfile.env.examplepackages/config/ww_config/settings.pyapps/api/ww_api/main.py 为准。遇到与你手上 compose 不一致处,一律以实际 docker-compose.yml 为准。

⚠️ 重要前提:本产品当前是「单用户原型」,没有任何认证 / 多租户users/owner_id 是桩)。这直接决定了安全部署的红线——见 §6 安全红线不要在没有网络层保护的情况下把它裸暴露到公网。


1. 架构与前置条件

1.1 三个组件

部署单元由三个容器组成(服务名即 docker-compose.yml 中的 key

服务名 角色 镜像来源 容器端口 compose 发布端口 健康检查
pg Postgres 16 数据库(唯一真源) postgres:16-alpine(拉取) 5432 5432:5432 pg_isready -U writerinterval 5s / timeout 3s / retries 10
api FastAPI 后端async + SSE + LangGraph 编排) apps/api/Dockerfile 构建,构建上下文 = 仓库根 . 8000 8000:8000 无(见 §1.5 缺口)
web Next.js 前端standalone 产物) apps/web/Dockerfile 构建,构建上下文 = ./apps/web 3000 3000:3000 无(见 §1.5 缺口)

依赖关系compose depends_on

  • api 依赖 pg,且等 pg 健康后才起(condition: service_healthy)。
  • web 依赖 api,但只是启动顺序(无 condition——web 可能先于 api 就绪,属可容忍的运行期竞态。

数据持久化:命名卷 pgdata 挂到 pg/var/lib/postgresql/data备份/迁移数据 = 备份这个卷(见 §7

1.2 镜像怎么来

两个应用镜像都从源码 Dockerfile 本地构建(仓库未发布预构建镜像):

  • apps/api/Dockerfilepython:3.12-slim + 从 ghcr.io/astral-sh/uv 拷入 uvCOPY pyproject.toml uv.lock* / packages / apps/apiuv sync --no-dev --frozen(失败回退非 frozenCMD uv run uvicorn ww_api.main:app --host 0.0.0.0 --port 8000
  • apps/web/Dockerfile多阶段。build 阶段 node:22-slim + corepack(pnpm)pnpm install --frozen-lockfilepnpm buildNext output: "standalone",见 apps/web/next.config.mjsrun 阶段只拷 .next/standalone + .next/static + publicCMD node server.js

web 镜像在构建期消费已提交的 OpenAPI TS 客户端 apps/web/lib/api/schema.d.ts(该文件在 git 里,仅 openapi.json 被 gitignore。因此 web 构建不需要联网跑 pnpm gen:api:后端 schema 改动后,必须先 cd apps/web && pnpm gen:api 重生成并提交 schema.d.ts,再构建 web 镜像,否则前后端契约漂移。

1.3 需要的环境变量(逐项)

变量来源 = packages/config/ww_config/settings.py(后端)+ apps/web/lib/api/config.ts(前端)。

后端api 容器):

变量 是否必填 默认值 说明
DATABASE_URL 建议显式设 postgresql+asyncpg://writer:writer@localhost:5432/writer 异步连接串asyncpg。运行时 + alembic upgrade head 都读它(packages/db/migrations/env.py 用它建 async engine 跑迁移)。容器内主机名用 compose 服务名 pg
DATABASE_URL_SYNC 建议显式设 postgresql+psycopg://writer:writer@localhost:5432/writer 同步连接串psycopg.env.example 与 compose 均定义;供同步工具链使用。
APP_ENV dev 环境名。生产设 prod
LOG_JSON false 结构化日志是否输出 JSON。生产设 truestructlog JSON便于采集
CREDENTIAL_ENC_KEY 必填 空(SecretStr("") provider 凭据对称加密密钥Fernet, urlsafe-base64 32Bapi 启动即校验main._lifespan_fernet(...)):缺失/非法 → 进程直接启动失败,不带病接流量。生产必须用新密钥,见 §2.3。
CORS_ORIGINS 生产强烈建议 ["http://localhost:3000","http://127.0.0.1:3000"] 浏览器跨源白名单,逗号分隔覆盖。启动断言不含通配 *(与 allow_credentials=True 不兼容,main.create_appassert "*" not in cors_origins)。生产填前端真实来源,如 https://writer.example.com

前端web 容器):

变量 是否必填 默认值(config.ts 回退) 说明
NEXT_PUBLIC_API_BASE 生产建议 http://localhost:8000 浏览器端访问 API 的基址。⚠️ Next.js 的 NEXT_PUBLIC_*构建期内联进客户端 bundle——只在运行期(compose environment)设它不会改已构建产物(见 §1.5 缺口)。生产需在构建期注入前端可达的公网 API 地址(通常是反代后的 https://writer.example.com/api 之类)。
API_BASE_INTERNAL 容器编排建议 回退到 NEXT_PUBLIC_API_BASE **服务端组件(RSC)**取数用的内网基址。容器内应设为 http://api:8000compose 服务名)。当前 compose 未设它(见 §1.5 缺口RSC 会回退到 localhost:8000 而打不到 api 容器。

1.4 LLM provider key 不进环境变量

不要把任何 LLM 供应商密钥DeepSeek/Kimi/OpenAI/Anthropic/Qwen/GLM/Gemini 等)写进 env 或 compose。 本产品的 provider 凭据是部署后在应用「设置」页录入,由后端用 CREDENTIAL_ENC_KEYFernet 加密后存进 Postgresprovider_credentials 表 / settings_providers 路由)。 Kimi Code 走 OAuth 订阅授权(kimi_oauth 路由),同样落库。

推论:CREDENTIAL_ENC_KEY 是解开所有已存凭据的唯一钥匙——丢了它 = 库里所有 provider 凭据不可解密,只能重新录入。务必备份保管(见 §2.3 / §6

1.5 已发现的部署面缺口(部署前需知)

以下是审阅 Dockerfile/compose 时发现的、影响「照抄即部署」的点,本文各节给出规避办法:

  1. api 镜像未拷 alembic.iniapps/api/DockerfileCOPY pyproject.toml / packages / apps/api没有 COPY alembic.ini(它在仓库根)。因此 docker compose run --rm api uv run alembic upgrade head 会因找不到 alembic.ini 而失败。规避:从宿主机 checkout 跑迁移§2.4 方式一,当前即可用),或给 Dockerfile 补一行 COPY alembic.ini ./§2.4 方式二)。
  2. compose 的 api 服务未提供 CREDENTIAL_ENC_KEY(也没 env_filebase compose 里 api.environment 只有 DATABASE_URL/DATABASE_URL_SYNC/APP_ENV=dev。由于该密钥启动即校验,base compose 直接 up 会让 api 启动失败。生产必须补齐§2.2 覆盖文件)。
  3. compose 的 web 服务未设 API_BASE_INTERNALRSC 服务端取数会回退到 localhost:8000,在容器里打不到 api。需在覆盖文件设 API_BASE_INTERNAL=http://api:8000§2.2)。
  4. NEXT_PUBLIC_API_BASE 是构建期内联web Dockerfile 未声明该 build ARG浏览器 bundle 里的 API 基址会固定为 http://localhost:8000。生产要改,需在构建阶段注入§3 反代方案里说明取舍)。
  5. api/web 无 HEALTHCHECK:两个 Dockerfile 和 compose 的 api/web 都没有健康检查(只有 pg 有)。编排/反代的存活探测需自行加health 端点已就绪,见 §7.1)。

以上缺口不阻断部署,但决定了「不能照抄 base compose 一把梭」。生产用 §2.2 的覆盖文件补齐即可。


2. 方案 A单机 docker compose推荐起步

适合:单台服务器 / VPS起步验证。前提服务器已装 Docker含 Docker Compose v2 插件)。

2.1 装 Docker + 拉代码

# 服务器上(以 Ubuntu 为例)安装 Docker Engine + compose 插件
curl -fsSL https://get.docker.com | sh
docker --version && docker compose version   # 确认 compose v2 可用

# 拉代码
git clone <你的仓库地址> writer-work-flow
cd writer-work-flow
git checkout main       # 或你的发布分支

2.2 准备生产配置(覆盖 base compose 的 dev 缺省)

base docker-compose.yml开发导向api 用 APP_ENV=dev、缺 CREDENTIAL_ENC_KEY、 web 缺 API_BASE_INTERNAL、pg 端口对外发布)。生产用一个覆盖文件 docker-compose.prod.yml 补齐,不改 base 文件:

# docker-compose.prod.yml —— 与 base 叠加docker compose -f docker-compose.yml -f docker-compose.prod.yml ...
services:
  pg:
    # 不把 5432 暴露到公网:仅绑本机回环(供宿主机跑迁移用),或整段删掉只走 compose 内网
    ports:
      - "127.0.0.1:5432:5432"

  api:
    environment:
      DATABASE_URL: postgresql+asyncpg://writer:${PG_PASSWORD}@pg:5432/writer
      DATABASE_URL_SYNC: postgresql+psycopg://writer:${PG_PASSWORD}@pg:5432/writer
      APP_ENV: prod
      LOG_JSON: "true"
      CREDENTIAL_ENC_KEY: ${CREDENTIAL_ENC_KEY}      # 见 §2.3 生成
      CORS_ORIGINS: "https://writer.example.com"      # 前端真实来源,逗号分隔;不得含 '*'
    # 可选:补健康检查(镜像里已装 uv/python可用 python 探 /health
    # 若加了反代做存活探测,这里可省

  web:
    environment:
      NEXT_PUBLIC_API_BASE: https://writer.example.com   # 浏览器可达的公网 API经反代
      API_BASE_INTERNAL: http://api:8000                 # RSC 服务端内网直连 api 容器

同时准备 pg 的强口令与密钥(不要提交进仓库),可放在同目录 .envcompose 会自动读取用于 ${...} 插值):

# .envcompose 变量插值用chmod 600勿入库
PG_PASSWORD=<强随机口令>
CREDENTIAL_ENC_KEY=<§2.3 生成的 Fernet key>

⚠️ base compose 里 pg 的 POSTGRES_PASSWORD: writer 是硬编码弱口令。生产请在覆盖文件里 用 POSTGRES_PASSWORD: ${PG_PASSWORD} 覆盖 pg并让 DATABASE_URL* 用同一口令 (上例已用 ${PG_PASSWORD})。若你保持 base 的 writer 口令,务必确保 pg 不可从公网访问§6

2.3 生成并妥善保管 CREDENTIAL_ENC_KEY

⚠️ 这是最关键的一步。用 Fernet 生成一把新密钥(.env.example 里那把是开发占位,生产禁用

# 任一装了 cryptography 的环境(也可用仓库 uv 环境)
uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# 或纯 pythonpython -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

把输出的一行密钥填进 §2.2 的 .envCREDENTIAL_ENC_KEY,并离线备份(密码管理器/密钥库)。

丢失 = 灾难:所有已录入的 provider 凭据(存在 Postgres 里、用它加密)将永久不可解密 只能清空后重新录入。轮换密钥需先用旧钥解密全部凭据、再用新钥重加密(当前无自动轮换脚本, 见 §6切勿复用 .env.example 里的占位 key那等于凭据未加密。

2.4 构建镜像 + 跑迁移 + 起服务

# 在仓库根,带上覆盖文件
export COMPOSE="docker compose -f docker-compose.yml -f docker-compose.prod.yml"

# 1) 构建两个应用镜像
$COMPOSE build

# 2) 先起数据库
$COMPOSE up -d pg
# 等 pg 健康compose 已配 healthcheck
$COMPOSE ps    # 看 pg 是否 healthy

# 3) 跑数据库迁移(含 LangGraph checkpointer 建表)—— 见下方两种方式

迁移的关键事实alembic upgrade head 一条命令会做两件事—— (a) 建/升级 16 张业务表;(b) 迁移 d3e4f5a6b7c8_langgraph_checkpoint_setup 会调用 LangGraph 的 checkpointer.setup()checkpoints/checkpoint_blobs/checkpoint_writes/checkpoint_migrations 四张检查点表。checkpointer 的建表只在迁移里发生,绝不在应用运行时(符合 CLAUDE.md「LangGraph」纪律。 所以部署时必须先把迁移升到 head应用才能正常写章/审稿/续跑

因 api 镜像未随包 alembic.ini§1.5 缺口 1有两种可用方式

方式一(当前即可用,无需改 Dockerfile——从宿主机 checkout 跑迁移:

# 宿主机需装 uvcurl -LsSf https://astral.sh/uv/install.sh | sh; export PATH="$HOME/.local/bin:$PATH"
cd writer-work-flow
uv sync
# 指向 compose 里 pg 暴露到 127.0.0.1:5432 的端口§2.2 覆盖文件已绑本机)
DATABASE_URL="postgresql+asyncpg://writer:${PG_PASSWORD}@127.0.0.1:5432/writer" \
DATABASE_URL_SYNC="postgresql+psycopg://writer:${PG_PASSWORD}@127.0.0.1:5432/writer" \
CREDENTIAL_ENC_KEY="${CREDENTIAL_ENC_KEY}" \
  uv run alembic upgrade head
uv run alembic check    # 可选:确认无漂移

方式二(更干净,需给 Dockerfile 补一行)——在 api 容器里跑迁移: 先在 apps/api/DockerfileCOPY alembic.ini ./(放在 COPY apps/api ./apps/api 之后), $COMPOSE build api 重建后:

$COMPOSE run --rm api uv run alembic upgrade head

这一步需要 parent/维护者决定是否接受改 Dockerfile。改前方式二不可用。

4) 起全部服务:

$COMPOSE up -d
$COMPOSE ps       # api/web 应为 running注意它们无 healthcheck看不到 healthy

2.5 健康检查

# API根 + health 端点(均返回 200 JSON
curl -f http://127.0.0.1:8000/          # {"service":"ww-api","status":"ok"}
curl -f http://127.0.0.1:8000/health    # {"status":"ok"}

# Web首页
curl -fI http://127.0.0.1:3000/

# 看日志api 启动会校验 CREDENTIAL_ENC_KEY、seed 单用户桩、reap 僵尸 job
$COMPOSE logs -f api

若 api 启动即退出,最常见原因是 CREDENTIAL_ENC_KEY 缺失/非法§1.5 缺口 2 / §2.3)—— 日志会显示 CredentialKeyError 相关堆栈。

部署完成后,打开前端「设置」页录入 LLM provider 凭据§1.4),产品才能真正调用模型。


3. 反向代理 + HTTPS

生产应在应用前放一层反代做 TLS 终止、单一入口、并(配合 §6做访问控制。 拓扑:浏览器 → 反代(443) → web:3000(页面/RSCapi:8000/api 或独立子域)。

注意 NEXT_PUBLIC_API_BASE 的构建期内联§1.5 缺口 4浏览器直接打 API 的地址在构建 web 镜像时就定死了。 若走「同源 /api 前缀反代」,需让前端构建时 NEXT_PUBLIC_API_BASE 指向该前缀(当前 web Dockerfile 未暴露该 build ARG需按缺口 4 处理); 若走「api 独立子域」(如 api.example.com),同理需在构建期注入。

3.1 Caddy推荐自动证书

# Caddyfile —— 自动申请/续签 Let's Encrypt 证书
writer.example.com {
    # 后端 API同源 /api/* 反代到 api 容器(去掉 /api 前缀按你的前端约定调整)
    handle_path /api/* {
        reverse_proxy api:8000
    }
    # 其余流量到 Next 前端
    reverse_proxy web:3000

    # SSE 长连接:关掉缓冲,避免审稿/写章流被缓存卡住
    reverse_proxy api:8000 {
        flush_interval -1
    }
}

将 Caddy 也放进 compose与 api/web 同网络,用服务名访问),暴露 80/443。

3.2 Nginx手动 / certbot 证书)

server {
    listen 443 ssl;
    server_name writer.example.com;

    ssl_certificate     /etc/letsencrypt/live/writer.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/writer.example.com/privkey.pem;

    # 前端
    location / {
        proxy_pass http://web:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 后端 API含 SSE写章/四审流)
    location /api/ {
        proxy_pass http://api:8000/;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Connection "";      # SSE保持长连接
        proxy_buffering off;                 # SSE关缓冲事件即时下发
        proxy_read_timeout 3600s;            # 长任务/流式容忍
    }
}

无论 Caddy/NginxSSE 必须关代理缓冲(写章、四审都是事件流),否则前端进度条会卡住。


4. 方案 BPaaS / 托管平台

容器化已就绪(两个 Dockerfile + standalone 前端),可托管到 PaaS。共性用托管 Postgres 在平台面板设 env尤其 CREDENTIAL_ENC_KEYCORS_ORIGINSDATABASE_URL* 部署后照 §2.4 跑一次 alembic upgrade head。provider key 仍在应用设置页录入(不进平台 env

4.1 Fly.io

  • 两个 appfly launch 分别为 apiapps/api/Dockerfile,构建上下文=仓库根)与 webapps/web/Dockerfile,上下文=apps/web)。
  • Postgres 用 fly postgres create(或外部托管 PGDATABASE_URL/DATABASE_URL_SYNC 设成其连接串(注意驱动前缀 +asyncpg/+psycopgFly 给的是原生 postgres://,需自行加前缀)。
  • fly secrets set CREDENTIAL_ENC_KEY=... CORS_ORIGINS=... PG_PASSWORD=...
  • 迁移:fly ssh console 进 api 机器跑 alembic upgrade head(同样受 §1.5 缺口 1 影响——需镜像含 alembic.ini,或用 fly ssh + release_command。可用 Fly [deploy] release_command 在每次发布前自动跑迁移(需 alembic.ini 在镜像里)。
  • web 的 NEXT_PUBLIC_API_BASE构建期注入Fly build args / Dockerfile ARG缺口 4
  • api 内部端口 8000、web 内部端口 3000fly.tomlinternal_port 对应。

4.2 Render

  • 两个 Web ServiceDocker 运行时),分别指向两个 Dockerfileweb 的 Docker build context 设 apps/webapi 设仓库根。
  • Postgres 用 Render Managed Postgres把内部连接串填进 api 的 DATABASE_URL*(加 +asyncpg/+psycopg 前缀)。
  • 在 api service 的 Environment 填 CREDENTIAL_ENC_KEY/CORS_ORIGINS/APP_ENV=prod/LOG_JSON=true
  • 迁移:用 Render 的 Pre-Deploy Commanduv run alembic upgrade head(需镜像含 alembic.ini,缺口 1
  • web 与 api 的互通用 Render 的内部地址;API_BASE_INTERNAL 指向 api 内部 URLNEXT_PUBLIC_API_BASE(构建期)指向 api 公网 URL。

两个平台的共同注意点:全部继承 §1.5 的缺口(尤其 alembic.ini 未随镜像、NEXT_PUBLIC 构建期内联)。 上线前按缺口处理,或接受「迁移只能从含源码的环境跑」。


5. 迁移与升级流程

5.1 常规升级

export COMPOSE="docker compose -f docker-compose.yml -f docker-compose.prod.yml"

git pull                      # 拉新代码(含新迁移 / schema.d.ts
$COMPOSE build                # 重建 api/web 镜像
# 先升迁移到 head含新增业务表 + 可能的 checkpointer 变更),再滚动重启
#   —— 用 §2.4 的方式一或方式二
uv run alembic upgrade head   # 方式一(宿主机 checkout指向 pg
$COMPOSE up -d                # 用新镜像重启 api/webpg 不动)

# 冒烟
curl -f http://127.0.0.1:8000/health && curl -fI http://127.0.0.1:3000/

顺序要点:先迁移、后换应用镜像——新代码通常假设 schema 已到 head。若后端 schema 有变, 记得升级前已在开发侧 pnpm gen:api 重生成并提交了 apps/web/lib/api/schema.d.ts§1.2 否则 web 构建/类型会漂移。

5.2 回滚

# 应用回滚:切回上一个 tag/commit 重建重启
git checkout <上一个可用 tag>
$COMPOSE build && $COMPOSE up -d

# 数据库回滚(谨慎):按需降级一版
uv run alembic downgrade -1

⚠️ DB 降级可能丢列/丢表数据,且 d3e4f5a6b7c8downgrade()DROP 四张 checkpoint 表 (在跑的写章/链任务会丢控制流状态)。回滚前先备份§7.3)。业务表回滚同理不可逆,优先「向前修复」。


6. 安全红线(单用户原型的硬约束)

⚠️ 本产品没有认证、没有多租户、没有登录users/owner_id 是桩,见 CLAUDE.md「Prototype scope」。 任何拿到 URL 的人都能读写全部小说数据、录入/触发用花你钱的 LLM 凭据。因此:

  1. 绝不裸暴露到公网。 至少满足其一:
    • 只在 VPN / 内网 / SSH 隧道内访问;
    • 反代前加 IP 白名单HTTP Basic AuthCaddy basic_auth / Nginx auth_basic
    • 先补一层认证再上公网(当前无,属产品级改造)。
  2. Postgres 不对公网开放。 base compose 把 pg 发布到 5432:5432(全网卡)。生产必须改成 仅回环 127.0.0.1:5432:5432§2.2)或删掉端口映射只走 compose 内网。改掉 base 里的弱口令 writer/writer
  3. CORS 显式白名单,禁通配。 CORS_ORIGINS 必须是精确来源;后端已在启动断言不含 * allow_credentials=True* 会被浏览器拒且语义危险)。别为图省事放宽。
  4. CREDENTIAL_ENC_KEY 的密钥管理与轮换:
    • 用 §2.3 生成的 Fernet key绝不用 .env.example 占位值;
    • 存进密钥库 / 平台 secrets.env chmod 600不入库
    • 备份它(丢了 = 所有 provider 凭据不可解密);
    • 轮换需「旧钥解密全部凭据 → 新钥重加密」(当前无自动脚本,轮换前规划停机 + 备份)。
  5. provider key 只走应用设置页(加密落库),永不写进 env/compose/日志§1.4)。
  6. 日志脱敏已内建structlog 结构化日志按 CLAUDE.md「Logging」纪律不记全 prompt/正文(记长度/哈希)、 不记 API key。保持这一约束,别在自建反代/中间件里回退成明文全量日志。
  7. HTTPS 强制§3别让凭据/正文走明文。

7. 可观测性与运维

7.1 健康端点

  • GET /{"service":"ww-api","status":"ok"}
  • GET /health{"status":"ok"}

两者均无鉴权、返回 200可作反代/编排的存活探测目标。 提醒api/web 的 Dockerfile 与 compose 未内置 HEALTHCHECK§1.5 缺口 5——存活探测靠反代/平台配。)

7.2 结构化日志

  • 后端 structlog生产设 LOG_JSON=true 输出 JSON便于日志系统采集/检索。
  • 每个请求带 request_id(贯穿 assemble→write→四审→accept 一条链),也进错误响应信封—— 用户报错时可用它端到端 grep。
  • 采集:docker compose logs,或把容器 stdout 接到你的日志栈Loki/ELK/云日志)。

7.3 备份 Postgres含凭据库

数据全在 pgdata 卷 + Postgres 里,包含加密后的 provider 凭据。定期逻辑备份:

export COMPOSE="docker compose -f docker-compose.yml -f docker-compose.prod.yml"

# 逻辑备份(推荐)
$COMPOSE exec pg pg_dump -U writer -d writer -Fc > backup_$(date +%F).dump

# 恢复
$COMPOSE exec -T pg pg_restore -U writer -d writer --clean < backup_YYYY-MM-DD.dump

备份数据库还不够CREDENTIAL_ENC_KEY单独异地备份——DB 里的凭据是密文, 没有这把钥匙备份也解不出§2.3 / §6。两者分开保管、都要备。

7.4 成本账本LLM 调用)

每次 LLM 调用都按 ARCHITECTURE §4.8 的字段集记结构化日志provider/model/tier/token 数/耗时/request_id 等), 喂成本账本。运维时用这些日志:

  • 核对每章/每次生成的 token 与花费;
  • 排查慢/失败调用(网关的路由/回退/熔断也在此可见);
  • request_id 关联「一次写章」的整条链。

采集方式同 §7.2JSON 日志接入日志栈后按字段聚合)。


附:核心事实速查

  • 服务/端口pg(5432, 唯一健康检查 pg_isready -U writer) · api(8000) · web(3000)。
  • 镜像均本地构建api 上下文=仓库根web 上下文=apps/webstandalone
  • 必填 envCREDENTIAL_ENC_KEY(启动即校验,缺失/非法则 api 起不来)。
  • 迁移alembic upgrade head 同时建业务表 + LangGraph checkpointer 表checkpointer 只在迁移建表,不在运行时。
  • provider key:不进 env部署后在设置页录入、加密落库。
  • 已知缺口§1.5api 镜像缺 alembic.inicompose apiCREDENTIAL_ENC_KEYwebAPI_BASE_INTERNALNEXT_PUBLIC_API_BASE 构建期内联api/web 无 HEALTHCHECK。
  • 红线:单用户无认证 → 不裸暴露公网pg 不对外CORS 禁 *CREDENTIAL_ENC_KEY 备份+轮换。