M35: harden the shared public demo

This commit is contained in:
NuklearRabbit
2026-08-10 22:32:58 +02:00
parent c7492bf6ad
commit 809ba0ddcc
9 changed files with 142 additions and 22 deletions
+38 -16
View File
@@ -1,10 +1,11 @@
from __future__ import annotations
import threading
import time
import uuid
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.api.deps import get_current_user, get_db, require_operations_manager
@@ -19,6 +20,9 @@ from app.services.sessions import revoke_session
router = APIRouter(prefix="/api/v1/demo", tags=["demo"])
settings = get_settings()
_reset_guard = threading.Lock()
_last_reset_monotonic = 0.0
_RESET_ADVISORY_LOCK_ID = 706_533_149
@router.get("/manifest", response_model=DemoManifestOut)
@@ -106,26 +110,44 @@ def demo_reset(
db: Session = Depends(get_db),
user: CurrentUser = Depends(require_operations_manager),
) -> dict:
global _last_reset_monotonic
if not settings.demo_allow_reset:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Demo reset is disabled on this deployment.",
)
result = reset_and_seed(db, preserve_integration_telemetry=True)
integrity = scenario_integrity_report(db)
record_audit_event(
db,
actor_type="user",
actor_label=user.display_name,
action="demo_reset",
entity_type="system",
metadata={
"counts": result.counts,
"anchor_date": result.anchor_date.isoformat(),
"scenario_integrity": integrity,
},
)
db.commit()
if not _reset_guard.acquire(blocking=False):
raise HTTPException(status_code=409, detail="A demo reset is already running.")
try:
elapsed = time.monotonic() - _last_reset_monotonic
if _last_reset_monotonic and elapsed < settings.demo_reset_cooldown_seconds:
retry_after = max(1, int(settings.demo_reset_cooldown_seconds - elapsed + 0.999))
raise HTTPException(
status_code=429,
detail=f"Demo reset is cooling down. Retry in {retry_after} seconds.",
headers={"Retry-After": str(retry_after)},
)
locked = db.scalar(select(func.pg_try_advisory_xact_lock(_RESET_ADVISORY_LOCK_ID)))
if not locked:
raise HTTPException(status_code=409, detail="A demo reset is already running.")
result = reset_and_seed(db, preserve_integration_telemetry=True)
integrity = scenario_integrity_report(db)
record_audit_event(
db,
actor_type="user",
actor_label=user.display_name,
action="demo_reset",
entity_type="system",
metadata={
"counts": result.counts,
"anchor_date": result.anchor_date.isoformat(),
"scenario_integrity": integrity,
},
)
db.commit()
_last_reset_monotonic = time.monotonic()
finally:
_reset_guard.release()
response.delete_cookie(settings.session_cookie_name)
return {
"status": "reset",