SurfSense/_bmad-output/implementation-artifacts/5-4-usage-tracking-rate-limit-enforcement.md
Vonic e7382b26de docs: rewrite story 3.5 and epic 5 stories to match actual codebase
Keeps subscription SaaS vision from PRD while adding accurate as-is
analysis of existing code. Each story now has an "As-Is" table showing
what exists and where the gaps are.

Key points:
- Story 3.5: Transition from BYOK to system-managed models with token
  billing. BYOK stays for self-hosted mode (deployment_mode=self-hosted),
  system models + subscription quota for hosted mode.
- Story 5.1: Pricing UI exists (Free/PAYG/Enterprise) but needs redesign
  to subscription tiers (Free/Pro) with monthly/yearly toggle.
- Story 5.2: PAYG checkout exists (mode=payment), need NEW subscription
  endpoint (mode=subscription) with stripe_customer_id binding.
- Story 5.3: Webhook infrastructure exists (signature verify, PAYG handlers).
  Need subscription event handlers (customer.subscription.*) alongside.
- Story 5.4: PageLimitService fully implemented. Gap is HTTP-layer pre-check,
  plan-based limits, frontend quota indicator, and 402 error handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 14:19:57 +07:00

5.2 KiB

Story 5.4: Hệ thống Khóa Tác vụ dựa trên Hạn Mức (Usage Tracking & Rate Limit Enforcement)

Status: ready-for-dev

Story

As a Kỹ sư Hệ thống, I want những người dùng hết quota (vượt quá file upload hoặc số lượng tin nhắn) bị từ chối dịch vụ cho đến khi nâng cấp, so that mô hình kinh doanh không bị lỗ do chi phí LLM và Storage, áp dụng theo FR13.

Acceptance Criteria

  1. Endpoint Upload (Document) kiểm tra pages_used vs pages_limit (dựa trên subscription tier) → từ chối nếu vượt.
  2. Endpoint Chat (RAG AI) kiểm tra tokens_used_this_month vs monthly_token_limit → từ chối nếu vượt (đã cover ở Story 3.5 Task 4).
  3. API trả lỗi 402 Payment Required khi quota exceeded.
  4. UI hiển thị Toast Error hoặc Modal up-sell "Upgrade to Pro".
  5. Dashboard hiển thị quota indicator (pages used / limit, tokens used / limit).

As-Is (Code hiện tại)

Component Hiện trạng File
Page Quota Service Đã implement đầy đủcheck_page_limit(), update_page_usage(), get_page_usage(), estimate_pages_*() surfsense_backend/app/services/page_limit_service.py
Enforcement trong Celery Đã có — check quota trước khi process document document_tasks.py
Enforcement trong Connectors Đã có — Google Drive, OneDrive, Dropbox indexers check remaining_quota connector_indexers/*.py
Enforcement tại HTTP API Chưa có — document upload route không check quota trước khi enqueue task routes/
Token Quota Service Chưa có — sẽ tạo ở Story 3.5
Frontend Quota UI Chưa có
Frontend Error Handling Chưa có — không bắt 402 từ upload/chat

Gap chính:

  1. Thiếu quota pre-check tại HTTP layer (upload route) — user phải đợi Celery task fail mới biết bị reject
  2. Thiếu frontend feedback khi quota exceeded
  3. Thiếu quota indicator trong dashboard

Tasks / Subtasks

  • Task 1: Thêm Page Quota Pre-check vào Document Upload Route (Backend)

    • Subtask 1.1: Tìm document upload route (search trong surfsense_backend/app/routes/).
    • Subtask 1.2: Inject PageLimitService, gọi estimate_pages_from_metadata(filename, file_size) rồi check_page_limit(user_id, estimated_pages).
    • Subtask 1.3: Nếu PageLimitExceededError → raise HTTPException(402, detail={"error": "page_quota_exceeded", "pages_used": X, "pages_limit": Y, "message": "..."}).
    • Subtask 1.4: Giữ nguyên enforcement trong Celery tasks (double-check layer cho trường hợp estimate sai).
  • Task 2: Plan-based Limits (Backend)

    • Subtask 2.1: Tạo config mapping plan_id → limits (liên kết với Story 5.3):
      PLAN_LIMITS = {
          "free": {"pages_limit": 500, "monthly_token_limit": 50_000, "max_docs": 10},
          "pro": {"pages_limit": 5000, "monthly_token_limit": 1_000_000, "max_docs": 100},
      }
      
    • Subtask 2.2: Khi subscription activate/update (webhook Story 5.3), update pages_limitmonthly_token_limit theo plan mới.
  • Task 3: Frontend — Bắt lỗi Quota Exceeded

    • Subtask 3.1: Document upload component — bắt HTTP 402 response, phân biệt page_quota_exceeded vs token_quota_exceeded.
    • Subtask 3.2: Hiển thị toast/modal: "Bạn đã dùng X/Y pages. Nâng cấp lên Pro để tiếp tục." với link đến /pricing.
    • Subtask 3.3: Chat component — bắt HTTP 402 từ SSE stream (tương tự Story 3.5 Task 6).
  • Task 4: Frontend — Quota Indicator (Dashboard)

    • Subtask 4.1: Expose pages_used, pages_limit, tokens_used_this_month, monthly_token_limit qua user endpoint hoặc Zero sync.
    • Subtask 4.2: Tạo component QuotaIndicator — hiển thị 2 progress bars (Pages, Tokens) trong sidebar/header.
    • Subtask 4.3: Warning state (amber) khi usage > 80%, critical state (red) khi > 95%.
  • Task 5: Anti-spam Rate Limiting (Optional — nếu cần)

    • Subtask 5.1: Thêm rate limit cho chat endpoint (e.g. max 60 requests/hour cho free tier).
    • Subtask 5.2: Dùng Redis counter hoặc FastAPI dependency.

Dev Notes

PageLimitService — đã sẵn sàng, không cần viết lại

page_limit_service.py đã có đầy đủ:

  • check_page_limit(user_id, estimated_pages) → raises PageLimitExceededError
  • update_page_usage(user_id, pages_to_add)
  • estimate_pages_before_processing(file_path)
  • estimate_pages_from_metadata(filename, file_size) (pure function, dùng cho HTTP layer)

Enforcement Layers

HTTP Upload Route → PageLimitService.check_page_limit() [FAST — pre-check]
       ↓ (nếu pass)
Celery Task → PageLimitService.check_page_limit() [ACCURATE — sau khi parse xong]
       ↓ (nếu pass)
PageLimitService.update_page_usage() [COMMIT — tăng pages_used]

References

  • surfsense_backend/app/services/page_limit_service.py — page quota (đọc, ít sửa)
  • surfsense_backend/app/tasks/celery_tasks/document_tasks.py — enforcement pattern hiện tại
  • surfsense_backend/app/routes/ — document upload route (cần tìm)
  • Frontend upload component (cần tìm)