#!/usr/bin/env python3 """ Configuration Management for Chat Server Supports both environment variables and .env files """ import os from typing import Optional, Any, cast from pathlib import Path try: from dotenv import load_dotenv # type: ignore load_dotenv(override=True) DOTENV_AVAILABLE = True except ImportError: DOTENV_AVAILABLE = False class Config: """Configuration class with environment variable support""" # Server Configuration HOST: str = os.getenv('HOST', '127.0.0.1') PORT: int = int(os.getenv('PORT', '8765')) MAX_HISTORY: int = int(os.getenv('MAX_HISTORY', '100')) # Admin Configuration ADMIN_PASSWORD: str = os.getenv('ADMIN_PASSWORD', '') # Should be set in environment # SSL Configuration USE_SSL: bool = os.getenv('USE_SSL', 'false').lower() == 'true' SSL_CERT_PATH: Optional[str] = os.getenv('SSL_CERT_PATH') SSL_KEY_PATH: Optional[str] = os.getenv('SSL_KEY_PATH') # Database Configuration DB_PATH: str = os.getenv('DB_PATH', 'data/chat.db') # Logging Configuration LOG_LEVEL: str = os.getenv('LOG_LEVEL', 'INFO') LOG_FILE: Optional[str] = os.getenv('LOG_FILE', 'logs/chat_server.log') # Security Configuration MAX_MESSAGE_LENGTH: int = int(os.getenv('MAX_MESSAGE_LENGTH', '4096')) MAX_NICKNAME_LENGTH: int = int(os.getenv('MAX_NICKNAME_LENGTH', '32')) RATE_LIMIT_MESSAGES: int = int(os.getenv('RATE_LIMIT_MESSAGES', '120')) RATE_LIMIT_WINDOW: int = int(os.getenv('RATE_LIMIT_WINDOW', '60')) # Session Configuration SESSION_TIMEOUT: int = int(os.getenv('SESSION_TIMEOUT', '3600')) KEEPALIVE_INTERVAL: int = int(os.getenv('KEEPALIVE_INTERVAL', '30')) RECONNECT_TIMEOUT: int = int(os.getenv('RECONNECT_TIMEOUT', '300')) # Backup Configuration BACKUP_DIR: str = os.getenv('BACKUP_DIR', './backups') AUTO_BACKUP_ENABLED: bool = os.getenv('AUTO_BACKUP_ENABLED', 'false').lower() == 'true' AUTO_BACKUP_INTERVAL: int = int(os.getenv('AUTO_BACKUP_INTERVAL', '86400')) # 24 hours # Room Configuration ROOM_EXPIRATION_HOURS: int = int(os.getenv('ROOM_EXPIRATION_HOURS', '24')) AUTO_HISTORY_CLEAR: bool = os.getenv('AUTO_HISTORY_CLEAR', 'false').lower() == 'true' @classmethod def validate(cls) -> bool: """Validate configuration""" errors = [] # Validate port range if not (1 <= cls.PORT <= 65535): errors.append(f"Invalid PORT: {cls.PORT}. Must be between 1-65535") # Validate SSL configuration if cls.USE_SSL: if not cls.SSL_CERT_PATH or not cls.SSL_KEY_PATH: errors.append("SSL enabled but SSL_CERT_PATH or SSL_KEY_PATH not set") elif not Path(cast(str, cls.SSL_CERT_PATH)).exists(): errors.append(f"SSL certificate not found: {cls.SSL_CERT_PATH}") elif not Path(cast(str, cls.SSL_KEY_PATH)).exists(): errors.append(f"SSL key not found: {cls.SSL_KEY_PATH}") # Validate positive integers if cls.MAX_HISTORY < 1: errors.append(f"MAX_HISTORY must be positive, got {cls.MAX_HISTORY}") if cls.MAX_MESSAGE_LENGTH < 1: errors.append(f"MAX_MESSAGE_LENGTH must be positive, got {cls.MAX_MESSAGE_LENGTH}") if cls.MAX_NICKNAME_LENGTH < 1: errors.append(f"MAX_NICKNAME_LENGTH must be positive, got {cls.MAX_NICKNAME_LENGTH}") # Print errors if errors: print("[ERROR] Configuration errors:") for error in errors: print(f" - {error}") return False return True @classmethod def display(cls): """Display current configuration""" print("\n" + "=" * 70) print("CHAT SERVER CONFIGURATION") print("=" * 70) print(f"Server: {cls.HOST}:{cls.PORT}") print(f"SSL: {'Enabled' if cls.USE_SSL else 'Disabled'}") print(f"Database: {cls.DB_PATH}") print(f"Log Level: {cls.LOG_LEVEL}") print(f"Log File: {cls.LOG_FILE or 'Console only'}") print(f"Max History: {cls.MAX_HISTORY} messages") print(f"Rate Limit: {cls.RATE_LIMIT_MESSAGES} msg/{cls.RATE_LIMIT_WINDOW}s") print(f"Auto Backup: {'Enabled' if cls.AUTO_BACKUP_ENABLED else 'Disabled'}") print("=" * 70 + "\n") # Validate configuration on import if __name__ != "__main__": if not Config.validate(): raise ValueError("Invalid configuration. Please check your environment variables or .env file")