- Add init_wizard.py: fetch config from AbstractWizard on startup - Create admin user if not exists (from wizard config) - Create default project if configured - Graceful fallback when wizard is unavailable
108 lines
3.4 KiB
Python
108 lines
3.4 KiB
Python
"""
|
|
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")
|