fix(affiliates): sync manual approval counts#317
Conversation
Greptile SummaryThis PR consolidates the affiliate application moderation flow into a single atomic PostgreSQL function (
Confidence Score: 5/5Safe to merge — the atomic RPC cleanly eliminates the desync window, and the REVOKE/GRANT statements properly restrict access to the service role. All moderation logic now lives in a single locked transaction; ownership checks, status transitions, count adjustments, and approved_at management are all atomic. The only remaining rough edges are an enrichment-SELECT whose error is silently dropped (producing a profileless response in rare failure scenarios) and a dead updatePayload variable in one test — neither affects correctness on the happy path. The post-RPC enrichment SELECT in route.ts silently discards its error; worth a look if monitoring shows unexpectedly profileless responses in production. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Route as PATCH /api/affiliates/offers/[id]/applications
participant RPC as moderate_affiliate_application (DB)
participant Select as affiliate_applications SELECT
participant Notify as notifications INSERT
Client->>Route: "{ application_id, action }"
Route->>RPC: p_offer_id, p_application_id, p_seller_id, p_status
Note over RPC: 1. Lock offer row (FOR UPDATE)<br/>2. Verify seller_id<br/>3. Lock application row (FOR UPDATE)<br/>4. Compute delta (idempotent)<br/>5. UPDATE application + approved_at<br/>6. UPDATE total_affiliates (GREATEST(0,…))
RPC-->>Route: updated application row (no profiles join)
Route->>Select: "SELECT *, profiles join WHERE id=… AND offer_id=…"
Select-->>Route: enriched row (or null on failure, error silently dropped)
Route->>Notify: INSERT notification for affiliate
Notify-->>Route: ok
Route-->>Client: "{ application: enriched || raw }"
Reviews (2): Last reviewed commit: "fix(affiliates): moderate counts atomica..." | Re-trigger Greptile |
Fixes #275.
Summary
affiliate_offers.total_affiliatesonly for non-approved -> approved transitionsapproved_atwhen an approved application is rejectedTests