config.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. from pydantic_settings import BaseSettings, SettingsConfigDict
  2. from typing import Any, Dict, List
  3. from urllib.parse import quote
  4. import os
  5. # 通过环境变量 APP_ENV 选择 .env 文件,默认为 uat
  6. # - 本地开发:APP_ENV=dev -> .env.dev
  7. # - 测试环境:APP_ENV=uat -> .env.uat(默认)
  8. _ENV_FILE = f".env.{os.getenv('APP_ENV', 'uat')}"
  9. class Settings(BaseSettings):
  10. # 基础配置
  11. PROJECT_NAME: str = "Alien Cloud Python"
  12. API_V1_STR: str = "/api/v1"
  13. # 鉴权配置 (原 alien-gateway 职责)
  14. SECRET_KEY: str = os.getenv("SECRET_KEY")
  15. ALGORITHM: str = os.getenv("ALGORITHM")
  16. ACCESS_TOKEN_EXPIRE_MINUTES: int = os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES") # 7天
  17. # 数据库配置
  18. DB_USER: str = os.getenv("DB_USER")
  19. DB_PASSWORD: str = os.getenv("DB_PASSWORD")
  20. DB_HOST: str = os.getenv("DB_HOST")
  21. DB_PORT: int = os.getenv("DB_PORT")
  22. DB_NAME: str = os.getenv("DB_NAME")
  23. # Redis Sentinel 高可用配置
  24. # 例: REDIS_SENTINELS=192.168.2.251:36379,192.168.2.252:36379,192.168.2.253:36379
  25. REDIS_SENTINELS: str = os.getenv("REDIS_SENTINELS", "")
  26. REDIS_MASTER_NAME: str = os.getenv("REDIS_MASTER_NAME", "mymaster")
  27. REDIS_PASSWORD: str = os.getenv("REDIS_PASSWORD", "")
  28. REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
  29. REDIS_SENTINEL_USERNAME: str = os.getenv("REDIS_SENTINEL_USERNAME", "")
  30. REDIS_SENTINEL_PASSWORD: str = os.getenv("REDIS_SENTINEL_PASSWORD", "")
  31. REDIS_SOCKET_TIMEOUT: float = float(os.getenv("REDIS_SOCKET_TIMEOUT", "0.5"))
  32. REDIS_CONNECT_TIMEOUT: float = float(os.getenv("REDIS_CONNECT_TIMEOUT", "1.0"))
  33. # 下游服务地址
  34. STORE_BASE_URL: str = os.getenv("STORE_BASE_URL") # alien_store 服务地址
  35. CONTRACT_BASE_URL: str = os.getenv("CONTRACT_BASE_URL") # alien_contract 服务地址
  36. # 阿里云短信配置
  37. ALIYUN_SMS_SIGN_NAME_CONTRACT: str = os.getenv("ALIYUN_SMS_SIGN_NAME_CONTRACT")
  38. ALIYUN_SMS_TEMPLATE_CODE_CONTRACT: str = os.getenv("ALIYUN_SMS_TEMPLATE_CODE_CONTRACT")
  39. ALIYUN_ACCESS_KEY_ID: str = os.getenv("ALIYUN_ACCESS_KEY_ID")
  40. ALIYUN_ACCESS_KEY_SECRET: str = os.getenv("ALIYUN_ACCESS_KEY_SECRET")
  41. @property
  42. def SQLALCHEMY_DATABASE_URI(self) -> str:
  43. return f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
  44. @property
  45. def REDIS_SENTINEL_NODES(self) -> List[tuple[str, int]]:
  46. nodes: List[tuple[str, int]] = []
  47. for item in self.REDIS_SENTINELS.split(","):
  48. entry = item.strip()
  49. if not entry:
  50. continue
  51. if ":" not in entry:
  52. raise ValueError(f"Invalid REDIS_SENTINELS entry: {entry}")
  53. host, port = entry.split(":", 1)
  54. nodes.append((host.strip(), int(port.strip())))
  55. return nodes
  56. @property
  57. def REDIS_SENTINEL_KWARGS(self) -> Dict[str, Any]:
  58. kwargs: Dict[str, Any] = {}
  59. if self.REDIS_SENTINEL_USERNAME:
  60. kwargs["username"] = self.REDIS_SENTINEL_USERNAME
  61. if self.REDIS_SENTINEL_PASSWORD:
  62. kwargs["password"] = self.REDIS_SENTINEL_PASSWORD
  63. return kwargs
  64. @property
  65. def REDIS_SENTINEL_URL(self) -> str:
  66. # 标准 Sentinel URL:
  67. # redis+sentinel://[username[:password]@]host1:port1,host2:port2/db?sentinel_master=mymaster
  68. if self.REDIS_SENTINEL_USERNAME:
  69. user = quote(self.REDIS_SENTINEL_USERNAME, safe="")
  70. pwd = quote(self.REDIS_PASSWORD, safe="")
  71. auth = f"{user}:{pwd}@"
  72. elif self.REDIS_PASSWORD:
  73. pwd = quote(self.REDIS_PASSWORD, safe="")
  74. auth = f":{pwd}@"
  75. else:
  76. auth = ""
  77. hosts = ",".join(f"{host}:{port}" for host, port in self.REDIS_SENTINEL_NODES)
  78. master = quote(self.REDIS_MASTER_NAME, safe="")
  79. return f"redis+sentinel://{auth}{hosts}/{self.REDIS_DB}?sentinel_master={master}"
  80. @property
  81. def REDIS_CELERY_SENTINEL_URL(self) -> str:
  82. # Celery/Kombu Sentinel URL 格式:
  83. # sentinel://:redis_password@sentinel_host:port/db;...
  84. # URL 中的 auth 密码 = Redis 主从节点密码
  85. # (Kombu 通过 Sentinel 发现主节点地址后,用此密码向 Redis 主节点认证)
  86. # Sentinel 自身的密码(如有)通过 broker_transport_options["sentinel_kwargs"]["password"] 传递
  87. auth = f":{quote(self.REDIS_PASSWORD, safe='')}@" if self.REDIS_PASSWORD else ""
  88. return ";".join(
  89. f"sentinel://{auth}{host}:{port}/{self.REDIS_DB}"
  90. for host, port in self.REDIS_SENTINEL_NODES
  91. )
  92. @property
  93. def REDIS_SENTINEL_TRANSPORT_OPTIONS(self) -> Dict[str, Any]:
  94. return {
  95. "master_name": self.REDIS_MASTER_NAME,
  96. "sentinel_kwargs": self.REDIS_SENTINEL_KWARGS,
  97. "password": self.REDIS_PASSWORD,
  98. "db": self.REDIS_DB,
  99. }
  100. model_config = SettingsConfigDict(
  101. case_sensitive=True,
  102. env_file=_ENV_FILE,
  103. extra="ignore",
  104. )
  105. settings = Settings()