config.py 6.0 KB

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