feat(demo): scheduled demo-account reset (5c)
A security audit of every mutating endpoint found no isolation gaps — all routes scope by current_user, admin routes are gated, and the demo account is sandboxed in its own user_id space (can't reach others' data, escalate, or hit admin routes). The remaining demo concern is communal pollution of its own shared state, so add an automatic reset: a new 'demo_reset' scheduler job (admin-tunable interval, default 12h) reuses the manual reset logic, and no-ops without ever creating a demo account if none exists. Verified: a triggered run wiped the demo's video states and re-seeded its sample playlists.
This commit is contained in:
parent
1f3d2b6b58
commit
c70ca495cb
6 changed files with 39 additions and 12 deletions
|
|
@ -270,14 +270,10 @@ def _seed_demo_playlists(db: Session, demo: User) -> int:
|
|||
return created
|
||||
|
||||
|
||||
@router.post("/demo/reset")
|
||||
def reset_demo(
|
||||
_: User = Depends(admin_user), db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
"""Wipe the shared demo account's per-user state (watch/save/hide states, playlists,
|
||||
preferences) back to a clean baseline and re-seed a few sample playlists. There's no
|
||||
automatic reset — this is the admin's manual 'clean up the sandbox' button."""
|
||||
demo = get_or_create_demo_user(db)
|
||||
def reset_demo_state(db: Session, demo: User) -> int:
|
||||
"""Wipe the demo account's per-user state (watch/save/hide states, playlists, preferences)
|
||||
back to a clean baseline and re-seed a few sample playlists. Returns the seeded count.
|
||||
Shared by the admin's manual reset button and the scheduled demo reset (see scheduler)."""
|
||||
db.execute(delete(VideoState).where(VideoState.user_id == demo.id))
|
||||
# Playlist items cascade on playlist delete (ondelete="CASCADE").
|
||||
db.execute(delete(Playlist).where(Playlist.user_id == demo.id))
|
||||
|
|
@ -285,4 +281,14 @@ def reset_demo(
|
|||
db.commit()
|
||||
seeded = _seed_demo_playlists(db, demo)
|
||||
db.commit()
|
||||
return {"reset": True, "playlists_seeded": seeded}
|
||||
return seeded
|
||||
|
||||
|
||||
@router.post("/demo/reset")
|
||||
def reset_demo(
|
||||
_: User = Depends(admin_user), db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
"""Manually clean up the shared demo sandbox back to a baseline. A scheduled job does the
|
||||
same automatically (admin-tunable interval); this is the admin's on-demand button."""
|
||||
demo = get_or_create_demo_user(db)
|
||||
return {"reset": True, "playlists_seeded": reset_demo_state(db, demo)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue