Merge pull request #27 from atridadl/dev

Temporarily dropped rate-limit library
This commit is contained in:
Atridad Lahiji 2023-07-12 12:49:18 -06:00 committed by GitHub
commit 0aae24e044
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 111 deletions

View file

@ -5,8 +5,6 @@ DATABASE_URL=""
UPSTASH_REDIS_REST_URL="" UPSTASH_REDIS_REST_URL=""
UPSTASH_REDIS_REST_TOKEN="" UPSTASH_REDIS_REST_TOKEN=""
UPSTASH_REDIS_EXPIRY_SECONDS="" UPSTASH_REDIS_EXPIRY_SECONDS=""
UPSTASH_RATELIMIT_REQUESTS=""
UPSTASH_RATELIMIT_SECONDS=""
#Next Auth Core #Next Auth Core
NEXTAUTH_SECRET="" NEXTAUTH_SECRET=""

View file

@ -21,7 +21,6 @@
"@trpc/next": "10.34.0", "@trpc/next": "10.34.0",
"@trpc/react-query": "10.34.0", "@trpc/react-query": "10.34.0",
"@trpc/server": "10.34.0", "@trpc/server": "10.34.0",
"@upstash/ratelimit": "^0.4.3",
"@upstash/redis": "^1.22.0", "@upstash/redis": "^1.22.0",
"ably": "^1.2.41", "ably": "^1.2.41",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",

20
pnpm-lock.yaml generated
View file

@ -32,9 +32,6 @@ dependencies:
'@trpc/server': '@trpc/server':
specifier: 10.34.0 specifier: 10.34.0
version: 10.34.0 version: 10.34.0
'@upstash/ratelimit':
specifier: ^0.4.3
version: 0.4.3
'@upstash/redis': '@upstash/redis':
specifier: ^1.22.0 specifier: ^1.22.0
version: 1.22.0 version: 1.22.0
@ -1351,23 +1348,6 @@ packages:
eslint-visitor-keys: 3.4.1 eslint-visitor-keys: 3.4.1
dev: true dev: true
/@upstash/core-analytics@0.0.6:
resolution: {integrity: sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==}
engines: {node: '>=16.0.0'}
dependencies:
'@upstash/redis': 1.22.0
transitivePeerDependencies:
- encoding
dev: false
/@upstash/ratelimit@0.4.3:
resolution: {integrity: sha512-Dsp9Mw09Flg28JRklKgFiCXqr3bqv8bbG0kgpUYoHjcgPPolFFyaYOj/I2HExvYLZiogl77NUavBoNvMOK0zUQ==}
dependencies:
'@upstash/core-analytics': 0.0.6
transitivePeerDependencies:
- encoding
dev: false
/@upstash/redis@1.22.0: /@upstash/redis@1.22.0:
resolution: {integrity: sha512-sXoJDoEqqik0HbrNE7yRWckOySEFsoBxfRdCgOqkc0w6py19ZZG50SpGkDDEUXSnBqP8VgGYXhWAiBpqxrt5oA==} resolution: {integrity: sha512-sXoJDoEqqik0HbrNE7yRWckOySEFsoBxfRdCgOqkc0w6py19ZZG50SpGkDDEUXSnBqP8VgGYXhWAiBpqxrt5oA==}
dependencies: dependencies:

View file

