From 749ef7f55affd7cae74903158206f37b5ecd3370 Mon Sep 17 00:00:00 2001 From: Xiaoxia AI Date: Mon, 15 Jun 2026 18:32:05 +0800 Subject: [PATCH] chore: add production environment configuration system --- .env.example | 86 +++++++++++- .env.production | 85 ++++++++++++ .env.staging | 85 ++++++++++++ .gitignore | 5 +- apps/api/app/core/config.py | 126 +++++++++++++++++- apps/api/app/core/storage.py | 29 ++-- apps/api/main.py | 4 +- docs/CONFIGURATION.md | 247 +++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 9 files changed, 637 insertions(+), 31 deletions(-) create mode 100644 .env.production create mode 100644 .env.staging create mode 100644 docs/CONFIGURATION.md diff --git a/.env.example b/.env.example index 7a76657e6..be61a4a54 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,85 @@ -# Environment example -APP_ENV=development +# ======================= +# 应用基础配置 +# ======================= +APP_ENV=development # development / staging / production APP_NAME=xiaoxia-saas +APP_VERSION=0.1.0 +DEBUG=true + +# ======================= +# API 服务配置 +# ======================= API_HOST=0.0.0.0 API_PORT=8000 +API_PREFIX=/api/v1 + +# ======================= +# Web 前端配置 +# ======================= WEB_PORT=3000 -POSTGRES_URL=postgresql+psycopg://postgres:postgres@localhost:5432/xiaoxia_saas +WEB_URL=http://localhost:3000 + +# ======================= +# 数据库配置 +# ======================= +DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/xiaoxia_saas +DATABASE_POOL_SIZE=20 +DATABASE_MAX_OVERFLOW=40 +DATABASE_POOL_TIMEOUT=30 +DATABASE_POOL_RECYCLE=3600 + +# ======================= +# Redis 配置 +# ======================= REDIS_URL=redis://localhost:6379/0 -OBJECT_STORAGE_PROVIDER=minio -OBJECT_STORAGE_ENDPOINT=http://localhost:9000 -OBJECT_STORAGE_BUCKET=xiaoxia-saas +REDIS_MAX_CONNECTIONS=50 + +# ======================= +# Celery Worker 配置 +# ======================= +CELERY_BROKER_URL=redis://localhost:6379/0 +CELERY_RESULT_BACKEND=redis://localhost:6379/1 +CELERY_WORKER_CONCURRENCY=4 +CELERY_WORKER_MAX_TASKS_PER_CHILD=1000 + +# ======================= +# MinIO 对象存储配置 +# ======================= +MINIO_ENDPOINT=localhost:9000 +MINIO_ACCESS_KEY=CHANGE_ME +MINIO_SECRET_KEY=CHANGE_ME +MINIO_BUCKET=xiaoxia-assets +MINIO_SECURE=false +MINIO_PUBLIC_URL=http://localhost:9000 + +# ======================= +# 日志配置 +# ======================= +LOG_LEVEL=INFO # DEBUG / INFO / WARNING / ERROR / CRITICAL +LOG_FORMAT=json # json / text +LOG_FILE=/var/log/xiaoxia-saas/app.log + +# ======================= +# CORS 配置 +# ======================= +CORS_ORIGINS=http://localhost:3000,http://localhost:8000 +CORS_ALLOW_CREDENTIALS=true + +# ======================= +# 文件上传限制 +# ======================= +MAX_UPLOAD_SIZE_MB=1000 +ALLOWED_FILE_TYPES=video/mp4,video/quicktime,video/x-msvideo,audio/mpeg,audio/wav,image/jpeg,image/png,image/gif + +# ======================= +# 安全配置 +# ======================= +SECRET_KEY=change-me-in-production-to-a-random-string-at-least-32-chars +ACCESS_TOKEN_EXPIRE_MINUTES=60 +REFRESH_TOKEN_EXPIRE_DAYS=7 + +# ======================= +# 监控与追踪(可选) +# ======================= +# SENTRY_DSN= +# PROMETHEUS_PORT=9090 diff --git a/.env.production b/.env.production new file mode 100644 index 000000000..2d2cd5b41 --- /dev/null +++ b/.env.production @@ -0,0 +1,85 @@ +# ======================= +# 生产环境配置 +# ======================= +APP_ENV=production +APP_NAME=xiaoxia-saas +APP_VERSION=0.1.0 +DEBUG=false + +# ======================= +# API 服务配置 +# ======================= +API_HOST=0.0.0.0 +API_PORT=8000 +API_PREFIX=/api/v1 + +# ======================= +# Web 前端配置 +# ======================= +WEB_PORT=3000 +WEB_URL=https://xiaoxiajianji.com + +# ======================= +# 数据库配置 +# ======================= +DATABASE_URL=postgresql+psycopg://postgres:CHANGE_ME@postgres:5432/xiaoxia_saas_production +DATABASE_POOL_SIZE=50 +DATABASE_MAX_OVERFLOW=100 +DATABASE_POOL_TIMEOUT=30 +DATABASE_POOL_RECYCLE=3600 + +# ======================= +# Redis 配置 +# ======================= +REDIS_URL=redis://redis:6379/0 +REDIS_MAX_CONNECTIONS=100 + +# ======================= +# Celery Worker 配置 +# ======================= +CELERY_BROKER_URL=redis://redis:6379/0 +CELERY_RESULT_BACKEND=redis://redis:6379/1 +CELERY_WORKER_CONCURRENCY=8 +CELERY_WORKER_MAX_TASKS_PER_CHILD=1000 + +# ======================= +# MinIO 对象存储配置 +# ======================= +MINIO_ENDPOINT=47.98.113.167:9000 +MINIO_ACCESS_KEY=admin +MINIO_SECRET_KEY=xiaoxia2026 +MINIO_BUCKET=xiaoxia-assets +MINIO_SECURE=false +MINIO_PUBLIC_URL=http://47.98.113.167:9000 + +# ======================= +# 日志配置 +# ======================= +LOG_LEVEL=INFO +LOG_FORMAT=json +LOG_FILE=/var/log/xiaoxia-saas/app.log + +# ======================= +# CORS 配置 +# ======================= +CORS_ORIGINS=https://xiaoxiajianji.com,https://api.xiaoxiajianji.com +CORS_ALLOW_CREDENTIALS=true + +# ======================= +# 文件上传限制 +# ======================= +MAX_UPLOAD_SIZE_MB=2000 +ALLOWED_FILE_TYPES=video/mp4,video/quicktime,video/x-msvideo,audio/mpeg,audio/wav,image/jpeg,image/png,image/gif + +# ======================= +# 安全配置 +# ======================= +SECRET_KEY=CHANGE_ME_TO_RANDOM_STRING_AT_LEAST_32_CHARS_IN_PRODUCTION +ACCESS_TOKEN_EXPIRE_MINUTES=60 +REFRESH_TOKEN_EXPIRE_DAYS=7 + +# ======================= +# 监控与追踪(可选) +# ======================= +# SENTRY_DSN= +# PROMETHEUS_PORT=9090 diff --git a/.env.staging b/.env.staging new file mode 100644 index 000000000..d58e723c1 --- /dev/null +++ b/.env.staging @@ -0,0 +1,85 @@ +# ======================= +# Staging 环境配置 +# ======================= +APP_ENV=staging +APP_NAME=xiaoxia-saas +APP_VERSION=0.1.0 +DEBUG=true + +# ======================= +# API 服务配置 +# ======================= +API_HOST=0.0.0.0 +API_PORT=8000 +API_PREFIX=/api/v1 + +# ======================= +# Web 前端配置 +# ======================= +WEB_PORT=3000 +WEB_URL=http://47.98.113.167:3001 + +# ======================= +# 数据库配置 +# ======================= +DATABASE_URL=postgresql+psycopg://postgres:postgres@postgres:5432/xiaoxia_saas_staging +DATABASE_POOL_SIZE=20 +DATABASE_MAX_OVERFLOW=40 +DATABASE_POOL_TIMEOUT=30 +DATABASE_POOL_RECYCLE=3600 + +# ======================= +# Redis 配置 +# ======================= +REDIS_URL=redis://redis:6379/1 +REDIS_MAX_CONNECTIONS=50 + +# ======================= +# Celery Worker 配置 +# ======================= +CELERY_BROKER_URL=redis://redis:6379/1 +CELERY_RESULT_BACKEND=redis://redis:6379/2 +CELERY_WORKER_CONCURRENCY=4 +CELERY_WORKER_MAX_TASKS_PER_CHILD=1000 + +# ======================= +# MinIO 对象存储配置 +# ======================= +MINIO_ENDPOINT=47.98.113.167:9000 +MINIO_ACCESS_KEY=CHANGE_ME_STAGING +MINIO_SECRET_KEY=CHANGE_ME_STAGING +MINIO_BUCKET=xiaoxia-assets +MINIO_SECURE=false +MINIO_PUBLIC_URL=http://47.98.113.167:9000 + +# ======================= +# 日志配置 +# ======================= +LOG_LEVEL=DEBUG +LOG_FORMAT=json +LOG_FILE=/var/log/xiaoxia-saas/staging.log + +# ======================= +# CORS 配置 +# ======================= +CORS_ORIGINS=http://47.98.113.167:3001,http://47.98.113.167:8001 +CORS_ALLOW_CREDENTIALS=true + +# ======================= +# 文件上传限制 +# ======================= +MAX_UPLOAD_SIZE_MB=1000 +ALLOWED_FILE_TYPES=video/mp4,video/quicktime,video/x-msvideo,audio/mpeg,audio/wav,image/jpeg,image/png,image/gif + +# ======================= +# 安全配置 +# ======================= +SECRET_KEY=staging-secret-key-not-for-production +ACCESS_TOKEN_EXPIRE_MINUTES=60 +REFRESH_TOKEN_EXPIRE_DAYS=7 + +# ======================= +# 监控与追踪(可选) +# ======================= +# SENTRY_DSN= +# PROMETHEUS_PORT=9090 diff --git a/.gitignore b/.gitignore index 1e98cdac4..0b3d66442 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,11 @@ ruff_cache/ # Env / secrets .env -.env.* +.env.local +.env.development +.env.production # 生产环境配置不提交(包含敏感信息) !.env.example +!.env.staging # staging 配置可以提交 # OS / editor .DS_Store diff --git a/apps/api/app/core/config.py b/apps/api/app/core/config.py index bf2120b69..c0c31cc90 100644 --- a/apps/api/app/core/config.py +++ b/apps/api/app/core/config.py @@ -1,11 +1,125 @@ -from pydantic import BaseModel +from pydantic_settings import BaseSettings, SettingsConfigDict +from typing import Optional +import os -class AppSettings(BaseModel): - app_name: str = "xiaoxia-saas-api" - app_env: str = "development" - api_prefix: str = "/api" +class AppSettings(BaseSettings): + """应用配置""" + + # 基础配置 + app_name: str = "xiaoxia-saas" + app_env: str = "development" # development / staging / production + app_version: str = "0.1.0" + debug: bool = True + + # API 配置 + api_host: str = "0.0.0.0" + api_port: int = 8000 + api_prefix: str = "/api/v1" + + # 数据库配置 + database_url: str = "postgresql+psycopg://postgres:postgres@localhost:5432/xiaoxia_saas" + database_pool_size: int = 20 + database_max_overflow: int = 40 + database_pool_timeout: int = 30 + database_pool_recycle: int = 3600 + + # Redis 配置 + redis_url: str = "redis://localhost:6379/0" + redis_max_connections: int = 50 + + # Celery 配置 + celery_broker_url: str = "redis://localhost:6379/0" + celery_result_backend: str = "redis://localhost:6379/1" + celery_worker_concurrency: int = 4 + celery_worker_max_tasks_per_child: int = 1000 + + # MinIO 配置 + minio_endpoint: str = "localhost:9000" + minio_access_key: str = "admin" + minio_secret_key: str = "xiaoxia2026" + minio_bucket: str = "xiaoxia-assets" + minio_secure: bool = False + minio_public_url: str = "http://localhost:9000" + + # 日志配置 + log_level: str = "INFO" + log_format: str = "json" # json / text + log_file: Optional[str] = None + + # CORS 配置 + cors_origins: str = "http://localhost:3000,http://localhost:8000" + cors_allow_credentials: bool = True + + # 文件上传限制 + max_upload_size_mb: int = 1000 + allowed_file_types: str = "video/mp4,video/quicktime,video/x-msvideo,audio/mpeg,audio/wav,image/jpeg,image/png,image/gif" + + # 安全配置 + secret_key: str = "change-me-in-production" + access_token_expire_minutes: int = 60 + refresh_token_expire_days: int = 7 + + # 监控配置(可选) + sentry_dsn: Optional[str] = None + prometheus_port: Optional[int] = None + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + case_sensitive=False, + extra="ignore", + ) + + @property + def cors_origins_list(self) -> list[str]: + """解析 CORS origins 为列表""" + return [origin.strip() for origin in self.cors_origins.split(",")] + + @property + def allowed_file_types_list(self) -> list[str]: + """解析允许的文件类型为列表""" + return [ft.strip() for ft in self.allowed_file_types.split(",")] + + @property + def is_production(self) -> bool: + """是否为生产环境""" + return self.app_env == "production" + + @property + def is_staging(self) -> bool: + """是否为 staging 环境""" + return self.app_env == "staging" + + @property + def is_development(self) -> bool: + """是否为开发环境""" + return self.app_env == "development" + + +# 全局配置实例 +_settings: Optional[AppSettings] = None def get_settings() -> AppSettings: - return AppSettings() + """获取配置实例(单例模式)""" + global _settings + if _settings is None: + # 根据环境加载不同的 .env 文件 + env = os.getenv("APP_ENV", "development") + env_file = f".env.{env}" if env != "development" else ".env" + + # 如果环境特定的配置文件存在,则使用它 + if os.path.exists(env_file): + _settings = AppSettings(_env_file=env_file) + else: + _settings = AppSettings() + + return _settings + + +def reload_settings(): + """重新加载配置(用于测试)""" + global _settings + _settings = None + return get_settings() diff --git a/apps/api/app/core/storage.py b/apps/api/app/core/storage.py index fe3803d3e..1442bfa83 100644 --- a/apps/api/app/core/storage.py +++ b/apps/api/app/core/storage.py @@ -3,27 +3,24 @@ from minio import Minio from minio.error import S3Error from typing import BinaryIO import os +from app.core.config import get_settings class MinIOService: """MinIO storage service.""" - def __init__( - self, - endpoint: str = "47.98.113.167:9000", - access_key: str = "admin", - secret_key: str = "xiaoxia2026", - bucket_name: str = "xiaoxia-assets", - secure: bool = False, - ): - """Initialize MinIO client.""" + def __init__(self): + """Initialize MinIO client from settings.""" + settings = get_settings() + self.client = Minio( - endpoint, - access_key=access_key, - secret_key=secret_key, - secure=secure, + settings.minio_endpoint, + access_key=settings.minio_access_key, + secret_key=settings.minio_secret_key, + secure=settings.minio_secure, ) - self.bucket_name = bucket_name + self.bucket_name = settings.minio_bucket + self.public_url = settings.minio_public_url self._ensure_bucket() def _ensure_bucket(self): @@ -81,14 +78,14 @@ class MinIOService: ) # Return public URL - return f"http://47.98.113.167:9000/{self.bucket_name}/{storage_key}" + return f"{self.public_url}/{self.bucket_name}/{storage_key}" except S3Error as e: raise Exception(f"Failed to upload file: {e}") def get_url(self, storage_key: str) -> str: """Get public URL for a storage key.""" - return f"http://47.98.113.167:9000/{self.bucket_name}/{storage_key}" + return f"{self.public_url}/{self.bucket_name}/{storage_key}" def delete_file(self, storage_key: str): """Delete file from MinIO.""" diff --git a/apps/api/main.py b/apps/api/main.py index c4dd5014c..55194ed4e 100644 --- a/apps/api/main.py +++ b/apps/api/main.py @@ -52,8 +52,8 @@ def create_app() -> FastAPI: # CORS middleware app.add_middleware( CORSMiddleware, - allow_origins=["*"], # TODO: Configure for production - allow_credentials=True, + allow_origins=settings.cors_origins_list, + allow_credentials=settings.cors_allow_credentials, allow_methods=["*"], allow_headers=["*"], ) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 000000000..6981badb6 --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,247 @@ +# 环境配置指南 + +## 环境类型 + +小虾 SaaS 支持三种环境: + +- **Development(开发环境)**:本地开发使用 +- **Staging(测试环境)**:用于测试和预发布 +- **Production(生产环境)**:正式生产环境 + +--- + +## 配置文件 + +### 1. `.env.example` - 配置模板 +包含所有可配置项的示例,用于参考和初始化。 + +### 2. `.env` - 开发环境配置(本地) +本地开发时使用,不提交到 Git。 + +### 3. `.env.staging` - Staging 环境配置 +测试环境配置,可以提交到 Git。 + +### 4. `.env.production` - 生产环境配置 +生产环境配置,**敏感信息需要单独管理,不要提交到 Git**。 + +--- + +## 使用方法 + +### 方法 1:使用环境变量指定环境 + +```bash +# 开发环境(默认) +python -m uvicorn apps.api.main:app --reload + +# Staging 环境 +export APP_ENV=staging +python -m uvicorn apps.api.main:app --host 0.0.0.0 --port 8000 + +# 生产环境 +export APP_ENV=production +python -m uvicorn apps.api.main:app --host 0.0.0.0 --port 8000 +``` + +### 方法 2:直接指定配置文件 + +```bash +# 使用 .env.staging +cp .env.staging .env +python -m uvicorn apps.api.main:app + +# 使用 .env.production +cp .env.production .env +python -m uvicorn apps.api.main:app +``` + +--- + +## 配置项说明 + +### 基础配置 +- `APP_ENV`: 环境类型(development / staging / production) +- `APP_NAME`: 应用名称 +- `DEBUG`: 是否启用调试模式 + +### API 配置 +- `API_HOST`: API 监听地址 +- `API_PORT`: API 端口 +- `API_PREFIX`: API 路由前缀(如 /api/v1) + +### 数据库配置 +- `DATABASE_URL`: PostgreSQL 连接字符串 +- `DATABASE_POOL_SIZE`: 连接池大小 +- `DATABASE_MAX_OVERFLOW`: 最大溢出连接数 +- `DATABASE_POOL_TIMEOUT`: 连接池超时时间(秒) +- `DATABASE_POOL_RECYCLE`: 连接回收时间(秒) + +### Redis 配置 +- `REDIS_URL`: Redis 连接字符串 +- `REDIS_MAX_CONNECTIONS`: 最大连接数 + +### Celery 配置 +- `CELERY_BROKER_URL`: Celery broker URL +- `CELERY_RESULT_BACKEND`: Celery result backend URL +- `CELERY_WORKER_CONCURRENCY`: Worker 并发数 +- `CELERY_WORKER_MAX_TASKS_PER_CHILD`: 每个 worker 最大任务数 + +### MinIO 配置 +- `MINIO_ENDPOINT`: MinIO 服务地址 +- `MINIO_ACCESS_KEY`: 访问密钥 +- `MINIO_SECRET_KEY`: 私密密钥 +- `MINIO_BUCKET`: 存储桶名称 +- `MINIO_SECURE`: 是否使用 HTTPS +- `MINIO_PUBLIC_URL`: 公开访问 URL + +### 日志配置 +- `LOG_LEVEL`: 日志级别(DEBUG / INFO / WARNING / ERROR / CRITICAL) +- `LOG_FORMAT`: 日志格式(json / text) +- `LOG_FILE`: 日志文件路径 + +### CORS 配置 +- `CORS_ORIGINS`: 允许的跨域来源(逗号分隔) +- `CORS_ALLOW_CREDENTIALS`: 是否允许携带凭证 + +### 安全配置 +- `SECRET_KEY`: 应用密钥(用于 JWT 签名等) +- `ACCESS_TOKEN_EXPIRE_MINUTES`: 访问令牌过期时间 +- `REFRESH_TOKEN_EXPIRE_DAYS`: 刷新令牌过期天数 + +--- + +## 生产环境最佳实践 + +### 1. 不要硬编码敏感信息 +❌ 不要这样: +```python +DATABASE_URL = "postgresql://user:password@localhost/db" +``` + +✅ 应该这样: +```python +from app.core.config import get_settings +settings = get_settings() +database_url = settings.database_url +``` + +### 2. 使用环境变量覆盖 +生产环境的敏感配置应该通过环境变量注入: + +```bash +export DATABASE_URL="postgresql://user:secure_password@prod-db/db" +export SECRET_KEY="very-long-random-string" +export MINIO_SECRET_KEY="another-secure-key" +``` + +### 3. 使用密钥管理工具 +推荐使用: +- Docker Secrets +- Kubernetes Secrets +- AWS Secrets Manager +- HashiCorp Vault + +### 4. 生产环境配置检查清单 + +- [ ] `DEBUG=false` +- [ ] `SECRET_KEY` 使用随机字符串(至少 32 字符) +- [ ] 数据库使用强密码 +- [ ] MinIO 使用强密码 +- [ ] `CORS_ORIGINS` 只包含信任的域名 +- [ ] 日志级别设置为 `INFO` 或 `WARNING` +- [ ] 数据库连接池配置合理 +- [ ] 启用 HTTPS(`MINIO_SECURE=true`) + +--- + +## Docker 部署配置 + +### docker-compose.yml 中使用环境文件 + +```yaml +services: + api: + image: xiaoxia-saas-api:latest + env_file: + - .env.production + environment: + - DATABASE_URL=${DATABASE_URL} # 从环境变量覆盖 +``` + +### Gitea Actions 部署脚本 + +在 `.gitea/workflows/deploy.yml` 中: + +```yaml +- name: Deploy to production + run: | + # 复制环境配置 + cp .env.production /var/lib/xiaoxia-saas-production/.env + + # 启动服务 + cd /var/lib/xiaoxia-saas-production + docker-compose up -d +``` + +--- + +## 配置验证 + +### 查看当前配置 + +```python +from app.core.config import get_settings + +settings = get_settings() +print(f"Environment: {settings.app_env}") +print(f"Debug mode: {settings.debug}") +print(f"Database: {settings.database_url}") +print(f"Is production: {settings.is_production}") +``` + +### 配置测试脚本 + +```bash +# 测试开发环境配置 +python -c "from app.core.config import get_settings; s=get_settings(); print(s.app_env)" + +# 测试 staging 环境配置 +APP_ENV=staging python -c "from app.core.config import get_settings; s=get_settings(); print(s.app_env)" + +# 测试生产环境配置 +APP_ENV=production python -c "from app.core.config import get_settings; s=get_settings(); print(s.app_env)" +``` + +--- + +## 常见问题 + +### Q: 为什么我的配置没有生效? +A: 检查: +1. 环境变量 `APP_ENV` 是否正确设置 +2. 配置文件 `.env.{环境}` 是否存在 +3. 环境变量名是否正确(大小写不敏感) +4. 是否重启了服务 + +### Q: 如何在 Docker 中使用环境配置? +A: 使用 `env_file` 或 `environment` 指定配置: +```yaml +services: + api: + env_file: .env.production +``` + +### Q: 生产环境如何保护敏感配置? +A: +1. 不要提交 `.env.production` 到 Git +2. 使用 Docker Secrets 或 Kubernetes Secrets +3. 通过 CI/CD 注入环境变量 +4. 使用密钥管理工具(Vault, AWS Secrets Manager 等) + +--- + +## 相关文档 + +- [部署文档](DEPLOYMENT.md) +- [API 文档](API.md) +- [开发指南](DEVELOPMENT.md) diff --git a/requirements.txt b/requirements.txt index d43aa52a9..39df8b3c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ fastapi uvicorn pydantic +pydantic-settings celery redis sqlalchemy