-
Notifications
You must be signed in to change notification settings - Fork 4
Open
Labels
Description
Overview
Implement a referral/invite system allowing users to invite friends via Email (SendGrid), SMS (Twilio), or WhatsApp (Twilio).
PR: #259
Branch: feat/invite
Status: Ready for review
Feature Description
User Flow
- Authenticated user calls
createInvitewith contact (email/phone) + method - System validates contact format, checks rate limits, generates secure token
- Notification sent via SendGrid (email) or Twilio (SMS/WhatsApp) with Firebase Dynamic Link
- New user (within 24h of account creation) calls
redeemInvitewith token - System validates token, checks contact match, marks invite as accepted
Key Components
Public API:
createInvitemutation - Create and send inviteredeemInvitemutation - Redeem invite as new userinvitePreviewquery - Preview invite details (unauthenticated)
Admin API:
inviteByIdquery - View invite details with inviter/redeemer infoinvitesListquery - List/filter invites with pagination- Admin functions: revoke, extend, reset rate limits
Rate Limiting (Redis-based):
- 10 invites/day per user
- 3 invites/day per target contact
- 24-hour invite expiration
Security:
- Tokens hashed with SHA-256 (plaintext never stored)
- Firebase Dynamic Links for mobile deep linking
Files Changed (37 files, +2117/-111)
Core:
src/app/invite/- Creation, redemption, rate limiting, queriessrc/domain/invite/- Domain types and validationsrc/services/mongoose/models/invite.ts- MongoDB schemasrc/services/notification/index.ts- Twilio/SendGrid servicesrc/services/notifications/invite.ts- Invite notifications
GraphQL:
src/graphql/public/root/mutation/create-invite.tssrc/graphql/public/root/mutation/redeem-invite.tssrc/graphql/public/root/query/invite-preview.tssrc/graphql/admin/root/query/invite-*.ts
Known Issues / Follow-up Work
🔴 HIGH PRIORITY
-
Dead Code / Duplicate Logic
- Location:
src/app/invite/redeem-invite.tsvs GraphQL mutation - Issue: Two implementations of redemption logic; app layer appears unused
- Action: Remove
src/app/invite/redeem-invite.tsor refactor mutation to use it
- Location:
-
Token Fragment Logged
- Location:
src/graphql/public/root/mutation/redeem-invite.tsline 156 - Issue: First 8 chars of token logged in error cases
- Risk: Reduces entropy for brute-force if logs compromised
- Action: Log invite ID or token hash instead
- Location:
🟡 MEDIUM PRIORITY
-
Email Validation Deferred (Documented in code)
- Location:
redeem-invite.tslines 115-127 - Issue: Email invites created/sent, but redemption can't validate email match
- Reason: Depends on email-only registration (feat/email-registration #212)
- Status: Documented with link to PR feat/email-registration #212
- Location:
-
Type Casting Hack
- Location:
src/app/invite/rate-limits.tsline 21 - Issue:
contact as IpAddress- incorrect type cast - Fix: Create proper branded type for rate limit keys
- Location:
-
WhatsApp Template Incomplete
- Location:
src/services/notification/index.tslines 176-189 - Issue:
TWILIO_WHATSAPP_TEMPLATE_SIDenv var not in schema - Impact: Template messages may fail in production
- Location:
-
No Transaction Boundary
- Location:
redeem-invite.tslines 128-132 - Issue: Status update not in transaction; concurrent requests could cause issues
- Location:
-
Status Transition Edge Case
- Location:
src/app/admin/invite.tsline 61 - Issue: Extending expired invite resets to PENDING (was SENT)
- Impact: Loses notification history
- Location:
🟢 LOW PRIORITY
- Undocumented GraphQL complexity (120)
- Hardcoded fallback URLs (
https://getflash.io) - Invite preview returns full contact to anyone with token
Testing Status
- TypeScript compiles for feature code
- Integration tests (blocked by shared test infrastructure issues)
Dependencies
- Twilio: SMS and WhatsApp delivery
- SendGrid: Email delivery
- Firebase Dynamic Links: Mobile deep linking (optional)
- Depends on: feat/email-registration #212 (feat/email-registration) for email invite validation
Environment Variables Required
# Existing (Twilio)
TWILIO_ACCOUNT_SID
TWILIO_AUTH_TOKEN
# New
TWILIO_FROM # SMS sender number
TWILIO_WHATSAPP_FROM # WhatsApp sender number
# Optional
FIREBASE_DYNAMIC_LINK_DOMAIN
APP_INSTALL_URL
ANDROID_PACKAGE_NAME
IOS_BUNDLE_ID
TWILIO_WHATSAPP_TEMPLATE_SID # For approved templates
Related Issues/PRs
- Depends on: feat: Email-Only User Registration #260 (feat: Email-Only Registration) - For email invite validation
- PR: feat(invite): Referral system with Email/SMS/WhatsApp invites #259