| |
| |
|
|
| const LIMIT = 2;
|
| const WINDOW = 120;
|
| const TARGET_HOST = "https://example.com";
|
|
|
| async function updateRequestStats(ip, path, isSuccess, env) {
|
|
|
| const statsKey = `${ip}`;
|
| let stats = await env.ad2api_ip_list.get(statsKey);
|
| stats = stats
|
| ? JSON.parse(stats)
|
| : {
|
| modelsCount: 0,
|
| successCount: 0,
|
| hitMaxLimitCount: 0,
|
| };
|
|
|
|
|
| if (path === "/v1/models") {
|
| stats.modelsCount++;
|
| } else if (!isSuccess) {
|
| stats.hitMaxLimitCount++;
|
| } else {
|
| stats.successCount++;
|
| }
|
|
|
|
|
| await env.ad2api_ip_list.put(statsKey, JSON.stringify(stats));
|
|
|
| return stats;
|
| }
|
|
|
| async function checkRateLimit(ip, path, env) {
|
|
|
| if (path === "/v1/models") {
|
| await updateRequestStats(ip, path, true, env);
|
| return { allowed: true };
|
| }
|
|
|
| const now = Math.floor(Date.now() / 1000);
|
| const key = `${ip}`;
|
|
|
|
|
| let history = await env.ad2api_rate_limits.get(key);
|
| let timestamps = [];
|
|
|
| if (history) {
|
| timestamps = JSON.parse(history);
|
|
|
| timestamps = timestamps.filter((ts) => now - ts < WINDOW);
|
| }
|
|
|
|
|
| if (timestamps.length >= LIMIT) {
|
|
|
| const oldestTimestamp = timestamps[0];
|
| const waitTime = WINDOW - (now - oldestTimestamp);
|
| if (waitTime > 0) {
|
| return { allowed: false, waitTime };
|
| }
|
|
|
| timestamps.shift();
|
| }
|
|
|
|
|
| timestamps.push(now);
|
|
|
|
|
| await env.ad2api_rate_limits.put(key, JSON.stringify(timestamps), {
|
| expirationTtl: WINDOW,
|
| });
|
|
|
| return { allowed: true };
|
| }
|
|
|
| async function handleRequest(request, env) {
|
| const ip = request.headers.get("cf-connecting-ip");
|
| const url = new URL(request.url);
|
|
|
|
|
| const result = await checkRateLimit(ip, url.pathname, env);
|
| if (!result.allowed) {
|
| return new Response("Too Many Requests", {
|
| status: 429,
|
| headers: {
|
| "Content-Type": "text/plain",
|
| "Retry-After": String(result.waitTime),
|
| "Access-Control-Allow-Origin": "*",
|
| },
|
| });
|
| }
|
|
|
|
|
| const targetUrl = new URL(TARGET_HOST + url.pathname + url.search);
|
|
|
|
|
| const headers = new Headers(request.headers);
|
| headers.set("Host", new URL(TARGET_HOST).host);
|
|
|
|
|
| const newRequest = new Request(targetUrl.toString(), {
|
| method: request.method,
|
| headers: headers,
|
| body: request.body,
|
| redirect: "follow",
|
| });
|
|
|
| try {
|
|
|
| const response = await fetch(newRequest);
|
| const isSuccess = response.status !== 500;
|
|
|
|
|
| await updateRequestStats(ip, url.pathname, isSuccess, env);
|
|
|
|
|
| if (!isSuccess) {
|
| const key = `${ip}`;
|
| let history = await env.ad2api_rate_limits.get(key);
|
| if (history) {
|
| let timestamps = JSON.parse(history);
|
| timestamps.pop();
|
| await env.ad2api_rate_limits.put(
|
| key,
|
| JSON.stringify(timestamps),
|
| {
|
| expirationTtl: WINDOW,
|
| }
|
| );
|
| }
|
| }
|
|
|
|
|
| const responseHeaders = new Headers(response.headers);
|
| responseHeaders.set("Access-Control-Allow-Origin", "*");
|
|
|
|
|
| return new Response(response.body, {
|
| status: response.status,
|
| headers: responseHeaders,
|
| });
|
| } catch (error) {
|
|
|
| await updateRequestStats(ip, url.pathname, false, env);
|
|
|
| const key = `${ip}`;
|
| let history = await env.ad2api_rate_limits.get(key);
|
| if (history) {
|
| let timestamps = JSON.parse(history);
|
| timestamps.pop();
|
| await env.ad2api_rate_limits.put(key, JSON.stringify(timestamps), {
|
| expirationTtl: WINDOW,
|
| });
|
| }
|
|
|
| return new Response("Internal Server Error", {
|
| status: 500,
|
| headers: {
|
| "Access-Control-Allow-Origin": "*",
|
| },
|
| });
|
| }
|
| }
|
|
|
| export default {
|
| async fetch(request, env, ctx) {
|
| return handleRequest(request, env);
|
| },
|
| };
|
|
|