diff --git a/.env.example b/.env.example index a6030fa..2dbc567 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,8 @@ DATABASE_URL="" DATABASE_AUTH_TOKEN="" # Redis -UPSTASH_REDIS_REST_URL="" -UPSTASH_REDIS_REST_TOKEN="" -UPSTASH_REDIS_EXPIRY_SECONDS="" -UPSTASH_RATELIMIT_REQUESTS="" -UPSTASH_RATELIMIT_SECONDS="" +REDIS_URL="" +REDIS_EXPIRY_SECONDS="" #Auth NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up" diff --git a/app/_components/Header.tsx b/app/_components/Header.tsx index 0cdc9c7..a78aeac 100644 --- a/app/_components/Header.tsx +++ b/app/_components/Header.tsx @@ -48,10 +48,7 @@ const Navbar = ({ title }: NavbarProps) => { width={32} height={32} /> - - {title} - {env.NEXT_PUBLIC_APP_ENV === "development" && " >> Staging"} - + {title} diff --git a/app/_lib/redis.ts b/app/_lib/redis.ts index 253cc8f..67b69e1 100644 --- a/app/_lib/redis.ts +++ b/app/_lib/redis.ts @@ -1,13 +1,12 @@ -import { Redis } from "@upstash/redis"; +import { Redis } from "ioredis"; import { env } from "env.mjs"; -export const redis = Redis.fromEnv(); +export const redis = env.REDIS_URL ? new Redis(env.REDIS_URL) : null; export const setCache = async (key: string, value: T) => { + console.log(env.REDIS_URL); try { - await redis.set(`${env.APP_ENV}_${key}`, value, { - ex: Number(env.UPSTASH_REDIS_EXPIRY_SECONDS), - }); + await redis?.set(`${env.APP_ENV}_${key}`, JSON.stringify(value)); return true; } catch { return false; @@ -16,8 +15,8 @@ export const setCache = async (key: string, value: T) => { export const fetchCache = async (key: string) => { try { - const result = await redis.get(`${env.APP_ENV}_${key}`); - return result as T; + const result = (await redis?.get(`${env.APP_ENV}_${key}`)) as string; + return JSON.parse(result) as T; } catch { return null; } @@ -25,7 +24,7 @@ export const fetchCache = async (key: string) => { export const invalidateCache = async (key: string) => { try { - await redis.del(`${env.APP_ENV}_${key}`); + await redis?.del(`${env.APP_ENV}_${key}`); return true; } catch { return false; diff --git a/app/api/internal/room/[roomId]/route.ts b/app/api/internal/room/[roomId]/route.ts index cc21f94..cf12f57 100644 --- a/app/api/internal/room/[roomId]/route.ts +++ b/app/api/internal/room/[roomId]/route.ts @@ -8,6 +8,7 @@ import { EventTypes } from "@/_utils/types"; import { getAuth } from "@clerk/nextjs/server"; import { createId } from "@paralleldrive/cuid2"; import { eq } from "drizzle-orm"; +import { env } from "env.mjs"; export async function GET( request: Request, @@ -61,7 +62,8 @@ export async function DELETE( const success = deletedRoom.length > 0; if (success) { - await invalidateCache(`kv_roomlist_${userId}`); + if (env.APP_ENV === "production") + await invalidateCache(`kv_roomlist_${userId}`); await publishToMultipleChannels( [`${userId}`, `${params.roomId}`], diff --git a/app/api/internal/room/route.ts b/app/api/internal/room/route.ts index 843a0fe..379d351 100644 --- a/app/api/internal/room/route.ts +++ b/app/api/internal/room/route.ts @@ -8,17 +8,21 @@ import { createId } from "@paralleldrive/cuid2"; import { publishToChannel } from "@/_lib/ably"; import { EventTypes } from "@/_utils/types"; import { getAuth } from "@clerk/nextjs/server"; +import { env } from "env.mjs"; export async function GET(request: Request) { const { userId } = getAuth(request as NextRequest); - const cachedResult = await fetchCache< - { - id: string; - createdAt: Date; - roomName: string; - }[] - >(`kv_roomlist_${userId}`); + const cachedResult = + env.APP_ENV === "production" + ? await fetchCache< + { + id: string; + createdAt: Date; + roomName: string; + }[] + >(`kv_roomlist_${userId}`) + : null; if (cachedResult) { return NextResponse.json(cachedResult, { @@ -30,7 +34,8 @@ export async function GET(request: Request) { where: eq(rooms.userId, userId || ""), }); - await setCache(`kv_roomlist_${userId}`, roomList); + if (env.APP_ENV === "production") + await setCache(`kv_roomlist_${userId}`, roomList); return NextResponse.json(roomList, { status: 200, @@ -60,7 +65,8 @@ export async function POST(request: Request) { const success = room.length > 0; if (room) { - await invalidateCache(`kv_roomlist_${userId}`); + if (env.APP_ENV === "production") + await invalidateCache(`kv_roomlist_${userId}`); await publishToChannel( `${userId}`, diff --git a/env.mjs b/env.mjs index 5ffb6d2..eba8e84 100644 --- a/env.mjs +++ b/env.mjs @@ -5,11 +5,8 @@ export const env = createEnv({ server: { DATABASE_URL: z.string().url(), DATABASE_AUTH_TOKEN: z.string(), - UPSTASH_REDIS_REST_URL: z.string().url(), - UPSTASH_REDIS_REST_TOKEN: z.string(), - UPSTASH_REDIS_EXPIRY_SECONDS: z.string(), - UPSTASH_RATELIMIT_REQUESTS: z.string(), - UPSTASH_RATELIMIT_SECONDS: z.string(), + REDIS_URL: z.string().url().optional(), + REDIS_EXPIRY_SECONDS: z.string().optional(), ABLY_API_KEY: z.string(), APP_ENV: z.string(), UNKEY_ROOT_KEY: z.string(), diff --git a/middleware.ts b/middleware.ts index 8e5d413..a045d7a 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,21 +1,9 @@ import { authMiddleware, redirectToSignIn } from "@clerk/nextjs"; import { validateRequest } from "./app/_lib/unkey"; import { NextResponse } from "next/server"; -import { Ratelimit } from "@upstash/ratelimit"; -import { Redis } from "@upstash/redis"; -import { env } from "./env.mjs"; const shitList = ["ama.ab.ca"]; -const rateLimit = new Ratelimit({ - redis: Redis.fromEnv(), - limiter: Ratelimit.slidingWindow( - Number(env.UPSTASH_RATELIMIT_REQUESTS), - `${Number(env.UPSTASH_RATELIMIT_SECONDS)}s` - ), - analytics: true, -}); - export default authMiddleware({ ignoredRoutes: ["/"], publicRoutes: [ @@ -25,14 +13,7 @@ export default authMiddleware({ ], afterAuth: async (auth, req) => { if (!auth.userId && auth.isPublicRoute) { - const { success } = await rateLimit.limit(req.ip || ""); - if (success) { - return NextResponse.next(); - } - return new NextResponse("TOO MANY REQUESTS", { - status: 429, - statusText: "Too many requests!", - }); + return NextResponse.next(); } if (auth.userId) { @@ -54,15 +35,6 @@ export default authMiddleware({ } if (req.nextUrl.pathname.includes("/api/internal")) { - const { success } = await rateLimit.limit(req.ip || ""); - - if (!success) { - return new NextResponse("TOO MANY REQUESTS", { - status: 429, - statusText: "Too many requests!", - }); - } - if (auth.userId) { return NextResponse.next(); } else { @@ -74,15 +46,6 @@ export default authMiddleware({ } if (req.nextUrl.pathname.includes("/api/external/private")) { - const { success } = await rateLimit.limit(req.ip || ""); - - if (!success) { - return new NextResponse("TOO MANY REQUESTS", { - status: 429, - statusText: "Too many requests!", - }); - } - const isValid = await validateRequest(req); if (isValid) { diff --git a/package.json b/package.json index c8b34a1..dfcd7f0 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,13 @@ "@t3-oss/env-nextjs": "0.7.1", "@tanstack/react-query": "5.8.4", "@unkey/api": "0.12.0", - "@upstash/ratelimit": "0.4.4", - "@upstash/redis": "1.25.1", "@vercel/analytics": "1.1.1", "ably": "1.2.47", "autoprefixer": "10.4.16", "csv42": "5.0.0", "dotenv": "16.3.1", "drizzle-orm": "0.29.0", + "ioredis": "^5.3.2", "next": "14.0.1", "nextjs-cors": "2.1.2", "postcss": "8.4.31", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0d43e8..5e6854c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,12 +26,6 @@ dependencies: '@unkey/api': specifier: 0.12.0 version: 0.12.0 - '@upstash/ratelimit': - specifier: 0.4.4 - version: 0.4.4 - '@upstash/redis': - specifier: 1.25.1 - version: 1.25.1 '@vercel/analytics': specifier: 1.1.1 version: 1.1.1 @@ -50,6 +44,9 @@ dependencies: drizzle-orm: specifier: 0.29.0 version: 0.29.0(@libsql/client@0.4.0-pre.1)(better-sqlite3@9.1.1) + ioredis: + specifier: ^5.3.2 + version: 5.3.2 next: specifier: 14.0.1 version: 14.0.1(react-dom@18.2.0)(react@18.2.0) @@ -800,6 +797,10 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -1548,25 +1549,6 @@ packages: resolution: {integrity: sha512-BT2I52rwTQ1j8S9aX9tKH3P3Efvtvgu8dD8pExYvKXG9uWPditNOwO6t8aeXLGCWdyzTqxmueBeR0uD0DN5U7A==} dev: false - /@upstash/core-analytics@0.0.6: - resolution: {integrity: sha512-cpPSR0XJAJs4Ddz9nq3tINlPS5aLfWVCqhhtHnXt4p7qr5+/Znlt1Es736poB/9rnl1hAHrOsOvVj46NEXcVqA==} - engines: {node: '>=16.0.0'} - dependencies: - '@upstash/redis': 1.25.1 - dev: false - - /@upstash/ratelimit@0.4.4: - resolution: {integrity: sha512-y3q6cNDdcRQ2MRPRf5UNWBN36IwnZ4kAEkGoH3i6OqdWwz4qlBxNsw4/Rpqn9h93+Nx1cqg5IOq7O2e2zMJY1w==} - dependencies: - '@upstash/core-analytics': 0.0.6 - dev: false - - /@upstash/redis@1.25.1: - resolution: {integrity: sha512-ACj0GhJ4qrQyBshwFgPod6XufVEfKX2wcaihsEvSdLYnY+m+pa13kGt1RXm/yTHKf4TQi/Dy2A8z/y6WUEOmlg==} - dependencies: - crypto-js: 4.2.0 - dev: false - /@vercel/analytics@1.1.1: resolution: {integrity: sha512-+NqgNmSabg3IFfxYhrWCfB/H+RCUOCR5ExRudNG2+pcRehq628DJB5e1u1xqwpLtn4pAYii4D98w7kofORAGQA==} dependencies: @@ -2031,6 +2013,11 @@ packages: mimic-response: 1.0.1 dev: false + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2097,10 +2084,6 @@ packages: which: 2.0.2 dev: true - /crypto-js@4.2.0: - resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - dev: false - /css-selector-tokenizer@0.8.0: resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} dependencies: @@ -2184,7 +2167,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} @@ -2233,6 +2215,11 @@ packages: engines: {node: '>=0.4.0'} dev: false + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -3371,6 +3358,23 @@ packages: side-channel: 1.0.4 dev: true + /ioredis@5.3.2: + resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.4 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -3692,6 +3696,14 @@ packages: p-locate: 5.0.0 dev: true + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: false + + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: false + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -3844,7 +3856,6 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4363,6 +4374,18 @@ packages: picomatch: 2.3.1 dev: true + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: false + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: false + /reflect.getprototypeof@1.0.4: resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} engines: {node: '>= 0.4'} @@ -4664,6 +4687,10 @@ packages: get-source: 2.0.12 dev: true + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: false + /stoppable@1.1.0: resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} engines: {node: '>=4', npm: '>=6'}