aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/routers
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-07-10 21:22:54 +0000
committerMohamed Bassem <me@mbassem.com>2025-07-10 22:03:30 +0000
commit613137ff99442885c5fe679b2cc1172adfc5a283 (patch)
tree97f2b940448357870090364c6f73b780d6f473d9 /packages/trpc/routers
parent333d1610fad10e70759545f223959503288a02c6 (diff)
downloadkarakeep-613137ff99442885c5fe679b2cc1172adfc5a283.tar.zst
feat: Add API ratelimits
Diffstat (limited to 'packages/trpc/routers')
-rw-r--r--packages/trpc/routers/apiKeys.ts21
-rw-r--r--packages/trpc/routers/invites.ts21
-rw-r--r--packages/trpc/routers/users.ts22
3 files changed, 62 insertions, 2 deletions
diff --git a/packages/trpc/routers/apiKeys.ts b/packages/trpc/routers/apiKeys.ts
index eb52189b..d4e01aa5 100644
--- a/packages/trpc/routers/apiKeys.ts
+++ b/packages/trpc/routers/apiKeys.ts
@@ -11,7 +11,12 @@ import {
logAuthenticationError,
validatePassword,
} from "../auth";
-import { authedProcedure, publicProcedure, router } from "../index";
+import {
+ authedProcedure,
+ createRateLimitMiddleware,
+ publicProcedure,
+ router,
+} from "../index";
const zApiKeySchema = z.object({
id: z.string(),
@@ -70,6 +75,13 @@ export const apiKeysAppRouter = router({
// Exchange the username and password with an API key.
// Homemade oAuth. This is used by the extension.
exchange: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "apiKey.exchange",
+ windowMs: 15 * 60 * 1000,
+ maxRequests: 10,
+ }),
+ ) // 10 requests per 15 minutes
.input(
z.object({
keyName: z.string(),
@@ -97,6 +109,13 @@ export const apiKeysAppRouter = router({
return await generateApiKey(input.keyName, user.id);
}),
validate: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "apiKey.validate",
+ windowMs: 60 * 1000,
+ maxRequests: 30,
+ }),
+ ) // 30 requests per minute
.input(z.object({ apiKey: z.string() }))
.output(z.object({ success: z.boolean() }))
.mutation(async ({ input, ctx }) => {
diff --git a/packages/trpc/routers/invites.ts b/packages/trpc/routers/invites.ts
index 5f7897c5..0a98f36a 100644
--- a/packages/trpc/routers/invites.ts
+++ b/packages/trpc/routers/invites.ts
@@ -7,7 +7,12 @@ import { invites, users } from "@karakeep/db/schema";
import { generatePasswordSalt, hashPassword } from "../auth";
import { sendInviteEmail } from "../email";
-import { adminProcedure, publicProcedure, router } from "../index";
+import {
+ adminProcedure,
+ createRateLimitMiddleware,
+ publicProcedure,
+ router,
+} from "../index";
import { createUserRaw } from "./users";
export const invitesAppRouter = router({
@@ -113,6 +118,13 @@ export const invitesAppRouter = router({
}),
get: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "invites.get",
+ windowMs: 60 * 1000,
+ maxRequests: 10,
+ }),
+ )
.input(
z.object({
token: z.string(),
@@ -153,6 +165,13 @@ export const invitesAppRouter = router({
}),
accept: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "invites.accept",
+ windowMs: 60 * 1000,
+ maxRequests: 10,
+ }),
+ )
.input(
z.object({
token: z.string(),
diff --git a/packages/trpc/routers/users.ts b/packages/trpc/routers/users.ts
index 58093b42..ebe7d96f 100644
--- a/packages/trpc/routers/users.ts
+++ b/packages/trpc/routers/users.ts
@@ -31,6 +31,7 @@ import {
adminProcedure,
authedProcedure,
Context,
+ createRateLimitMiddleware,
publicProcedure,
router,
} from "../index";
@@ -124,6 +125,13 @@ export async function createUser(
export const usersAppRouter = router({
create: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "users.create",
+ windowMs: 60 * 1000,
+ maxRequests: 3,
+ }),
+ )
.input(zSignUpSchema)
.output(
z.object({
@@ -541,6 +549,13 @@ export const usersAppRouter = router({
.where(eq(userSettings.userId, ctx.user.id));
}),
verifyEmail: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "users.verifyEmail",
+ windowMs: 5 * 60 * 1000,
+ maxRequests: 10,
+ }),
+ ) // 10 requests per 5 minutes
.input(
z.object({
email: z.string().email(),
@@ -572,6 +587,13 @@ export const usersAppRouter = router({
return { success: true };
}),
resendVerificationEmail: publicProcedure
+ .use(
+ createRateLimitMiddleware({
+ name: "users.resendVerificationEmail",
+ windowMs: 5 * 60 * 1000,
+ maxRequests: 3,
+ }),
+ ) // 3 requests per 5 minutes
.input(
z.object({
email: z.string().email(),