diff --git a/backend/src/index.ts b/backend/src/index.ts index 7dcaf21..ce40246 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,6 +1,7 @@ import { GoogleGenerativeAI } from '@google/generative-ai'; export interface Env { + RATE_LIMIT: KVNamespace; GEMINI_API_KEY: string; } @@ -10,6 +11,33 @@ const corsHeaders = { 'Access-Control-Allow-Headers': 'Content-Type', }; +const MAX_REQUESTS_ALLOWED = 10; +const DURATION = 60_000; + +async function checkRateLimit(ip: string, env: Env) { + const key = `ip_key:${ip}`; + const now = Date.now(); + let value = await env.RATE_LIMIT.get(key); + let data = { count: 0, time: now }; + + if (value) { + try { + data = JSON.parse(value); + } catch { + data = { count: 0, time: now }; + } + } + + if (now - data.time > DURATION) { + data.count = 0; + data.time = now; + } + + data.count += 1; + await env.RATE_LIMIT.put(key, JSON.stringify(data), { expirationTtl: 65 }); + + return data.count <= MAX_REQUESTS_ALLOWED; +} async function handleTranslate(request: Request, model: ReturnType) { const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>(); @@ -73,11 +101,22 @@ export default { } try { + const ip = request.headers.get('CF-Connecting-IP') || 'unknown'; + const allowed = await checkRateLimit(ip, env); + if (!allowed) { + return new Response(JSON.stringify({ error: "Too many requests. Try again later." }), { + status: 429, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } const url = new URL(request.url); const path = url.pathname; const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY); const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' }); + if(path==="/test-rate-limit"){ + return new Response(JSON.stringify("Proceed !")) + } if (path === '/' || path === '/v1/translate') { return await handleTranslate(request, model); } diff --git a/backend/worker-configuration.d.ts b/backend/worker-configuration.d.ts index 33f5779..af03300 100644 --- a/backend/worker-configuration.d.ts +++ b/backend/worker-configuration.d.ts @@ -3,6 +3,7 @@ // Runtime types generated with workerd@1.20250712.0 2025-07-15 declare namespace Cloudflare { interface Env { + RATE_LIMIT: KVNamespace; } } interface Env extends Cloudflare.Env {} diff --git a/backend/wrangler.jsonc b/backend/wrangler.jsonc index e6d2565..aa2104c 100644 --- a/backend/wrangler.jsonc +++ b/backend/wrangler.jsonc @@ -9,7 +9,14 @@ "compatibility_date": "2025-07-15", "observability": { "enabled": true - } + }, + "kv_namespaces": [ + { + "binding": "RATE_LIMIT", + "id": "" + } + ] + /** * Smart Placement * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement