Merge feature/auth-5c-demo (demo security audit + scheduled reset)
Audit: no authorization/isolation gaps. Add an admin-tunable scheduled demo-reset job (reuses the manual reset; no-op if no demo account).
This commit is contained in:
commit
ab56752a90
6 changed files with 39 additions and 12 deletions
|
|
@ -94,6 +94,8 @@ class Settings(BaseSettings):
|
|||
autotag_interval_minutes: int = 30
|
||||
subscriptions_resync_minutes: int = 360
|
||||
playlist_sync_minutes: int = 360
|
||||
# How often the shared demo account is auto-reset to a clean baseline (communal sandbox).
|
||||
demo_reset_minutes: int = 720
|
||||
# --- Maintenance / validation job ---
|
||||
# Runs once a day by default (admin-tunable via the Scheduler dashboard). Grace periods
|
||||
# are in days because that's the granularity that matters; the rolling re-validation
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ JOB_INTERVALS: dict[str, int] = {
|
|||
"subscriptions": settings.subscriptions_resync_minutes,
|
||||
"playlist_sync": settings.playlist_sync_minutes,
|
||||
"maintenance": settings.maintenance_interval_minutes,
|
||||
"demo_reset": settings.demo_reset_minutes,
|
||||
}
|
||||
|
||||
# Sane bounds for an admin-set interval (minutes).
|
||||
|
|
@ -253,6 +254,20 @@ def _maintenance_job() -> None:
|
|||
_job("maintenance", work)
|
||||
|
||||
|
||||
def _demo_reset_job() -> None:
|
||||
# Auto-clean the shared demo sandbox. No-op (and never creates one) if there's no demo
|
||||
# account. Lazy import avoids a routes<->scheduler import cycle.
|
||||
def work(db):
|
||||
from app.models import User
|
||||
demo = db.query(User).filter(User.is_demo.is_(True)).one_or_none()
|
||||
if demo is None:
|
||||
return {"skipped": "no demo account"}
|
||||
from app.routes.admin import reset_demo_state
|
||||
return {"playlists_seeded": reset_demo_state(db, demo)}
|
||||
|
||||
_job("demo_reset", work)
|
||||
|
||||
|
||||
# job_id -> wrapper. The single source of truth for which jobs exist and how to run one,
|
||||
# shared by start_scheduler (recurring registration) and trigger_job (manual "run now").
|
||||
JOB_FUNCS: dict[str, Callable[[], None]] = {
|
||||
|
|
@ -264,6 +279,7 @@ JOB_FUNCS: dict[str, Callable[[], None]] = {
|
|||
"subscriptions": _subscriptions_job,
|
||||
"playlist_sync": _playlist_sync_job,
|
||||
"maintenance": _maintenance_job,
|
||||
"demo_reset": _demo_reset_job,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@
|
|||
"shorts": "Shorts-Klassifizierung",
|
||||
"subscriptions": "Abo-Resync",
|
||||
"playlist_sync": "YouTube-Wiedergabelisten-Sync",
|
||||
"maintenance": "Wartung + Validierung"
|
||||
"maintenance": "Wartung + Validierung",
|
||||
"demo_reset": "Demo-Konto-Reset"
|
||||
},
|
||||
"queue": {
|
||||
"recentPending": "Zu synchronisierende Kanäle",
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@
|
|||
"shorts": "Shorts classification",
|
||||
"subscriptions": "Subscription resync",
|
||||
"playlist_sync": "YouTube playlist sync",
|
||||
"maintenance": "Maintenance + validation"
|
||||
"maintenance": "Maintenance + validation",
|
||||
"demo_reset": "Demo account reset"
|
||||
},
|
||||
"queue": {
|
||||
"recentPending": "Channels to sync",
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@
|
|||
"shorts": "Shorts-besorolás",
|
||||
"subscriptions": "Feliratkozások újraszinkronja",
|
||||
"playlist_sync": "YouTube lejátszási listák szinkronja",
|
||||
"maintenance": "Karbantartás + ellenőrzés"
|
||||
"maintenance": "Karbantartás + ellenőrzés",
|
||||
"demo_reset": "Demo fiók visszaállítása"
|
||||
},
|
||||
"queue": {
|
||||
"recentPending": "Szinkronra váró csatorna",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue