fix(ports): docker 默认对外8502并修复空DB连接串回退

This commit is contained in:
Tony Zhang
2025-12-17 10:35:14 +08:00
parent 81a4faabf5
commit e365a94dd1
5 changed files with 333 additions and 4 deletions

37
Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
# Video Flow - Python 后端 Dockerfile
FROM python:3.11-slim
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
libsm6 \
libxext6 \
libgl1 \
fonts-noto-cjk \
fonts-wqy-zenhei \
curl \
&& rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建必要的目录
RUN mkdir -p /app/output /app/temp /app/assets
# 暴露端口
# - 8000: FastAPI
# - 8503: Streamlitdocker-compose.yml 中运行在 85038502 是历史 runtime 标识)
EXPOSE 8000 8503
# 默认命令
CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]

138
api/main.py Normal file
View File

@@ -0,0 +1,138 @@
"""
Video Flow API - FastAPI Backend
前后端分离架构的后端服务
端口8000与 Streamlit 8503 共存8502 为历史 runtime/素材目录标识)
"""
import os
import sys
import logging
from pathlib import Path
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
# 确保项目根目录在 path 中
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
import config
from api.routes import projects, editor, assets, compose
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
logger.info("Video Flow API 启动中...")
logger.info(f"项目根目录: {PROJECT_ROOT}")
logger.info(f"输出目录: {config.OUTPUT_DIR}")
logger.info(f"临时目录: {config.TEMP_DIR}")
yield
logger.info("Video Flow API 关闭中...")
# 创建 FastAPI 应用
app = FastAPI(
title="Video Flow API",
description="视频工作流后端 API - 支持项目管理、素材处理、视频编辑与合成",
version="1.0.0",
lifespan=lifespan,
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json"
)
# CORS 配置 - 允许 React 前端访问
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000", # React dev server
"http://localhost:5173", # Vite dev server
"http://127.0.0.1:3000",
"http://127.0.0.1:5173",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 挂载静态文件目录(用于访问生成的视频/图片)
app.mount("/static/output", StaticFiles(directory=str(config.OUTPUT_DIR)), name="output")
app.mount("/static/temp", StaticFiles(directory=str(config.TEMP_DIR)), name="temp")
app.mount("/static/assets", StaticFiles(directory=str(config.ASSETS_DIR)), name="assets")
# Legacy mounts8502 runtime 产物,宿主机目录通过 docker-compose 挂载到容器内)
try:
app.mount("/static/legacy-temp", StaticFiles(directory="/legacy/temp"), name="legacy-temp")
app.mount("/static/legacy-output", StaticFiles(directory="/legacy/output"), name="legacy-output")
except Exception:
# 本地非 docker 环境可能不存在这些目录
pass
# 注册路由
app.include_router(projects.router, prefix="/api/projects", tags=["Projects"])
app.include_router(editor.router, prefix="/api/editor", tags=["Editor"])
app.include_router(assets.router, prefix="/api/assets", tags=["Assets"])
app.include_router(compose.router, prefix="/api/compose", tags=["Compose"])
@app.get("/api/health")
async def health_check():
"""健康检查"""
return {
"status": "ok",
"service": "video-flow-api",
"version": "1.0.0"
}
@app.get("/api/config")
async def get_config():
"""获取前端所需的配置信息"""
return {
"video_settings": config.VIDEO_SETTINGS,
"available_voices": [
{"id": config.VOLC_TTS_DEFAULT_VOICE, "name": "三通永 (默认)"},
{"id": "zh_female_meilinvyou_saturn_bigtts", "name": "美丽女友"},
],
"available_bgm": _list_bgm_files(),
}
def _list_bgm_files():
"""列出可用的 BGM 文件"""
bgm_dir = config.ASSETS_DIR / "bgm"
if not bgm_dir.exists():
return []
bgm_files = []
for f in bgm_dir.iterdir():
if f.suffix.lower() in ['.mp3', '.mp4', '.m4a', '.wav']:
bgm_files.append({
"id": f.name,
"name": f.stem,
"path": f"/static/assets/bgm/{f.name}"
})
return bgm_files
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"api.main:app",
host="0.0.0.0",
port=8000,
reload=True,
log_level="info"
)

View File

@@ -15,6 +15,10 @@ load_dotenv()
VOLC_API_KEY = os.getenv("VOLC_API_KEY", "05aed9c1-f5e6-487b-9273-fe7d6be51957")
VOLC_BASE_URL = os.getenv("VOLC_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3")
# OpenAI (for GPT models)
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
# Models (Updated with User-Provided Endpoint IDs)
# LLM: Doubao Pro 1.5 (Using provided brain/vision endpoint)
BRAIN_MODEL_ID = os.getenv("BRAIN_MODEL_ID", "ep-20251203231055-dpsp7")
@@ -115,8 +119,8 @@ FONTS_DIR.mkdir(exist_ok=True)
# Database Configuration
# ============================================================
# Format: postgresql://user:password@host:port/dbname
# Default to SQLite if not provided
DB_CONNECTION_STRING = os.getenv("DB_CONNECTION_STRING", f"sqlite:///{BASE_DIR}/video_flow.db")
# Default to SQLite if not provided OR provided as empty string
DB_CONNECTION_STRING = os.getenv("DB_CONNECTION_STRING") or f"sqlite:///{BASE_DIR}/video_flow.db"
# ============================================================
# Font Settings (字体配置)

