Files
bincio-activity/bincio/serve/init_cmd.py
T
2026-04-10 12:38:17 +02:00

93 lines
4.5 KiB
Python

"""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")
@click.option("--max-users", default=0, type=int, help="Maximum number of users allowed (0 = unlimited)")
def init(data_dir: str, handle: str, password: str, display_name: str, name: str, max_users: int) -> 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, set_setting
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")
# ── User limit ────────────────────────────────────────────────────────────
if max_users > 0:
set_setting(db, "max_users", str(max_users))
console.print(f" [green]✓[/green] user limit set to {max_users}")
else:
console.print(" [dim]·[/dim] no user limit (unlimited)")
# ── 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",
))