Files
documenso/packages/lib/jobs/definitions/internal/cleanup-rate-limits.ts
T
Lucas Smith 653ab3678a feat: better ratelimiting (#2520)
Replace hono-rate-limiter with a Prisma/PostgreSQL bucketed counter
approach that works correctly across multiple instances without sticky
sessions.

- Add RateLimit model with composite PK (key, action, bucket) and atomic
upsert
- Create rate limit factory with window parsing, bucket computation, and
fail-open
- Define auth-tier and API-tier rate limit instances
- Add Hono middleware, rateLimitResponse helper, and tRPC
assertRateLimit helper
- Wire rate limit headers through AppError constructor (was declared but
never assigned)
- Apply rate limits to auth routes (email-password, passkey), tRPC
routes
  (2FA email, link org account), API routes, and file upload endpoints
- Add cleanup cron job for expired rate limit rows (batched delete every
15 min)
- Remove hono-rate-limiter dependency
2026-02-20 12:23:02 +11:00

31 lines
937 B
TypeScript

import { z } from 'zod';
import { type JobDefinition } from '../../client/_internal/job';
const CLEANUP_RATE_LIMITS_JOB_DEFINITION_ID = 'internal.cleanup-rate-limits';
const CLEANUP_RATE_LIMITS_JOB_DEFINITION_SCHEMA = z.object({});
export type TCleanupRateLimitsJobDefinition = z.infer<
typeof CLEANUP_RATE_LIMITS_JOB_DEFINITION_SCHEMA
>;
export const CLEANUP_RATE_LIMITS_JOB_DEFINITION = {
id: CLEANUP_RATE_LIMITS_JOB_DEFINITION_ID,
name: 'Cleanup Rate Limits',
version: '1.0.0',
trigger: {
name: CLEANUP_RATE_LIMITS_JOB_DEFINITION_ID,
schema: CLEANUP_RATE_LIMITS_JOB_DEFINITION_SCHEMA,
cron: '*/15 * * * *', // Every 15 minutes.
},
handler: async ({ payload, io }) => {
const handler = await import('./cleanup-rate-limits.handler');
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof CLEANUP_RATE_LIMITS_JOB_DEFINITION_ID,
TCleanupRateLimitsJobDefinition
>;