chore: add production docker smoke check
This commit is contained in:
1
RULES.md
1
RULES.md
@@ -24,6 +24,7 @@
|
||||
- 服务器目录:`/opt/skg-marketing-studio`
|
||||
- 生产启动:`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build`
|
||||
- 生产架构:`web` 容器用 Nginx 承载 Next 静态导出;`/login/`、`/_next/`、`/assets/`、`/skg-logo-black.svg`、`/oasis-source/` 等登录页必需静态资源公开访问;未登录访问工作台跳转 `/login/`,`/api/` 通过 Nginx `auth_request` 校验 FastAPI 会话 Cookie 后反代到 `skg-marketing-api:4291`;Traefik 通过 `coolify` 外部网络接入 80/443
|
||||
- Web 验收必须以生产 Docker 形态为准:前端是 `next export` 静态产物 + Nginx,不是 `next dev` / `next start`。任何 Web 改动部署后必须运行 `./scripts/verify-prod-docker.sh`,确认 `/login/`、`/_next/`、`/api/health`、本地 API 地址泄漏和 API 镜像 `.env` 污染检查通过;不能只用本地 `npm run build` 作为上线依据。
|
||||
- 持久化目录:服务器 `./data/jobs` 挂载到后端 `/data/jobs`;全局资源中心持久化在 `./data/asset_library`、`./data/prompt_library` 和 `./data/_trash`
|
||||
- TikTok 下载登录态:公开视频默认不带 cookies 直接下载,生产环境变量必须显式保持 `YTDLP_COOKIES_FILE=`、`YTDLP_COOKIES_FROM_BROWSER=` 为空,防止容器读取不存在的浏览器 cookies。只有 TikTok 明确要求登录态时,才使用服务器私有 cookies 文件 `./secrets/tiktok_cookies.txt` 挂载到 API 容器 `/run/secrets/tiktok_cookies.txt` 并配置 `YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt`;`yt-dlp` 会在任务结束时回写 cookies,因此不要把该挂载设为只读;不要使用云端浏览器读取方案,也不要把 cookies 入库。生产容器严禁使用 `YTDLP_COOKIES_FROM_BROWSER=chrome`。
|
||||
- 登录凭证:用户名写下方快捷登录;密码明文备份只放服务器 `/root/skg-marketing-studio-login.txt`,生产环境变量 `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET` 只放服务器 `deploy/.env.production`
|
||||
|
||||
@@ -541,7 +541,7 @@
|
||||
<tr>
|
||||
<td>生产部署</td>
|
||||
<td><code>docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build</code></td>
|
||||
<td>服务器目录为 <code>/opt/skg-marketing-studio</code>;后端任务文件挂载到 <code>./data/jobs</code>,全局资源中心挂载到 <code>./data/asset_library</code>、<code>./data/prompt_library</code> 和 <code>./data/_trash</code>,真实 Key 只放服务器 <code>deploy/.env.production</code>。</td>
|
||||
<td>服务器目录为 <code>/opt/skg-marketing-studio</code>;后端任务文件挂载到 <code>./data/jobs</code>,全局资源中心挂载到 <code>./data/asset_library</code>、<code>./data/prompt_library</code> 和 <code>./data/_trash</code>,真实 Key 只放服务器 <code>deploy/.env.production</code>。Web 上线验收必须按 Docker 静态形态跑 <code>./scripts/verify-prod-docker.sh</code>,不能只用本地 <code>npm run build</code> 替代。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>前端开发服务</td>
|
||||
@@ -1108,6 +1108,18 @@ ProductRefStateItem {
|
||||
<h2>变更记录</h2>
|
||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||
<div class="changelog">
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-19 · 固化生产 Docker Web 验收</h3>
|
||||
<span class="tag red">Deploy</span>
|
||||
<span class="tag violet">UI</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p><strong>问题:</strong>线上 Web 不是开发态 Next 服务,而是 Docker 内 Nginx 承载的 <code>next export</code> 静态产物。只在本地跑 <code>npm run build</code> 或 dev server,无法覆盖 Nginx 登录跳转、<code>/api</code> 反代、静态资源路径、生产构建参数和镜像内环境污染。</p>
|
||||
<p><strong>改动:</strong>新增 <code>scripts/verify-prod-docker.sh</code>,直接在 VPS 的 <code>skg-marketing-web</code> / <code>skg-marketing-api</code> 容器内检查路由状态、静态包是否残留本地 API 地址、API health、<code>/app/.env</code> 是否泄漏,以及 yt-dlp cookies 参数是否被开发环境污染。<code>RULES.md</code> 明确 Web 改动部署后必须跑该脚本。</p>
|
||||
<p><strong>影响:</strong>后续前端上线验收以生产 Docker 形态为准;本地 dev 只用于开发预览,不能作为生产适配结论。</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-19 · 修正生产 TikTok 下载 cookies 污染</h3>
|
||||
|
||||
29
scripts/verify-prod-docker.sh
Executable file
29
scripts/verify-prod-docker.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${1:-root@76.13.31.179}"
|
||||
APP_DIR="${APP_DIR:-/opt/skg-marketing-studio}"
|
||||
|
||||
ssh "$HOST" "cd '$APP_DIR' && \
|
||||
docker ps --filter name=skg-marketing --format '{{.Names}} {{.Status}}' && \
|
||||
docker exec skg-marketing-web sh -lc '
|
||||
set -e
|
||||
echo web:no_local_api_refs
|
||||
if grep -Rao \"http://localhost:4291\\|http://127.0.0.1:4291\\|localhost:4290\\|127.0.0.1:4290\" /usr/share/nginx/html/_next/static 2>/dev/null | head -1 | grep -q .; then
|
||||
echo \"ERROR: local API/dev URL leaked into web static bundle\" >&2
|
||||
exit 1
|
||||
fi
|
||||
for p in / /login/ /_next/does-not-exist.js /api/health; do
|
||||
code=\$(curl -sS -o /tmp/skg-smoke.out -w \"%{http_code}\" \"http://127.0.0.1\$p\")
|
||||
case \"\$p:\$code\" in
|
||||
/:302|/login/:200|/_next/does-not-exist.js:404|/api/health:401) echo \"web:\$p \$code\" ;;
|
||||
*) echo \"ERROR: unexpected web route status \$p \$code\" >&2; head -c 200 /tmp/skg-smoke.out >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
' && \
|
||||
docker exec skg-marketing-api sh -lc '
|
||||
set -e
|
||||
test ! -f /app/.env || { echo \"ERROR: /app/.env leaked into API image\" >&2; exit 1; }
|
||||
python -c \"import main; assert main.YTDLP_COOKIES_FROM_BROWSER == \\\"\\\", main.YTDLP_COOKIES_FROM_BROWSER; print(\\\"api:ytdlp_cookie_args\\\", main.ytdlp_cookie_args())\"
|
||||
curl -sS http://127.0.0.1:4291/health | python -c \"import json,sys; d=json.load(sys.stdin); assert d[\\\"ok\\\"] is True; assert d[\\\"auth_configured\\\"] is True; print(\\\"api:health ok\\\")\"
|
||||
'"
|
||||
Reference in New Issue
Block a user