Major additions: - Web UI: Jinja2 templates in FastAPI (login, dashboard, catalog, corporate memory, admin) - API: catalog profiles/metrics, telegram verify/unlink/status, admin table registry CRUD - Corporate memory governance: approve/reject/mandate/revoke/edit/batch + audit log - Sync: real DataSyncManager trigger, sync-settings, table-subscriptions - CLI: setup (init/test/deploy/verify), server (logs/restart/deploy/backup), explore - Instance config integration (instance.yaml loaded at startup) - 140 tests passing (25 new)
131 lines
3.9 KiB
Python
131 lines
3.9 KiB
Python
"""Setup commands — da setup init/test-connection/deploy/first-sync/verify."""
|
|
|
|
import json
|
|
import os
|
|
|
|
import typer
|
|
|
|
from cli.client import api_get, api_post
|
|
|
|
setup_app = typer.Typer(help="Instance setup (guided by AI agent)")
|
|
|
|
|
|
@setup_app.command("init")
|
|
def setup_init(
|
|
server: str = typer.Option("http://localhost:8000", help="Server URL"),
|
|
):
|
|
"""Initialize a new instance configuration."""
|
|
typer.echo(f"Server: {server}")
|
|
typer.echo("Creating config directory...")
|
|
|
|
from cli.config import _config_dir
|
|
config_dir = _config_dir()
|
|
config_file = config_dir / "config.yaml"
|
|
|
|
import yaml
|
|
config = {"server": server}
|
|
config_file.write_text(yaml.dump(config))
|
|
typer.echo(f"Config saved to {config_file}")
|
|
|
|
os.environ["DA_SERVER"] = server
|
|
typer.echo("\nNext: da setup test-connection")
|
|
|
|
|
|
@setup_app.command("test-connection")
|
|
def test_connection():
|
|
"""Test connection to the server and data source."""
|
|
typer.echo("Testing server connection...")
|
|
try:
|
|
resp = api_get("/api/health")
|
|
health = resp.json()
|
|
typer.echo(f" Server: {health.get('status', 'unknown')}")
|
|
for svc, info in health.get("services", {}).items():
|
|
typer.echo(f" {svc}: {info.get('status', '?')}")
|
|
except Exception as e:
|
|
typer.echo(f" FAILED: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
|
|
typer.echo("\nNext: da setup first-sync")
|
|
|
|
|
|
@setup_app.command("first-sync")
|
|
def first_sync():
|
|
"""Trigger the first data sync."""
|
|
typer.echo("Triggering initial data sync...")
|
|
try:
|
|
resp = api_post("/api/sync/trigger")
|
|
if resp.status_code == 200:
|
|
typer.echo(f" {resp.json().get('message', 'Sync triggered')}")
|
|
else:
|
|
typer.echo(f" Failed: {resp.text}", err=True)
|
|
except Exception as e:
|
|
typer.echo(f" Error: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
|
|
typer.echo("\nNext: da setup verify")
|
|
|
|
|
|
@setup_app.command("verify")
|
|
def verify():
|
|
"""Verify the instance is working end-to-end."""
|
|
typer.echo("Running verification checks...")
|
|
checks = []
|
|
|
|
# 1. Health
|
|
try:
|
|
resp = api_get("/api/health")
|
|
h = resp.json()
|
|
checks.append(("Health", h.get("status") == "healthy", h.get("status")))
|
|
except Exception as e:
|
|
checks.append(("Health", False, str(e)))
|
|
|
|
# 2. Data
|
|
try:
|
|
resp = api_get("/api/sync/manifest")
|
|
m = resp.json()
|
|
table_count = len(m.get("tables", {}))
|
|
checks.append(("Data", table_count > 0, f"{table_count} tables"))
|
|
except Exception as e:
|
|
checks.append(("Data", False, str(e)))
|
|
|
|
# 3. Users
|
|
try:
|
|
resp = api_get("/api/users")
|
|
if resp.status_code == 200:
|
|
count = len(resp.json())
|
|
checks.append(("Users", count > 0, f"{count} users"))
|
|
else:
|
|
checks.append(("Users", True, "requires admin token"))
|
|
except Exception as e:
|
|
checks.append(("Users", False, str(e)))
|
|
|
|
for name, ok, detail in checks:
|
|
status = "PASS" if ok else "FAIL"
|
|
typer.echo(f" [{status}] {name}: {detail}")
|
|
|
|
all_ok = all(ok for _, ok, _ in checks)
|
|
if all_ok:
|
|
typer.echo("\nAll checks passed! Instance is ready.")
|
|
else:
|
|
typer.echo("\nSome checks failed. Review above.")
|
|
raise typer.Exit(1)
|
|
|
|
|
|
@setup_app.command("add-user")
|
|
def add_first_user(
|
|
email: str = typer.Argument(..., help="Admin email"),
|
|
name: str = typer.Option("", help="Display name"),
|
|
):
|
|
"""Add the first admin user to the instance."""
|
|
resp = api_post("/api/users", json={
|
|
"email": email,
|
|
"name": name or email.split("@")[0],
|
|
"role": "admin",
|
|
})
|
|
if resp.status_code == 201:
|
|
typer.echo(f"Admin user created: {email}")
|
|
elif resp.status_code == 409:
|
|
typer.echo(f"User {email} already exists.")
|
|
else:
|
|
typer.echo(f"Failed: {resp.text}", err=True)
|
|
raise typer.Exit(1)
|