diff --git a/RULES.md b/RULES.md index dcfd514..9fb45ca 100644 --- a/RULES.md +++ b/RULES.md @@ -31,14 +31,15 @@ - 文档 / 解析:`docs/source-analysis.html`(项目内独立文档,不公开挂主应用路由) - 管理后台:待定 - 服务器目录:`/opt/skg-marketing-studio` -- 生产启动:`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build` +- 生产部署唯一入口:`./scripts/deploy-prod-safe.sh`(先在服务器备份 `deploy/.env.production`、`data/jobs`、资源库和 `secrets`,再用受保护 rsync 同步代码,最后 Docker 重建并运行 `verify-prod-docker.sh`) +- 生产容器重建命令:`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build`;只允许脚本内部或明确只重启容器时使用,不允许再用裸 `rsync --delete` 手动同步。 - 生产架构:`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` 作为上线依据。 - 当前音频解析:`https://ai.skg.com/azure/v1` 的 `gpt-4o-transcribe` 当前返回 `DeploymentNotFound`,且官方 Azure OpenAI transcription 路径探测也未返回可用部署;生产临时复制本地成功策略,直接使用容器内 `faster-whisper tiny.en` 真实转写,关闭 Gemini 多模态音频兜底。拿到真实 Azure ASR deployment 名后再恢复 `ASR_REMOTE_ENABLED=true`。 - 持久化目录:服务器 `./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` -- 手动 `rsync` 到服务器时必须排除本机开发文件和真实生产 env:`.git`、`.memory`、`.logs`、`.pids`、`data`、`jobs`、`secrets`、`api/.env`、`api/.env.local`、`api/.env.production`、`deploy/.env.production`、`web/node_modules`、`web/.next`、`web/out`。不要把本地 `api/.env` 或 `deploy/.env.production` 覆盖到 `/opt/skg-marketing-studio`,否则会把开发 cookies / API 配置烤进生产镜像或清空生产登录与模型配置。 +- 禁止手动裸 `rsync --delete` 到服务器;必须使用 `./scripts/deploy-prod-safe.sh`。如遇极端情况必须手动同步,命令必须同时包含 protect/exclude:`.git`、`.memory`、`.logs`、`.pids`、`data`、`jobs`、`secrets`、`api/jobs`、`api/.env`、`api/.env.local`、`api/.env.production`、`deploy/.env.production`、`web/node_modules`、`web/.next`、`web/out`。不要把本地 `api/.env` 或 `deploy/.env.production` 覆盖到 `/opt/skg-marketing-studio`,也不要删除服务器 `data/jobs`,否则会清空案例、登录和模型配置。 ## 快捷登录 - 登录地址:`https://marketing.skg.com/login/` diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 07f1c25..15f024a 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -540,8 +540,8 @@ 生产部署 - docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build - 服务器目录为 /opt/skg-marketing-studio;后端任务文件挂载到 ./data/jobs,全局资源中心挂载到 ./data/asset_library./data/prompt_library./data/_trash,真实 Key 只放服务器 deploy/.env.production。Web 上线验收必须按 Docker 静态形态跑 ./scripts/verify-prod-docker.sh,不能只用本地 npm run build 替代。 + ./scripts/deploy-prod-safe.sh + 服务器目录为 /opt/skg-marketing-studio;后端任务文件挂载到 ./data/jobs,全局资源中心挂载到 ./data/asset_library./data/prompt_library./data/_trash,真实 Key 只放服务器 deploy/.env.production。生产部署唯一入口是 deploy-prod-safe.sh:先备份服务器 env、案例和资源库,再用 protect/exclude 保护 data/jobs/secrets/deploy/.env.production 后同步代码,最后 Docker 重建并跑 verify-prod-docker.sh。禁止再用裸 rsync --delete 手动同步。 前端开发服务 @@ -1131,6 +1131,18 @@ ProductRefStateItem {

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-20 · 生产部署增加数据保护脚本

