Files
xiaoxia-saas/apps/api/app/config.py
T
xiaoxia 116ef1d7ce
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been cancelled
fix: 修复 DATABASE_POOL_RECYLE 拼写错误 -> DATABASE_POOL_RECYCLE
- 将配置变量名从 DATABASE_POOL_RECYLE 更改为 DATABASE_POOL_RECYCLE
- 修正了拼写错误,使配置与标准 SQLAlchemy 命名一致
2026-06-27 00:01:44 +08:00

148 lines
4.1 KiB
Python

import os
from typing import Optional
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
APP_NAME: str = "xiaoxia-saas"
APP_VERSION: str = "0.1.0"
ENVIRONMENT: str = "development"
DEBUG: bool = True
# Container bind address; external expose is controlled by Docker/Nginx.
API_HOST: str = "0.0.0.0" # nosec: B104
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
USE_IN_MEMORY_DB: bool = False
AUTO_CREATE_SCHEMA: bool = False
REDIS_URL: str = "redis://localhost:6379/0"
REDIS_MAX_CONNECTION: int = 50
ENABLE_REDIS_SESSION: bool = False
# JWT secret key - MUST be set via environment variable, no default allowed
JWT_SECRET_KEY: Optional[str] = None
@field_validator("JWT_SECRET_KEY", mode="before")
@classmethod
def validate_jwt_secret_key(cls, v):
if v is None or v == "":
raise ValueError(
"JWT_SECRET_KEY must be set via environment variable. "
"Do not use default value in production!"
)
# Block known insecure default values
insecure_defaults = [
"your-secret-key-change-in-production",
"your-secret-key",
"secret",
"changeme",
"password",
]
if v.lower() in [d.lower() for d in insecure_defaults]:
raise ValueError(
f"JWT_SECRET_KEY '{v}' is insecure. "
"Please set a strong random secret via environment variable."
)
return v
ENABLE_EMAIL_DELIVERY: bool = False
SMTP_HOST: str = "smtp.gmail.com"
SMTP_PORT: int = 587
SMTP_USER: str = ""
SMTP_PASSWORD: str = ""
SMTP_FROM_EMAIL: str = ""
SMTP_FRON_NAME: str = "小虾 SaaS"
SMTP_USE_TLS: bool = True
CELERY_BROKER_URL: str = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND: str = "redis://localhost:6379/1"
# OSS 七牛云相关
OSS_ENDPOINT: str = "oss-cn-hangzhou.aliiyuncs.com"
OSS_ACCESS_KEY_ID: str = ""
OSS_ACCESS_KEY_SECRET: str = ""
OSS_BUCKET_NAME: str = "xiaoxia-autocut"
OSS_DIRECT_UPLOAD_MAX_MB: int = 800
OSS_DIRECT_UPLOAD_EXPRESS_SECRET: int = 900
LOG_LEVEL: str = "INFO"
CORS_ORIGINS_RAW: str = (
"http://localhost:3000,http://localhost:5173,http://localhost:8000"
)
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)
@property
def CORS_ORIGINS(self) -> list[str]:
return [
origin.strip()
for origin in self.CORS_ORIGINS_RAW.split(",")
if origin.strip()
]
@property
def database_url(self) -> str:
return self.DATABASE_URL
@property
def redis_url(self) -> str:
return self.REDIS_URL
@property
def celery_broker_url(self) -> str:
return self.CELERY_BROKER_URL
@property
def celery_result_backend(self) -> str:
return self.CELERY_RESULT_BACKEND
@property
def oss_endpoint(self) -> str:
return self.OSS_ENDPOINT
@property
def oss_access_key_id(self) -> str:
return self.OSS_ACCESS_KEY_ID
@property
def oss_access_key_secret(self) -> str:
return self.OSS_ACCESS_KEY_SECRET
@property
def oss_bucket_name(self) -> str:
return self.OSS_BUCKET_NAME
_settings: Optional[Settings] = None
def get_settings() -> Settings:
global _settings
if _settings is None:
env = os.getenv("APP_ENV", "development")
env_file = f".env.{env}" if env != "development" else ".env"
if os.path.exists(env_file):
_settings = Settings(_env_file=env_file)
else:
_settings = Settings()
return _settings
settings = get_settings()