config.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. from pydantic_settings import BaseSettings, SettingsConfigDict
  2. import os
  3. from typing import Optional
  4. import redis
  5. import logging
  6. logger = logging.getLogger(__name__)
  7. class Settings(BaseSettings):
  8. model_config = SettingsConfigDict(
  9. env_file=".env",
  10. env_file_encoding="utf-8",
  11. extra="ignore",
  12. # Allow reading from system environment variables if not found in .env
  13. case_sensitive=True
  14. )
  15. PROJECT_NAME: str = "MADF User Management API"
  16. API_V1_STR: str = "/api/v1"
  17. # LLM API Configuration
  18. API_KEY: Optional[str] = None
  19. MODEL_NAME: Optional[str] = None
  20. BASE_URL: Optional[str] = None
  21. @property
  22. def has_api_key(self) -> bool:
  23. return bool(self.API_KEY or os.environ.get("API_KEY"))
  24. @property
  25. def final_api_key(self) -> str:
  26. key = self.API_KEY or os.environ.get("API_KEY")
  27. if not key:
  28. raise ValueError("模型服务尚未配置 API Key,请联系管理员完成配置后重试。")
  29. return key
  30. @property
  31. def final_model_name(self) -> str:
  32. return self.MODEL_NAME or os.environ.get("MODEL_NAME") or "step-3.7-flash"
  33. @property
  34. def final_base_url(self) -> str:
  35. return self.BASE_URL or os.environ.get("BASE_URL") or "https://api.stepfun.com/step_plan/v1/"
  36. # Security
  37. SECRET_KEY: str = "MADF_DEFAULT_INSECURE_SECRET_KEY_PLEASE_CHANGE_IN_PROD"
  38. ACCESS_TOKEN_EXPIRE_MINUTES: int = 10080 # 7 days
  39. CORS_ORIGINS: str = "http://localhost:5173,http://localhost:8000"
  40. @property
  41. def cors_origins(self) -> list[str]:
  42. return [origin.strip() for origin in self.CORS_ORIGINS.split(",") if origin.strip()]
  43. def validate_production_security(self) -> None:
  44. if os.environ.get("MADF_ENV", "development").lower() in {"production", "prod"}:
  45. if self.SECRET_KEY == "MADF_DEFAULT_INSECURE_SECRET_KEY_PLEASE_CHANGE_IN_PROD":
  46. raise ValueError("SECRET_KEY must be overridden in production")
  47. # Database Configuration
  48. TURSO_DATABASE_URL: Optional[str] = None
  49. TURSO_AUTH_TOKEN: Optional[str] = None
  50. DATABASE_URL_OVERRIDE: Optional[str] = None # Renamed from DATABASE_URL to avoid conflict
  51. # Redis Configuration
  52. # Default to localhost inside the same container or service mesh
  53. REDIS_URL: str = "redis://localhost:6379/0"
  54. # Determine which database to use
  55. @property
  56. def DATABASE_URL(self) -> str:
  57. # 1. Check environment variable DATABASE_URL first
  58. env_db = os.environ.get("DATABASE_URL")
  59. if env_db:
  60. return env_db
  61. # 2. Turso (Legacy support)
  62. if self.TURSO_DATABASE_URL and self.TURSO_AUTH_TOKEN:
  63. return self.TURSO_DATABASE_URL
  64. # 3. Local SQLite (Dev/Docker default)
  65. if self.DATABASE_URL_OVERRIDE:
  66. return self.DATABASE_URL_OVERRIDE
  67. return "file:madf.db"
  68. settings = Settings()
  69. # Global Redis Client
  70. redis_client: Optional[redis.Redis] = None
  71. try:
  72. redis_client = redis.from_url(
  73. settings.REDIS_URL,
  74. decode_responses=True,
  75. socket_timeout=5,
  76. socket_connect_timeout=5
  77. )
  78. redis_client.ping()
  79. logger.info(f"Redis connected to {settings.REDIS_URL}")
  80. except Exception as e:
  81. logger.warning(f"Redis connection failed: {e}")
  82. redis_client = None