Skip to content

Feat: dedicated rate limits account linking privacy - #179

Merged
Kaylahray merged 4 commits into
artisyn-io:mainfrom
Ezeh20:feat/dedicated-rate-limits-account-linking-privacy
Apr 29, 2026
Merged

Feat: dedicated rate limits account linking privacy#179
Kaylahray merged 4 commits into
artisyn-io:mainfrom
Ezeh20:feat/dedicated-rate-limits-account-linking-privacy

Conversation

@Ezeh20

@Ezeh20 Ezeh20 commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR resolves three related issues against the data-export and user-preferences API surface.

#153 — Dedicated rate limits for account-linking and privacy routes

  • Exports rateLimitStore from src/middleware/rateLimiter.ts and adds accountLinking (10/hr) and privacyUpdates (20/hr) configs
  • Exports accountLinkingRateLimiter and privacyRateLimiter pre-built middleware, scoped per authenticated user
  • Wires accountLinkingRateLimiter onto write routes: POST /account-links, POST /account-links/check-availability, POST /account-links/verify, DELETE /account-links/:provider
  • Wires privacyRateLimiter onto all POST /privacy/* routes — GET routes unaffected
  • Adds vitest.config.ts setupFiles entry and a global afterEach(() => rateLimitStore.clear()) in testSetup.ts so feature limiters don't bleed across test cases
  • Adds src/controllers/__tests__/rate-limits.test.ts with happy-path header checks and exhaustion (429) tests for both limiters

#154 — Align export request collection responses with meta.pagination contract

  • Fixes getExportRequests to return meta: { pagination: ... } instead of top-level pagination:
  • Fixes DataExportRequestCollection.data() to forward pagination so the class works correctly through the JsonResource pipeline
  • Adds getExportRequests tests verifying the meta.pagination shape and empty-state zeroed pagination

#155 — Separate cancelled export requests from expired download links

  • Adds cancelled to DataExportRequestStatus enum in prisma/schema.prisma
  • Adds migration 20260428000000_add_cancelled_export_status (ALTER TYPE ... ADD VALUE 'cancelled')
  • cancelExport now writes status: 'cancelled' instead of 'expired'
  • cancelExport guard rejects already-cancelled requests (400) alongside ready and expired
  • 24h active-request gate uses notIn: ['expired', 'cancelled'] so a cancelled export does not block a new request
  • Updates docs to describe expired as TTL-based and cancelled as user-initiated
  • Updates and extends tests in data-export-http.test.ts: fixes stale expired assertion, fixes paginationmeta.pagination, adds idempotent-cancel and post-cancel re-request tests

Test plan

  • POST /account-links — 11th request in window returns 429 with Retry-After; GET /account-links unaffected
  • POST /privacy — 21st request in window returns 429; GET /privacy unaffected
  • GET /api/data-export/requests returns meta.pagination with correct shape; no top-level pagination key
  • Cancelling a pending export sets status: 'cancelled', not 'expired'
  • Cancelling an already-cancelled export returns 400
  • A new export request succeeds immediately after cancellation
  • expired status is only set by TTL-based download-link expiry, not by user action
  • Run full test suite — no cross-test rate-limit bleed

Closes #153
Closes #154
Closes #155

@Kaylahray
Kaylahray merged commit 5b95fa2 into artisyn-io:main Apr 29, 2026
1 check 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

3 participants