134 lines
5.7 KiB
Python
134 lines
5.7 KiB
Python
from functools import lru_cache
|
||
from pathlib import Path
|
||
|
||
from dotenv import load_dotenv
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
# .env 在仓库根(server/ 的上一级),供各部分共用
|
||
_ROOT_DIR = Path(__file__).resolve().parents[2]
|
||
load_dotenv(_ROOT_DIR / ".env")
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""运行时与密钥。模型清单见 config/models.yaml。"""
|
||
|
||
model_config = SettingsConfigDict(
|
||
env_file=str(_ROOT_DIR / ".env"),
|
||
env_file_encoding="utf-8",
|
||
extra="ignore",
|
||
)
|
||
|
||
# ── AI 密钥 ──
|
||
deepseek_api_key: str = ""
|
||
openai_api_key: str = ""
|
||
dashscope_api_key: str = ""
|
||
# 华北2(北京)业务空间时需填:https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1
|
||
dashscope_base_http_api_url: str = ""
|
||
|
||
# ── 运行 ──
|
||
host: str = "127.0.0.1"
|
||
port: int = 8800
|
||
cors_origins: str = ""
|
||
|
||
# ── V2:数据层 ──
|
||
# 本地过渡用 SQLite;上线切 PostgreSQL:postgresql+asyncpg://user:pass@host:5432/ozon_seller
|
||
database_url: str = "sqlite+aiosqlite:///./data/app.db"
|
||
|
||
# 店铺凭证 AES-GCM 加密密钥(shops/categories/publish 冻结链路使用;
|
||
# 鉴权/账户体系已按 V2.1 决策移除,后续引入账户时一并重做)
|
||
secret_key: str = ""
|
||
|
||
# ── V2:七牛(图片存储)──
|
||
qiniu_access_key: str = ""
|
||
qiniu_secret_key: str = ""
|
||
qiniu_bucket: str = ""
|
||
qiniu_domain: str = "" # 绑定域名,如 https://cdn.example.com
|
||
# 为空时用本地文件系统兜底(开发期),不为空时走七牛
|
||
storage_backend: str = "local" # local | qiniu
|
||
|
||
# ── V2:对外地址(插件/前端回写、生成图回调)──
|
||
app_base_url: str = "http://127.0.0.1:8800"
|
||
|
||
# ── 图片与导出资源上限(字节)──
|
||
# 单张图片在后端需要解码/加水印,限制单文件可避免一次请求耗尽内存。
|
||
image_upload_max_bytes: int = 20 * 1024 * 1024
|
||
image_download_max_bytes: int = 20 * 1024 * 1024
|
||
# 图片解码前依据真实宽高限制像素,避免超大图耗尽 Pillow/水印处理内存。
|
||
image_max_pixels: int = 40_000_000
|
||
# 远程图片下载防护:连接/读写/总耗时分别受控;重定向逐跳校验目标地址。
|
||
image_download_connect_timeout: float = 10.0
|
||
image_download_read_timeout: float = 30.0
|
||
image_download_total_timeout: float = 60.0
|
||
image_download_max_redirects: int = 3
|
||
collection_max_images: int = 100
|
||
export_max_images: int = 50
|
||
export_zip_max_bytes: int = 200 * 1024 * 1024
|
||
# ZIP 超过该值自动落临时磁盘,不继续占用进程内存。
|
||
export_zip_memory_threshold_bytes: int = 2 * 1024 * 1024
|
||
|
||
# ── 商品轮播视频(FFmpeg)──
|
||
# Ubuntu 默认安装包会提供同名命令;容器或特殊路径可在 .env 覆盖。
|
||
video_ffmpeg_path: str = "ffmpeg"
|
||
video_ffprobe_path: str = "ffprobe"
|
||
video_width: int = 810
|
||
video_height: int = 1080
|
||
video_fps: int = 30
|
||
video_transition_seconds: float = 0.35
|
||
video_crf: int = 22
|
||
video_max_rate: str = "3M"
|
||
video_buffer_size: str = "6M"
|
||
video_max_bytes: int = 45 * 1024 * 1024
|
||
video_max_duration_seconds: int = 180
|
||
video_render_timeout_seconds: int = 120
|
||
background_job_lease_seconds: int = 300
|
||
|
||
# ── V2.1 套图生图(provider 路由与 image-suite-studio 一致)──
|
||
image_provider: str = "doubao" # 未知模型时的兜底 provider:doubao | tongyi | rightapi
|
||
request_timeout: int = 300 # 单张生图请求超时(秒)
|
||
poll_max_wait: int = 600 # 异步任务轮询上限(秒)
|
||
# 水印徽章源图(图片水印用,可 .env 覆盖)
|
||
watermark_image_path: str = str(Path(__file__).resolve().parents[1] / "assets" / "watermark.jpg")
|
||
|
||
# 豆包 / 火山方舟 Seedream
|
||
ark_api_key: str = ""
|
||
ark_base_url: str = "https://ark.cn-beijing.volces.com/api/v3/images/generations"
|
||
ark_image_model: str = "doubao-seedream-4-5-251128"
|
||
|
||
# 通义 / DashScope(key 复用上方 dashscope_api_key;dashscope_base_http_api_url 是业务空间专用,与此处无关)
|
||
dashscope_model: str = "wan2.7-image-pro"
|
||
dashscope_base_url: str = "" # 留空按模型自动选择万象异步/千问同步端点
|
||
|
||
# RightAPI(OpenAI 兼容中转:gpt-image / nano-banana 系列)
|
||
rightapi_api_key: str = ""
|
||
rightapi_base_url: str = "https://rightapi.ai/draw"
|
||
rightapi_image_model: str = "gpt-image-2"
|
||
rightapi_max_retries: int = 3 # 429/5xx/超时的重试次数(1 = 不重试)
|
||
rightapi_retry_wait: int = 60 # 重试基础等待秒数,按 60→120→240 递增
|
||
|
||
# Pcore(OpenAI 兼容中转:pcore.ai,异步图片任务;模型值带 @pcore 后缀路由到此)
|
||
pcore_api_key: str = ""
|
||
pcore_base_url: str = "https://pcore.ai"
|
||
pcore_max_retries: int = 3 # 429/5xx/超时的重试次数(1 = 不重试)
|
||
pcore_retry_wait: int = 60 # 重试基础等待秒数,按 60→120→240 递增
|
||
|
||
# DeepSeek 出图方案规划器(key 复用上方 deepseek_api_key;文案生成的模型走 config/models.yaml,互不影响)
|
||
deepseek_base_url: str = "https://api.deepseek.com/v1"
|
||
deepseek_model: str = "deepseek-v4-flash"
|
||
|
||
@property
|
||
def cors_origin_list(self) -> list[str]:
|
||
if not self.cors_origins.strip():
|
||
return []
|
||
return [o.strip() for o in self.cors_origins.split(",") if o.strip()]
|
||
|
||
@property
|
||
def use_qiniu(self) -> bool:
|
||
return self.storage_backend == "qiniu" and bool(
|
||
self.qiniu_access_key and self.qiniu_secret_key and self.qiniu_bucket
|
||
)
|
||
|
||
|
||
@lru_cache
|
||
def get_settings() -> Settings:
|
||
return Settings()
|