""" Setup wizard utilities for first-time system configuration. Detects if the system needs initial setup and provides required settings list. """ import logging from typing import Any, Dict, List from app.config import settings logger = logging.getLogger(__name__) def get_required_settings() -> List[Dict[str, Any]]: """ Get list of settings that are absolutely required for the system to operate. Returns: List of required setting definitions with metadata """ return [ { "key": "database_url", "label": "Database URL", "description": "Database connection string (e.g., sqlite:///./app/database.db)", "type": "string", "sensitive": False, "default": "sqlite:///./app/database.db", "wizard_step": 1, "wizard_category": "Core Infrastructure", }, { "key": "redis_url", "label": "Redis URL", "description": "Redis connection for task queue (e.g., redis://localhost:6379/0)", "type": "string", "sensitive": False, "default": "redis://localhost:6379/0", "wizard_step": 1, "wizard_category": "Core Infrastructure", }, { "key": "workdir", "label": "Working Directory", "description": "Directory for temporary file storage and processing", "type": "string", "sensitive": False, "default": "/workdir", "wizard_step": 1, "wizard_category": "Core Infrastructure", }, { "key": "gotenberg_url", "label": "Gotenberg URL", "description": "Gotenberg service URL for document conversion", "type": "string", "sensitive": False, "default": "http://gotenberg:3000", "wizard_step": 1, "wizard_category": "Core Infrastructure", }, { "key": "session_secret", "label": "Session Secret", "description": "Secret key for session encryption (min 32 characters, auto-generate recommended)", "type": "string", "sensitive": True, "default": None, # Should be generated "wizard_step": 2, "wizard_category": "Security", }, { "key": "admin_username", "label": "Admin Username", "description": "Username for the admin account", "type": "string", "sensitive": False, "default": "admin", "wizard_step": 2, "wizard_category": "Security", }, { "key": "admin_password", "label": "Admin Password", "description": "Password for the admin account", "type": "string", "sensitive": True, "default": None, # Must be set "wizard_step": 2, "wizard_category": "Security", }, { "key": "ai_provider", "label": "AI Provider", "description": "AI provider for metadata extraction and OCR refinement (openai, azure, anthropic, gemini, ollama, openrouter, portkey, litellm)", "type": "string", "sensitive": False, "default": "openai", "options": ["openai", "azure", "anthropic", "gemini", "ollama", "openrouter", "portkey", "litellm"], "wizard_step": 3, "wizard_category": "AI Services", }, { "key": "openai_api_key", "label": "API Key (OpenAI / Azure / LiteLLM)", "description": "API key for OpenAI, Azure OpenAI, or LiteLLM (not required for Ollama)", "type": "string", "sensitive": True, "default": None, "wizard_step": 3, "wizard_category": "AI Services", }, { "key": "openai_model", "label": "Default Model", "description": "Model name used when AI_MODEL is not set (e.g. gpt-4o-mini, claude-3-5-sonnet-20241022, llama3.2)", "type": "string", "sensitive": False, "default": "gpt-4o-mini", "wizard_step": 3, "wizard_category": "AI Services", }, ] def is_setup_required() -> bool: """ Check if the system requires initial setup. Returns True if any critical required settings are missing or have placeholder values. Returns: True if setup wizard should be shown, False otherwise """ try: # Critical settings that must be configured critical_settings = [ ("session_secret", ["INSECURE_DEFAULT_FOR_DEVELOPMENT_ONLY_DO_NOT_USE_IN_PRODUCTION_MINIMUM_32_CHARS"]), ("admin_password", [None, "", "your_secure_password", "changeme", "admin"]), ] for setting_key, invalid_values in critical_settings: value = getattr(settings, setting_key, None) if value in invalid_values: logger.warning(f"Setup required: {setting_key} has placeholder or missing value") return True # All critical settings are configured return False except Exception as e: logger.error(f"Error checking if setup required: {e}") # If we can't check, assume setup is not required (fail open) return False def get_missing_required_settings() -> List[str]: """ Get list of required settings that are missing or have placeholder values. Returns: List of setting keys that need to be configured """ missing = [] for required_setting in get_required_settings(): key = required_setting["key"] value = getattr(settings, key, None) # Check if value is missing or is a placeholder placeholder_values = [ None, "", f"<{key.upper()}>", "test-key", "your_secure_password", "changeme", "INSECURE_DEFAULT_FOR_DEVELOPMENT_ONLY_DO_NOT_USE_IN_PRODUCTION_MINIMUM_32_CHARS", ] if value in placeholder_values: missing.append(key) return missing def get_wizard_steps() -> Dict[int, List[Dict[str, Any]]]: """ Get setup wizard steps organized by step number. Returns: Dictionary mapping step number to list of settings in that step """ steps = {} for setting in get_required_settings(): step_num = setting.get("wizard_step", 1) if step_num not in steps: steps[step_num] = [] steps[step_num].append(setting) return steps