Merge pull request #3 from MarconLP/feature/rate-limiting

add rate limiting
This commit is contained in:
Marcus Hof 2023-04-23 20:56:27 +02:00 committed by GitHub
commit 51d59e57ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 2 deletions

View file

@ -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
View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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
View 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"),
});