"""bincio init — bootstrap a fresh multi-user instance.""" from __future__ import annotations from pathlib import Path import click from rich.console import Console from rich.panel import Panel console = Console() @click.command("init") @click.option("--data-dir", required=True, type=click.Path(), help="BAS data directory to initialise") @click.option("--handle", required=True, help="Admin user handle (e.g. 'dave')") @click.option("--password", required=True, hide_input=True, confirmation_prompt=True, help="Admin password") @click.option("--display-name", default="", help="Admin display name (defaults to handle)") @click.option("--name", default="", help="Instance name shown in the feed") def init(data_dir: str, handle: str, password: str, display_name: str, name: str) -> None: """Bootstrap a fresh bincio multi-user instance. Creates the SQLite database, the admin user, the per-user data directory, and prints a first invite code. Safe to re-run — skips steps already done. """ from bincio.serve.db import create_invite, create_user, get_user, open_db dd = Path(data_dir).expanduser().resolve() dd.mkdir(parents=True, exist_ok=True) console.print(f"[bold]Initialising bincio instance[/bold] at [cyan]{dd}[/cyan]") # ── Database ───────────────────────────────────────────────────────────── db = open_db(dd) console.print(" [green]✓[/green] instance.db ready") # ── Admin user ─────────────────────────────────────────────────────────── existing = get_user(db, handle) if existing: console.print(f" [yellow]·[/yellow] user '{handle}' already exists — skipping") else: create_user(db, handle, display_name or handle, password, is_admin=True) console.print(f" [green]✓[/green] admin user '{handle}' created") # ── User data directory ─────────────────────────────────────────────────── user_dir = dd / handle user_dir.mkdir(exist_ok=True) (user_dir / "activities").mkdir(exist_ok=True) (user_dir / "edits").mkdir(exist_ok=True) console.print(f" [green]✓[/green] data dir {dd / handle}/ ready") # ── Root index.json shard manifest ─────────────────────────────────────── import json from datetime import datetime, timezone root_index = dd / "index.json" if not root_index.exists(): manifest = { "bas_version": "1.0", "instance": {"name": name or "BincioActivity", "private": True}, "generated_at": datetime.now(timezone.utc).isoformat(), "shards": [{"handle": handle, "url": f"{handle}/index.json"}], "activities": [], } root_index.write_text(json.dumps(manifest, indent=2)) console.print(" [green]✓[/green] root index.json manifest written") else: console.print(" [yellow]·[/yellow] root index.json already exists — skipping") # ── First invite code ───────────────────────────────────────────────────── code = create_invite(db, handle) console.print() console.print(Panel( f"[bold green]Instance ready![/bold green]\n\n" f"Admin: [cyan]{handle}[/cyan]\n" f"Data dir: [cyan]{dd}[/cyan]\n\n" f"First invite code:\n\n" f" [bold yellow]{code}[/bold yellow]\n\n" f"Share this link with your first user:\n" f" /register/?code={code}", title="bincio init", border_style="green", ))