@ -9,8 +9,6 @@ const server = z.object({
UPSTASH_REDIS_REST_URL: z.string().url(), UPSTASH_REDIS_REST_URL: z.string().url(),
UPSTASH_REDIS_REST_TOKEN: z.string(), UPSTASH_REDIS_REST_TOKEN: z.string(),
UPSTASH_REDIS_EXPIRY_SECONDS: z.string(), UPSTASH_REDIS_EXPIRY_SECONDS: z.string(),
UPSTASH_RATELIMIT_REQUESTS: z.string(),
UPSTASH_RATELIMIT_SECONDS: z.string(),
NODE_ENV: z.enum(["development", "test", "production"]), NODE_ENV: z.enum(["development", "test", "production"]),
NEXTAUTH_SECRET: NEXTAUTH_SECRET:
process.env.NODE_ENV === "production" process.env.NODE_ENV === "production"
@ -52,8 +50,6 @@ const processEnv = {
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
UPSTASH_REDIS_EXPIRY_SECONDS: process.env.UPSTASH_REDIS_EXPIRY_SECONDS, UPSTASH_REDIS_EXPIRY_SECONDS: process.env.UPSTASH_REDIS_EXPIRY_SECONDS,
UPSTASH_RATELIMIT_REQUESTS: process.env.UPSTASH_RATELIMIT_REQUESTS,
UPSTASH_RATELIMIT_SECONDS: process.env.UPSTASH_RATELIMIT_SECONDS,
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
NEXTAUTH_URL: process.env.NEXTAUTH_URL, NEXTAUTH_URL: process.env.NEXTAUTH_URL,

View file

@ -63,7 +63,7 @@ const Room: NextPage = () => {
export default Room; export default Room;
const RoomBody: React.FC = () => { const RoomBody: React.FC = ({}) => {
const { data: sessionData } = useSession(); const { data: sessionData } = useSession();
const { query } = useRouter(); const { query } = useRouter();
const roomId = z.string().parse(query.id); const roomId = z.string().parse(query.id);
@ -224,7 +224,7 @@ const RoomBody: React.FC = () => {
if (visible) { if (visible) {
if (!!matchedVote) { if (!!matchedVote) {
return <p>{ matchedVote.value }</p>; return <p>{matchedVote.value}</p>;
} else { } else {
return <IoHourglassOutline className="text-xl mx-auto text-red-400" />; return <IoHourglassOutline className="text-xl mx-auto text-red-400" />;
} }
@ -243,39 +243,39 @@ const RoomBody: React.FC = () => {
if (roomFromDb === undefined) { if (roomFromDb === undefined) {
return ( return (
<div className="flex flex-col items-center justify-center text-center"> <div className="flex flex-col items-center justify-center text-center">
<span className="loading loading-dots loading-lg"></span>{ " " } <span className="loading loading-dots loading-lg"></span>{" "}
</div> </div>
); );
// Room has been loaded // Room has been loaded
} else if (roomFromDb) { } else if (roomFromDb) {
return ( return (
<span className="text-center"> <span className="text-center">
<div className="text-2xl">{ roomFromDb.roomName }</div> <div className="text-2xl">{roomFromDb.roomName}</div>
<div className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md mx-auto"> <div className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md mx-auto">
<div>ID:</div> <div>ID:</div>
<div>{ roomFromDb.id }</div> <div>{roomFromDb.id}</div>
<button> <button>
{ copied ? ( {copied ? (
<IoCheckmarkCircleOutline className="mx-1 text-green-400 animate-bounce" /> <IoCheckmarkCircleOutline className="mx-1 text-green-400 animate-bounce" />
) : ( ) : (
<IoCopyOutline <IoCopyOutline
className="mx-1 hover:text-primary" className="mx-1 hover:text-primary"
onClick={ copyRoomURLHandler } onClick={copyRoomURLHandler}
></IoCopyOutline> ></IoCopyOutline>
) } )}
</button> </button>
</div> </div>
{ roomFromDb && ( {roomFromDb && (
<div className="card card-compact bg-neutral shadow-xl mx-auto m-4"> <div className="card card-compact bg-neutral shadow-xl mx-auto m-4">
<div className="card-body"> <div className="card-body">
<h2 className="card-title mx-auto"> <h2 className="card-title mx-auto">
Story: { roomFromDb.storyName } Story: {roomFromDb.storyName}
</h2> </h2>
<ul className="p-0 mx-auto flex flex-row flex-wrap justify-center items-center text-ceter gap-4"> <ul className="p-0 mx-auto flex flex-row flex-wrap justify-center items-center text-ceter gap-4">
{ presenceData && {presenceData &&
presenceData presenceData
.filter( .filter(
(value, index, self) => (value, index, self) =>
@ -288,72 +288,73 @@ const RoomBody: React.FC = () => {
.map((presenceItem) => { .map((presenceItem) => {
return ( return (
<li <li
key={ presenceItem.clientId } key={presenceItem.clientId}
className="flex flex-row items-center justify-center gap-2" className="flex flex-row items-center justify-center gap-2"
> >
<div className="w-10 rounded-full avatar mx-auto"> <div className="w-10 rounded-full avatar mx-auto">
<Image <Image
src={ presenceItem.data.image } src={presenceItem.data.image}
alt={ `${presenceItem.data.name}'s Profile Picture` } alt={`${presenceItem.data.name}'s Profile Picture`}
height={ 32 } height={32}
width={ 32 } width={32}
/> />
</div> </div>
<p className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md mx-auto"> <p className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md mx-auto">
{ presenceItem.data.name }{ " " } {presenceItem.data.name}{" "}
{ presenceItem.data.role === "ADMIN" && ( {presenceItem.data.role === "ADMIN" && (
<div <div
className="tooltip tooltip-primary" className="tooltip tooltip-primary"
data-tip="Admin" data-tip="Admin"
> >
<FaShieldAlt className="inline-block text-primary" /> <FaShieldAlt className="inline-block text-primary" />
</div> </div>
) }{ " " } )}{" "}
{ presenceItem.clientId === roomFromDb.userId && ( {presenceItem.clientId === roomFromDb.userId && (
<div <div
className="tooltip tooltip-warning" className="tooltip tooltip-warning"
data-tip="Room Owner" data-tip="Room Owner"
> >
<RiVipCrownFill className="inline-block text-yellow-500" /> <RiVipCrownFill className="inline-block text-yellow-500" />
</div> </div>
) } )}
{ " : " } {" : "}
</p> </p>
{ roomFromDb && {roomFromDb &&
votesFromDb && votesFromDb &&
voteString( voteString(
roomFromDb.visible, roomFromDb.visible,
votesFromDb, votesFromDb,
presenceItem.data presenceItem.data
) } )}
</li> </li>
); );
}) } })}
</ul> </ul>
<div className="join md:btn-group-horizontal mx-auto"> <div className="join md:btn-group-horizontal mx-auto">
{ roomFromDb.scale.split(",").map((scaleItem, index) => { {roomFromDb.scale.split(",").map((scaleItem, index) => {
return ( return (
<button <button
key={ index } key={index}
className={ `join-item ${getVoteForCurrentUser()?.value === scaleItem className={`join-item ${
getVoteForCurrentUser()?.value === scaleItem
? "btn btn-active btn-primary" ? "btn btn-active btn-primary"
: "btn" : "btn"
}` } }`}
onClick={ () => setVote(scaleItem) } onClick={() => setVote(scaleItem)}
> >
{ scaleItem } {scaleItem}
</button> </button>
); );
}) } })}
</div> </div>
</div> </div>
</div> </div>
) } )}
{ sessionData && {sessionData &&
!!roomFromDb && !!roomFromDb &&
roomFromDb.userId === sessionData.user.id && ( roomFromDb.userId === sessionData.user.id && (
<> <>
@ -362,38 +363,38 @@ const RoomBody: React.FC = () => {
<h2 className="card-title mx-auto">Room Settings</h2> <h2 className="card-title mx-auto">Room Settings</h2>
<label className="label mx-auto"> <label className="label mx-auto">
{ "Vote Scale (Comma Separated):" }{ " " } {"Vote Scale (Comma Separated):"}{" "}
</label> </label>
<input <input
type="text" type="text"
placeholder="Scale (Comma Separated)" placeholder="Scale (Comma Separated)"
className="input input-bordered m-auto" className="input input-bordered m-auto"
value={ roomScale } value={roomScale}
onChange={ (event) => { onChange={(event) => {
setRoomScale(event.target.value); setRoomScale(event.target.value);
} } }}
/> />
<label className="label mx-auto">{ "Story Name:" } </label> <label className="label mx-auto">{"Story Name:"} </label>
<input <input
type="text" type="text"
placeholder="Story Name" placeholder="Story Name"
className="input input-bordered m-auto" className="input input-bordered m-auto"
value={ storyNameText } value={storyNameText}
onChange={ (event) => { onChange={(event) => {
setStoryNameText(event.target.value); setStoryNameText(event.target.value);
} } }}
/> />
<div className="flex flex-row flex-wrap text-center items-center justify-center gap-2"> <div className="flex flex-row flex-wrap text-center items-center justify-center gap-2">
<div> <div>
<button <button
onClick={ () => saveRoom(!roomFromDb.visible, false) } onClick={() => saveRoom(!roomFromDb.visible, false)}
className="btn btn-primary inline-flex" className="btn btn-primary inline-flex"
> >
{ roomFromDb.visible ? ( {roomFromDb.visible ? (
<> <>
<IoEyeOffOutline className="text-xl mr-1" /> <IoEyeOffOutline className="text-xl mr-1" />
Hide Hide
@ -403,13 +404,13 @@ const RoomBody: React.FC = () => {
<IoEyeOutline className="text-xl mr-1" /> <IoEyeOutline className="text-xl mr-1" />
Show Show
</> </>
) } )}
</button> </button>
</div> </div>
<div> <div>
<button <button
onClick={ () => onClick={() =>
saveRoom( saveRoom(
false, false,
true, true,
@ -426,7 +427,7 @@ const RoomBody: React.FC = () => {
).length <= 1 ).length <= 1
} }
> >
{ roomFromDb.storyName === storyNameText || {roomFromDb.storyName === storyNameText ||
votesFromDb?.length === 0 ? ( votesFromDb?.length === 0 ? (
<> <>
<IoReloadOutline className="text-xl mr-1" /> Reset <IoReloadOutline className="text-xl mr-1" /> Reset
@ -435,16 +436,16 @@ const RoomBody: React.FC = () => {
<> <>
<IoSaveOutline className="text-xl mr-1" /> Save <IoSaveOutline className="text-xl mr-1" /> Save
</> </>
) } )}
</button> </button>
</div> </div>
{ votesFromDb && {votesFromDb &&
(roomFromDb.logs.length > 0 || (roomFromDb.logs.length > 0 ||
votesFromDb.length > 0) && ( votesFromDb.length > 0) && (
<div> <div>
<button <button
onClick={ () => downloadLogs() } onClick={() => downloadLogs()}
className="btn btn-primary inline-flex hover:animate-pulse" className="btn btn-primary inline-flex hover:animate-pulse"
> >
<> <>
@ -452,12 +453,12 @@ const RoomBody: React.FC = () => {
</> </>
</button> </button>
</div> </div>
) } )}
</div> </div>
</div> </div>
</div> </div>
</> </>
) } )}
</span> </span>
); );
// Room does not exist // Room does not exist

View file

@ -17,7 +17,6 @@
import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type Session } from "next-auth"; import { type Session } from "next-auth";
import { Redis } from "@upstash/redis";
import { getServerAuthSession } from "~/server/auth"; import { getServerAuthSession } from "~/server/auth";
import { prisma } from "~/server/db"; import { prisma } from "~/server/db";
@ -65,9 +64,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
* This is where the tRPC API is initialized, connecting the context and transformer. * This is where the tRPC API is initialized, connecting the context and transformer.
*/ */
import { initTRPC, TRPCError } from "@trpc/server"; import { initTRPC, TRPCError } from "@trpc/server";
import { Ratelimit } from "@upstash/ratelimit";
import superjson from "superjson"; import superjson from "superjson";
import { env } from "~/env.mjs";
const t = initTRPC.context<typeof createTRPCContext>().create({ const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson, transformer: superjson,
@ -106,33 +103,11 @@ const enforceRouteProtection = t.middleware(async ({ ctx, next }) => {
throw new TRPCError({ code: "UNAUTHORIZED" }); throw new TRPCError({ code: "UNAUTHORIZED" });
} }
try {
const rateLimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(
Number(env.UPSTASH_RATELIMIT_REQUESTS),
`${Number(env.UPSTASH_RATELIMIT_SECONDS)}s`
),
analytics: true,
});
const { success } = await rateLimit.limit(
`${env.APP_ENV}_${ctx.session.user.id}`
);
if (!success) throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
return next({ return next({
ctx: { ctx: {
session: { ...ctx.session, user: ctx.session.user }, session: { ...ctx.session, user: ctx.session.user },
}, },
}); });
} catch {
return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
}
}); });
/** /**