diff --git a/src/middleware.ts b/src/middleware.ts index c804059..c869689 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,24 +1,10 @@ -import { authMiddleware } from '@clerk/nextjs' +import { publicRoutes } from './middleware/public-routes' +import { stackMiddlewares } from './middleware/stack-middlewares' +import { withAuth } from './middleware/with-clerk-auth' +import { withRateLimit } from './middleware/with-rate-limit' -// This example protects all routes including api/trpc routes -// Please edit this to allow other routes to be public as needed. -// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware -export default authMiddleware({ - // routes that do not require authentication - publicRoutes: [ - '/', - '/messages/(.*)', - '/chats/(.*)', - '/api/chat/livekit_token', - '/api/files/upload', - '/api/msg/new', - '/api/msg/destroy', - '/api/msg/message-viewed', - '/api/ip' - ] -}) +export default stackMiddlewares([withRateLimit, withAuth]) export const config = { - // matcher: ['/((?!_next/image|_next/static|favicon.ico|logo.png).*)'] - matcher: ["/((?!.*\\..*|_next).*)","/","/(api|trpc)(.*)"], -} + matcher: ["/((?!.*\\..*|_next).*)","/","/(api|trpc)(.*)"] +} \ No newline at end of file diff --git a/src/middleware/public-routes.ts b/src/middleware/public-routes.ts new file mode 100644 index 0000000..714ec7b --- /dev/null +++ b/src/middleware/public-routes.ts @@ -0,0 +1,12 @@ +export const publicRoutes = [ + '/', + '/messages/(.*)', + '/chats/(.*)', + '/api/chat/livekit_token', + '/api/files/upload', + '/api/msg/new', + '/api/msg/destroy', + '/api/msg/message-viewed', + '/api/msg/:messageId', + '/api/ip' +] diff --git a/src/middleware/stack-middlewares.ts b/src/middleware/stack-middlewares.ts new file mode 100644 index 0000000..2498616 --- /dev/null +++ b/src/middleware/stack-middlewares.ts @@ -0,0 +1,14 @@ + +import { NextMiddleware, NextResponse } from "next/server"; +import { MiddlewareFactory } from "../shared/types"; +export function stackMiddlewares( + functions: MiddlewareFactory[] = [], + index = 0 +): NextMiddleware { + const current = functions[index]; + if (current) { + const next = stackMiddlewares(functions, index + 1); + return current(next); + } + return () => NextResponse.next(); +} \ No newline at end of file diff --git a/src/middleware/with-clerk-auth.ts b/src/middleware/with-clerk-auth.ts new file mode 100644 index 0000000..052ea71 --- /dev/null +++ b/src/middleware/with-clerk-auth.ts @@ -0,0 +1,17 @@ +import { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server' +import { authMiddleware } from '@clerk/nextjs' +import { publicRoutes } from './public-routes' +import { MiddlewareFactory } from '../shared/types' + +// This example protects all routes including api/trpc routes +// Please edit this to allow other routes to be public as needed. +// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware + +export const withAuth: MiddlewareFactory = (next: NextMiddleware) => { + return async (request: NextRequest, _next: NextFetchEvent) => { + return await authMiddleware({ + // routes that do not require authentication + publicRoutes: publicRoutes + })(next.) + } +} diff --git a/src/middleware/with-rate-limit.ts b/src/middleware/with-rate-limit.ts new file mode 100644 index 0000000..72d3dbe --- /dev/null +++ b/src/middleware/with-rate-limit.ts @@ -0,0 +1,33 @@ +import { NextFetchEvent, NextMiddleware, NextRequest, NextResponse } from 'next/server' +import { Ratelimit } from '@upstash/ratelimit' +import { Redis } from '@upstash/redis' +import { publicRoutes } from './public-routes' +import { MiddlewareFactory } from '../shared/types' + +const redis = new Redis({ + url: process.env.UPSTASH_REDIS_REST_URL as string, + token: process.env.UPSTASH_REDIS_REST_TOKEN as string +}) + +const ratelimit = new Ratelimit({ + redis: redis, + // 100 messages per day + limiter: Ratelimit.slidingWindow(parseInt(process.env.FREE_TIER_MESSAGES_LIMIT || '1'), '1 d') +}) + +export const withRateLimit: MiddlewareFactory = (next: NextMiddleware) => { + // return async (request: NextRequest, _next: NextFetchEvent) => { + // const ip = + // request.ip || request.headers.get('x-vercel-forwarded-for') || '127.0.0.1' + // const { success } = await ratelimit.limit(ip) + // console.log(`Rate limit ${ip} ${success ? 'passed' : 'blocked'}`) + // return success + // ? next(request, _next) + // : NextResponse.redirect(new URL('/blocked', request.url)) + // } + return async (request: NextRequest, _next: NextFetchEvent) => { + console.log("Log some data here", request.nextUrl.pathname); + return next(request, _next); + }; +} + diff --git a/src/pages/blocked.tsx b/src/pages/blocked.tsx new file mode 100644 index 0000000..1ed4cce --- /dev/null +++ b/src/pages/blocked.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export default function BlockedPage() { + return ( +