Files
documenso/packages/trpc/server/enterprise-router/link-organisation-account.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

32 lines
1.0 KiB
TypeScript

import { linkOrganisationAccount } from '@documenso/ee/server-only/lib/link-organisation-account';
import { assertRateLimit } from '@documenso/lib/server-only/rate-limit/rate-limit-middleware';
import { linkOrgAccountRateLimit } from '@documenso/lib/server-only/rate-limit/rate-limits';
import { procedure } from '../trpc';
import {
ZLinkOrganisationAccountRequestSchema,
ZLinkOrganisationAccountResponseSchema,
} from './link-organisation-account.types';
/**
* Unauthenicated procedure, do not copy paste.
*/
export const linkOrganisationAccountRoute = procedure
.input(ZLinkOrganisationAccountRequestSchema)
.output(ZLinkOrganisationAccountResponseSchema)
.mutation(async ({ input, ctx }) => {
const { token } = input;
const rateLimitResult = await linkOrgAccountRateLimit.check({
ip: ctx.metadata.requestMetadata.ipAddress ?? 'unknown',
identifier: token,
});
assertRateLimit(rateLimitResult);
await linkOrganisationAccount({
token,
requestMeta: ctx.metadata.requestMetadata,
});
});