aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/lib
diff options
context:
space:
mode:
Diffstat (limited to 'packages/trpc/lib')
-rw-r--r--packages/trpc/lib/rateLimit.ts48
1 files changed, 48 insertions, 0 deletions
diff --git a/packages/trpc/lib/rateLimit.ts b/packages/trpc/lib/rateLimit.ts
new file mode 100644
index 00000000..bf8a8e8b
--- /dev/null
+++ b/packages/trpc/lib/rateLimit.ts
@@ -0,0 +1,48 @@
+import { TRPCError } from "@trpc/server";
+
+import type { RateLimitConfig } from "@karakeep/shared/ratelimiting";
+import serverConfig from "@karakeep/shared/config";
+import { getRateLimitClient } from "@karakeep/shared/ratelimiting";
+
+/**
+ * Create a tRPC middleware for rate limiting
+ * @param config Rate limit configuration
+ * @returns tRPC middleware function
+ */
+export function createRateLimitMiddleware<T>(config: RateLimitConfig) {
+ return async function rateLimitMiddleware(opts: {
+ path: string;
+ ctx: { req: { ip: string | null } };
+ next: () => Promise<T>;
+ }) {
+ if (!serverConfig.rateLimiting.enabled) {
+ return opts.next();
+ }
+
+ const ip = opts.ctx.req.ip;
+
+ if (!ip) {
+ return opts.next();
+ }
+
+ const client = await getRateLimitClient();
+
+ if (!client) {
+ // If no rate limit client is registered, allow the request
+ return opts.next();
+ }
+
+ // Build the rate limiting key from IP and path
+ const key = `${ip}:${opts.path}`;
+ const result = client.checkRateLimit(config, key);
+
+ if (!result.allowed) {
+ throw new TRPCError({
+ code: "TOO_MANY_REQUESTS",
+ message: `Rate limit exceeded. Try again in ${result.resetInSeconds} seconds.`,
+ });
+ }
+
+ return opts.next();
+ };
+}