Spaces:
Sleeping
Sleeping
| """ | |
| Tier Manager — Enforces usage limits for unregistered users. | |
| Uses in-memory session tracking keyed by a session token from the frontend | |
| (stored in localStorage). Lightweight — no database or IP tracking needed. | |
| Unregistered limits: | |
| - 5 processing sessions per rolling 7-day window | |
| - 3 files per session | |
| - 10MB max per file | |
| Registered users bypass all limits (placeholder for Supabase auth integration). | |
| """ | |
| import time | |
| from collections import defaultdict | |
| from models.schemas import TierStatus, TierCheckResponse | |
| # Rolling window: 7 days in seconds | |
| WEEK_SECONDS = 7 * 24 * 60 * 60 | |
| # In-memory store: { session_token: [timestamp1, timestamp2, ...] } | |
| _usage_store: dict[str, list[float]] = defaultdict(list) | |
| # Track files per active session: { session_token: int } | |
| _session_file_counts: dict[str, int] = defaultdict(int) | |
| # Limits | |
| UNREGISTERED_SESSIONS_PER_WEEK = 5 | |
| UNREGISTERED_FILES_PER_SESSION = 3 | |
| UNREGISTERED_MAX_FILE_SIZE_MB = 10 | |
| def _prune_old_entries(session_token: str) -> None: | |
| """Remove usage entries older than 7 days.""" | |
| cutoff = time.time() - WEEK_SECONDS | |
| _usage_store[session_token] = [ | |
| ts for ts in _usage_store[session_token] if ts > cutoff | |
| ] | |
| def get_tier_status(session_token: str, is_registered: bool = False) -> TierStatus: | |
| """Get the current usage status for a session.""" | |
| if is_registered: | |
| return TierStatus( | |
| is_registered=True, | |
| sessions_used=0, | |
| sessions_limit=999999, | |
| files_this_session=0, | |
| files_limit=999999, | |
| max_file_size_mb=50, | |
| ) | |
| _prune_old_entries(session_token) | |
| return TierStatus( | |
| is_registered=False, | |
| sessions_used=len(_usage_store[session_token]), | |
| sessions_limit=UNREGISTERED_SESSIONS_PER_WEEK, | |
| files_this_session=_session_file_counts.get(session_token, 0), | |
| files_limit=UNREGISTERED_FILES_PER_SESSION, | |
| max_file_size_mb=UNREGISTERED_MAX_FILE_SIZE_MB, | |
| ) | |
| def check_upload_allowed( | |
| session_token: str, | |
| file_size_bytes: int, | |
| is_registered: bool = False, | |
| ) -> TierCheckResponse: | |
| """ | |
| Check if the user is allowed to upload a file. | |
| Returns a TierCheckResponse indicating whether the upload is permitted. | |
| """ | |
| tier = get_tier_status(session_token, is_registered) | |
| # Registered users bypass all limits | |
| if is_registered: | |
| return TierCheckResponse(allowed=True, tier=tier, message="Registered user — no limits.") | |
| # Check weekly session limit | |
| if tier.sessions_used >= UNREGISTERED_SESSIONS_PER_WEEK: | |
| return TierCheckResponse( | |
| allowed=False, | |
| tier=tier, | |
| message=f"Weekly limit reached ({UNREGISTERED_SESSIONS_PER_WEEK} sessions). Register for unlimited access!", | |
| ) | |
| # Check files-per-session limit | |
| if tier.files_this_session >= UNREGISTERED_FILES_PER_SESSION: | |
| return TierCheckResponse( | |
| allowed=False, | |
| tier=tier, | |
| message=f"File limit reached ({UNREGISTERED_FILES_PER_SESSION} files per session). Register for unlimited uploads!", | |
| ) | |
| # Check file size | |
| max_bytes = UNREGISTERED_MAX_FILE_SIZE_MB * 1024 * 1024 | |
| if file_size_bytes > max_bytes: | |
| return TierCheckResponse( | |
| allowed=False, | |
| tier=tier, | |
| message=f"File too large (max {UNREGISTERED_MAX_FILE_SIZE_MB}MB for free tier). Register for larger uploads!", | |
| ) | |
| return TierCheckResponse(allowed=True, tier=tier, message="Upload permitted.") | |
| def record_usage(session_token: str) -> None: | |
| """Record a processing session usage event.""" | |
| _prune_old_entries(session_token) | |
| _usage_store[session_token].append(time.time()) | |
| _session_file_counts[session_token] = _session_file_counts.get(session_token, 0) + 1 | |
| def reset_session_files(session_token: str) -> None: | |
| """Reset the file count for a new session (called when session starts fresh).""" | |
| _session_file_counts[session_token] = 0 | |