admin: mark ghost users (no DB account) and add Delete dir button

- /api/admin/disk now includes in_db flag per user (true if account exists in DB)
- Ghost users (directory exists, no DB account) show amber 'ghost' badge and only
  Diag + Delete dir buttons (no Re-extract, Rebuild, Reset pwd, Reset data)
- DELETE /api/admin/users/{handle}/directory wipes the entire directory and updates
  the root manifest; refuses if the account still exists in the DB
- Wires up rmdir-btn with a window.confirm before calling the new endpoint
This commit is contained in:
Davide Scaini
2026-04-15 14:58:54 +02:00
parent dfd56e4448
commit a33fea91cf
2 changed files with 103 additions and 24 deletions
+35
View File
@@ -509,6 +509,8 @@ async def admin_disk(bincio_session: Optional[str] = Cookie(default=None)) -> JS
return 0
return sum(1 for f in path.glob(pattern) if f.is_file())
db = _get_db()
from bincio.serve.db import get_user as _get_user
users = []
for user_dir in sorted(data_dir.iterdir()):
if not user_dir.is_dir() or user_dir.name.startswith("_"):
@@ -517,6 +519,7 @@ async def admin_disk(bincio_session: Optional[str] = Cookie(default=None)) -> JS
leaked = [f for f in user_dir.glob("tmp*.zip") if f.is_file()]
users.append({
"handle": user_dir.name,
"in_db": _get_user(db, user_dir.name) is not None,
"total_mb": _mb(user_dir),
"activities_mb": _mb(user_dir / "activities"),
"activities_count": _count(user_dir / "activities", "*.json"),
@@ -850,6 +853,38 @@ async def admin_delete_activities(
return JSONResponse({"ok": True, "deleted": deleted})
@app.delete("/api/admin/users/{handle}/directory")
async def admin_delete_user_directory(
handle: str,
bincio_session: Optional[str] = Cookie(default=None),
) -> JSONResponse:
"""Delete the entire user directory from disk (for ghost users not in the DB).
Refuses if the handle exists as an account in the database — use
DELETE /api/admin/users/{handle}/activities for registered users.
"""
import shutil
_require_admin(bincio_session)
db = _get_db()
from bincio.serve.db import get_user as _get_user
if _get_user(db, handle) is not None:
raise HTTPException(
400,
f"User '{handle}' is still in the database. Remove the account first, "
"or use 'Reset data' to wipe only activity files.",
)
user_dir = _get_data_dir() / handle
if not user_dir.is_dir():
raise HTTPException(404, f"No directory for '{handle}'")
shutil.rmtree(user_dir)
# Rebuild root manifest so the ghost shard disappears from the site
from bincio.render.cli import _write_root_manifest
try:
_write_root_manifest(_get_data_dir())
except Exception:
pass
return JSONResponse({"ok": True})
# ── Write API (ported from bincio edit, auth-gated) ───────────────────────────