From d72ad03c422179352b9d769b4c824afc3f7a7c83 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 14:19:24 +0530 Subject: [PATCH 1/2] fix(api): require authentication on loom-download endpoint Co-Authored-By: Claude Sonnet 4.6 --- apps/web/app/api/tools/loom-download/route.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/web/app/api/tools/loom-download/route.ts b/apps/web/app/api/tools/loom-download/route.ts index 44293cf52f..504bd65751 100644 --- a/apps/web/app/api/tools/loom-download/route.ts +++ b/apps/web/app/api/tools/loom-download/route.ts @@ -1,4 +1,5 @@ import { randomUUID } from "node:crypto"; +import { getCurrentUser } from "@cap/database/auth/session"; import { type NextRequest, NextResponse } from "next/server"; import { fetchConvertedVideoViaMediaServer, @@ -160,6 +161,11 @@ async function tryMp4CandidateDownload( } export async function GET(request: NextRequest) { + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + const videoId = request.nextUrl.searchParams.get("id"); const videoName = request.nextUrl.searchParams.get("name"); From 3f2db032c414aa4ad0d98c0a4723a8da37cf1915 Mon Sep 17 00:00:00 2001 From: Minit Date: Wed, 22 Apr 2026 14:49:03 +0530 Subject: [PATCH 2/2] fix(api): add per-user rate limiting to loom-download endpoint --- apps/web/app/api/tools/loom-download/route.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/web/app/api/tools/loom-download/route.ts b/apps/web/app/api/tools/loom-download/route.ts index 504bd65751..4e55be1712 100644 --- a/apps/web/app/api/tools/loom-download/route.ts +++ b/apps/web/app/api/tools/loom-download/route.ts @@ -7,6 +7,10 @@ import { } from "@/lib/media-client"; import { convertRemoteVideoToMp4Buffer } from "@/lib/video-convert"; +const rateLimitMap = new Map(); +const WINDOW_MS = 60_000; +const MAX_REQUESTS = 10; + function isHlsUrl(url: string): boolean { return (url.split("?")[0] ?? "").toLowerCase().endsWith(".m3u8"); } @@ -166,6 +170,16 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } + const now = Date.now(); + const entry = rateLimitMap.get(user.id); + if (!entry || now > entry.resetAt) { + rateLimitMap.set(user.id, { count: 1, resetAt: now + WINDOW_MS }); + } else if (entry.count >= MAX_REQUESTS) { + return NextResponse.json({ error: "Rate limit exceeded" }, { status: 429 }); + } else { + entry.count++; + } + const videoId = request.nextUrl.searchParams.get("id"); const videoName = request.nextUrl.searchParams.get("name");