config.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from pydantic_settings import BaseSettings, SettingsConfigDict
  2. from typing import Any, Dict, List, Optional, Tuple
  3. from urllib.parse import quote
  4. import os
  5. # 通过环境变量 APP_ENV 选择 .env 文件,默认 dev:
  6. # - 本地开发:APP_ENV=dev -> .env.dev
  7. # - 测试环境:APP_ENV=sit -> .env.sit
  8. # - UAT 环境:APP_ENV=uat -> .env.uat
  9. # - 生产环境:APP_ENV=produ -> .env.produ
  10. APP_ENV = os.getenv("APP_ENV", "dev")
  11. _ENV_FILE = f".env.{APP_ENV}"
  12. class Settings(BaseSettings):
  13. # 基础配置
  14. PROJECT_NAME: str = "Alien Cloud Python"
  15. API_V1_STR: str = "/api/v1"
  16. APP_ENV: str = APP_ENV
  17. # 鉴权配置(原 alien-gateway 职责)
  18. SECRET_KEY: str = os.getenv("SECRET_KEY", "")
  19. ALGORITHM: str = os.getenv("ALGORITHM", "HS256")
  20. ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "10080"))
  21. # 数据库配置
  22. DB_USER: str = os.getenv("DB_USER", "")
  23. DB_PASSWORD: str = os.getenv("DB_PASSWORD", "")
  24. DB_HOST: str = os.getenv("DB_HOST", "")
  25. DB_PORT: int = int(os.getenv("DB_PORT", "3306"))
  26. DB_NAME: str = os.getenv("DB_NAME", "")
  27. # Redis 单机配置(与 Sentinel 二选一;REDIS_URL 非空时走单机模式)
  28. REDIS_URL: str = os.getenv("REDIS_URL", "")
  29. # Redis Sentinel 高可用配置(REDIS_SENTINELS 非空时走哨兵模式)
  30. # 例:REDIS_SENTINELS=192.168.2.251:36379,192.168.2.252:36379,192.168.2.253:36379
  31. REDIS_SENTINELS: str = os.getenv("REDIS_SENTINELS", "")
  32. REDIS_MASTER_NAME: str = os.getenv("REDIS_MASTER_NAME", "mymaster")
  33. REDIS_PASSWORD: str = os.getenv("REDIS_PASSWORD", "")
  34. REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
  35. REDIS_SENTINEL_USERNAME: str = os.getenv("REDIS_SENTINEL_USERNAME", "")
  36. REDIS_SENTINEL_PASSWORD: str = os.getenv("REDIS_SENTINEL_PASSWORD", "")
  37. REDIS_SOCKET_TIMEOUT: float = float(os.getenv("REDIS_SOCKET_TIMEOUT", "0.5"))
  38. REDIS_CONNECT_TIMEOUT: float = float(os.getenv("REDIS_CONNECT_TIMEOUT", "1.0"))
  39. # 各服务监听端口(容器内端口;外部映射由 Jenkinsfile / docker-compose 控制)
  40. GATEWAY_PORT: int = int(os.getenv("GATEWAY_PORT", "33333"))
  41. STORE_PORT: int = int(os.getenv("STORE_PORT", "8001"))
  42. CONTRACT_PORT: int = int(os.getenv("CONTRACT_PORT", "8002"))
  43. LAWYER_PORT: int = int(os.getenv("LAWYER_PORT", "8004"))
  44. # 下游服务地址(网关反向代理用)
  45. STORE_BASE_URL: str = os.getenv("STORE_BASE_URL", "http://127.0.0.1:8001")
  46. CONTRACT_BASE_URL: str = os.getenv("CONTRACT_BASE_URL", "http://127.0.0.1:8002")
  47. # 阿里云短信配置
  48. ALIYUN_SMS_SIGN_NAME_CONTRACT: str = os.getenv("ALIYUN_SMS_SIGN_NAME_CONTRACT", "")
  49. ALIYUN_SMS_TEMPLATE_CODE_CONTRACT: str = os.getenv("ALIYUN_SMS_TEMPLATE_CODE_CONTRACT", "")
  50. ALIYUN_ACCESS_KEY_ID: str = os.getenv("ALIYUN_ACCESS_KEY_ID", "")
  51. ALIYUN_ACCESS_KEY_SECRET: str = os.getenv("ALIYUN_ACCESS_KEY_SECRET", "")
  52. @property
  53. def SQLALCHEMY_DATABASE_URI(self) -> str:
  54. return f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
  55. @property
  56. def REDIS_MODE(self) -> str:
  57. # standalone(单机) / sentinel(哨兵)
  58. return "sentinel" if self.REDIS_SENTINELS.strip() else "standalone"
  59. @property
  60. def REDIS_SENTINEL_NODES(self) -> List[Tuple[str, int]]:
  61. nodes: List[Tuple[str, int]] = []
  62. for item in self.REDIS_SENTINELS.split(","):
  63. entry = item.strip()
  64. if not entry:
  65. continue
  66. if ":" not in entry:
  67. raise ValueError(f"Invalid REDIS_SENTINELS entry: {entry}")
  68. host, port = entry.split(":", 1)
  69. nodes.append((host.strip(), int(port.strip())))
  70. return nodes
  71. @property
  72. def REDIS_SENTINEL_KWARGS(self) -> Dict[str, Any]:
  73. kwargs: Dict[str, Any] = {}
  74. if self.REDIS_SENTINEL_USERNAME:
  75. kwargs["username"] = self.REDIS_SENTINEL_USERNAME
  76. if self.REDIS_SENTINEL_PASSWORD:
  77. kwargs["password"] = self.REDIS_SENTINEL_PASSWORD
  78. return kwargs
  79. @property
  80. def REDIS_SENTINEL_URL(self) -> str:
  81. # 标准 Sentinel URL:
  82. # redis+sentinel://[username[:password]@]host1:port1,host2:port2/db?sentinel_master=mymaster
  83. if self.REDIS_SENTINEL_USERNAME:
  84. user = quote(self.REDIS_SENTINEL_USERNAME, safe="")
  85. pwd = quote(self.REDIS_PASSWORD, safe="")
  86. auth = f"{user}:{pwd}@"
  87. elif self.REDIS_PASSWORD:
  88. pwd = quote(self.REDIS_PASSWORD, safe="")
  89. auth = f":{pwd}@"
  90. else:
  91. auth = ""
  92. hosts = ",".join(f"{host}:{port}" for host, port in self.REDIS_SENTINEL_NODES)
  93. master = quote(self.REDIS_MASTER_NAME, safe="")
  94. return f"redis+sentinel://{auth}{hosts}/{self.REDIS_DB}?sentinel_master={master}"
  95. @property
  96. def REDIS_CELERY_SENTINEL_URL(self) -> str:
  97. # Celery/Kombu Sentinel URL 格式:
  98. # sentinel://:redis_password@sentinel_host:port/db;...
  99. auth = f":{quote(self.REDIS_PASSWORD, safe='')}@" if self.REDIS_PASSWORD else ""
  100. return ";".join(
  101. f"sentinel://{auth}{host}:{port}/{self.REDIS_DB}"
  102. for host, port in self.REDIS_SENTINEL_NODES
  103. )
  104. @property
  105. def REDIS_SENTINEL_TRANSPORT_OPTIONS(self) -> Dict[str, Any]:
  106. return {
  107. "master_name": self.REDIS_MASTER_NAME,
  108. "sentinel_kwargs": self.REDIS_SENTINEL_KWARGS,
  109. "password": self.REDIS_PASSWORD,
  110. "db": self.REDIS_DB,
  111. }
  112. @property
  113. def CELERY_BROKER_URL(self) -> str:
  114. # 单机优先用 REDIS_URL;哨兵则用 Celery Sentinel URL
  115. return self.REDIS_CELERY_SENTINEL_URL if self.REDIS_MODE == "sentinel" else self.REDIS_URL
  116. @property
  117. def CELERY_BACKEND_URL(self) -> str:
  118. return self.CELERY_BROKER_URL
  119. @property
  120. def CELERY_TRANSPORT_OPTIONS(self) -> Optional[Dict[str, Any]]:
  121. return self.REDIS_SENTINEL_TRANSPORT_OPTIONS if self.REDIS_MODE == "sentinel" else None
  122. model_config = SettingsConfigDict(
  123. case_sensitive=True,
  124. env_file=_ENV_FILE,
  125. extra="ignore",
  126. )
  127. settings = Settings()