Skip to content

Deduplicate referral invite emails - #384

Merged
ralyodio merged 1 commit into
profullstack:masterfrom
phucnguyen1707:fix/referral-invite-dedup
Jun 4, 2026
Merged

Deduplicate referral invite emails#384
ralyodio merged 1 commit into
profullstack:masterfrom
phucnguyen1707:fix/referral-invite-dedup

Conversation

@phucnguyen1707

Copy link
Copy Markdown
Contributor

Summary

  • Deduplicate normalized, valid invite emails in POST /api/referrals
  • Prevent duplicate sends/inserts when one request repeats the same address with different casing or whitespace
  • Add a focused regression test in a separate included test file

Fixes #383.

Tests

  • npm run test:run -- src/app/api/referrals/route.dedupe.test.ts
  • npm run lint -- src/app/api/referrals/route.ts src/app/api/referrals/route.dedupe.test.ts

Note: lint exits successfully; the repo still reports existing unrelated warnings outside these changes.

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR deduplicates invite emails in POST /api/referrals by applying new Set() after normalizing (trim + lowercase) the input. It also splits the previous single cap of 20 into two guards: a loose "raw entries" limit of 200 (pre-dedup) and the existing 20-invite limit applied post-dedup, so identical emails submitted with different casing or whitespace no longer waste quota.

  • route.ts: Introduces MAX_EMAIL_ENTRIES_PER_REQUEST = 200 as the pre-dedup guard, wraps the valid-email filter with Array.from(new Set(...)) for deduplication, then checks validEmails.length > MAX_INVITES_PER_REQUEST (20) as the post-dedup invite cap.
  • route.dedupe.test.ts: New focused test file covering the deduplication happy path and verifying that 21 identical raw entries still succeed because they deduplicate to one.

Confidence Score: 5/5

Safe to merge; the deduplication logic is correct and the two-stage cap ordering is sound.

The production code path is straightforward: normalize then deduplicate then cap then rate-limit then send. All existing guards are preserved in the right order, and the new Set-based deduplication is a minimal, well-scoped addition with no side effects on rate-limit or DB-insert steps.

The test file would benefit from a failure-mode case for the new post-dedup invite cap, but this does not affect merge safety.

Important Files Changed

Filename Overview
src/app/api/referrals/route.ts Adds email deduplication via Set after normalization, splits the cap into a 200-entry raw limit and a 20-invite post-dedup limit; logic is correct and the ordering is sound.
src/app/api/referrals/route.dedupe.test.ts Covers the deduplication success path well, but the new MAX_INVITES_PER_REQUEST rejection branch has no test; the second test name implies cap rejection but only verifies the non-rejection case.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[POST /api/referrals] --> B{Auth OK?}
    B -- No --> Z1[401 Unauthorized]
    B -- Yes --> C{emails array valid?}
    C -- No --> Z2[400 Bad Request]
    C -- Yes --> D{emails.length > 200?}
    D -- Yes --> Z3[400 Max 200 entries]
    D -- No --> E[normalize: trim + toLowerCase]
    E --> F[filter valid syntax via regex]
    F --> G[deduplicate via new Set]
    G --> H{validEmails.length === 0?}
    H -- Yes --> Z4[400 No valid emails]
    H -- No --> I{validEmails.length > 20?}
    I -- Yes --> Z5[400 Max 20 invites]
    I -- No --> J{Hourly rate limit OK?}
    J -- No --> Z6[429 Too many invites]
    J -- Yes --> K{Daily rate limit OK?}
    K -- No --> Z7[429 Daily limit reached]
    K -- Yes --> L[Query existing invites]
    L --> M[Filter out already-invited]
    M --> N{Any new emails?}
    N -- No --> Z8[400 All already invited]
    N -- Yes --> O[Send emails]
    O --> P[Insert successful sends into DB]
    P --> Q[200 Success]
Loading

Reviews (2): Last reviewed commit: "Deduplicate referral invite emails" | Re-trigger Greptile

@phucnguyen1707
phucnguyen1707 force-pushed the fix/referral-invite-dedup branch from a313856 to ee3da7f Compare June 4, 2026 09:56
@phucnguyen1707

Copy link
Copy Markdown
Contributor Author

Updated in ee3da7f to address Greptile's cap-order note: the 20-invite limit now applies after normalized valid emails are deduplicated, while a separate 200-entry hard cap still protects against oversized duplicate-heavy payloads. Added a regression test for 21 repeated variants collapsing to one invite.

@ralyodio
ralyodio merged commit e646d29 into profullstack:master Jun 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: referral invites still allow duplicate emails in one request

2 participants