feat(backend)!: kill AbstractWizard, env-driven config + hf-cli
Drops the AbstractWizard config-volume bootstrap entirely. All deploy-time
config now comes from docker env vars (.env). First-deploy admin user + OIDC
provider config are operator-driven via `docker exec hf_backend hf-cli ...`.
Backend changes:
- entrypoint.sh: drop config-wait loop, just exec uvicorn
- app/core/config.py: drop _resolve_db_url + OIDC_* env vars (DB only now);
keep HARBORFORGE_OIDC_ONLY (deploy-time policy)
- app/init_wizard.py → app/init_bootstrap.py: drop load_config / admin / OIDC /
default-project bootstrap; keep idempotent startup seed (permissions,
default roles, acc-mgr + deleted-user builtins)
- app/main.py: /config/status now returns {initialized: <admin exists>};
startup() imports init_bootstrap.run_bootstrap
- app/api/routers/oidc.py: get_effective_oidc reads DB only (no env fallback)
- app/services/harborforge_config.py: removed (replaced by direct env reads)
- app/services/discord_wakeup.py: HF_DISCORD_GUILD_ID / HF_DISCORD_BOT_TOKEN env
- app/api/routers/users.py + tests/conftest.py: rename init_wizard refs
New hf-cli surface (app/cli/, invoked via /usr/local/bin/hf-cli shim):
hf-cli admin create-user --email <e> [--username <u>] [--password <p>]
[--oidc-issuer <url> --oidc-subject <sub>]
hf-cli admin list
hf-cli admin set-role --username <u> --role <admin|mgr|dev|guest|account-manager>
hf-cli admin reset-password --username <u> --password <p>
hf-cli admin bind-oidc --username <u> --oidc-issuer <url> --oidc-subject <sub>
hf-cli config oidc [--issuer/...] [--client-id/...] [--client-secret/...]
[--redirect-uri/...] [--enabled true|false] [--show-secret]
Bootstrap migration on existing deployments: existing admin / OIDC settings
in the DB are preserved across the cutover; only the wizard config-volume
+ wizard sidecar services need to be removed from compose. Restart picks
up the new entrypoint + skips the config wait.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.services.harborforge_config import get_discord_wakeup_config
|
||||
|
||||
DISCORD_API_BASE = "https://discord.com/api/v10"
|
||||
WAKEUP_CATEGORY_NAME = "HarborForge Wakeup"
|
||||
|
||||
|
||||
def _discord_config() -> dict[str, str | None]:
|
||||
"""Discord wakeup is configured via env vars (previously read from the
|
||||
AbstractWizard config file). Returns guild_id+bot_token or Nones."""
|
||||
return {
|
||||
"guild_id": os.getenv("HARBORFORGE_DISCORD_GUILD_ID") or None,
|
||||
"bot_token": os.getenv("HARBORFORGE_DISCORD_BOT_TOKEN") or None,
|
||||
}
|
||||
|
||||
|
||||
def _headers(bot_token: str) -> dict[str, str]:
|
||||
return {
|
||||
"Authorization": f"Bot {bot_token}",
|
||||
@@ -34,7 +42,7 @@ def _ensure_category(guild_id: str, bot_token: str) -> str | None:
|
||||
|
||||
|
||||
def create_private_wakeup_channel(discord_user_id: str, title: str, message: str) -> dict[str, Any]:
|
||||
cfg = get_discord_wakeup_config()
|
||||
cfg = _discord_config()
|
||||
guild_id = cfg.get("guild_id")
|
||||
bot_token = cfg.get("bot_token")
|
||||
if not guild_id or not bot_token:
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
CONFIG_DIR = os.getenv("CONFIG_DIR", "/config")
|
||||
CONFIG_FILE = os.getenv("CONFIG_FILE", "harborforge.json")
|
||||
|
||||
|
||||
def load_runtime_config() -> dict[str, Any]:
|
||||
config_path = os.path.join(CONFIG_DIR, CONFIG_FILE)
|
||||
if not os.path.exists(config_path):
|
||||
return {}
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def get_discord_wakeup_config() -> dict[str, str | None]:
|
||||
cfg = load_runtime_config()
|
||||
discord_cfg = cfg.get("discord") or {}
|
||||
return {
|
||||
"guild_id": discord_cfg.get("guild_id"),
|
||||
"bot_token": discord_cfg.get("bot_token"),
|
||||
}
|
||||
Reference in New Issue
Block a user