149
docker-compose.yml Normal file
View File

@@ -0,0 +1,149 @@
version: '3.8'
# Video Flow - Docker Compose 配置
# 支持水平扩展的视频处理服务
services:
# ============================================================
# Redis - 消息队列和缓存
# ============================================================
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# ============================================================
# FastAPI Backend - API 服务
# ============================================================
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- ./modules:/app/modules:ro
- ./api:/app/api:ro
- ./config.py:/app/config.py:ro
- ./assets:/app/assets
- ./output:/app/output
- ./temp:/app/temp
- ./video_flow.db:/app/video_flow.db
# 方案A通过 Unix Socket 连接宿主机 Postgres不暴露 5432
- /var/run/postgresql:/var/run/postgresql
# 复用 8502/root/video-flow生成的历史素材
- /root/video-flow/temp:/legacy/temp:ro
- /root/video-flow/output:/legacy/output:ro
environment:
- REDIS_URL=redis://redis:6379/0
- PYTHONUNBUFFERED=1
# 8503 读写 8502 的 Postgres从 .env 注入,避免写死密码)
- DB_CONNECTION_STRING=${VIDEO_FLOW_DB_CONNECTION_STRING}
depends_on:
redis:
condition: service_healthy
command: uvicorn api.main:app --host 0.0.0.0 --port 8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
# ============================================================
# Celery Worker - 视频处理 Worker
# 可以通过 docker-compose scale worker=N 水平扩展
# ============================================================
worker:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./modules:/app/modules:ro
- ./api:/app/api:ro
- ./config.py:/app/config.py:ro
- ./bin:/app/bin:ro
- ./assets:/app/assets
- ./output:/app/output
- ./temp:/app/temp
- ./video_flow.db:/app/video_flow.db
- /var/run/postgresql:/var/run/postgresql
- /root/video-flow/temp:/legacy/temp:ro
- /root/video-flow/output:/legacy/output:ro
environment:
- REDIS_URL=redis://redis:6379/0
- PYTHONUNBUFFERED=1
- C_FORCE_ROOT=1
- DB_CONNECTION_STRING=${VIDEO_FLOW_DB_CONNECTION_STRING}
depends_on:
redis:
condition: service_healthy
command: celery -A api.celery_app worker --loglevel=info --concurrency=2 -Q default,video,audio
restart: unless-stopped
deploy:
# 生产环境可以增加副本数
replicas: 1
resources:
limits:
cpus: '2'
memory: 4G
# ============================================================
# React Frontend - 前端服务 (生产环境)
# ============================================================
web:
build:
context: ./web
dockerfile: Dockerfile
ports:
- "3000:80"
depends_on:
- api
restart: unless-stopped
# ============================================================
# Streamlit - 原有调试界面 (保留在 8503)
# 连接到 8502 的数据目录,复用历史素材
# ============================================================
streamlit:
build:
context: .
dockerfile: Dockerfile
ports:
# 默认对外保持 8502兼容历史入口容器内仍运行在 8503
- "${STREAMLIT_PORT:-8502}:8503"
volumes:
- ./app.py:/app/app.py:ro
- ./modules:/app/modules:ro
- ./config.py:/app/config.py:ro
- ./assets:/app/assets
- ./output:/app/output
# 挂载 8502 服务的 temp 目录,复用历史项目数据
- /opt/gloda-factory/temp:/app/temp
- ./video_flow.db:/app/video_flow.db
- /var/run/postgresql:/var/run/postgresql
- /root/video-flow/temp:/legacy/temp:ro
- /root/video-flow/output:/legacy/output:ro
environment:
- PYTHONUNBUFFERED=1
- DB_CONNECTION_STRING=${VIDEO_FLOW_DB_CONNECTION_STRING}
- WEB_BASE_URL=${WEB_BASE_URL}
command: streamlit run app.py --server.port 8503 --server.address 0.0.0.0
restart: unless-stopped
volumes:
redis_data:
networks:
default:
name: video-flow-network

View File

@@ -21,7 +21,8 @@ echo ""
echo "📌 访问地址:"
echo " - 视频编辑器: http://localhost:3000"
echo " - API 文档: http://localhost:8000/api/docs"
echo " - 工作流控制台: http://localhost:8503"
STREAMLIT_PORT="${STREAMLIT_PORT:-8502}"
echo " - 工作流控制台: http://localhost:${STREAMLIT_PORT}"
echo ""
echo "📊 查看日志: docker-compose logs -f"
echo "🔧 扩展 Worker: docker-compose scale worker=3"