Make install_server handle one-time admin bootstrap prompt
This commit is contained in:
@@ -38,6 +38,7 @@ This creates:
|
||||
- `/home/bestmidi/chgrid/server/.venv`
|
||||
- `/home/bestmidi/chgrid/server/config.toml` (if missing)
|
||||
- `/home/bestmidi/chgrid/server/.env` with `CHGRID_AUTH_SECRET` (if missing)
|
||||
- On first run only, if no admin exists, it prompts to create one immediately.
|
||||
|
||||
Edit `/home/bestmidi/chgrid/server/config.toml`:
|
||||
- `server.bind_ip = "127.0.0.1"`
|
||||
@@ -48,6 +49,14 @@ Edit `/home/bestmidi/chgrid/server/config.toml`:
|
||||
- `storage.state_file = "runtime/items.json"`
|
||||
- `auth.db_file = "runtime/chatgrid.db"`
|
||||
|
||||
If you skip first-run admin creation, run later:
|
||||
|
||||
```bash
|
||||
cd /home/bestmidi/chgrid/server
|
||||
source .env
|
||||
.venv/bin/python main.py --config config.toml --bootstrap-admin
|
||||
```
|
||||
|
||||
## 4) Build and publish client
|
||||
|
||||
```bash
|
||||
|
||||
@@ -61,5 +61,63 @@ PY
|
||||
echo "created $SERVER_DIR/.env with CHGRID_AUTH_SECRET"
|
||||
fi
|
||||
|
||||
# Load generated/shared auth secret for bootstrap checks.
|
||||
if [[ -f .env ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
if [[ -n "${CHGRID_AUTH_SECRET:-}" ]]; then
|
||||
HAS_ADMIN="$(
|
||||
.venv/bin/python - <<'PY'
|
||||
from pathlib import Path
|
||||
import os
|
||||
from app.auth_service import AuthService
|
||||
from app.config import load_config
|
||||
|
||||
cfg = load_config(Path("config.toml"))
|
||||
secret = os.getenv("CHGRID_AUTH_SECRET", "").strip()
|
||||
if not secret:
|
||||
print("unknown")
|
||||
raise SystemExit(0)
|
||||
db_file = cfg.auth.db_file.strip() or "runtime/chatgrid.db"
|
||||
db_path = Path(db_file)
|
||||
if not db_path.is_absolute():
|
||||
db_path = Path.cwd() / db_path
|
||||
svc = AuthService(
|
||||
db_path=db_path,
|
||||
token_hash_secret=secret,
|
||||
password_min_length=cfg.auth.password_min_length,
|
||||
password_max_length=cfg.auth.password_max_length,
|
||||
username_min_length=cfg.auth.username_min_length,
|
||||
username_max_length=cfg.auth.username_max_length,
|
||||
)
|
||||
try:
|
||||
print("yes" if svc.has_admin() else "no")
|
||||
finally:
|
||||
svc.close()
|
||||
PY
|
||||
)"
|
||||
|
||||
if [[ "$HAS_ADMIN" == "no" ]]; then
|
||||
if [[ -t 0 ]]; then
|
||||
read -r -p "No admin account found. Create one now? [y/N] " CREATE_ADMIN_NOW
|
||||
case "${CREATE_ADMIN_NOW:-}" in
|
||||
y|Y|yes|YES)
|
||||
.venv/bin/python main.py --config config.toml --bootstrap-admin
|
||||
;;
|
||||
*)
|
||||
echo "skipped admin bootstrap (you can run: .venv/bin/python main.py --config config.toml --bootstrap-admin)"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "no admin account found (non-interactive run)."
|
||||
echo "run once to bootstrap: .venv/bin/python main.py --config config.toml --bootstrap-admin"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "server install complete"
|
||||
echo "next: edit $SERVER_DIR/config.toml (TLS, bind_ip, port)"
|
||||
|
||||
@@ -83,12 +83,17 @@ class AuthService:
|
||||
def bootstrap_admin(self, username: str, password: str, email: str | None = None) -> AuthUser:
|
||||
"""Create the first admin account, or fail if one already exists."""
|
||||
|
||||
existing = self._conn.execute("SELECT 1 FROM users WHERE role = 'admin' LIMIT 1").fetchone()
|
||||
if existing is not None:
|
||||
if self.has_admin():
|
||||
raise AuthError("An admin account already exists.")
|
||||
created = self.register(username, password, email=email, role="admin")
|
||||
return created.user
|
||||
|
||||
def has_admin(self) -> bool:
|
||||
"""Return True when at least one admin account exists."""
|
||||
|
||||
existing = self._conn.execute("SELECT 1 FROM users WHERE role = 'admin' LIMIT 1").fetchone()
|
||||
return existing is not None
|
||||
|
||||
def register(
|
||||
self,
|
||||
username: str,
|
||||
|
||||
Reference in New Issue
Block a user