fix: prevent dev env leaking into api image

This commit is contained in:
2026-05-19 12:03:50 +08:00
parent b5855fd457
commit f576875af2
4 changed files with 21 additions and 4 deletions

View File

@@ -13,6 +13,9 @@ web/.next
web/out web/out
api/.venv api/.venv
api/.env
api/.env.local
api/.env.production
api/jobs api/jobs
jobs jobs
data data

View File

@@ -25,8 +25,9 @@
- 生产启动:`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build` - 生产启动:`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` 容器用 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
- 持久化目录:服务器 `./data/jobs` 挂载到后端 `/data/jobs`;全局资源中心持久化在 `./data/asset_library``./data/prompt_library``./data/_trash` - 持久化目录:服务器 `./data/jobs` 挂载到后端 `/data/jobs`;全局资源中心持久化在 `./data/asset_library``./data/prompt_library``./data/_trash`
- TikTok 下载登录态:云端使用服务器私有 cookies 文件 `./secrets/tiktok_cookies.txt`挂载到 API 容器 `/run/secrets/tiktok_cookies.txt`,生产环境变量 `YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt``yt-dlp` 会在任务结束时回写 cookies因此不要把该挂载设为只读不要使用云端浏览器读取方案也不要把 cookies 入库 - 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` - 登录凭证:用户名写下方快捷登录;密码明文备份只放服务器 `/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 配置烤进生产镜像或清空生产登录与模型配置。
## 快捷登录 ## 快捷登录
- 登录地址:`https://marketing.skg.com/login/` - 登录地址:`https://marketing.skg.com/login/`

View File

@@ -47,7 +47,8 @@ SUBJECT_ASSET_IMAGE_MODELS=gpt-image-2
AI_HTTP_PROXY= AI_HTTP_PROXY=
# Optional TikTok download login state for yt-dlp. Keep cookies files private. # Optional TikTok download login state for yt-dlp. Keep cookies files private.
YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt # Leave blank for public TikTok videos. Set to /run/secrets/tiktok_cookies.txt only when a link explicitly requires login cookies.
YTDLP_COOKIES_FILE=
YTDLP_COOKIES_FROM_BROWSER= YTDLP_COOKIES_FROM_BROWSER=
# Audio rewrite and Azure OpenAI TTS # Audio rewrite and Azure OpenAI TTS

View File

