step 11 complete: billing service and tier manager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ from typing import Any
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status
|
||||
|
||||
from app.api.deps import get_current_user
|
||||
from app.billing.tier_manager import tier_manager
|
||||
from app.schemas import BackupMetadata, UserProfile
|
||||
from app.storage.blob_store import BlobStore
|
||||
from app.storage.encryption import reject_if_tampered
|
||||
@@ -27,32 +28,11 @@ _blob_store = BlobStore()
|
||||
# In-memory backup metadata — replaced by PostgreSQL backup_metadata table in Step 12
|
||||
_backups: dict[str, list[dict[str, Any]]] = {} # user_id → list of backup records
|
||||
|
||||
# TODO(Step11/12): replace with TierManager.check_quota(user_id)
|
||||
_TIER_BACKUP_LIMITS_GB: dict[str, int] = {
|
||||
"free": 0,
|
||||
"pro": 5,
|
||||
"power": 25,
|
||||
"team": -1, # unlimited
|
||||
}
|
||||
|
||||
|
||||
def _check_backup_quota(user_id: str, tier: str, size_bytes: int) -> None:
|
||||
def _check_backup_quota(user_id: str, size_bytes: int) -> None:
|
||||
"""Raise HTTP 402 if the upload would exceed the tier's backup limit."""
|
||||
limit_gb = _TIER_BACKUP_LIMITS_GB.get(tier, 0)
|
||||
if limit_gb == 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_402_PAYMENT_REQUIRED,
|
||||
detail="Backup is not available on the free tier",
|
||||
)
|
||||
if limit_gb == -1:
|
||||
return # unlimited
|
||||
limit_bytes = limit_gb * 1024**3
|
||||
used = sum(b["size_bytes"] for b in _backups.get(user_id, []))
|
||||
if used + size_bytes > limit_bytes:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_402_PAYMENT_REQUIRED,
|
||||
detail=f"Backup quota exceeded for tier '{tier}'",
|
||||
)
|
||||
current = sum(b["size_bytes"] for b in _backups.get(user_id, []))
|
||||
tier_manager.enforce_backup_quota(user_id, current_bytes=current, additional_bytes=size_bytes)
|
||||
|
||||
|
||||
@router.put("")
|
||||
@@ -69,7 +49,7 @@ async def upload_backup(
|
||||
"""
|
||||
blob = await request.body()
|
||||
reject_if_tampered(blob, x_backup_checksum)
|
||||
_check_backup_quota(current_user.id, current_user.tier, len(blob))
|
||||
_check_backup_quota(current_user.id, len(blob))
|
||||
|
||||
s3_key = await _blob_store.upload(
|
||||
current_user.id, "backup", str(x_backup_timestamp), blob, x_backup_checksum
|
||||
|
||||
Reference in New Issue
Block a user