mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 08:46:22 +02:00
Stories were written for a subscription SaaS model, but SurfSense is a self-hosted product with BYOK + optional PAYG page packs via Stripe. Key corrections: - Story 3.5: Not "remove BYOK + token billing" → actual gap is adding HTTP-layer quota pre-check before document upload enqueue - Story 5.1: Pricing UI already exists (Free/PAYG/Enterprise) → gap is wiring "Get Started" button to existing Stripe checkout endpoint - Story 5.2: mode=payment PAYG already works → needs verification/hardening not a subscription checkout rewrite - Story 5.3: Webhook already handles checkout.session.completed correctly → no subscription events needed, gap is idempotency test + purchase history UI - Story 5.4: PageLimitService + enforcement in tasks/connectors already exists → gap is HTTP-layer pre-check, quota UI indicator, and 402 frontend handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
4.9 KiB
Markdown
93 lines
4.9 KiB
Markdown
# Story 5.4: Enforce Page Quota tại HTTP API Layer & Frontend Feedback
|
|
|
|
Status: ready-for-dev
|
|
|
|
## Context / Correction Note
|
|
> **⚠️ Story gốc bị sai một phần.** Story gốc mô tả implement quota từ đầu (tạo `check_upload_quota()`, v.v.). Thực tế, hệ thống quota **đã tồn tại đầy đủ** với `PageLimitService`, enforcement trong Celery tasks và tất cả connector indexers. Task thực tế chỉ là: (1) thêm check tại HTTP API layer trước khi enqueue task, (2) feedback rõ ràng ở Frontend, (3) hiển thị quota usage trong UI.
|
|
|
|
## Story
|
|
|
|
As a Người dùng,
|
|
I want nhận phản hồi ngay lập tức khi tôi hết page quota thay vì đợi task xử lý xong mới biết,
|
|
so that tôi có thể mua thêm pages trước khi tiếp tục upload.
|
|
|
|
## Actual Architecture (as-is)
|
|
|
|
**Đã implement và đang hoạt động:**
|
|
- `surfsense_backend/app/services/page_limit_service.py`:
|
|
- `PageLimitService.check_page_limit(user_id, estimated_pages)` — raises `PageLimitExceededError`
|
|
- `PageLimitService.update_page_usage(user_id, pages_to_add)`
|
|
- `PageLimitService.get_page_usage(user_id)` → `(pages_used, pages_limit)`
|
|
- `PageLimitService.estimate_pages_before_processing(file_path)` — ước tính từ file size/type
|
|
- `PageLimitService.estimate_pages_from_metadata(filename, file_size)` — pure function, không cần file
|
|
- Enforcement **đã có** trong:
|
|
- `surfsense_backend/app/tasks/celery_tasks/document_tasks.py` — check trước khi xử lý
|
|
- `surfsense_backend/app/tasks/connector_indexers/google_drive_indexer.py`
|
|
- `surfsense_backend/app/tasks/connector_indexers/onedrive_indexer.py`
|
|
- `surfsense_backend/app/tasks/connector_indexers/dropbox_indexer.py`
|
|
- (và các connectors khác)
|
|
|
|
**Còn thiếu:**
|
|
- Check tại HTTP API layer (document upload route) — hiện tại task mới fail sau khi đã enqueue
|
|
- Frontend hiển thị `pages_used` / `pages_limit` (quota indicator)
|
|
- Frontend bắt lỗi 402 từ upload và hiển thị upgrade prompt
|
|
|
|
## Acceptance Criteria
|
|
|
|
1. Khi user upload document và estimated pages sẽ vượt `pages_limit`, API trả về 402 **ngay lập tức** (không enqueue Celery task).
|
|
2. Frontend upload component bắt HTTP 402 và hiển thị toast: "Bạn đã hết page quota (X/Y pages). Mua thêm tại /pricing".
|
|
3. Dashboard hiển thị quota indicator: "X / Y pages used" với progress bar.
|
|
4. API `GET /api/v1/users/me` (hoặc equivalent) trả về `pages_used` và `pages_limit`.
|
|
|
|
## Tasks / Subtasks
|
|
|
|
- [ ] Task 1: Thêm quota pre-check vào Document Upload HTTP route
|
|
- [ ] Subtask 1.1: Tìm document upload route (search `surfsense_backend/app/routes/` cho endpoint nhận file upload).
|
|
- [ ] Subtask 1.2: Inject `PageLimitService`, gọi `estimate_pages_from_metadata(filename, file_size)` để ước tính.
|
|
- [ ] Subtask 1.3: Gọi `check_page_limit(user_id, estimated_pages)` — nếu raises `PageLimitExceededError`, return `HTTPException(402, "Page quota exceeded. Purchase more pages at /pricing.")`.
|
|
- [ ] Task 2: Expose quota info trên user endpoint
|
|
- [ ] Subtask 2.1: Đảm bảo `GET /api/v1/users/me` response bao gồm `pages_used` và `pages_limit` (kiểm tra `UserRead` schema trong `db.py`).
|
|
- [ ] Task 3: Frontend quota indicator
|
|
- [ ] Subtask 3.1: Đọc `pages_used` / `pages_limit` từ current user data (đã có trong Zero/DB sync hoặc API).
|
|
- [ ] Subtask 3.2: Hiển thị progress bar nhỏ trong Dashboard sidebar hoặc header: "X / Y pages".
|
|
- [ ] Subtask 3.3: Khi `pages_used / pages_limit > 0.9`, đổi màu indicator sang warning (amber).
|
|
- [ ] Task 4: Frontend upload error handling
|
|
- [ ] Subtask 4.1: Trong document upload component, bắt HTTP 402 response.
|
|
- [ ] Subtask 4.2: Hiển thị toast/alert với link đến `/pricing`.
|
|
|
|
## Dev Notes
|
|
|
|
### Không cần viết lại PageLimitService
|
|
`PageLimitService` đã có đầy đủ logic. Chỉ cần inject và gọi tại HTTP layer.
|
|
|
|
### Pattern dùng trong Celery tasks (làm mẫu)
|
|
```python
|
|
# Trong document_tasks.py
|
|
page_limit_service = PageLimitService(db_session)
|
|
await page_limit_service.check_page_limit(user_id, estimated_pages)
|
|
# → raises PageLimitExceededError nếu vượt quota
|
|
```
|
|
|
|
### UserRead schema — kiểm tra xem đã expose pages_used chưa
|
|
```python
|
|
# Tìm trong db.py hoặc schemas/
|
|
class UserRead(schemas.BaseUser[uuid.UUID]):
|
|
pages_limit: int
|
|
pages_used: int # ← check xem field này có trong schema không
|
|
```
|
|
|
|
### References
|
|
- `surfsense_backend/app/services/page_limit_service.py`
|
|
- `surfsense_backend/app/tasks/celery_tasks/document_tasks.py` (xem cách dùng làm mẫu)
|
|
- `surfsense_backend/app/db.py` (UserRead schema / User model)
|
|
- `surfsense_web/` (document upload component — cần tìm)
|
|
|
|
## Dev Agent Record
|
|
|
|
### Agent Model Used
|
|
_TBD_
|
|
|
|
### File List
|
|
- `surfsense_backend/app/routes/` (document upload route)
|
|
- `surfsense_backend/app/services/page_limit_service.py` (đọc, không sửa)
|
|
- Frontend upload + dashboard components
|