@@ -572,7 +572,7 @@
<p>当前产品方向已收窄为“信息流广告快速复刻”:主界面左侧是素材输入列,右侧是信息流复刻工作表。顶部固定显示 01-09 流程顺序和每一步的判定依据,编号不再是装饰文本,而是按素材任务、源视频、音频文案、抽帧、主体资产、产品资产、分镜文案、三字段规划和视频候选这些状态解锁。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动音频文案路和视频视觉路。音频文案路提取原音频文案/字幕,分析讲话人、语速节奏、背景音乐/环境声/音效,并为后续新口播和分镜文案提供时间轴;视频视觉路同步抽取参考帧,参考帧只用于人工选择主体并生成相似主体白底视图。产品图上传后独立形成产品资产包:自动识别视角、左右/上下/内外侧、结构点、比例和风险,并补缺角度。最终分镜规划按逐句时间轴把文案、相似主体资产和产品资产汇合;客户默认只看“文案 / 场景一句话 / 人物+产品+动作”三字段,一键为单条抽 4 张视频候选或整片批量抽卡。首尾帧、视觉规划、产品出现方式等细节保留在高级抽屉和后端自动展开逻辑里,不再作为客户默认闸门。</p> <p>当前产品方向已收窄为“信息流广告快速复刻”:主界面左侧是素材输入列,右侧是信息流复刻工作表。顶部固定显示 01-09 流程顺序和每一步的判定依据,编号不再是装饰文本,而是按素材任务、源视频、音频文案、抽帧、主体资产、产品资产、分镜文案、三字段规划和视频候选这些状态解锁。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动音频文案路和视频视觉路。音频文案路提取原音频文案/字幕,分析讲话人、语速节奏、背景音乐/环境声/音效,并为后续新口播和分镜文案提供时间轴;视频视觉路同步抽取参考帧,参考帧只用于人工选择主体并生成相似主体白底视图。产品图上传后独立形成产品资产包:自动识别视角、左右/上下/内外侧、结构点、比例和风险,并补缺角度。最终分镜规划按逐句时间轴把文案、相似主体资产和产品资产汇合;客户默认只看“文案 / 场景一句话 / 人物+产品+动作”三字段,一键为单条抽 4 张视频候选或整片批量抽卡。首尾帧、视觉规划、产品出现方式等细节保留在高级抽屉和后端自动展开逻辑里,不再作为客户默认闸门。</p>
<div class="pipeline"> <div class="pipeline">
<div class="step"><div class="num">01</div><h3>素材输入</h3><p>有当前素材任务即通过;输入框只负责创建或切换任务。</p></div> <div class="step"><div class="num">01</div><h3>素材输入</h3><p>有当前素材任务即通过;输入框只负责创建或切换任务。</p></div>
<div class="step"><div class="num">02</div><h3>源视频下载</h3><p><code>job.video_url</code> 存在即通过;<code>created/downloading</code> 视为运行中。TikTok 受限视频可通过 <code>YTDLP_COOKIES_FILE</code> <code>YTDLP_COOKIES_FROM_BROWSER</code> 提供登录态;生产云端使用服务器私有 cookies 文件挂载,失败后可对同一素材重新下载</p></div> <div class="step"><div class="num">02</div><h3>源视频下载</h3><p><code>job.video_url</code> 存在即通过;<code>created/downloading</code> 视为运行中。公开视频默认不带 cookies 下载;只有 TikTok 明确要求登录态时才配置 <code>YTDLP_COOKIES_FILE</code>,生产容器禁止使用 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code></p></div>
<div class="step"><div class="num">03</div><h3>音频文案</h3><p><code>audio_script.source_text</code><code>transcript</code> 逐句时间轴有内容即通过。</p></div> <div class="step"><div class="num">03</div><h3>音频文案</h3><p><code>audio_script.source_text</code><code>transcript</code> 逐句时间轴有内容即通过。</p></div>
<div class="step"><div class="num">04</div><h3>抽帧参考</h3><p><code>job.frames.length &gt; 0</code> 即通过;参考帧只做主体重构证据。</p></div> <div class="step"><div class="num">04</div><h3>抽帧参考</h3><p><code>job.frames.length &gt; 0</code> 即通过;参考帧只做主体重构证据。</p></div>
<div class="step"><div class="num">05</div><h3>相似主体</h3><p>关键帧里存在 <code>subject_assets</code> 即通过;生成类似创新主体,不复刻原人。</p></div> <div class="step"><div class="num">05</div><h3>相似主体</h3><p>关键帧里存在 <code>subject_assets</code> 即通过;生成类似创新主体,不复刻原人。</p></div>
@@ -965,7 +965,7 @@ ProductRefStateItem {
<tr><td>网页登录</td><td><code>POST /auth/login</code><code>GET /auth/check</code><code>POST /auth/logout</code></td><td><code>web/app/login/page.tsx</code>、Nginx <code>auth_request</code></td><td>登录页提交账号密码到 <code>/api/auth/login</code>,后端设置 HttpOnly 会话 Cookie生产 Nginx 对工作台和 <code>/api/</code><code>/auth/check</code> 做统一校验,未登录页面跳 <code>/login/</code>API 返回 JSON 401。</td></tr> <tr><td>网页登录</td><td><code>POST /auth/login</code><code>GET /auth/check</code><code>POST /auth/logout</code></td><td><code>web/app/login/page.tsx</code>、Nginx <code>auth_request</code></td><td>登录页提交账号密码到 <code>/api/auth/login</code>,后端设置 HttpOnly 会话 Cookie生产 Nginx 对工作台和 <code>/api/</code><code>/auth/check</code> 做统一校验,未登录页面跳 <code>/login/</code>API 返回 JSON 401。</td></tr>
<tr><td>运行配置 / 模型标注</td><td><code>GET /health</code></td><td><code>getRuntimeHealth</code><code>ModelTrace</code></td><td>返回 <code>models</code>ASR、<code>asr_base_url</code><code>asr_remote_enabled</code><code>asr_local_fallback_enabled</code><code>asr_audio_fallback_enabled</code><code>faster_whisper</code>、本机 ASR、ASR fallback、翻译、GPT 改写、GPT 画面理解、产品视角识别 <code>product_view</code>、GPT 图像模型、主体 6 视图 GPT 图像模型、Azure OpenAI TTS、视频别名和 Seedance 服务商。当前 <code>REWRITE_MODEL</code><code>AUDIO_REWRITE_MODEL</code><code>VISION_MODEL</code> 默认使用 <code>gpt-4o</code>;如果旧环境变量仍写 <code>gemini-*</code>,后端会归一化回 <code>GPT_TEXT_MODEL</code> / <code>REWRITE_MODEL</code>。语音只走 Azure OpenAI TTS<code>models.voice_tts_paths</code> 会回传当前尝试的语音路径,方便区分路径错误和语音服务不可用。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。</td></tr> <tr><td>运行配置 / 模型标注</td><td><code>GET /health</code></td><td><code>getRuntimeHealth</code><code>ModelTrace</code></td><td>返回 <code>models</code>ASR、<code>asr_base_url</code><code>asr_remote_enabled</code><code>asr_local_fallback_enabled</code><code>asr_audio_fallback_enabled</code><code>faster_whisper</code>、本机 ASR、ASR fallback、翻译、GPT 改写、GPT 画面理解、产品视角识别 <code>product_view</code>、GPT 图像模型、主体 6 视图 GPT 图像模型、Azure OpenAI TTS、视频别名和 Seedance 服务商。当前 <code>REWRITE_MODEL</code><code>AUDIO_REWRITE_MODEL</code><code>VISION_MODEL</code> 默认使用 <code>gpt-4o</code>;如果旧环境变量仍写 <code>gemini-*</code>,后端会归一化回 <code>GPT_TEXT_MODEL</code> / <code>REWRITE_MODEL</code>。语音只走 Azure OpenAI TTS<code>models.voice_tts_paths</code> 会回传当前尝试的语音路径,方便区分路径错误和语音服务不可用。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key 或敏感凭证。</td></tr>
<tr><td>历史列表</td><td><code>GET /jobs</code></td><td><code>listJobs</code></td><td>所有 job 精简列表id/url/status/thumbnail/mtime…按 state.json mtime 倒序。前端 URL 无 <code>?job=</code> 时拉它回填全部历史;带 <code>limit</code> 可截断。</td></tr> <tr><td>历史列表</td><td><code>GET /jobs</code></td><td><code>listJobs</code></td><td>所有 job 精简列表id/url/status/thumbnail/mtime…按 state.json mtime 倒序。前端 URL 无 <code>?job=</code> 时拉它回填全部历史;带 <code>limit</code> 可截断。</td></tr>
<tr><td>创建任务</td><td><code>POST /jobs</code></td><td><code>createJob</code></td><td>提交 TK 链接,后台开始下载;前端“开始”队列会在 downloaded 后自动触发音频解析。下载阶段优先使用 <code>YTDLP_COOKIES_FILE</code>,其次使用 <code>YTDLP_COOKIES_FROM_BROWSER</code>;生产云端固定走 <code>/run/secrets/tiktok_cookies.txt</code>,由宿主机 <code>./secrets/tiktok_cookies.txt</code> 挂载进容器。TikTok 要求登录态时会提示上传 MP4 或配置后端 cookies</td></tr> <tr><td>创建任务</td><td><code>POST /jobs</code></td><td><code>createJob</code></td><td>提交 TK 链接,后台开始下载;前端“开始”队列会在 downloaded 后自动触发音频解析。下载阶段默认不带 cookies生产环境必须显式保持 <code>YTDLP_COOKIES_FILE=</code> <code>YTDLP_COOKIES_FROM_BROWSER=</code> 为空,避免容器内误读被打进镜像的开发 <code>api/.env</code>。只有 TikTok 明确要求登录态时,才把宿主机 <code>./secrets/tiktok_cookies.txt</code> 挂载进容器并设置 <code>YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt</code>。生产容器没有 Chrome cookies 数据库,不能配置 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code></td></tr>
<tr><td>重试下载</td><td><code>POST /jobs/{id}/download/retry</code></td><td><code>retryJobDownload</code></td><td>用于 TK 链接下载失败且没有 <code>video_url</code> 的素材;清空错误、重新进入下载状态,并在后台再次执行 <code>pipeline_download</code>。上传视频不能重下载,需要重新上传文件。</td></tr> <tr><td>重试下载</td><td><code>POST /jobs/{id}/download/retry</code></td><td><code>retryJobDownload</code></td><td>用于 TK 链接下载失败且没有 <code>video_url</code> 的素材;清空错误、重新进入下载状态,并在后台再次执行 <code>pipeline_download</code>。上传视频不能重下载,需要重新上传文件。</td></tr>
<tr><td>上传视频</td><td><code>POST /jobs/upload</code></td><td><code>uploadJob</code></td><td>保存 source.mp4然后同样进入下载完成状态当前上传后也加入第一步队列下载完成后自动解析音频。</td></tr> <tr><td>上传视频</td><td><code>POST /jobs/upload</code></td><td><code>uploadJob</code></td><td>保存 source.mp4然后同样进入下载完成状态当前上传后也加入第一步队列下载完成后自动解析音频。</td></tr>
<tr><td>删除输入视频</td><td><code>DELETE /jobs/{id}</code></td><td><code>deleteJob</code></td><td>从任务队列、URL 和磁盘 <code>jobs/&lt;id&gt;</code> 目录移除整个 job包括源视频、关键帧、元素提取图和生成视频。</td></tr> <tr><td>删除输入视频</td><td><code>DELETE /jobs/{id}</code></td><td><code>deleteJob</code></td><td>从任务队列、URL 和磁盘 <code>jobs/&lt;id&gt;</code> 目录移除整个 job包括源视频、关键帧、元素提取图和生成视频。</td></tr>
@@ -1108,6 +1108,18 @@ ProductRefStateItem {
<h2>变更记录</h2> <h2>变更记录</h2>
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p> <p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
<div class="changelog"> <div class="changelog">
<article class="change">
<header>
<h3>2026-05-19 · 修正生产 TikTok 下载 cookies 污染</h3>
<span class="tag red">Deploy</span>
<span class="tag cyan">Source Video</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产 API 镜像把本地开发 <code>api/.env</code> 复制进 <code>/app/.env</code>,其中 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code><code>load_dotenv()</code> 读入。容器没有 Chrome cookies 数据库,导致公开视频也在下载阶段失败,错误为 <code>could not find chrome cookies database</code></p>
<p><strong>改动:</strong><code>.dockerignore</code> 明确排除 <code>api/.env</code><code>api/.env.local</code><code>api/.env.production</code>,生产 env 显式保持 <code>YTDLP_COOKIES_FILE=</code><code>YTDLP_COOKIES_FROM_BROWSER=</code> 为空。需要登录态时只允许配置服务器 cookies 文件,不允许在生产容器使用 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code></p>
<p><strong>影响:</strong>公开视频下载恢复为无 cookies 直连;受限视频仍可通过服务器私有 cookies 文件处理。后续 rsync / Docker build 必须排除本地 <code>api/.env</code>否则开发代理、cookies 或 API 配置会污染生产镜像。</p>
</div>
</article>
<article class="change"> <article class="change">
<header> <header>
<h3>2026-05-19 · 分镜工作台折叠化和候选区紧凑化</h3> <h3>2026-05-19 · 分镜工作台折叠化和候选区紧凑化</h3>