Files
meijiaka-zy/python-api/app/config.py
T

209 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
配置管理 - Pydantic Settings
==========================
所有配置项通过环境变量或 .env 文件注入。
"""
from functools import lru_cache
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""应用配置"""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
arbitrary_types_allowed=True,
)
# 应用基础配置
APP_NAME: str = Field(default="美家卡智影 API", description="应用名称")
APP_VERSION: str = Field(default="0.1.0", description="应用版本")
DEBUG: bool = Field(default=True, description="调试模式")
ENV: Literal["development", "staging", "production"] = Field(
default="development", description="运行环境"
)
# 服务器配置
HOST: str = Field(default="0.0.0.0", description="监听地址")
PORT: int = Field(default=8000, description="监听端口")
WORKERS: int = Field(default=1, description="工作进程数(生产环境建议 > 1)")
# 数据库配置(统一使用 PostgreSQL)
DATABASE_URL: str = Field(
default="postgresql+asyncpg://postgres:postgres@localhost:5432/meijiaka",
description="数据库连接字符串(PostgreSQL)",
)
DATABASE_POOL_SIZE: int = Field(default=10, description="数据库连接池大小")
DATABASE_MAX_OVERFLOW: int = Field(default=20, description="连接池溢出上限")
# Redis 配置
REDIS_HOST: str = Field(
default="localhost",
description="Redis 主机地址",
)
REDIS_PORT: int = Field(
default=6379,
description="Redis 端口",
)
REDIS_DB: int = Field(
default=0,
description="Redis 数据库编号",
)
REDIS_PASSWORD: str | None = Field(
default=None,
description="Redis 密码(无密码请留空)",
)
# 安全配置
SECRET_KEY: str = Field(
default="your-secret-key-here-change-in-production",
description="JWT 签名密钥(生产环境必须修改)",
)
ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(
default=60 * 24 * 7, # 7 天
description="访问令牌过期时间(分钟)",
)
ALGORITHM: str = Field(default="HS256", description="JWT 算法")
# CORS 配置
CORS_ORIGINS: str = Field(
default="http://localhost:1420,http://127.0.0.1:1420,http://localhost:8080,http://127.0.0.1:8080",
description="允许的跨域来源(逗号分隔)",
)
# AI 模型配置
# 字节跳动 - 火山方舟
# 文档:https://www.volcengine.com/docs/82379/1399009
VOLCENGINE_API_KEY: str | None = Field(default=None, description="火山方舟 API Key")
VOLCENGINE_BASE_URL: str = Field(
default="https://ark.cn-beijing.volces.com/api/v3",
description="火山方舟 Base URL",
)
VOLCENGINE_MODEL: str = Field(
default="doubao-seed-2-0-lite-260215",
description="火山方舟默认模型(Model ID",
)
# 火山引擎音视频字幕服务
VOLCENGINE_CAPTION_APPID: str | None = Field(default=None, description="火山字幕 AppID")
VOLCENGINE_CAPTION_TOKEN: str | None = Field(default=None, description="火山字幕 Token")
# OpenAI
OPENAI_API_KEY: str | None = Field(default=None, description="OpenAI API Key")
OPENAI_BASE_URL: str = Field(default="https://api.openai.com/v1", description="OpenAI Base URL")
OPENAI_DEFAULT_MODEL: str = Field(default="gpt-3.5-turbo", description="默认 OpenAI 模型")
# 文心一言 (百度)
WENXIN_API_KEY: str | None = Field(default=None, description="文心一言 API Key")
WENXIN_SECRET_KEY: str | None = Field(default=None, description="文心一言 Secret Key")
# 通义千问 (阿里云)
QIANWEN_API_KEY: str | None = Field(default=None, description="通义千问 API Key")
# 数字人服务配置
DIGITAL_HUMAN_PROVIDER: Literal["heygen", "did", "mock"] = Field(
default="mock",
description="数字人服务提供商",
)
HEYGEN_API_KEY: str | None = Field(default=None, description="HeyGen API Key")
DID_API_KEY: str | None = Field(default=None, description="D-ID API Key")
# KlingAI 配置
KLINGAI_ACCESS_KEY: str | None = Field(default=None, description="KlingAI Access Key")
KLINGAI_SECRET_KEY: str | None = Field(default=None, description="KlingAI Secret Key")
# 七牛云存储配置
QINIU_ACCESS_KEY: str | None = Field(default=None, description="七牛云 Access Key")
QINIU_SECRET_KEY: str | None = Field(default=None, description="七牛云 Secret Key")
QINIU_VIDEO_BUCKET: str = Field(default="media-liche", description="视频存储 Bucket")
QINIU_VIDEO_DOMAIN: str = Field(default="media.liche.cn", description="视频存储域名")
QINIU_IMAGE_BUCKET: str = Field(default="img-liche", description="图片存储 Bucket")
QINIU_IMAGE_DOMAIN: str = Field(default="img.liche.cn", description="图片存储域名")
# AnyToCopy 文案提取服务
ANYTOCOPY_API_KEY: str | None = Field(default=None, description="AnyToCopy API Key")
ANYTOCOPY_API_SECRET: str | None = Field(default=None, description="AnyToCopy API Secret")
ANYTOCOPY_BASE_URL: str = Field(
default="https://api.anytocopy.com/vip/open-api/v1",
description="AnyToCopy Base URL",
)
# 视频生成配置
DEFAULT_EMPTY_SHOT_VOICE_ID: str = Field(
default="829826792415842333",
description="空镜视频默认音色ID(Kling官方音色,默认:播报男声)",
)
# Async Engine 槽位配置
KLING_VIDEO_MAX_CONCURRENT: int = Field(default=18, description="Kling视频生成最大并发数")
KLING_IMAGE_MAX_CONCURRENT: int = Field(default=9, description="Kling图片生成最大并发数")
KLING_AVATAR_MAX_CONCURRENT: int = Field(default=2, description="Kling形象克隆最大并发数")
ANYTOCOPY_MAX_CONCURRENT: int = Field(default=5, description="AnyToCopy文案提取最大并发数")
VOLC_SUBTITLE_MAX_CONCURRENT: int = Field(default=5, description="火山字幕生成最大并发数")
# 任务超时配置(秒)
KLING_VIDEO_TIMEOUT_PER_SHOT: int = Field(
default=600, description="Kling视频单镜头超时时间(秒)"
)
KLING_IMAGE_TIMEOUT: int = Field(default=120, description="Kling图片生成超时时间(秒)")
VOLC_SUBTITLE_TIMEOUT: int = Field(default=600, description="火山字幕生成超时时间(秒)")
# AnyToCopy 轮询配置
ANYTOCOPY_POLL_INTERVAL: float = Field(default=3.0, description="AnyToCopy轮询间隔(秒)")
ANYTOCOPY_MAX_POLL: int = Field(default=60, description="AnyToCopy最大轮询次数")
# 日志配置
LOG_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = Field(
default="DEBUG",
description="日志级别",
)
@property
def cors_origins_list(self) -> list[str]:
"""将 CORS_ORIGINS 字符串解析为列表"""
return [origin.strip() for origin in self.CORS_ORIGINS.split(",")]
@property
def use_redis(self) -> bool:
"""是否使用 Redis"""
return bool(self.REDIS_HOST)
@lru_cache
def get_settings() -> Settings:
"""获取配置单例(带缓存)"""
settings = Settings()
# 生产环境安全检查
if settings.ENV == "production":
default_keys = [
"your-secret-key-here-change-in-production",
"change-me-in-production",
"secret-key",
"",
]
if not settings.SECRET_KEY or settings.SECRET_KEY in default_keys:
raise ValueError(
"生产环境必须设置强随机 SECRET_KEY!"
"请在 .env 文件中设置一个随机字符串(至少 32 位)。"
)
# 检查 CORS 配置
if settings.CORS_ORIGINS and "localhost" in settings.CORS_ORIGINS.lower():
import warnings
warnings.warn(
"生产环境 CORS 配置中包含 localhost,建议限制为实际域名",
RuntimeWarning,
stacklevel=2,
)
return settings