diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e6a5510 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +# 使用Python 3.9.6基础镜像 +FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/python:3.12.9-slim + +# 设置工作目录 +WORKDIR /app + +# 设置环境变量 +ENV PYTHONPATH=/app +ENV PYTHONUNBUFFERED=1 + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + gcc \ + default-libmysqlclient-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# 复制依赖文件 +COPY requirements-prod.txt . + +# 安装Python依赖 +RUN pip install --no-cache-dir -r requirements-prod.txt + +# 复制应用代码 +COPY . . + +# 暴露端口 +EXPOSE 8001 + +# 启动命令 +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001"] \ No newline at end of file diff --git a/app/core/config.py b/app/core/config.py index 7056185..fda57d4 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -22,7 +22,7 @@ MARKDOWN_DIR.mkdir(exist_ok=True) DATABASE_CONFIG = { 'host': os.getenv('DB_HOST', 'localhost'), 'user': os.getenv('DB_USER', 'root'), - 'password': os.getenv('DB_PASSWORD', ''), + 'password': os.getenv('DB_PASSWORD', 'sagacity'), 'database': os.getenv('DB_NAME', 'imeeting'), 'port': int(os.getenv('DB_PORT', '3306')), 'charset': 'utf8mb4' @@ -31,8 +31,7 @@ DATABASE_CONFIG = { # API配置 API_CONFIG = { 'host': os.getenv('API_HOST', '0.0.0.0'), - 'port': int(os.getenv('API_PORT', '8000')), - 'cors_origins': os.getenv('CORS_ORIGINS', 'http://localhost:5173').split(',') + 'port': int(os.getenv('API_PORT', '8000')) } # 七牛云配置 diff --git a/app/core/database.py b/app/core/database.py index dc438d5..cfbab7c 100644 --- a/app/core/database.py +++ b/app/core/database.py @@ -2,24 +2,14 @@ from fastapi import HTTPException import mysql.connector from mysql.connector import Error +from app.core.config import DATABASE_CONFIG from contextlib import contextmanager -DB_CONFIG = { - 'host': 'localhost', - 'database': 'imeeting', - 'user': 'root', - 'password': 'sagacity', - 'port': 3306, - 'charset': 'utf8mb4', - 'autocommit': False, # 禁用自动提交 - 'consume_results': True # 自动消费未读结果 -} - @contextmanager def get_db_connection(): connection = None try: - connection = mysql.connector.connect(**DB_CONFIG) + connection = mysql.connector.connect(**DATABASE_CONFIG) yield connection except Error as e: print(f"数据库连接错误: {e}") diff --git a/deploy-prod.sh b/deploy-prod.sh new file mode 100755 index 0000000..e2d875a --- /dev/null +++ b/deploy-prod.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +echo "🚀 开始部署iMeeting后端服务..." + +# 检查虚拟环境 +if [ ! -d "venv" ]; then + echo "❌ 虚拟环境不存在!请先创建虚拟环境" + exit 1 +fi + +echo "✅ 发现虚拟环境,继续部署..." + +# 停止并删除现有容器 +echo "📦 停止现有容器..." +docker-compose -f docker-compose.prod.yml down + +# 构建新镜像 +echo "🔨 构建Docker镜像..." +docker-compose -f docker-compose.prod.yml build --no-cache + +# 启动服务 +echo "▶️ 启动服务..." +docker-compose -f docker-compose.prod.yml up -d + +# 检查服务状态 +echo "🔍 检查服务状态..." +sleep 10 +docker-compose -f docker-compose.prod.yml ps + +# 检查健康状态 +echo "🏥 检查健康状态..." +curl -f http://localhost:8001/health && echo "✅ 后端服务健康检查通过" || echo "❌ 后端服务健康检查失败" + +echo "" +echo "🎉 部署完成!" +echo "🔧 后端服务访问地址: http://localhost:8001" +echo "📊 查看日志: docker-compose -f docker-compose.prod.yml logs -f" +echo "🛑 停止服务: docker-compose -f docker-compose.prod.yml down" +echo "" +echo "💡 提示:如需更新后端,请:" +echo " 1. 修改代码后运行 ./deploy-prod.sh 重新部署" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..844430d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,59 @@ +version: '3.8' + +services: + imeeting-backend: + build: + context: . + dockerfile: Dockerfile + ports: + - "8001:8001" + environment: + # Python运行环境 + - PYTHONPATH=/app + - PYTHONUNBUFFERED=1 + + # 数据库配置 + - DB_HOST=host.docker.internal + - DB_USER=root + - DB_PASSWORD=sagacity + - DB_NAME=imeeting + - DB_PORT=3306 + + # Redis配置 + - REDIS_HOST=host.docker.internal + - REDIS_PORT=6379 + - REDIS_DB=6 + - REDIS_PASSWORD= + + # API配置 + - API_HOST=0.0.0.0 + - API_PORT=8001 + + # 应用配置 + - BASE_URL=http://imeeting.unisspace.com + + # 七牛云配置 + - QINIU_ACCESS_KEY=A0tp96HCtg-wZCughTgi5vc2pJnw3btClwxRE_e8 + - QINIU_SECRET_KEY=Lj-MSHpaVbmzpS86kMIjmwikvYOT9iPBjCk9hm6k + - QINIU_BUCKET=imeeting + - QINIU_DOMAIN=t0vogyxkz.hn-bkt.clouddn.com + + # LLM配置 + - QWEN_API_KEY=sk-c2bf06ea56b4491ea3d1e37fdb472b8f + - LLM_MODEL_NAME=qwen-plus + - LLM_API_URL=https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation + - LLM_MAX_TOKENS=2000 + - LLM_TEMPERATURE=0.7 + - LLM_TOP_P=0.9 + + volumes: + # 挂载上传目录保持数据持久化 + - ./uploads:/app/uploads + restart: unless-stopped + container_name: imeeting-backend + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8001/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s \ No newline at end of file diff --git a/main.py b/main.py index 7be2e59..6b02c71 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,9 @@ import uvicorn -from fastapi import FastAPI +from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.api.endpoints import auth, users, meetings -from app.core.config import UPLOAD_DIR, API_CONFIG +from app.core.config import UPLOAD_DIR, API_CONFIG, MAX_FILE_SIZE import os app = FastAPI( @@ -12,10 +12,23 @@ app = FastAPI( version="1.0.0" ) +# 添加请求体大小检查中间件 +@app.middleware("http") +async def check_content_size(request: Request, call_next): + # 检查Content-Length头 + content_length = request.headers.get("content-length") + if content_length: + content_length = int(content_length) + if content_length > MAX_FILE_SIZE: + raise HTTPException(status_code=413, detail=f"Request entity too large. Maximum size is {MAX_FILE_SIZE//1024//1024}MB") + + response = await call_next(request) + return response + # 添加CORS中间件 app.add_middleware( CORSMiddleware, - allow_origins=API_CONFIG['cors_origins'], + # allow_origins=API_CONFIG['cors_origins'], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], @@ -34,5 +47,26 @@ app.include_router(meetings.router, prefix="/api", tags=["Meetings"]) def read_root(): return {"message": "Welcome to iMeeting API"} +@app.get("/health") +def health_check(): + """健康检查端点""" + return { + "status": "healthy", + "service": "iMeeting API", + "version": "1.0.0" + } + if __name__ == "__main__": - uvicorn.run(app, host=API_CONFIG['host'], port=API_CONFIG['port']) \ No newline at end of file + # 简单的uvicorn配置,避免参数冲突 + uvicorn.run( + "main:app", + host=API_CONFIG['host'], + port=API_CONFIG['port'], + limit_max_requests=1000, + timeout_keep_alive=30, + reload=True, + # 设置更大的请求体限制以支持音频文件上传 + h11_max_incomplete_event_size=104857600, # 100MB + + + ) \ No newline at end of file diff --git a/requirements-prod.txt b/requirements-prod.txt new file mode 100644 index 0000000..e8230bc --- /dev/null +++ b/requirements-prod.txt @@ -0,0 +1,58 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.12.15 +aiosignal==1.4.0 +annotated-types==0.7.0 +anyio==3.7.1 +async-timeout==5.0.1 +attrs==25.3.0 +bcrypt==4.3.0 +certifi==2025.8.3 +cffi==1.17.1 +charset-normalizer==3.4.3 +click==8.1.8 +cryptography==45.0.6 +dashscope==1.24.1 +distro==1.9.0 +dnspython==2.7.0 +ecdsa==0.19.1 +email_validator==2.2.0 +exceptiongroup==1.3.0 +fastapi==0.104.1 +frozenlist==1.7.0 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +jiter==0.10.0 +multidict==6.6.4 +mysql-connector-python==8.2.0 +openai==1.99.9 +passlib==1.7.4 +propcache==0.3.2 +protobuf==4.21.12 +pyasn1==0.6.1 +pycparser==2.22 +pydantic==2.5.0 +pydantic_core==2.14.1 +PyJWT==2.10.1 +python-dotenv==1.1.1 +python-jose==3.5.0 +python-multipart==0.0.6 +PyYAML==6.0.2 +qiniu==7.17.0 +redis==6.4.0 +requests==2.32.4 +rsa==4.9.1 +six==1.17.0 +sniffio==1.3.1 +starlette==0.27.0 +tqdm==4.67.1 +typing_extensions==4.14.1 +urllib3==2.5.0 +uvicorn==0.24.0 +uvloop==0.21.0 +watchfiles==1.1.0 +websocket-client==1.8.0 +websockets==15.0.1 +yarl==1.20.1