diff --git a/app/api/files.py b/app/api/files.py index 35fb3022..35691a7b 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -1303,7 +1303,8 @@ async def ui_upload(request: Request, db: DbSession, file: UploadFile = File(... try: check_upload_allowed(db, upload_owner_id, tier_id) except QuotaExceeded as qe: - # Clean up the already-saved file before rejecting + # Clean up the temporarily written file before returning the error + # to avoid consuming disk space for a rejected upload. if os.path.exists(target_path): os.remove(target_path) raise HTTPException( diff --git a/app/api/subscriptions.py b/app/api/subscriptions.py index e34dfb69..031d5527 100644 --- a/app/api/subscriptions.py +++ b/app/api/subscriptions.py @@ -14,6 +14,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request, status from sqlalchemy import func from sqlalchemy.orm import Session +from app.api.admin_users import _require_admin from app.database import get_db from app.utils.subscription import ( TIER_ORDER, @@ -29,22 +30,7 @@ logger = logging.getLogger(__name__) router = APIRouter(prefix="/subscriptions", tags=["subscriptions"]) DbSession = Annotated[Session, Depends(get_db)] - - -# --------------------------------------------------------------------------- -# Auth helpers -# --------------------------------------------------------------------------- - - -def _get_current_user(request: Request) -> dict | None: - return request.session.get("user") - - -def _require_admin(request: Request) -> dict: - user = request.session.get("user") - if not user or not user.get("is_admin"): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required") - return user +AdminUser = Annotated[dict, Depends(_require_admin)] # --------------------------------------------------------------------------- @@ -67,7 +53,7 @@ def my_subscription(request: Request, db: DbSession) -> dict[str, Any]: """Return the authenticated user's subscription tier and current usage counts.""" from app.config import settings - user = _get_current_user(request) + user = request.session.get("user") if not settings.multi_user_enabled: # In single-user mode there is no concept of a subscription plan @@ -94,10 +80,8 @@ def my_subscription(request: Request, db: DbSession) -> dict[str, Any]: @router.get("/platform", summary="Platform-wide usage statistics (admin only)") -def platform_stats(request: Request, db: DbSession) -> dict[str, Any]: +def platform_stats(request: Request, db: DbSession, _admin: AdminUser) -> dict[str, Any]: """Return aggregate statistics across all users and tiers (admin only).""" - _require_admin(request) - from app.models import FileRecord, UserProfile today = datetime.now(timezone.utc).date() diff --git a/app/utils/subscription.py b/app/utils/subscription.py index 711c0888..ecdfa8a2 100644 --- a/app/utils/subscription.py +++ b/app/utils/subscription.py @@ -170,15 +170,17 @@ def _today_utc() -> date: return datetime.now(timezone.utc).date() +def _scalar_count(query) -> int: + """Execute a count query and return an int, defaulting to 0 for NULL.""" + return query.scalar() or 0 + + def get_lifetime_file_count(db: Session, owner_id: str) -> int: """Total files ever processed by this user (not counting duplicates).""" from app.models import FileRecord - return ( - db.query(func.count(FileRecord.id)) - .filter(FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False)) - .scalar() - or 0 + return _scalar_count( + db.query(func.count(FileRecord.id)).filter(FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False)) ) @@ -187,15 +189,12 @@ def get_today_file_count(db: Session, owner_id: str) -> int: from app.models import FileRecord today = _today_utc() - return ( - db.query(func.count(FileRecord.id)) - .filter( + return _scalar_count( + db.query(func.count(FileRecord.id)).filter( FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False), func.date(FileRecord.created_at) == today, ) - .scalar() - or 0 ) @@ -204,15 +203,12 @@ def get_month_file_count(db: Session, owner_id: str) -> int: from app.models import FileRecord today = _today_utc() - return ( - db.query(func.count(FileRecord.id)) - .filter( + return _scalar_count( + db.query(func.count(FileRecord.id)).filter( FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False), func.strftime("%Y-%m", FileRecord.created_at) == today.strftime("%Y-%m"), ) - .scalar() - or 0 ) diff --git a/frontend/templates/index.html b/frontend/templates/index.html index 3e6cd250..eaf520a1 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/index.html @@ -113,7 +113,7 @@ {% if user_tier.lifetime_file_limit > 0 %} {% set pct = ([((user_usage.lifetime / user_tier.lifetime_file_limit) * 100) | int, 100] | min) %} -