"""Compatible configuration management with MariaDB and Redis support.""" import os from functools import lru_cache from typing import Optional, Dict, Any from pydantic_settings import BaseSettings from pydantic import Field, validator class Settings(BaseSettings): """Application settings with backward compatibility.""" # Application settings app_name: str = Field(default="My Uploader Bot", env="APP_NAME") debug: bool = Field(default=False, env="DEBUG") environment: str = Field(default="production", env="ENVIRONMENT") node_env: str = Field(default="production", env="NODE_ENV") host: str = Field(default="0.0.0.0", env="HOST") port: int = Field(default=15100, env="PORT") # API settings api_host: str = Field(default="0.0.0.0", env="API_HOST") api_port: int = Field(default=15100, env="API_PORT") api_workers: int = Field(default=1, env="API_WORKERS") # Security settings secret_key: str = Field(env="SECRET_KEY", default="your-secret-key-change-this") jwt_secret_key: str = Field(env="JWT_SECRET_KEY", default="jwt-secret-change-this") jwt_secret: str = Field(env="JWT_SECRET", default="jwt-secret-change-this") encryption_key: str = Field(env="ENCRYPTION_KEY", default="encryption-key-change-this") jwt_algorithm: str = Field(default="HS256", env="JWT_ALGORITHM") jwt_expire_minutes: int = Field(default=30, env="JWT_EXPIRE_MINUTES") # MariaDB/MySQL settings (preserving existing configuration) mysql_host: str = Field(default="maria_db", env="MYSQL_HOST") mysql_port: int = Field(default=3306, env="MYSQL_PORT") mysql_user: str = Field(default="myuploader", env="MYSQL_USER") mysql_password: str = Field(default="password", env="MYSQL_PASSWORD") mysql_database: str = Field(default="myuploader", env="MYSQL_DATABASE") mysql_root_password: str = Field(default="password", env="MYSQL_ROOT_PASSWORD") # Database pool settings database_pool_size: int = Field(default=20, env="DATABASE_POOL_SIZE") database_max_overflow: int = Field(default=30, env="DATABASE_MAX_OVERFLOW") database_pool_timeout: int = Field(default=30, env="DATABASE_POOL_TIMEOUT") database_pool_recycle: int = Field(default=3600, env="DATABASE_POOL_RECYCLE") # Optional new database URL (for future migration) database_url: Optional[str] = Field(default=None, env="DATABASE_URL") # Redis settings (new addition) redis_enabled: bool = Field(default=True, env="REDIS_ENABLED") redis_url: str = Field(default="redis://localhost:6379/0", env="REDIS_URL") redis_host: str = Field(default="redis", env="REDIS_HOST") redis_port: int = Field(default=6379, env="REDIS_PORT") redis_password: Optional[str] = Field(default=None, env="REDIS_PASSWORD") redis_db: int = Field(default=0, env="REDIS_DB") redis_max_connections: int = Field(default=50, env="REDIS_MAX_CONNECTIONS") redis_socket_timeout: int = Field(default=30, env="REDIS_SOCKET_TIMEOUT") redis_socket_connect_timeout: int = Field(default=30, env="REDIS_SOCKET_CONNECT_TIMEOUT") # Cache settings cache_enabled: bool = Field(default=True, env="CACHE_ENABLED") cache_default_ttl: int = Field(default=300, env="CACHE_DEFAULT_TTL") # 5 minutes cache_user_ttl: int = Field(default=600, env="CACHE_USER_TTL") # 10 minutes cache_content_ttl: int = Field(default=1800, env="CACHE_CONTENT_TTL") # 30 minutes # Storage settings (preserving existing paths) storage_path: str = Field(default="/Storage/storedContent", env="STORAGE_PATH") logs_path: str = Field(default="/Storage/logs", env="LOGS_PATH") sql_storage_path: str = Field(default="/Storage/sqlStorage", env="SQL_STORAGE_PATH") # File upload settings max_file_size: int = Field(default=100 * 1024 * 1024, env="MAX_FILE_SIZE") # 100MB max_upload_size: str = Field(default="100MB", env="MAX_UPLOAD_SIZE") upload_path: str = Field(default="./data/uploads", env="UPLOAD_PATH") allowed_extensions: str = Field(default=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.txt", env="ALLOWED_EXTENSIONS") # Rate limiting rate_limit_enabled: bool = Field(default=True, env="RATE_LIMIT_ENABLED") rate_limit_requests: int = Field(default=100, env="RATE_LIMIT_REQUESTS") rate_limit_window: int = Field(default=3600, env="RATE_LIMIT_WINDOW") # 1 hour # TON Blockchain settings (preserving existing) ton_network: str = Field(default="mainnet", env="TON_NETWORK") ton_api_key: Optional[str] = Field(default=None, env="TON_API_KEY") ton_wallet_address: Optional[str] = Field(default=None, env="TON_WALLET_ADDRESS") # Telegram Bot settings telegram_api_key: Optional[str] = Field(default=None, env="TELEGRAM_API_KEY") client_telegram_api_key: Optional[str] = Field(default=None, env="CLIENT_TELEGRAM_API_KEY") telegram_webhook_enabled: bool = Field(default=False, env="TELEGRAM_WEBHOOK_ENABLED") # MY Network settings my_network_node_id: str = Field(default="local-node", env="MY_NETWORK_NODE_ID") my_network_port: int = Field(default=15100, env="MY_NETWORK_PORT") my_network_host: str = Field(default="0.0.0.0", env="MY_NETWORK_HOST") my_network_domain: str = Field(default="localhost", env="MY_NETWORK_DOMAIN") my_network_ssl_enabled: bool = Field(default=False, env="MY_NETWORK_SSL_ENABLED") my_network_bootstrap_nodes: str = Field(default="", env="MY_NETWORK_BOOTSTRAP_NODES") # License settings license_check_enabled: bool = Field(default=True, env="LICENSE_CHECK_ENABLED") license_server_url: Optional[str] = Field(default=None, env="LICENSE_SERVER_URL") # Indexer settings indexer_enabled: bool = Field(default=True, env="INDEXER_ENABLED") indexer_interval: int = Field(default=300, env="INDEXER_INTERVAL") # 5 minutes # Convert process settings convert_enabled: bool = Field(default=True, env="CONVERT_ENABLED") convert_queue_size: int = Field(default=10, env="CONVERT_QUEUE_SIZE") # Logging settings log_level: str = Field(default="INFO", env="LOG_LEVEL") log_format: str = Field(default="json", env="LOG_FORMAT") log_file: str = Field(default="./logs/app.log", env="LOG_FILE") log_file_enabled: bool = Field(default=True, env="LOG_FILE_ENABLED") log_file_max_size: int = Field(default=10 * 1024 * 1024, env="LOG_FILE_MAX_SIZE") # 10MB log_file_backup_count: int = Field(default=5, env="LOG_FILE_BACKUP_COUNT") # Maintenance maintenance_mode: bool = Field(default=False, env="MAINTENANCE_MODE") # API settings api_title: str = Field(default="My Uploader Bot API", env="API_TITLE") api_version: str = Field(default="1.0.0", env="API_VERSION") api_description: str = Field(default="File upload and management API", env="API_DESCRIPTION") cors_enabled: bool = Field(default=True, env="CORS_ENABLED") cors_origins: str = Field(default="*", env="CORS_ORIGINS") # Health check settings health_check_enabled: bool = Field(default=True, env="HEALTH_CHECK_ENABLED") health_check_interval: int = Field(default=60, env="HEALTH_CHECK_INTERVAL") # Metrics settings metrics_enabled: bool = Field(default=True, env="METRICS_ENABLED") metrics_endpoint: str = Field(default="/metrics", env="METRICS_ENDPOINT") @validator("allowed_extensions") def validate_extensions(cls, v): """Validate and normalize file extensions.""" if isinstance(v, str): return [ext.strip().lower() for ext in v.split(",") if ext.strip()] return v @validator("cors_origins") def validate_cors_origins(cls, v): """Validate and normalize CORS origins.""" if isinstance(v, str) and v != "*": return [origin.strip() for origin in v.split(",") if origin.strip()] return v @validator("log_level") def validate_log_level(cls, v): """Validate log level.""" valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] if v.upper() not in valid_levels: raise ValueError(f"Log level must be one of: {valid_levels}") return v.upper() def get_database_url(self) -> str: """Get complete database URL.""" if self.database_url: return self.database_url return f"mysql+aiomysql://{self.mysql_user}:{self.mysql_password}@{self.mysql_host}:{self.mysql_port}/{self.mysql_database}" def get_redis_url(self) -> str: """Get complete Redis URL.""" if self.redis_password: return f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}" return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}" def get_allowed_extensions_set(self) -> set: """Get allowed extensions as a set.""" if isinstance(self.allowed_extensions, list): return set(self.allowed_extensions) return set(ext.strip().lower() for ext in self.allowed_extensions.split(",") if ext.strip()) def get_cors_origins_list(self) -> list: """Get CORS origins as a list.""" if self.cors_origins == "*": return ["*"] if isinstance(self.cors_origins, list): return self.cors_origins return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()] def is_development(self) -> bool: """Check if running in development mode.""" return self.environment.lower() in ["development", "dev", "local"] def is_production(self) -> bool: """Check if running in production mode.""" return self.environment.lower() in ["production", "prod"] def get_cache_config(self) -> Dict[str, Any]: """Get cache configuration dictionary.""" return { "enabled": self.cache_enabled and self.redis_enabled, "default_ttl": self.cache_default_ttl, "user_ttl": self.cache_user_ttl, "content_ttl": self.cache_content_ttl, "redis_url": self.get_redis_url(), "max_connections": self.redis_max_connections, } def get_database_config(self) -> Dict[str, Any]: """Get database configuration dictionary.""" return { "url": self.get_database_url(), "pool_size": self.database_pool_size, "max_overflow": self.database_max_overflow, "pool_timeout": self.database_pool_timeout, "pool_recycle": self.database_pool_recycle, } class Config: env_file = ".env" env_file_encoding = "utf-8" case_sensitive = False @lru_cache() def get_settings() -> Settings: """Get cached settings instance.""" return Settings() # Backward compatibility functions def get_mysql_config() -> Dict[str, Any]: """Get MySQL configuration for backward compatibility.""" settings = get_settings() return { "host": settings.mysql_host, "port": settings.mysql_port, "user": settings.mysql_user, "password": settings.mysql_password, "database": settings.mysql_database, } def get_storage_config() -> Dict[str, str]: """Get storage configuration for backward compatibility.""" settings = get_settings() return { "storage_path": settings.storage_path, "logs_path": settings.logs_path, "sql_storage_path": settings.sql_storage_path, } def get_redis_config() -> Dict[str, Any]: """Get Redis configuration.""" settings = get_settings() return { "enabled": settings.redis_enabled, "host": settings.redis_host, "port": settings.redis_port, "password": settings.redis_password, "db": settings.redis_db, "max_connections": settings.redis_max_connections, "socket_timeout": settings.redis_socket_timeout, "socket_connect_timeout": settings.redis_socket_connect_timeout, } # Environment variables validation def validate_environment(): """Validate required environment variables.""" settings = get_settings() required_vars = [ "SECRET_KEY", "JWT_SECRET_KEY", "MYSQL_PASSWORD", ] missing_vars = [] for var in required_vars: if not os.getenv(var): missing_vars.append(var) if missing_vars: raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}") return True