+ Deploy + Safety +
+
+

问题:手动 rsync --delete 如果没有排除服务器 data/jobs 和真实 deploy/.env.production,会把生产案例、资源库或登录配置删掉。

+

改动:新增 scripts/deploy-prod-safe.sh 作为生产部署唯一入口。脚本部署前会在服务器创建 /opt/skg-marketing-studio-backups/skg-marketing-preserve-*.tgz,备份真实 env、案例、资源库和 secrets;同步时用 rsync --filter='P ...' 和 exclude 双重保护 data/jobs/secrets/api/jobsdeploy/.env.production 和本地开发文件。

+

影响:后续发布不再手写裸 rsync --delete;脚本会自动 Docker 重建并调用 verify-prod-docker.sh。若误操作,先从最新 skg-marketing-preserve-*.tgz 恢复。

+
+

2026-05-20 · 转换层改为提示词确认后生成

diff --git a/scripts/deploy-prod-safe.sh b/scripts/deploy-prod-safe.sh new file mode 100755 index 0000000..13b8821 --- /dev/null +++ b/scripts/deploy-prod-safe.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euo pipefail + +HOST="${HOST:-root@76.13.31.179}" +APP_DIR="${APP_DIR:-/opt/skg-marketing-studio}" +BACKUP_DIR="${BACKUP_DIR:-/opt/skg-marketing-studio-backups}" + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +if [[ "${1:-}" == "--no-build" ]]; then + BUILD_FLAG="" +else + BUILD_FLAG="--build" +fi + +echo "==> Preflight: creating remote data/env backup" +ssh "$HOST" "set -euo pipefail + cd '$APP_DIR' + mkdir -p '$BACKUP_DIR' + stamp=\$(date +%Y%m%d%H%M%S) + tar -czf '$BACKUP_DIR/skg-marketing-preserve-'\$stamp'.tgz' \ + deploy/.env.production \ + data/jobs \ + data/asset_library \ + data/prompt_library \ + data/_trash \ + secrets 2>/tmp/skg-backup-warnings.log || { + cat /tmp/skg-backup-warnings.log >&2 || true + exit 1 + } + find '$BACKUP_DIR' -name 'skg-marketing-preserve-*.tgz' -type f -printf '%T@ %p\n' | sort -nr | tail -n +8 | cut -d' ' -f2- | xargs -r rm -f + echo backup:\$(ls -t '$BACKUP_DIR'/skg-marketing-preserve-*.tgz | head -1) +" + +echo "==> Syncing code with production data protected" +rsync -az --delete \ + --filter='P /data/***' \ + --filter='P /jobs/***' \ + --filter='P /secrets/***' \ + --filter='P /deploy/.env.production' \ + --filter='P /api/jobs/***' \ + --filter='P /api/.env' \ + --filter='P /api/.env.local' \ + --filter='P /api/.env.production' \ + --exclude='/.git/' \ + --exclude='/.memory/' \ + --exclude='/.logs/' \ + --exclude='/.pids/' \ + --exclude='/data/' \ + --exclude='/jobs/' \ + --exclude='/secrets/' \ + --exclude='/api/jobs/' \ + --exclude='/api/.env' \ + --exclude='/api/.env.local' \ + --exclude='/api/.env.production' \ + --exclude='/deploy/.env.production' \ + --exclude='/web/node_modules/' \ + --exclude='/web/.next/' \ + --exclude='/web/out/' \ + --exclude='/node_modules/' \ + --exclude='内部分享-口播脚本.md' \ + ./ "$HOST:$APP_DIR/" + +echo "==> Rebuilding production containers" +ssh "$HOST" "cd '$APP_DIR' && docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d $BUILD_FLAG" + +echo "==> Verifying production" +"$ROOT_DIR/scripts/verify-prod-docker.sh" "$HOST" + +echo "==> Done"