feat: turn waitlist members into invited signups#252
Conversation
|
Warning Review limit reached
Next review available in: 50 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: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
✨ 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 |
Convert the waitlist into invited signups (ticket #251). A per-email, email-bound, single-use invite code is minted + emailed from the waitlist by an admin, and redeemed at /sign-up/email. - migration 0007: `invites` table (email PK, unique code, sentAt, redeemedAt) - worker/invites.ts: code minting, invite email, pending-waitlist selection, and the redeem validate/stamp split - worker/auth.ts: /sign-up/email now accepts EITHER a per-email invite bound to the exact email OR the shared INVITE_CODES backdoor. Validate in `before` (no burn on later failure); stamp redeemedAt in `after` on success only, via a conditional UPDATE that can't double-burn - POST /api/admin/invite (session + ADMIN_EMAILS, 404 for non-admins): mints + emails codes. Lives on the Worker because the send needs the send_email binding, which only exists in fetch - scripts/invite.ts (`bun run invite`): thin admin-authenticated CLI driving the endpoint (explicit emails, --limit N, or --all) Verified against a live local Worker + D1: happy-path redeem, wrong-code and email-bound rejections, single-use (redeemedAt stamped), idempotent re-mint, admin 404 for non-admins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ae60d7d to
229fb7e
Compare
Summary
Turns the alpha waitlist into invited signups: an admin mints per-email, single-use, email-bound invite codes from the waitlist, and each is redeemed once at signup by the address it was sent to. Keeps the existing
INVITE_CODESshared secret as an admin/testing backdoor.Fixes #251
Changes
INVITE_CODESvalue — and stays fail-closed otherwise.POST /api/admin/invite(session +ADMIN_EMAILS, 404 to everyone else) mints codes and emails them through the oneworker/email.tsseam; it lives on the Worker because thesend_emailbinding only exists insidefetch.bun run invitedrives that endpoint from the CLI — explicit addresses,--limit N, or--allpending waitlist rows.redeemedAtonly after the account is actually created, via an atomic conditional update.0007adds theinvitestable (email-keyed, unique code).Start here: change 4 — validation is in the Better Auth
beforehook, the stamp is inafter(gated on success, so a code isn't burned when signup later fails).Flow
Breaking / Migration
bun run db:migrate:remote(migration0007) before deploying.0007assumes Stripe's0006(Wire Stripe subscriptions via @better-auth/stripe (#172) #173) lands first. Harmless if not — wrangler applies by sorted filename and the table usesCREATE TABLE IF NOT EXISTS— but merging Wire Stripe subscriptions via @better-auth/stripe (#172) #173 first keeps the numbering clean.Test plan
typecheck(app + worker + test),bun test(639),lint,fmt:check, and migration0007applies cleanly to local D1.redeemedAtstamped), idempotent re-mint, admin route returns 404 for unauthenticated GET and POST; invite email fired through the realsend_emailbinding./security-review(auth/signup-gate diff): no HIGH/MEDIUM findings.bun run invitewithDOTFLOWY_ADMIN_EMAIL/_PASSWORDset) after deploy — the send path is not e2e-reachable.