""" HarborForge initialization via AbstractWizard. On startup, reads config from AbstractWizard and creates: - Admin user (if not exists) - Default project (if configured) """ import os import logging import httpx from sqlalchemy.orm import Session from app.models import models from app.api.deps import get_password_hash logger = logging.getLogger("harborforge.init") WIZARD_URL = os.getenv("WIZARD_URL", "http://wizard:8080") WIZARD_CONFIG = os.getenv("WIZARD_CONFIG", "harborforge.json") def fetch_wizard_config() -> dict | None: """Fetch initialization config from AbstractWizard.""" url = f"{WIZARD_URL}/api/v1/config/{WIZARD_CONFIG}" try: resp = httpx.get(url, timeout=10) if resp.status_code == 200: data = resp.json() # AbstractWizard wraps in {"data": ...} return data.get("data", data) elif resp.status_code == 404: logger.info("No wizard config found at %s, skipping initialization", url) return None else: logger.warning("Wizard returned %d: %s", resp.status_code, resp.text) return None except httpx.ConnectError: logger.info("AbstractWizard not available at %s, skipping", WIZARD_URL) return None except Exception as e: logger.warning("Failed to fetch wizard config: %s", e) return None def init_admin_user(db: Session, admin_cfg: dict) -> None: """Create admin user if not exists.""" username = admin_cfg.get("username", "admin") existing = db.query(models.User).filter(models.User.username == username).first() if existing: logger.info("Admin user '%s' already exists (id=%d), skipping", username, existing.id) return password = admin_cfg.get("password", "changeme") user = models.User( username=username, email=admin_cfg.get("email", f"{username}@harborforge.local"), full_name=admin_cfg.get("full_name", "Admin"), hashed_password=get_password_hash(password), is_admin=True, is_active=True, ) db.add(user) db.commit() db.refresh(user) logger.info("Created admin user '%s' (id=%d)", username, user.id) def init_default_project(db: Session, project_cfg: dict, admin_user_id: int) -> None: """Create default project if configured and not exists.""" name = project_cfg.get("name", "Default") existing = db.query(models.Project).filter(models.Project.name == name).first() if existing: logger.info("Project '%s' already exists (id=%d), skipping", name, existing.id) return project = models.Project( name=name, description=project_cfg.get("description", ""), owner_id=admin_user_id, ) db.add(project) db.commit() db.refresh(project) logger.info("Created default project '%s' (id=%d)", name, project.id) def run_init(db: Session) -> None: """Main initialization entry point.""" config = fetch_wizard_config() if not config: return logger.info("Running HarborForge initialization from AbstractWizard") # Admin user admin_cfg = config.get("admin") if admin_cfg: init_admin_user(db, admin_cfg) # Default project project_cfg = config.get("default_project") if project_cfg: admin = db.query(models.User).filter(models.User.is_admin == True).first() if admin: init_default_project(db, project_cfg, admin.id) logger.info("Initialization complete")