feat: Allow paid guest users to access API endpoints#112
feat: Allow paid guest users to access API endpoints#112AnthonyRonning merged 2 commits intomasterfrom
Conversation
- Add is_user_paid() method to BillingClient to check if user is on paid plan - Update chat, transcription, TTS, documents, and Responses API to allow paid guests - Keep models endpoint accessible to all guests (no billing check needed) - Block free guest users from accessing paid features - Maintain backward compatibility: billing client unavailable = guests blocked Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThe PR adds a billing-aware guest access gate across multiple web endpoints and a new BillingClient helper. Guests are now allowed only if the billing service reports they are on a paid plan. A new Changes
Sequence DiagramsequenceDiagram
actor Guest as Guest User
participant Endpoint
participant BillingClient
participant App as Application
Guest->>Endpoint: Request (upload/chat/models/transcribe/tts)
Endpoint->>Endpoint: Detect user is guest
alt Billing client present
Endpoint->>BillingClient: is_user_paid(user_id)
BillingClient-->>Endpoint: Ok(true)
rect rgba(76, 175, 80, 0.12)
note right of Endpoint: Paid guest — allow
Endpoint->>App: Continue processing
App-->>Guest: Return result
end
BillingClient-->>Endpoint: Ok(false) or Err(...)
rect rgba(244, 67, 54, 0.12)
note right of Endpoint: Free guest or error — deny
Endpoint-->>Guest: Unauthorized
end
else No billing client
rect rgba(244, 67, 54, 0.12)
note right of Endpoint: Safe default — deny
Endpoint-->>Guest: Unauthorized
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (2)
⏰ Context from checks skipped due to timeout of 100000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
🔇 Additional comments (2)
Comment |
There was a problem hiding this comment.
Greptile Overview
Greptile Summary
This PR adds differentiated access control for guest users based on their billing status, allowing premium guests to use API features while maintaining blocks on free guests.
Key changes:
- Added
BillingClient::is_user_paid()method to check subscription tier - Updated 5 endpoints to check billing status for guest users before blocking
- Removed guest restrictions entirely from models endpoint
- Maintains fail-safe behavior: blocks all guests if billing service unavailable
Minor inefficiency:
- Paid guests trigger duplicate billing API calls (
is_user_paid+can_user_chat), both callingcheck_usage()with same parameters
Confidence Score: 4/5
- Safe to merge with minor performance inefficiency
- The implementation correctly handles all edge cases (no billing client, billing errors, free vs paid tiers) with proper fail-safe defaults. The duplicate API calls for paid guests are inefficient but not incorrect - both checks serve different purposes (
is_freevscan_use). Code follows existing patterns consistently across all endpoints. - Pay attention to
src/web/openai.rsandsrc/web/documents.rs- these have duplicate billing checks that could be optimized
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| src/web/openai.rs | 4/5 | Updated 3 endpoints (proxy_openai, proxy_transcription, proxy_tts) to allow paid guests, removed guest block from proxy_models. Duplicate billing API calls for paid guests. |
| src/web/documents.rs | 4/5 | Updated upload_document and check_document_status to allow paid guests. Duplicate billing API calls for paid guests. |
Sequence Diagram
sequenceDiagram
participant C as Client
participant E as Endpoint
participant B as BillingClient
participant P as Provider
C->>E: Request
alt is_guest
E->>B: is_user_paid
alt paid
Note over E: Allow
else free
E-->>C: Unauthorized
end
end
E->>B: can_user_chat
alt can_use
E->>P: Forward
P-->>E: Result
E-->>C: Response
else limit reached
E-->>C: Error
end
4 files reviewed, 1 comment
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
src/web/responses/handlers.rs (1)
855-880: Paid-guest gate logic LGTM; consider DRY + log level tweak
- Logic matches spec: allow paid guests; block free/missing/failed checks.
- This pattern is duplicated across modules; extract a small helper (e.g., enforce_paid_guest(state, user, feature)) to avoid drift.
- Suggest downgrading “free guest attempted …” from error to warn to reduce noise.
Example helper (outside this hunk):
pub async fn enforce_paid_guest(state: &AppState, user: &User, feature: &str) -> Result<(), ApiError> { if !user.is_guest() { return Ok(()); } let Some(billing) = &state.billing_client else { tracing::error!("Guest user attempted to use {} without billing client: {}", feature, user.uuid); return Err(ApiError::Unauthorized); }; match billing.is_user_paid(user.uuid).await { Ok(true) => { tracing::debug!("Paid guest user allowed for {}: {}", feature, user.uuid); Ok(()) } Ok(false) => { tracing::warn!("Free guest user attempted to use {}: {}", feature, user.uuid); Err(ApiError::Unauthorized) } Err(e) => { tracing::error!("Billing check failed for guest {} on {}: {}", user.uuid, feature, e); Err(ApiError::Unauthorized) } } }src/web/documents.rs (2)
76-101: Guest gate matches policy; centralize to helper and tone logs
- Behavior correct; duplicates across endpoints.
- Recommend using a shared enforce_paid_guest(...) helper.
- Consider warn for free-guest attempts instead of error.
240-268: Same guest gate duplication; apply helper for consistencyReplicate the helper here to reduce copy/paste and ensure uniform logging/behavior.
src/web/openai.rs (3)
216-241: Chat guest gate is correct; prefer shared helper + warn for free attempts
- Logic aligns with PR.
- Suggest reuse via enforce_paid_guest(state, user, "chat") to avoid drift.
- Consider warn (not error) for free-guest attempts.
1032-1057: Transcription guest gate consistent; extract helperSame recommendation: centralize guest gating and adjust log severity to warn for expected denials.
1403-1428: TTS guest gate consistent; extract helperApply the shared helper for maintainability; consider warn for free-guest denials.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/billing.rs(1 hunks)src/web/documents.rs(2 hunks)src/web/openai.rs(4 hunks)src/web/responses/handlers.rs(1 hunks)
⏰ Context from checks skipped due to timeout of 100000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Development Reproducible Build
🔇 Additional comments (1)
src/web/openai.rs (1)
915-915: Intentional unused user bindingRenaming to
_useravoids unused binding warnings after removing inline guest checks. LGTM.
There was a problem hiding this comment.
Greptile Overview
Greptile Summary
This PR enables paid guest users to access API endpoints while blocking free guests. Implementation adds is_user_paid() method to check billing status and updates 6 endpoints.
Key Changes
- Added
BillingClient.is_user_paid()helper that queries billing service - Updated chat, transcription, TTS, document upload/status, and Responses API endpoints
- Removed guest restriction from models endpoint (now accessible to all)
- PCR hashes updated for dev and prod environments
Issues Found
- Duplicate billing calls: Chat and document upload endpoints call billing service twice for paid guests (
is_user_paid()thencan_user_chat()), causing unnecessary HTTP requests - Inconsistent billing checks: Transcription, TTS, and document status endpoints skip usage limit validation entirely - they only check if guest is paid but don't verify usage quotas like chat/upload do
- Non-guest bypass: Transcription, TTS, and document status endpoints allow non-guest users to bypass all billing checks
Confidence Score: 2/5
- This PR has critical logical inconsistencies in billing validation that could allow usage limit bypasses
- The implementation correctly adds paid guest support but has critical flaws: (1) duplicate billing API calls waste resources, (2) transcription/TTS/document status endpoints completely skip usage limit checks that chat/upload have, creating billing enforcement gaps, and (3) inconsistent validation patterns across endpoints make the system unpredictable
- src/web/openai.rs (transcription and TTS functions) and src/web/documents.rs (check_document_status function) need usage limit validation added
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| src/billing.rs | 5/5 | Added is_user_paid() helper method to check if user is on paid plan - clean implementation |
| src/web/openai.rs | 3/5 | Updated chat, transcription, TTS endpoints for paid guest access; duplicate billing calls in chat endpoint; transcription/TTS missing usage checks |
| src/web/documents.rs | 3/5 | Updated document upload and status check for paid guests; duplicate billing calls in upload endpoint |
| src/web/responses/handlers.rs | 5/5 | Updated Responses API for paid guest access - correctly implemented without duplicate billing calls |
Sequence Diagram
sequenceDiagram
participant C as Client
participant E as API Endpoint
participant A as Auth
participant B as BillingClient
participant P as Proxy
C->>E: Request to API
E->>A: Authenticate
A-->>E: User information
alt Guest User Flow
E->>B: Check paid plan status
B->>B: Query billing service
B-->>E: Return paid status
alt Free Guest
E-->>C: 401 Unauthorized
else Paid Guest
E->>E: Continue processing
end
end
alt Chat or Document Upload
E->>B: Check usage limits
B->>B: Query billing service again
B-->>E: Return usage status
alt Usage Exceeded
E-->>C: 429 UsageLimitReached
end
end
E->>P: Forward request
P-->>E: Response
E-->>C: Encrypted response
4 files reviewed, 3 comments
Summary
This PR enables paid guest users to access API endpoints while continuing to block free guest users.
Changes
is_user_paid()method toBillingClientto check if a user is on a paid planproxy_openai)proxy_transcription)proxy_tts)upload_document)check_document_status)validate_and_normalize_input)proxy_models) - now accessible to all guestsBehavior
is_free = false): Allowed to use all API featuresis_free = true): Blocked withApiError::UnauthorizedTesting
cargo fmtpassedcargo clippypassed with no warningsSummary by CodeRabbit
New Features
Improvements
Chores