puff-n-parse-backend / services /tier_manager.py
Gifted-oNe's picture
Fix python absolute imports in services for root execution context
15085a6
Raw
History Blame Contribute Delete
4 kB
"""
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