feat(invitations): config-gated lifetime cap on invitation creation - #3987
Conversation
Add a DB-backed lifetime cap per inviter in InvitationsService.create(): counts ALL invitations ever created by the same invitedBy (any status) and rejects (422, same AppError shape as the self-invite guard) once the count reaches config.invitations.maxLifetime. Complements the existing rateLimit.invitationsCreate burst limiter, which bounds request rate but not cumulative volume — the exposure that matters when billing.referral rewards are enabled (credit farming via fake referrer/referee pairs). Config default: absent/null = disabled (mirrors billing.referral, OFF at stack level) — no behavior change until a downstream consumer opts in with a number in their config layer. Closes #3986
…on coverage Address PRF Phase 0 review findings on #3986: - Document the count-then-create race as an accepted best-effort characteristic, mirroring the existing outstanding-pending-invite guard's identical race in the same function (no atomic counter — would need a schema change, out of scope for this fix). - Add an integration test exercising the real DB count + real HTTP 422 path (previously covered only by mocked unit tests), plus a default-off integration check.
|
Warning Review limit reached
Next review available in: 38 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughAdds an optional DB-backed lifetime invitation cap per inviter. Invitation creation counts all invitations for the inviter and returns a 422 validation error at the configured threshold. Repository, service, and integration tests cover capped and uncapped behavior. ChangesInvitation lifetime cap
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant InvitationsService
participant InvitationRepository
participant InvitationModel
Client->>InvitationsService: Create invitation
InvitationsService->>InvitationRepository: Count by inviter
InvitationRepository->>InvitationModel: Count documents across statuses
InvitationModel-->>InvitationsService: Return count
InvitationsService-->>Client: Return 422 at maxLifetime
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3987 +/- ##
=======================================
Coverage 93.28% 93.29%
=======================================
Files 170 170
Lines 5647 5653 +6
Branches 1817 1819 +2
=======================================
+ Hits 5268 5274 +6
Misses 306 306
Partials 73 73
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
@coderabbitai full review |
✅ Action performedFull review finished. Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 44 minutes. |
…bled Addresses a /critical-review nit on #3986: 0 >= 0 is always true, so maxLifetime: 0 rejects every invitation immediately — distinct from null/absent (disabled). Doc-only, no behavior change.
Summary
InvitationsService.create(). It counts ALL invitations ever created by the sameinvitedBy(any status) and rejects with a 422AppError(same shape as the existing self-invite guard) once the count reachesconfig.invitations.maxLifetime.rateLimit.invitationsCreatebounds request rate but not cumulative volume. Whenbilling.referralrewards are enabled, total invitations per account — not rate — is the exposure that matters (credit farming via fake referrer/referee pairs). This closes that gap without touching the burst limiter, which stays out of scope (already wired, per-route profile).Scope
invitations(config, repository, service, tests only)nonelowValidation
npm run lintnpm test(full suite: 213 test suites / 2768 tests passing, incl. new unit + integration coverage)Guardrails check
.env*,secrets/**, keys, tokens)null(absent = disabled), no behavior change until a downstream consumer opts in with a number in their own config layerNotes for reviewers
create()function; a hard atomic ceiling would need a separate incrementing counter (schema change), judged out of scope for this fix.invitationsmodule, no shared/core files touched.Summary by CodeRabbit
New Features
Tests