refactor(backend): quota.measured helper, consistent admin gating, messageable predicate

- quota.measured() context manager folds the before/after units diff that the
  manual sync routes each re-spelled (also makes quota_remaining_today consistent).
- sync pause/resume now use Depends(admin_user) instead of inline role checks.
- is_messageable_user() unifies the 'real, active, non-suspended human' rule that
  was encoded three ways (WS auth, send recipient, and the SQL _messageable()).
This commit is contained in:
npeter83 2026-06-26 03:15:53 +02:00
parent 63701f5344
commit 5b81e22677
3 changed files with 38 additions and 27 deletions

View file

@ -3,7 +3,7 @@ from sqlalchemy import and_, case, func, select, update
from sqlalchemy.orm import Session
from app import quota, state
from app.auth import current_user, has_read_scope, require_human
from app.auth import admin_user, current_user, has_read_scope, require_human
from app.db import get_db
from app.models import Channel, Subscription, User, Video
from app.sync.runner import (
@ -40,11 +40,10 @@ def sync_subscriptions(
status_code=403,
detail="Connect your YouTube account (read access) to import subscriptions.",
)
before = quota.units_used_today(db)
with quota.attribute(user.id, quota.QuotaAction.SUBSCRIPTIONS_PULL):
with quota.measured(db, user.id, quota.QuotaAction.SUBSCRIPTIONS_PULL) as m:
result = import_subscriptions(db, user)
result["quota_used_estimate"] = quota.units_used_today(db) - before
result["quota_remaining_today"] = quota.remaining_today(db)
result["quota_used_estimate"] = m.used
result["quota_remaining_today"] = m.remaining
return result
@ -62,10 +61,9 @@ def sync_backfill(
db: Session = Depends(get_db),
max_channels: int = 25,
) -> dict:
before = quota.units_used_today(db)
with quota.attribute(user.id, quota.QuotaAction.VIDEOS_BACKFILL_RECENT):
with quota.measured(db, user.id, quota.QuotaAction.VIDEOS_BACKFILL_RECENT) as m:
result = run_recent_backfill(db, _user_channels(db, user), max_channels=max_channels)
result["quota_used_estimate"] = quota.units_used_today(db) - before
result["quota_used_estimate"] = m.used
return result
@ -73,13 +71,9 @@ def sync_backfill(
def sync_enrich(
user: User = Depends(require_human), db: Session = Depends(get_db)
) -> dict:
before = quota.units_used_today(db)
with quota.attribute(user.id, quota.QuotaAction.VIDEOS_ENRICH):
with quota.measured(db, user.id, quota.QuotaAction.VIDEOS_ENRICH) as m:
enriched = run_enrich(db)
return {
"enriched": enriched,
"quota_used_estimate": quota.units_used_today(db) - before,
}
return {"enriched": enriched, "quota_used_estimate": m.used}
@router.get("/status")
@ -194,20 +188,12 @@ def deep_all(
@router.post("/pause")
def pause_sync(
user: User = Depends(current_user), db: Session = Depends(get_db)
) -> dict:
if user.role != "admin":
raise HTTPException(status_code=403, detail="Admin only")
def pause_sync(_: User = Depends(admin_user), db: Session = Depends(get_db)) -> dict:
state.set_sync_paused(db, True)
return {"paused": True}
@router.post("/resume")
def resume_sync(
user: User = Depends(current_user), db: Session = Depends(get_db)
) -> dict:
if user.role != "admin":
raise HTTPException(status_code=403, detail="Admin only")
def resume_sync(_: User = Depends(admin_user), db: Session = Depends(get_db)) -> dict:
state.set_sync_paused(db, False)
return {"paused": False}