diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..64bca66
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,23 @@
+.git
+.gitignore
+.memory
+.logs
+.pids
+.playwright-mcp
+.DS_Store
+*.log
+
+node_modules
+web/node_modules
+web/.next
+web/out
+
+api/.venv
+api/jobs
+jobs
+data
+
+.env
+.env.local
+.env.production
+deploy/.env.production
diff --git a/.gitignore b/.gitignore
index ffee6a0..721bf0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ __pycache__/
*.log
.logs/
.pids/
+deploy/.env.production
# api
api/.venv/
diff --git a/.memory/worklog.json b/.memory/worklog.json
index 854786a..91eae08 100644
--- a/.memory/worklog.json
+++ b/.memory/worklog.json
@@ -1,18 +1,5 @@
{
"entries": [
- {
- "files_changed": 2,
- "hash": "dd2e8c9",
- "message": "auto-save 2026-05-13 23:17 (~2)",
- "ts": "2026-05-13T23:18:31+08:00",
- "type": "commit"
- },
- {
- "files_changed": 1,
- "message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 23:17 (~2)",
- "ts": "2026-05-13T15:23:08Z",
- "type": "session-heartbeat"
- },
{
"files_changed": 1,
"hash": "38091d3",
@@ -3251,6 +3238,19 @@
"message": "auto-save 2026-05-15 14:33 (~1)",
"hash": "8090674",
"files_changed": 1
+ },
+ {
+ "ts": "2026-05-15T14:42:29+08:00",
+ "type": "commit",
+ "message": "auto-save 2026-05-15 14:42 (~1)",
+ "hash": "ce04cf4",
+ "files_changed": 1
+ },
+ {
+ "ts": "2026-05-15T06:44:46Z",
+ "type": "session-heartbeat",
+ "message": "Codex 会话活跃 · 最近命令:codex · 5 项未提交变更 · 最近提交:auto-save 2026-05-15 14:42 (~1)",
+ "files_changed": 5
}
]
}
diff --git a/.project.json b/.project.json
index fdc576c..f95bef8 100644
--- a/.project.json
+++ b/.project.json
@@ -2,16 +2,28 @@
"company" : "SKG",
"created" : "2026-05-12",
"credentials" : [
+ {
+ "description" : "SKG AI 网关 API Key,生产只放服务器 deploy/.env.production 的 LLM_API_KEY,本地开发放 api/.env,不入库",
+ "name" : "LLM_API_KEY",
+ "storage" : "api/.env / deploy/.env.production",
+ "type" : "api_key"
+ },
{
"description" : "MiniMax T2A 配音 API Key,本地开发只放 api/.env 的 MINIMAX_API_KEY,不入库",
"name" : "MINIMAX_API_KEY",
"storage" : "api/.env",
"type" : "api_key"
+ },
+ {
+ "description" : "SKG 豆包 / Seedance 视频生成 API Key,生产只放服务器 deploy/.env.production 的 VIDEO_API_KEY,本地开发放 api/.env,不入库",
+ "name" : "VIDEO_API_KEY",
+ "storage" : "api/.env / deploy/.env.production",
+ "type" : "api_key"
}
],
"description" : "SKG AI 素材生产管线第二条思路验证:TK 链接 → 拆轨 → 目标化关键帧 + ASR\/翻译 → 接 SKG 产品信息改写口播 → MiniMax 配音 → nano-banana-pro\/GPT Image 生图 → Seedance\/Kling\/Veo3 多模型生视频 → 合成带文案成品",
"kind" : "app",
- "name" : "SKG AI 素材管线 - TK 二创验证",
+ "name" : "SKG Marketing Studio / SKG 营销内容工作台",
"ownership" : "company",
"pin_order" : 1778664997,
"pinned" : true,
@@ -32,7 +44,21 @@
],
"status" : "active",
"urls" : [
-
+ {
+ "label" : "production",
+ "type" : "app",
+ "url" : "https://marketing.skg.com"
+ },
+ {
+ "label" : "production-api",
+ "type" : "backend",
+ "url" : "https://marketing.skg.com/api"
+ },
+ {
+ "label" : "source-analysis",
+ "type" : "docs",
+ "url" : "docs/source-analysis.html"
+ }
],
"worklog" : {
"auto" : true,
diff --git a/Dockerfile.api b/Dockerfile.api
new file mode 100644
index 0000000..d6b6823
--- /dev/null
+++ b/Dockerfile.api
@@ -0,0 +1,22 @@
+FROM python:3.12-slim
+
+ENV PYTHONDONTWRITEBYTECODE=1 \
+ PYTHONUNBUFFERED=1 \
+ PIP_NO_CACHE_DIR=1 \
+ JOBS_DIR=/data/jobs
+
+WORKDIR /app
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends ffmpeg ca-certificates curl \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY api/requirements.txt /app/requirements.txt
+RUN pip install --no-cache-dir -r /app/requirements.txt
+
+COPY api /app
+RUN mkdir -p /data/jobs
+
+EXPOSE 4291
+
+CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "4291", "--proxy-headers"]
diff --git a/Dockerfile.web b/Dockerfile.web
new file mode 100644
index 0000000..df8ca58
--- /dev/null
+++ b/Dockerfile.web
@@ -0,0 +1,22 @@
+FROM node:22-bookworm-slim AS builder
+
+WORKDIR /app
+
+RUN corepack enable && corepack prepare pnpm@10.28.2 --activate
+
+COPY web/package.json web/pnpm-lock.yaml ./
+RUN pnpm install --frozen-lockfile
+
+COPY web ./
+
+ARG NEXT_PUBLIC_API_BASE=/api
+ENV NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE}
+
+RUN pnpm build
+
+FROM nginx:1.27-alpine
+
+COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf
+COPY --from=builder /app/out /usr/share/nginx/html
+
+EXPOSE 80
diff --git a/RULES.md b/RULES.md
index 05aa0fe..2f7f730 100644
--- a/RULES.md
+++ b/RULES.md
@@ -13,12 +13,16 @@
- 第一冲刺:步骤 1-4(下载 / 拆轨 / 关键帧 / ASR+翻译)
## 部署事实
-- 平台:待定
-- 发布状态:未部署
-- 主站 / 前端:待定
-- API / 后端:待定
-- 文档 / 解析:待定
+- 平台:VPS `76.13.31.179`(Ubuntu 24.04 / Docker Compose / Coolify Traefik)
+- 发布状态:生产部署配置已生成;公司域名 `marketing.skg.com` 已解析到 VPS,待执行 Compose 上线和 HTTPS 验证
+- 主站 / 前端:`https://marketing.skg.com`
+- API / 后端:`https://marketing.skg.com/api`
+- 文档 / 解析:`docs/source-analysis.html`(项目内独立文档,不公开挂主应用路由)
- 管理后台:待定
+- 服务器目录:`/opt/skg-marketing-studio`
+- 生产启动:`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build`
+- 生产架构:`web` 容器用 Nginx 承载 Next 静态导出并反代 `/api/`,`api` 容器跑 FastAPI 4291;Traefik 通过 `coolify` 外部网络接入 80/443
+- 持久化目录:服务器 `./data/jobs` 挂载到后端 `/data/jobs`
## 快捷登录
- 登录地址:待补充
@@ -45,6 +49,7 @@
- `MINIMAX_TTS_BASE_URL` / `MINIMAX_TTS_MODEL` / `MINIMAX_TTS_VOICE_ID`:MiniMax 配音端点、模型和兜底音色配置
- `MINIMAX_TTS_VOICE_POOL`:MiniMax 英文随机音色池;当前默认男声 `English_magnetic_voiced_man`、女声 `English_Upbeat_Woman`、成熟声 `English_MaturePartner`
- `POE_API_KEY` / `VIDEO_API_KEY`:视频生成通道 Key,只能放本地环境变量
+- 生产环境变量:服务器只使用 `deploy/.env.production`,模板为 `deploy/.env.production.example`;真实 Key 不入库
## 规则
- 不允许编造不存在的部署域名、账号、密码
diff --git a/deploy/.env.production.example b/deploy/.env.production.example
new file mode 100644
index 0000000..08f219b
--- /dev/null
+++ b/deploy/.env.production.example
@@ -0,0 +1,43 @@
+# Copy this file to deploy/.env.production on the VPS.
+# Keep real API keys out of git.
+
+# Runtime
+JOBS_DIR=/data/jobs
+KEYFRAME_COUNT=12
+CORS_ORIGINS=https://marketing.skg.com
+API_PORT=4291
+
+# SKG AI gateway, OpenAI-compatible
+LLM_BASE_URL=https://ai.skg.com/ezlink/v1
+LLM_API_KEY=
+
+# Model routing
+ASR_MODEL=whisper-1
+ASR_FALLBACK_MODEL=gemini-2.5-flash
+TRANSLATE_MODEL=gemini-2.5-flash
+REWRITE_MODEL=gemini-2.5-pro
+IMAGE_MODEL=gemini-3-pro-image-preview
+
+# Audio rewrite and MiniMax TTS
+AUDIO_REWRITE_MODEL=gemini-2.5-pro
+AUDIO_PRODUCT_BRIEF="SKG smart massage products for daily neck, shoulder, back, eye, knee, and foot relaxation. Keep claims premium, clean, credible, and non-medical."
+MINIMAX_API_KEY=
+MINIMAX_TTS_BASE_URL=https://api.minimax.io
+MINIMAX_TTS_MODEL=speech-2.8-turbo
+MINIMAX_TTS_VOICE_ID=English_expressive_narrator
+MINIMAX_TTS_VOICE_POOL=English_magnetic_voiced_man,English_Upbeat_Woman,English_MaturePartner
+
+# Video generation. Use SKG Doubao / Seedance gateway in production.
+POE_API_BASE_URL=https://api.poe.com/v1
+POE_API_KEY=
+VIDEO_API_BASE_URL=https://ai.skg.com/doubao
+VIDEO_API_KEY=
+VIDEO_MODEL=seedance
+VIDEO_MODEL_SEEDANCE=doubao-seedance-2-0-fast-260128
+VIDEO_MODEL_KLING=kling-omni
+VIDEO_MODEL_VEO3=veo-3.1-fast
+VIDEO_CREATE_PATHS=/api/v3/contents/generations/tasks
+VIDEO_STATUS_PATH=/api/v3/contents/generations/tasks/{id}
+VIDEO_CONTENT_PATH=/api/v3/contents/generations/tasks/{id}/content
+VIDEO_DURATION_FIELD=seconds
+VIDEO_POLL_TIMEOUT_SECONDS=900
diff --git a/deploy/nginx.conf b/deploy/nginx.conf
new file mode 100644
index 0000000..9e29505
--- /dev/null
+++ b/deploy/nginx.conf
@@ -0,0 +1,32 @@
+server {
+ listen 80;
+ server_name _;
+
+ client_max_body_size 2g;
+
+ gzip on;
+ gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml;
+
+ location = /api {
+ return 308 /api/;
+ }
+
+ location /api/ {
+ proxy_pass http://api:4291/;
+ proxy_http_version 1.1;
+ proxy_request_buffering off;
+ proxy_buffering off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_read_timeout 1800s;
+ proxy_send_timeout 1800s;
+ proxy_connect_timeout 60s;
+ }
+
+ location / {
+ root /usr/share/nginx/html;
+ try_files $uri $uri/ /index.html;
+ }
+}
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..791eb39
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,55 @@
+name: skg-marketing-studio
+
+services:
+ api:
+ build:
+ context: .
+ dockerfile: Dockerfile.api
+ container_name: skg-marketing-api
+ env_file:
+ - ./deploy/.env.production
+ environment:
+ JOBS_DIR: /data/jobs
+ CORS_ORIGINS: https://marketing.skg.com
+ volumes:
+ - ./data/jobs:/data/jobs
+ restart: unless-stopped
+ networks:
+ - skg-marketing-internal
+
+ web:
+ build:
+ context: .
+ dockerfile: Dockerfile.web
+ args:
+ NEXT_PUBLIC_API_BASE: /api
+ container_name: skg-marketing-web
+ depends_on:
+ - api
+ restart: unless-stopped
+ networks:
+ - skg-marketing-internal
+ - coolify
+ labels:
+ - "traefik.enable=true"
+ - "traefik.docker.network=coolify"
+ - "traefik.http.middlewares.skg-marketing-gzip.compress=true"
+ - "traefik.http.middlewares.skg-marketing-redirect.redirectscheme.scheme=https"
+ - "traefik.http.routers.skg-marketing-http.entryPoints=http"
+ - "traefik.http.routers.skg-marketing-http.rule=Host(`marketing.skg.com`) && PathPrefix(`/`)"
+ - "traefik.http.routers.skg-marketing-http.middlewares=skg-marketing-redirect"
+ - "traefik.http.routers.skg-marketing-http.service=skg-marketing-http"
+ - "traefik.http.routers.skg-marketing-https.entryPoints=https"
+ - "traefik.http.routers.skg-marketing-https.rule=Host(`marketing.skg.com`) && PathPrefix(`/`)"
+ - "traefik.http.routers.skg-marketing-https.middlewares=skg-marketing-gzip"
+ - "traefik.http.routers.skg-marketing-https.tls=true"
+ - "traefik.http.routers.skg-marketing-https.tls.certresolver=letsencrypt"
+ - "traefik.http.routers.skg-marketing-https.service=skg-marketing-https"
+ - "traefik.http.services.skg-marketing-http.loadbalancer.server.port=80"
+ - "traefik.http.services.skg-marketing-https.loadbalancer.server.port=80"
+
+networks:
+ skg-marketing-internal:
+ name: skg-marketing-internal
+ coolify:
+ external: true
diff --git a/docs/deploy-vps.md b/docs/deploy-vps.md
new file mode 100644
index 0000000..a4745b5
--- /dev/null
+++ b/docs/deploy-vps.md
@@ -0,0 +1,63 @@
+# SKG Marketing Studio VPS Deployment
+
+Production domain:
+
+- App: `https://marketing.skg.com`
+- API: `https://marketing.skg.com/api`
+
+Current VPS target:
+
+- Host: `76.13.31.179`
+- OS: Ubuntu 24.04
+- Runtime: Docker Compose
+- Public ingress: existing Coolify Traefik on ports 80/443
+
+DNS:
+
+```text
+marketing.skg.com A 76.13.31.179
+```
+
+Do not run a host Nginx on ports 80/443. Those ports are already owned by Coolify / Traefik. This project publishes through Docker labels on the external `coolify` network.
+
+## First Deploy
+
+On the VPS:
+
+```bash
+mkdir -p /opt/skg-marketing-studio
+cd /opt/skg-marketing-studio
+cp deploy/.env.production.example deploy/.env.production
+```
+
+Fill `deploy/.env.production` with the real production keys. Keep this file out of git.
+
+Then start:
+
+```bash
+docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build
+```
+
+Verify:
+
+```bash
+curl -I https://marketing.skg.com
+curl https://marketing.skg.com/api/health
+docker compose -f docker-compose.prod.yml ps
+```
+
+## Update
+
+Sync the repo to `/opt/skg-marketing-studio`, then run:
+
+```bash
+docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build
+```
+
+## Runtime Notes
+
+- `web` is a static Next export served by Nginx.
+- `web` proxies `/api/` to `api:4291`.
+- `api` is only on the internal project network and stores jobs under `/data/jobs`.
+- Server-side job files persist in `./data/jobs` on the VPS.
+- Large uploads are allowed up to `2g` at the Nginx proxy layer.
diff --git a/docs/source-analysis.html b/docs/source-analysis.html
index e3aa7f9..0216075 100644
--- a/docs/source-analysis.html
+++ b/docs/source-analysis.html
@@ -533,6 +533,16 @@
./scripts/stop-dev-background.sh |
按 .pids/ 里的 PID 停止后台前端 / 后端进程。 |
+
+ | 生产站点 |
+ https://marketing.skg.com |
+ 公司域名已解析到 VPS 76.13.31.179。线上由既有 Coolify / Traefik 负责 HTTPS 入口,项目 web 容器用 Nginx 承载静态前端并把 /api/ 反代到 FastAPI。 |
+
+
+ | 生产部署 |
+ docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build |
+ 服务器目录为 /opt/skg-marketing-studio;后端任务文件挂载到 ./data/jobs,真实 Key 只放服务器 deploy/.env.production。 |
+
| 前端开发服务 |
cd web && pnpm dev |
@@ -929,6 +939,17 @@ SubjectAsset {
变更记录
这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。
+
+
+ 2026-05-15 · 公司域名生产部署配置
+ Runtime
+ Deploy
+
+
+
改动:把生产入口确定为 https://marketing.skg.com,DNS 已解析到 VPS 76.13.31.179。新增 Docker Compose 生产配置:前端用 Next 静态导出 + Nginx,/api/ 反代到 FastAPI;后端任务目录持久化到服务器 ./data/jobs;Traefik 通过既有 coolify 外部网络接管 80/443。
+
影响:Dockerfile.web、Dockerfile.api、docker-compose.prod.yml、deploy/nginx.conf、deploy/.env.production.example、docs/deploy-vps.md、.project.json、RULES.md、docs/source-analysis.html。
+
+
2026-05-15 · 本地启动改为后台不弹 Terminal