Enforce member cap on imports + warn before exceeding it#77
Merged
Conversation
Imports bypassed the per-account member cap entirely: every importer added members directly, so a large import sailed past the limit and the account only hit the wall on the next manual add. Now every import path hard-fails up front if it would exceed the cap, and the UI warns first. - New member_limits service: single source of truth for the effective limit (tier default, per-user override wins, 0 = unlimited), the current count, and enforce_import_member_cap which raises ImportPayloadError (a clean job failure) when an import would overflow. The normal create path now uses the same helper. - All four importers (Sheaf, PluralKit, SimplyPlural, Tupperbox) call the check after applying the member selector, before writing anything. SP counts custom fronts too, since they are members and count toward the cap. - New GET /v1/members/limit (limit / current / remaining) so the import flows can warn. Each flow shows a warning and disables Import when the selection would exceed the remaining headroom; deselecting members clears it. Tests: import over an overridden cap hard-fails and writes nothing; the limit endpoint shape holds across tier configs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Imports bypassed the per-account member cap entirely. Every importer added members directly via
db.add, so a large import sailed past the limit (free tier = 512) and the account only hit the wall on the next manual "Add member" (403). This enforces the cap on every import path (hard-fail up front) and warns in the UI before it would happen.Per the agreed behaviour: over-cap imports hard-fail the whole job rather than partially filling.
Backend
member_limitsservice as the single source of truth:get_member_limit(tier default, per-usermember_limitoverride wins, 0 = unlimited),count_members, andenforce_import_member_capwhich raisesImportPayloadError(a classified, clean job failure) when an import would overflow. The normalPOST /v1/memberscreate path now uses the same helper, so enforcement can't drift between places.Memberrows and count toward the cap (matching the create path).GET /v1/members/limitreturning{limit, current, remaining}(remaining null when unlimited).Frontend
Tests
Migrations
None. Reuses the existing
user.member_limitcolumn, themember_limit_*config, and a memberCOUNT(*).