Merge pull request #3 from MarconLP/feature/rate-limiting
add rate limiting
This commit is contained in:
commit
51d59e57ec
6 changed files with 178 additions and 2 deletions
|
|
@ -40,4 +40,8 @@ NEXT_PUBLIC_CRISP_WEBSITE_ID=""
|
|||
NEXT_PUBLIC_POSTHOG_KEY=""
|
||||
NEXT_PUBLIC_POSTHOG_HOST=""
|
||||
NEXT_PUBLIC_POSTHOG_PROXY_HOST="/ioafe"
|
||||
POSTHOG_PROXY_PATH="ioafe"
|
||||
POSTHOG_PROXY_PATH="ioafe"
|
||||
|
||||
# redis for ratelimiting
|
||||
UPSTASH_REDIS_REST_URL=""
|
||||
UPSTASH_REDIS_REST_TOKEN="="
|
||||
149
package-lock.json
generated
149
package-lock.json
generated
|
|
@ -25,6 +25,8 @@
|
|||
"@trpc/server": "^10.18.0",
|
||||
"@types/recordrtc": "^5.6.11",
|
||||
"@upstash/qstash": "^0.3.6",
|
||||
"@upstash/ratelimit": "^0.4.2",
|
||||
"@upstash/redis": "^1.20.4",
|
||||
"axios": "^1.3.5",
|
||||
"crisp-sdk-web": "^1.0.18",
|
||||
"dayjs": "^1.11.7",
|
||||
|
|
@ -2523,6 +2525,17 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@upstash/core-analytics": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/core-analytics/-/core-analytics-0.0.6.tgz",
|
||||
"integrity": "sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==",
|
||||
"dependencies": {
|
||||
"@upstash/redis": "^1.19.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@upstash/qstash": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/qstash/-/qstash-0.3.6.tgz",
|
||||
|
|
@ -2531,6 +2544,25 @@
|
|||
"@deno/shim-crypto": "~0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@upstash/ratelimit": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/ratelimit/-/ratelimit-0.4.2.tgz",
|
||||
"integrity": "sha512-nuuPK2IoPJywAK/8cosNiyM3vlcL6tFPaAwrLes0j2J5b1Hs/W7XDO72/AuC+K+dt89TP50WK9bvDFUwLRuhvw==",
|
||||
"dependencies": {
|
||||
"@upstash/core-analytics": "^0.0.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@upstash/redis": "^1.20.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@upstash/redis": {
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.20.4.tgz",
|
||||
"integrity": "sha512-U7j7py+yPvafB5KS7o+F19j2CWzZCwmQ4Tvs+n2lpCWuw/8CeFtWrFWPtQa5dgrVu6tu+Ki9DmhDiAbgMS5fGA==",
|
||||
"dependencies": {
|
||||
"isomorphic-fetch": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
|
|
@ -4921,6 +4953,15 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/isomorphic-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.18.2",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
||||
|
|
@ -5399,6 +5440,25 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
||||
|
|
@ -6864,6 +6924,11 @@
|
|||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/ts-interface-checker": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
|
|
@ -7060,6 +7125,25 @@
|
|||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
@ -9065,6 +9149,14 @@
|
|||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"@upstash/core-analytics": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/core-analytics/-/core-analytics-0.0.6.tgz",
|
||||
"integrity": "sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==",
|
||||
"requires": {
|
||||
"@upstash/redis": "^1.19.3"
|
||||
}
|
||||
},
|
||||
"@upstash/qstash": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/qstash/-/qstash-0.3.6.tgz",
|
||||
|
|
@ -9073,6 +9165,22 @@
|
|||
"@deno/shim-crypto": "~0.3.0"
|
||||
}
|
||||
},
|
||||
"@upstash/ratelimit": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/ratelimit/-/ratelimit-0.4.2.tgz",
|
||||
"integrity": "sha512-nuuPK2IoPJywAK/8cosNiyM3vlcL6tFPaAwrLes0j2J5b1Hs/W7XDO72/AuC+K+dt89TP50WK9bvDFUwLRuhvw==",
|
||||
"requires": {
|
||||
"@upstash/core-analytics": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"@upstash/redis": {
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.20.4.tgz",
|
||||
"integrity": "sha512-U7j7py+yPvafB5KS7o+F19j2CWzZCwmQ4Tvs+n2lpCWuw/8CeFtWrFWPtQa5dgrVu6tu+Ki9DmhDiAbgMS5fGA==",
|
||||
"requires": {
|
||||
"isomorphic-fetch": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
|
|
@ -10823,6 +10931,15 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"jiti": {
|
||||
"version": "1.18.2",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
||||
|
|
@ -11162,6 +11279,14 @@
|
|||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
||||
|
|
@ -12144,6 +12269,11 @@
|
|||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"ts-interface-checker": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
|
|
@ -12283,6 +12413,25 @@
|
|||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
"@trpc/server": "^10.18.0",
|
||||
"@types/recordrtc": "^5.6.11",
|
||||
"@upstash/qstash": "^0.3.6",
|
||||
"@upstash/ratelimit": "^0.4.2",
|
||||
"@upstash/redis": "^1.20.4",
|
||||
"axios": "^1.3.5",
|
||||
"crisp-sdk-web": "^1.0.18",
|
||||
"dayjs": "^1.11.7",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ const server = z.object({
|
|||
STRIPE_MONTHLY_PRICE_ID: z.string(),
|
||||
STRIPE_ANNUAL_PRICE_ID: z.string(),
|
||||
POSTHOG_PROXY_PATH: z.string(),
|
||||
UPSTASH_REDIS_REST_URL: z.string(),
|
||||
UPSTASH_REDIS_REST_TOKEN: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -80,6 +82,8 @@ const processEnv = {
|
|||
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||
POSTHOG_PROXY_PATH: process.env.POSTHOG_PROXY_PATH,
|
||||
NEXT_PUBLIC_POSTHOG_PROXY_HOST: process.env.NEXT_PUBLIC_POSTHOG_PROXY_HOST,
|
||||
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
|
||||
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
|
||||
};
|
||||
|
||||
// Don't touch the part below
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ import { s3 } from "~/server/aws/s3";
|
|||
import { stripe } from "~/server/stripe";
|
||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||
import { posthog } from "~/server/posthog";
|
||||
import { rateLimit } from "~/server/rateLimit";
|
||||
|
||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
||||
transformer: superjson,
|
||||
|
|
@ -120,10 +121,15 @@ export const createTRPCRouter = t.router;
|
|||
export const publicProcedure = t.procedure;
|
||||
|
||||
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
||||
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
|
||||
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.session || !ctx.session.user) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
const { success } = await rateLimit.limit(ctx.session.user.id);
|
||||
if (!success) {
|
||||
throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
// infers the `session` as non-nullable
|
||||
|
|
|
|||
11
src/server/rateLimit.ts
Normal file
11
src/server/rateLimit.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { Ratelimit } from "@upstash/ratelimit";
|
||||
import { Redis } from "@upstash/redis";
|
||||
import { env } from "~/env.mjs";
|
||||
|
||||
export const rateLimit = new Ratelimit({
|
||||
redis: new Redis({
|
||||
url: env.UPSTASH_REDIS_REST_URL,
|
||||
token: env.UPSTASH_REDIS_REST_TOKEN,
|
||||
}),
|
||||
limiter: Ratelimit.slidingWindow(2, "10 s"),
|
||||
});
|
||||
Loading…
Reference in a new issue