""" Core Configuration - ServiceManagerWeb Configuración centralizada usando Pydantic Settings v2 """ from functools import lru_cache from typing import List, Optional from pydantic_settings import BaseSettings from pydantic import field_validator, Field import os class Settings(BaseSettings): """Configuración de la aplicación.""" model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False } # =================================== # GENERAL # =================================== ENVIRONMENT: str = Field(default="development", env="ENVIRONMENT") DEBUG: bool = Field(default=False, env="DEBUG") SECRET_KEY: str = Field(..., env="SECRET_KEY") API_VERSION: str = Field(default="v1", env="API_VERSION") # =================================== # DATABASE # =================================== DATABASE_URL: str = Field(..., env="DATABASE_URL") # =================================== # REDIS # =================================== REDIS_URL: str = Field(..., env="REDIS_URL") # =================================== # JWT AUTHENTICATION # =================================== JWT_SECRET_KEY: str = Field(..., env="JWT_SECRET_KEY") JWT_ALGORITHM: str = Field(default="HS256", env="JWT_ALGORITHM") ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(default=60, env="ACCESS_TOKEN_EXPIRE_MINUTES") REFRESH_TOKEN_EXPIRE_DAYS: int = Field(default=7, env="REFRESH_TOKEN_EXPIRE_DAYS") # =================================== # CORS # =================================== CORS_ORIGINS: str = Field( default="http://localhost:3000,http://localhost:3001", env="CORS_ORIGINS" ) # =================================== # EMAIL # =================================== SMTP_HOST: str = Field(default="localhost", env="SMTP_HOST") SMTP_PORT: int = Field(default=587, env="SMTP_PORT") SMTP_USER: Optional[str] = Field(default=None, env="SMTP_USER") SMTP_PASSWORD: Optional[str] = Field(default=None, env="SMTP_PASSWORD") SMTP_USE_TLS: bool = Field(default=True, env="SMTP_USE_TLS") SMTP_USE_SSL: bool = Field(default=False, env="SMTP_USE_SSL") DEFAULT_FROM_EMAIL: str = Field(default="noreply@servicemanager.local", env="DEFAULT_FROM_EMAIL") DEFAULT_FROM_NAME: str = Field(default="ServiceManager", env="DEFAULT_FROM_NAME") # =================================== # FILE UPLOADS # =================================== MAX_UPLOAD_SIZE_MB: int = Field(default=10, env="MAX_UPLOAD_SIZE_MB") ALLOWED_FILE_EXTENSIONS: List[str] = Field( default=["pdf", "jpg", "jpeg", "png", "doc", "docx", "xls", "xlsx", "txt"], env="ALLOWED_FILE_EXTENSIONS" ) UPLOAD_PATH: str = Field(default="/app/uploads", env="UPLOAD_PATH") @field_validator("ALLOWED_FILE_EXTENSIONS", mode='before') @classmethod def validate_file_extensions(cls, v): if isinstance(v, str): return [ext.strip().lower() for ext in v.split(",")] return [ext.lower() for ext in v] # =================================== # SECURITY # =================================== RATE_LIMIT_ENABLED: bool = Field(default=True, env="RATE_LIMIT_ENABLED") PASSWORD_MIN_LENGTH: int = Field(default=8, env="PASSWORD_MIN_LENGTH") # Argon2 settings ARGON2_TIME_COST: int = Field(default=3, env="ARGON2_TIME_COST") ARGON2_MEMORY_COST: int = Field(default=65536, env="ARGON2_MEMORY_COST") ARGON2_PARALLELISM: int = Field(default=4, env="ARGON2_PARALLELISM") # =================================== # LOGGING # =================================== LOG_LEVEL: str = Field(default="INFO", env="LOG_LEVEL") LOG_FORMAT: str = Field(default="json", env="LOG_FORMAT") LOG_FILE: Optional[str] = Field(default=None, env="LOG_FILE") # =================================== # FRONTEND URLS # =================================== CLIENT_FRONTEND_URL: str = Field(default="http://localhost:3000", env="CLIENT_FRONTEND_URL") INTERNAL_FRONTEND_URL: str = Field(default="http://localhost:3001", env="INTERNAL_FRONTEND_URL") # =================================== # HEALTH CHECKS # =================================== HEALTH_CHECK_TIMEOUT: int = Field(default=30, env="HEALTH_CHECK_TIMEOUT") # =================================== # CELERY # =================================== CELERY_BROKER_URL: str = Field(..., env="CELERY_BROKER_URL") CELERY_RESULT_BACKEND: str = Field(..., env="CELERY_RESULT_BACKEND") def is_production(self) -> bool: """Check if environment is production.""" return self.ENVIRONMENT.lower() == "production" def is_development(self) -> bool: """Check if environment is development.""" return self.ENVIRONMENT.lower() == "development" def is_testing(self) -> bool: """Check if environment is testing.""" return self.ENVIRONMENT.lower() == "testing" @lru_cache() def get_settings() -> Settings: """ Get cached settings instance. Using lru_cache to create a singleton pattern for settings. """ return Settings()