""" Runtime configuration for the Brief Extractor GUI server Extends the existing core configuration with web-specific settings """ import os from typing import List, Optional from dotenv import load_dotenv # Load environment variables load_dotenv() class ServerConfig: """Server-specific configuration extending core config""" # Server Configuration HOST: str = os.getenv('SERVER_HOST', '0.0.0.0') PORT: int = int(os.getenv('SERVER_PORT', '8000')) WORKERS: int = int(os.getenv('SERVER_WORKERS', '2')) DEBUG: bool = os.getenv('DEBUG', 'false').lower() == 'true' # Development Mode DEV_MODE: bool = os.getenv('DEV_MODE', 'true').lower() == 'true' # CORS Configuration ALLOWED_ORIGINS: List[str] = [ origin.strip() for origin in os.getenv('ALLOWED_ORIGINS', 'http://localhost:3000,http://localhost:5173').split(',') ] # MSAL Authentication MSAL_CLIENT_ID: str = os.getenv('MSAL_CLIENT_ID', '') MSAL_CLIENT_SECRET: str = os.getenv('MSAL_CLIENT_SECRET', '') MSAL_TENANT_ID: str = os.getenv('MSAL_TENANT_ID', '') MSAL_REDIRECT_URI: str = os.getenv('MSAL_REDIRECT_URI', 'http://localhost:3000/auth/callback') MSAL_AUTHORITY: str = os.getenv('MSAL_AUTHORITY', f'https://login.microsoftonline.com/{MSAL_TENANT_ID}') # Security Configuration SESSION_SECRET: str = os.getenv('SESSION_SECRET', 'your-session-secret-here') SECURE_COOKIES: bool = os.getenv('SECURE_COOKIES', 'false').lower() == 'true' HTTPS_ONLY: bool = os.getenv('HTTPS_ONLY', 'false').lower() == 'true' # File Upload Configuration MAX_UPLOAD_SIZE_MB: int = int(os.getenv('MAX_UPLOAD_SIZE_MB', '200')) MAX_CONTENT_LENGTH: int = MAX_UPLOAD_SIZE_MB * 1024 * 1024 # Convert to bytes ALLOWED_EXTENSIONS: set = {'.pdf', '.pptx', '.docx', '.xlsx', '.ppt', '.doc', '.xls'} # Job Management Configuration MAX_CONCURRENT_JOBS: int = int(os.getenv('MAX_CONCURRENT_JOBS', '2')) FILE_RETENTION_HOURS: int = int(os.getenv('FILE_RETENTION_HOURS', '24')) # WebSocket Configuration WS_PING_INTERVAL_SECONDS: int = int(os.getenv('WS_PING_INTERVAL_SECONDS', '30')) # Data Storage Paths DATA_DIR: str = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') UPLOAD_DIR: str = os.path.join(DATA_DIR, 'uploads') OUTPUT_DIR: str = os.path.join(DATA_DIR, 'outputs') @classmethod def ensure_directories(cls): """Ensure all required directories exist""" os.makedirs(cls.DATA_DIR, exist_ok=True) os.makedirs(cls.UPLOAD_DIR, exist_ok=True) os.makedirs(cls.OUTPUT_DIR, exist_ok=True) @classmethod def validate_auth_config(cls) -> bool: """Validate MSAL configuration is complete for production""" if cls.DEV_MODE: return True # Skip validation in dev mode # For PKCE flow (public client), client secret is not required # Only validate client ID and tenant ID required_fields = [ cls.MSAL_CLIENT_ID, cls.MSAL_TENANT_ID ] return all(field and field.strip() != '' for field in required_fields) @classmethod def get_cors_config(cls) -> dict: """Get CORS configuration for Quart""" return { 'allow_origin': cls.ALLOWED_ORIGINS, 'allow_methods': ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], 'allow_headers': ['Content-Type', 'Authorization', 'Accept'], 'allow_credentials': True } # Global instance server_config = ServerConfig()