Spaces:
Sleeping
Sleeping
| """ | |
| Configuration settings for Spa microservice. | |
| Loads environment variables and provides application settings. | |
| """ | |
| import os | |
| from typing import Optional, List | |
| from pydantic import model_validator | |
| from pydantic_settings import BaseSettings, SettingsConfigDict | |
| class Settings(BaseSettings): | |
| """Application settings loaded from environment variables""" | |
| # Application | |
| APP_NAME: str = "Spa Microservice" | |
| APP_VERSION: str = "1.0.0" | |
| DEBUG: bool = False | |
| # MongoDB Configuration | |
| MONGODB_URI: str = os.getenv("MONGODB_URI", "mongodb://localhost:27017") | |
| MONGODB_DB_NAME: str = os.getenv("MONGODB_DB_NAME", "cuatrolabs") | |
| # PostgreSQL Configuration (for trans schema sync) | |
| POSTGRES_HOST: str = os.getenv("DB_HOST", "ep-sweet-surf-a1qeduoy.ap-southeast-1.aws.neon.tech") | |
| POSTGRES_PORT: int = int(os.getenv("DB_PORT", "5432")) | |
| POSTGRES_DB: str = os.getenv("DB_NAME", "cuatrolabs") | |
| POSTGRES_USER: str = os.getenv("DB_USER", "trans_owner") | |
| POSTGRES_PASSWORD: str = os.getenv("DB_PASSWORD", "BookMyService7") | |
| POSTGRES_MIN_POOL_SIZE: int = int(os.getenv("POSTGRES_MIN_POOL_SIZE", "5")) | |
| POSTGRES_MAX_POOL_SIZE: int = int(os.getenv("POSTGRES_MAX_POOL_SIZE", "20")) | |
| POSTGRES_CONNECT_MAX_RETRIES: int = int(os.getenv("POSTGRES_CONNECT_MAX_RETRIES", "20")) | |
| POSTGRES_CONNECT_INITIAL_DELAY_MS: int = int(os.getenv("POSTGRES_CONNECT_INITIAL_DELAY_MS", "500")) | |
| POSTGRES_CONNECT_BACKOFF_MULTIPLIER: float = float(os.getenv("POSTGRES_CONNECT_BACKOFF_MULTIPLIER", "1.5")) | |
| POSTGRES_SSL_MODE: str = os.getenv("DB_SSLMODE", "disable") | |
| POSTGRES_SSL_ROOT_CERT: Optional[str] = os.getenv("POSTGRES_SSL_ROOT_CERT") | |
| POSTGRES_SSL_CERT: Optional[str] = os.getenv("POSTGRES_SSL_CERT") | |
| POSTGRES_SSL_KEY: Optional[str] = os.getenv("POSTGRES_SSL_KEY") | |
| POSTGRES_URI: Optional[str] = None | |
| # MinIO / Object Storage | |
| MINIO_ENDPOINT: str = os.getenv("MINIO_ENDPOINT", "localhost:9000") | |
| MINIO_ACCESS_KEY: Optional[str] = os.getenv("MINIO_ACCESS_KEY") | |
| MINIO_SECRET_KEY: Optional[str] = os.getenv("MINIO_SECRET_KEY") | |
| MINIO_SECURE: bool = os.getenv("MINIO_SECURE", "false").lower() == "true" | |
| MINIO_REGION: Optional[str] = os.getenv("MINIO_REGION") | |
| # Storage buckets | |
| STORAGE_BUCKET_IMAGES_PUBLIC: str = os.getenv("STORAGE_BUCKET_IMAGES_PUBLIC", "cutra-spa-images-public") | |
| STORAGE_BUCKET_IMAGES_PRIVATE: str = os.getenv("STORAGE_BUCKET_IMAGES_PRIVATE", "cutra-spa-images") | |
| STORAGE_MAX_IMAGE_MB: int = 10 | |
| STORAGE_PRESIGN_EXPIRY_SECONDS: int = 900 | |
| STORAGE_IMAGE_CDN_EXPIRY_SECONDS: int = 604800 | |
| # Public URL / CDN configuration | |
| STORAGE_PUBLIC_BASE_URL: Optional[str] = os.getenv("STORAGE_PUBLIC_BASE_URL") | |
| STORAGE_ENABLE_PUBLIC_URLS: bool = os.getenv("STORAGE_ENABLE_PUBLIC_URLS", "true").lower() == "true" | |
| def PUBLIC_BASE_URL(self) -> str: | |
| """Get the public base URL for images, preferring CDN over direct MinIO endpoint.""" | |
| if self.STORAGE_PUBLIC_BASE_URL: | |
| return self.STORAGE_PUBLIC_BASE_URL.rstrip("/") | |
| protocol = "https" if self.MINIO_SECURE else "http" | |
| return f"{protocol}://{self.MINIO_ENDPOINT}" | |
| def IS_CDN_ENABLED(self) -> bool: | |
| """Check if CDN is configured (not using direct MinIO endpoint).""" | |
| return bool(self.STORAGE_PUBLIC_BASE_URL) | |
| # Redis Configuration | |
| REDIS_HOST: str = os.getenv("REDIS_HOST", "localhost") | |
| REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379")) | |
| REDIS_PASSWORD: Optional[str] = os.getenv("REDIS_PASSWORD") | |
| REDIS_DB: int = int(os.getenv("REDIS_DB", "0")) | |
| # JWT Configuration | |
| SECRET_KEY: str = os.getenv("SECRET_KEY", "your-secret-key-change-in-production") | |
| ALGORITHM: str = os.getenv("ALGORITHM", "HS256") | |
| TOKEN_EXPIRATION_HOURS: int = int(os.getenv("TOKEN_EXPIRATION_HOURS", "8")) | |
| # OTP Configuration | |
| OTP_TTL_SECONDS: int = int(os.getenv("OTP_TTL_SECONDS", "600")) | |
| OTP_RATE_LIMIT_MAX: int = int(os.getenv("OTP_RATE_LIMIT_MAX", "10")) | |
| OTP_RATE_LIMIT_WINDOW: int = int(os.getenv("OTP_RATE_LIMIT_WINDOW", "600")) | |
| # Twilio Configuration | |
| TWILIO_ACCOUNT_SID: Optional[str] = os.getenv("TWILIO_ACCOUNT_SID") | |
| TWILIO_AUTH_TOKEN: Optional[str] = os.getenv("TWILIO_AUTH_TOKEN") | |
| TWILIO_PHONE_NUMBER: Optional[str] = os.getenv("TWILIO_PHONE_NUMBER") | |
| # SMTP Configuration | |
| SMTP_HOST: Optional[str] = os.getenv("SMTP_HOST") | |
| SMTP_PORT: int = int(os.getenv("SMTP_PORT", "587")) | |
| SMTP_USERNAME: Optional[str] = os.getenv("SMTP_USERNAME") | |
| SMTP_PASSWORD: Optional[str] = os.getenv("SMTP_PASSWORD") | |
| SMTP_FROM_EMAIL: Optional[str] = os.getenv("SMTP_FROM_EMAIL") | |
| SMTP_USE_TLS: bool = os.getenv("SMTP_USE_TLS", "true").lower() == "true" | |
| # Logging | |
| LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") | |
| # Dashboard Cache Configuration | |
| DASHBOARD_REDIS_TTL_SECONDS: int = int(os.getenv("DASHBOARD_REDIS_TTL_SECONDS", "60")) # 60 seconds | |
| DASHBOARD_MONGO_TTL_HOURS: int = int(os.getenv("DASHBOARD_MONGO_TTL_HOURS", "3")) # 3 hours (optional) | |
| DASHBOARD_ENABLE_MONGO_TTL: bool = os.getenv("DASHBOARD_ENABLE_MONGO_TTL", "false").lower() == "true" | |
| # CORS | |
| CORS_ORIGINS: List[str] = [ | |
| "http://localhost:3000", | |
| "http://localhost:8000", | |
| ] | |
| # Pydantic v2 config | |
| model_config = SettingsConfigDict( | |
| env_file=".env", | |
| env_file_encoding="utf-8", | |
| case_sensitive=True, | |
| extra="allow", # allows extra environment variables without error | |
| ) | |
| def assemble_db_connection(self) -> 'Settings': | |
| from urllib.parse import quote_plus | |
| # Prefer DATABASE_URL and DATABASE_URI like TMS settings.py | |
| env_url = (os.getenv("DATABASE_URL") or os.getenv("DATABASE_URI") or "").strip() | |
| if env_url: | |
| self.POSTGRES_URI = env_url | |
| print(f"[CONFIG] Using provided DATABASE_URL/URI") | |
| return self | |
| # Build DSN from individual parts | |
| if all([self.POSTGRES_USER, self.POSTGRES_PASSWORD, self.POSTGRES_HOST, self.POSTGRES_DB]): | |
| protocol = os.getenv("DB_PROTOCOL", "postgresql+asyncpg") | |
| # Ensure no spaces in connection components | |
| user = self.POSTGRES_USER.strip() | |
| host = self.POSTGRES_HOST.strip() | |
| port = str(self.POSTGRES_PORT).strip() | |
| db = self.POSTGRES_DB.strip() | |
| self.POSTGRES_URI = f"{protocol}://{user}:{quote_plus(self.POSTGRES_PASSWORD)}@{host}:{port}/{db}" | |
| print(f"[CONFIG] Built POSTGRES_URI from components") | |
| print(f"[CONFIG] Protocol: {protocol}") | |
| print(f"[CONFIG] User: {self.POSTGRES_USER}") | |
| print(f"[CONFIG] Host: {self.POSTGRES_HOST}") | |
| print(f"[CONFIG] Port: {self.POSTGRES_PORT}") | |
| print(f"[CONFIG] Database: {self.POSTGRES_DB}") | |
| print(f"[CONFIG] Password: {'SET' if self.POSTGRES_PASSWORD else 'EMPTY'}") | |
| print(f"[CONFIG] SSL Mode: {self.POSTGRES_SSL_MODE}") | |
| else: | |
| self.POSTGRES_URI = None | |
| print(f"[CONFIG] ERROR: Cannot build POSTGRES_URI - missing required components") | |
| print(f"[CONFIG] POSTGRES_USER: {'SET' if self.POSTGRES_USER else 'MISSING'}") | |
| print(f"[CONFIG] POSTGRES_PASSWORD: {'SET' if self.POSTGRES_PASSWORD else 'MISSING'}") | |
| print(f"[CONFIG] POSTGRES_HOST: {'SET' if self.POSTGRES_HOST else 'MISSING'}") | |
| print(f"[CONFIG] POSTGRES_DB: {'SET' if self.POSTGRES_DB else 'MISSING'}") | |
| print(f"[CONFIG] Checking alternative env vars:") | |
| print(f"[CONFIG] DB_USER: {os.getenv('DB_USER', 'NOT SET')}") | |
| print(f"[CONFIG] DB_PASSWORD: {'SET' if os.getenv('DB_PASSWORD') else 'NOT SET'}") | |
| print(f"[CONFIG] DB_HOST: {os.getenv('DB_HOST', 'NOT SET')}") | |
| print(f"[CONFIG] DB_NAME: {os.getenv('DB_NAME', 'NOT SET')}") | |
| return self | |
| # Global settings instance | |
| settings = Settings() | |