100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
import sys
|
||
from pathlib import Path
|
||
from pydantic import field_validator, model_validator
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
from typing import Optional
|
||
|
||
_env_path = Path(__file__).resolve().parent.parent.parent / ".env"
|
||
if not _env_path.exists():
|
||
_env_path = Path(".env")
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
model_config = SettingsConfigDict(env_file=str(_env_path), extra="ignore")
|
||
|
||
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres123@db:5432/geo_platform"
|
||
REDIS_URL: str = "redis://redis:6379/0"
|
||
|
||
# JWT 密钥:必须通过环境变量设置,不提供任何默认值
|
||
JWT_SECRET: str
|
||
JWT_EXPIRE_HOURS: int = 24
|
||
|
||
# NextAuth 密钥
|
||
SECRET_KEY: Optional[str] = None
|
||
|
||
PLAYWRIGHT_BROWSERS_PATH: str = "/ms-playwright"
|
||
ENABLE_LLM: bool = True
|
||
ZHIPU_API_KEY: str = ""
|
||
TONGYI_API_KEY: str = ""
|
||
CORS_ORIGINS: str = "http://localhost:3000,http://localhost:3001"
|
||
|
||
@field_validator("CORS_ORIGINS")
|
||
@classmethod
|
||
def validate_cors_origins(cls, v: str) -> str:
|
||
import os
|
||
if os.getenv("ENVIRONMENT", "development") == "production":
|
||
origins = [o.strip() for o in v.split(",") if o.strip()]
|
||
localhost_origins = [o for o in origins if "localhost" in o or "127.0.0.1" in o]
|
||
if localhost_origins:
|
||
print(
|
||
f"[WARNING] CORS_ORIGINS contains localhost in production: {localhost_origins}. "
|
||
"This is a security risk. Please configure proper production origins.",
|
||
file=sys.stderr,
|
||
)
|
||
return v
|
||
|
||
# ---- LLM Provider 配置 ----
|
||
DEFAULT_LLM_PROVIDER: str = "openai"
|
||
DEFAULT_LLM_MODEL: str = "qwen3-coder-plus"
|
||
OPENAI_API_KEY: str = ""
|
||
OPENAI_MODEL: str = "qwen3-coder-plus"
|
||
OPENAI_BASE_URL: str = "https://coding.dashscope.aliyuncs.com/v1"
|
||
DEEPSEEK_API_KEY: str = ""
|
||
DEEPSEEK_MODEL: str = "deepseek-chat"
|
||
DEEPSEEK_BASE_URL: str = "https://api.deepseek.com/v1"
|
||
DEEPSEEK_MAX_CONTEXT: int = 64000
|
||
|
||
# AI平台API配置
|
||
MOONSHOT_API_KEY: str = "" # Kimi (月之暗面) API Key
|
||
BAIDU_QIANFAN_API_KEY: str = "" # 百度千帆 API Key
|
||
BAIDU_QIANFAN_SECRET_KEY: str = "" # 百度千帆 Secret Key
|
||
DOUBAO_API_KEY: str = "" # 豆包 (字节跳动) API Key
|
||
DOUBAO_ENDPOINT_ID: str = "" # 豆包推理接入点 ID
|
||
|
||
# AI平台API调用频率限制(每分钟请求数)
|
||
API_RATE_LIMIT_RPM: int = 10
|
||
|
||
@field_validator("JWT_SECRET")
|
||
@classmethod
|
||
def validate_jwt_secret(cls, v: str) -> str:
|
||
if not v or v.strip() == "":
|
||
print(
|
||
"[FATAL] JWT_SECRET is not set. "
|
||
"Please set a strong secret key (>= 32 characters) in your .env file.",
|
||
file=sys.stderr,
|
||
)
|
||
sys.exit(1)
|
||
if len(v) < 32:
|
||
print(
|
||
f"[FATAL] JWT_SECRET is too short ({len(v)} chars). "
|
||
"It must be at least 32 characters long.",
|
||
file=sys.stderr,
|
||
)
|
||
sys.exit(1)
|
||
return v
|
||
|
||
@model_validator(mode="after")
|
||
def validate_secret_key(self) -> "Settings":
|
||
if self.SECRET_KEY is not None:
|
||
if len(self.SECRET_KEY) < 32:
|
||
print(
|
||
f"[FATAL] SECRET_KEY is too short ({len(self.SECRET_KEY)} chars). "
|
||
"It must be at least 32 characters long.",
|
||
file=sys.stderr,
|
||
)
|
||
sys.exit(1)
|
||
return self
|
||
|
||
|
||
settings = Settings()
|