from pathlib import Path from typing import Optional from urllib.parse import quote_plus from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): PROJECT_NAME: str = "TJWater Server" ENVIRONMENT: str = "production" API_V1_STR: str = "/api/v1" NETWORK_NAME: str = "default_network" # JWT 配置 SECRET_KEY: str = ( "your-secret-key-here-change-in-production-use-openssl-rand-hex-32" ) ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 # 数据加密密钥 (使用 Fernet) ENCRYPTION_KEY: str = "" # 必须从环境变量设置 DATABASE_ENCRYPTION_KEY: str = "" # project_databases.dsn_encrypted 专用 # Database Config (PostgreSQL) DB_NAME: str = "tjwater" DB_HOST: str = "localhost" DB_PORT: str = "5432" DB_USER: str = "postgres" DB_PASSWORD: str = "password" # Database Config (TimescaleDB) TIMESCALEDB_DB_NAME: str = "tjwater" TIMESCALEDB_DB_HOST: str = "localhost" TIMESCALEDB_DB_PORT: str = "5433" TIMESCALEDB_DB_USER: str = "postgres" TIMESCALEDB_DB_PASSWORD: str = "password" # InfluxDB INFLUXDB_URL: str = "http://localhost:8086" INFLUXDB_TOKEN: str = "token" INFLUXDB_ORG: str = "org" INFLUXDB_BUCKET: str = "bucket" # Metadata Database Config (PostgreSQL) METADATA_DB_NAME: str = "system_hub" METADATA_DB_HOST: str = "localhost" METADATA_DB_PORT: str = "5432" METADATA_DB_USER: str = "postgres" METADATA_DB_PASSWORD: str = "password" METADATA_DB_POOL_SIZE: int = 5 METADATA_DB_MAX_OVERFLOW: int = 10 PROJECT_PG_CACHE_SIZE: int = 50 PROJECT_TS_CACHE_SIZE: int = 50 PROJECT_PG_POOL_SIZE: int = 5 PROJECT_PG_MAX_OVERFLOW: int = 10 PROJECT_TS_POOL_MIN_SIZE: int = 1 PROJECT_TS_POOL_MAX_SIZE: int = 10 # Keycloak JWT (optional override) KEYCLOAK_PUBLIC_KEY: str = "" KEYCLOAK_ALGORITHM: str = "RS256" KEYCLOAK_AUDIENCE: str = "" @property def SQLALCHEMY_DATABASE_URI(self) -> str: db_password = quote_plus(self.DB_PASSWORD) return f"postgresql://{self.DB_USER}:{db_password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" @property def METADATA_DATABASE_URI(self) -> str: metadata_password = quote_plus(self.METADATA_DB_PASSWORD) return ( f"postgresql+psycopg://{self.METADATA_DB_USER}:{metadata_password}" f"@{self.METADATA_DB_HOST}:{self.METADATA_DB_PORT}/{self.METADATA_DB_NAME}" ) model_config = SettingsConfigDict( env_file=Path(__file__).resolve().parents[2] / ".env", extra="ignore", ) settings = Settings() def get_pgconn_string( db_name: Optional[str] = None, db_host: Optional[str] = None, db_port: Optional[str] = None, db_user: Optional[str] = None, db_password: Optional[str] = None, ) -> str: """Return PostgreSQL connection string in psycopg conninfo format.""" resolved_db_name = db_name or settings.DB_NAME resolved_db_host = db_host or settings.DB_HOST resolved_db_port = db_port or settings.DB_PORT resolved_db_user = db_user or settings.DB_USER resolved_db_password = db_password or settings.DB_PASSWORD return ( f"dbname={resolved_db_name} host={resolved_db_host} port={resolved_db_port} " f"user={resolved_db_user} password={resolved_db_password}" ) def get_pg_config() -> dict: """Return PostgreSQL configuration except password.""" return { "name": settings.DB_NAME, "host": settings.DB_HOST, "port": settings.DB_PORT, "user": settings.DB_USER, } def get_pg_password() -> str: """Return PostgreSQL password (use with care).""" return settings.DB_PASSWORD def get_timescaledb_pgconn_string( db_name: Optional[str] = None, db_host: Optional[str] = None, db_port: Optional[str] = None, db_user: Optional[str] = None, db_password: Optional[str] = None, ) -> str: """Return TimescaleDB connection string in psycopg conninfo format.""" resolved_db_name = db_name or settings.TIMESCALEDB_DB_NAME resolved_db_host = db_host or settings.TIMESCALEDB_DB_HOST resolved_db_port = db_port or settings.TIMESCALEDB_DB_PORT resolved_db_user = db_user or settings.TIMESCALEDB_DB_USER resolved_db_password = db_password or settings.TIMESCALEDB_DB_PASSWORD return ( f"dbname={resolved_db_name} host={resolved_db_host} port={resolved_db_port} " f"user={resolved_db_user} password={resolved_db_password}" ) def get_timescaledb_pg_config() -> dict: """Return TimescaleDB configuration except password.""" return { "name": settings.TIMESCALEDB_DB_NAME, "host": settings.TIMESCALEDB_DB_HOST, "port": settings.TIMESCALEDB_DB_PORT, "user": settings.TIMESCALEDB_DB_USER, } def get_timescaledb_pg_password() -> str: """Return TimescaleDB password (use with care).""" return settings.TIMESCALEDB_DB_PASSWORD