From 3c3936a6dce387834651f4c7d8134c5e34dc12b8 Mon Sep 17 00:00:00 2001 From: Leon van Zyl Date: Sun, 30 Nov 2025 15:20:49 +0200 Subject: [PATCH] remove rate limiting --- create-agentic-app/package-lock.json | 4 +- create-agentic-app/package.json | 2 +- .../template/src/app/api/chat/route.ts | 12 -- .../template/src/lib/rate-limit.ts | 157 ------------------ src/app/api/chat/route.ts | 12 -- src/lib/rate-limit.ts | 157 ------------------ 6 files changed, 3 insertions(+), 341 deletions(-) delete mode 100644 create-agentic-app/template/src/lib/rate-limit.ts delete mode 100644 src/lib/rate-limit.ts diff --git a/create-agentic-app/package-lock.json b/create-agentic-app/package-lock.json index ce81ee3..2cf8408 100644 --- a/create-agentic-app/package-lock.json +++ b/create-agentic-app/package-lock.json @@ -1,12 +1,12 @@ { "name": "create-agentic-app", - "version": "1.1.21", + "version": "1.1.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "create-agentic-app", - "version": "1.1.21", + "version": "1.1.22", "license": "MIT", "dependencies": { "chalk": "^5.3.0", diff --git a/create-agentic-app/package.json b/create-agentic-app/package.json index 8b04a03..e854d4b 100644 --- a/create-agentic-app/package.json +++ b/create-agentic-app/package.json @@ -1,6 +1,6 @@ { "name": "create-agentic-app", - "version": "1.1.21", + "version": "1.1.22", "description": "Scaffold a new agentic AI application with Next.js, Better Auth, and AI SDK", "type": "module", "bin": { diff --git a/create-agentic-app/template/src/app/api/chat/route.ts b/create-agentic-app/template/src/app/api/chat/route.ts index aff9faa..b4e5772 100644 --- a/create-agentic-app/template/src/app/api/chat/route.ts +++ b/create-agentic-app/template/src/app/api/chat/route.ts @@ -3,11 +3,6 @@ import { createOpenRouter } from "@openrouter/ai-sdk-provider"; import { streamText, UIMessage, convertToModelMessages } from "ai"; import { z } from "zod"; import { auth } from "@/lib/auth"; -import { - checkRateLimit, - createRateLimitResponse, - rateLimitConfigs, -} from "@/lib/rate-limit"; // Zod schema for message validation const messagePartSchema = z.object({ @@ -36,13 +31,6 @@ export async function POST(req: Request) { }); } - // Rate limiting by user ID - const rateLimitKey = `chat:${session.user.id}`; - const rateLimit = checkRateLimit(rateLimitKey, rateLimitConfigs.chat); - if (!rateLimit.allowed) { - return createRateLimitResponse(rateLimit.resetTime); - } - // Parse and validate request body let body: unknown; try { diff --git a/create-agentic-app/template/src/lib/rate-limit.ts b/create-agentic-app/template/src/lib/rate-limit.ts deleted file mode 100644 index d750343..0000000 --- a/create-agentic-app/template/src/lib/rate-limit.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Simple in-memory rate limiter for API routes. - * For production, consider using a Redis-based solution. - */ - -interface RateLimitConfig { - /** Number of requests allowed in the window */ - limit: number; - /** Time window in milliseconds */ - windowMs: number; -} - -interface RateLimitEntry { - count: number; - resetTime: number; -} - -const rateLimitStore = new Map(); - -/** - * Default rate limit configurations for different endpoints. - */ -export const rateLimitConfigs = { - // AI chat endpoint - more restrictive - chat: { - limit: 20, - windowMs: 60 * 1000, // 20 requests per minute - }, - // Auth endpoints - auth: { - limit: 10, - windowMs: 60 * 1000, // 10 requests per minute - }, - // General API - api: { - limit: 100, - windowMs: 60 * 1000, // 100 requests per minute - }, -} as const; - -/** - * Check if a request should be rate limited. - * - * @param key - Unique identifier for the client (e.g., IP address, user ID) - * @param config - Rate limit configuration - * @returns Object with allowed status and remaining requests - */ -export function checkRateLimit( - key: string, - config: RateLimitConfig -): { - allowed: boolean; - remaining: number; - resetTime: number; -} { - const now = Date.now(); - const entry = rateLimitStore.get(key); - - // Clean up expired entries periodically - if (Math.random() < 0.01) { - cleanupExpiredEntries(); - } - - if (!entry || now > entry.resetTime) { - // Create new entry - const newEntry: RateLimitEntry = { - count: 1, - resetTime: now + config.windowMs, - }; - rateLimitStore.set(key, newEntry); - return { - allowed: true, - remaining: config.limit - 1, - resetTime: newEntry.resetTime, - }; - } - - if (entry.count >= config.limit) { - return { - allowed: false, - remaining: 0, - resetTime: entry.resetTime, - }; - } - - // Increment count - entry.count++; - return { - allowed: true, - remaining: config.limit - entry.count, - resetTime: entry.resetTime, - }; -} - -/** - * Create a rate limit response with appropriate headers. - */ -export function createRateLimitResponse(resetTime: number): Response { - const retryAfter = Math.ceil((resetTime - Date.now()) / 1000); - return new Response( - JSON.stringify({ - error: "Too Many Requests", - message: "Rate limit exceeded. Please try again later.", - retryAfter, - }), - { - status: 429, - headers: { - "Content-Type": "application/json", - "Retry-After": String(retryAfter), - "X-RateLimit-Reset": String(Math.ceil(resetTime / 1000)), - }, - } - ); -} - -/** - * Get the client identifier from a request. - * Uses IP address or forwarded header. - */ -export function getClientIdentifier(request: Request): string { - const forwarded = request.headers.get("x-forwarded-for"); - if (forwarded) { - const ip = forwarded.split(",")[0]; - if (ip) { - return ip.trim(); - } - } - // Fallback to a hash of user-agent if no IP available - const userAgent = request.headers.get("user-agent") || "unknown"; - return `ua-${hashString(userAgent)}`; -} - -/** - * Simple string hash for fallback identifier. - */ -function hashString(str: string): string { - let hash = 0; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - return Math.abs(hash).toString(36); -} - -/** - * Clean up expired rate limit entries. - */ -function cleanupExpiredEntries(): void { - const now = Date.now(); - for (const [key, entry] of rateLimitStore.entries()) { - if (now > entry.resetTime) { - rateLimitStore.delete(key); - } - } -} diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index aff9faa..b4e5772 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -3,11 +3,6 @@ import { createOpenRouter } from "@openrouter/ai-sdk-provider"; import { streamText, UIMessage, convertToModelMessages } from "ai"; import { z } from "zod"; import { auth } from "@/lib/auth"; -import { - checkRateLimit, - createRateLimitResponse, - rateLimitConfigs, -} from "@/lib/rate-limit"; // Zod schema for message validation const messagePartSchema = z.object({ @@ -36,13 +31,6 @@ export async function POST(req: Request) { }); } - // Rate limiting by user ID - const rateLimitKey = `chat:${session.user.id}`; - const rateLimit = checkRateLimit(rateLimitKey, rateLimitConfigs.chat); - if (!rateLimit.allowed) { - return createRateLimitResponse(rateLimit.resetTime); - } - // Parse and validate request body let body: unknown; try { diff --git a/src/lib/rate-limit.ts b/src/lib/rate-limit.ts deleted file mode 100644 index d750343..0000000 --- a/src/lib/rate-limit.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Simple in-memory rate limiter for API routes. - * For production, consider using a Redis-based solution. - */ - -interface RateLimitConfig { - /** Number of requests allowed in the window */ - limit: number; - /** Time window in milliseconds */ - windowMs: number; -} - -interface RateLimitEntry { - count: number; - resetTime: number; -} - -const rateLimitStore = new Map(); - -/** - * Default rate limit configurations for different endpoints. - */ -export const rateLimitConfigs = { - // AI chat endpoint - more restrictive - chat: { - limit: 20, - windowMs: 60 * 1000, // 20 requests per minute - }, - // Auth endpoints - auth: { - limit: 10, - windowMs: 60 * 1000, // 10 requests per minute - }, - // General API - api: { - limit: 100, - windowMs: 60 * 1000, // 100 requests per minute - }, -} as const; - -/** - * Check if a request should be rate limited. - * - * @param key - Unique identifier for the client (e.g., IP address, user ID) - * @param config - Rate limit configuration - * @returns Object with allowed status and remaining requests - */ -export function checkRateLimit( - key: string, - config: RateLimitConfig -): { - allowed: boolean; - remaining: number; - resetTime: number; -} { - const now = Date.now(); - const entry = rateLimitStore.get(key); - - // Clean up expired entries periodically - if (Math.random() < 0.01) { - cleanupExpiredEntries(); - } - - if (!entry || now > entry.resetTime) { - // Create new entry - const newEntry: RateLimitEntry = { - count: 1, - resetTime: now + config.windowMs, - }; - rateLimitStore.set(key, newEntry); - return { - allowed: true, - remaining: config.limit - 1, - resetTime: newEntry.resetTime, - }; - } - - if (entry.count >= config.limit) { - return { - allowed: false, - remaining: 0, - resetTime: entry.resetTime, - }; - } - - // Increment count - entry.count++; - return { - allowed: true, - remaining: config.limit - entry.count, - resetTime: entry.resetTime, - }; -} - -/** - * Create a rate limit response with appropriate headers. - */ -export function createRateLimitResponse(resetTime: number): Response { - const retryAfter = Math.ceil((resetTime - Date.now()) / 1000); - return new Response( - JSON.stringify({ - error: "Too Many Requests", - message: "Rate limit exceeded. Please try again later.", - retryAfter, - }), - { - status: 429, - headers: { - "Content-Type": "application/json", - "Retry-After": String(retryAfter), - "X-RateLimit-Reset": String(Math.ceil(resetTime / 1000)), - }, - } - ); -} - -/** - * Get the client identifier from a request. - * Uses IP address or forwarded header. - */ -export function getClientIdentifier(request: Request): string { - const forwarded = request.headers.get("x-forwarded-for"); - if (forwarded) { - const ip = forwarded.split(",")[0]; - if (ip) { - return ip.trim(); - } - } - // Fallback to a hash of user-agent if no IP available - const userAgent = request.headers.get("user-agent") || "unknown"; - return `ua-${hashString(userAgent)}`; -} - -/** - * Simple string hash for fallback identifier. - */ -function hashString(str: string): string { - let hash = 0; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - return Math.abs(hash).toString(36); -} - -/** - * Clean up expired rate limit entries. - */ -function cleanupExpiredEntries(): void { - const now = Date.now(); - for (const [key, entry] of rateLimitStore.entries()) { - if (now > entry.resetTime) { - rateLimitStore.delete(key); - } - } -}