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:
@@ -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) ───────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user