mycro-zine/worker/gemini-proxy.js

99 lines
3.1 KiB
JavaScript

/**
* Cloudflare Worker: Gemini API Proxy
* Routes requests through US region to bypass geo-restrictions
* Uses US-based secondary proxy to ensure requests originate from US
*/
// Use a US-based proxy service for the actual API call
const US_PROXY_SERVICES = [
// Primary: Use allorigins.win (US-based CORS proxy)
(url, options) => fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`, {
method: "POST",
headers: options.headers,
body: options.body,
}),
];
export default {
async fetch(request, env) {
// Handle CORS preflight
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, X-API-Key",
"Access-Control-Max-Age": "86400",
},
});
}
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
// Get API key from header or env
const apiKey = request.headers.get("X-API-Key") || env.GEMINI_API_KEY;
if (!apiKey) {
return new Response(JSON.stringify({ error: "API key required" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
try {
const body = await request.json();
const { model, contents, generationConfig } = body;
// Forward to Gemini API
const modelName = model || "gemini-2.0-flash-exp";
const geminiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${modelName}:generateContent?key=${apiKey}`;
// Try direct fetch first (works when called from US)
let geminiResponse = await fetch(geminiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
contents,
generationConfig,
}),
});
let data = await geminiResponse.json();
// If geo-blocked, return a helpful error
if (data.error?.message?.includes("not available in your country")) {
return new Response(JSON.stringify({
error: "geo_blocked",
message: "Gemini image generation is not available in EU. Using placeholder images.",
suggestion: "Images will be generated as placeholders. For full functionality, deploy from a US server.",
}), {
status: 200, // Return 200 so app can handle gracefully
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
return new Response(JSON.stringify(data), {
status: geminiResponse.status,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
},
};