feat: add Cloudflare Worker proxy for Gemini image generation
- Created gemini-proxy worker to route requests through US edge - Updated generate-page to use proxy for bypassing geo-restrictions - Gemini image generation is blocked in Germany, proxy routes through US Worker URL: https://gemini-proxy.jeffemmett.workers.dev 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
779a0806a2
commit
06dc335013
|
|
@ -119,39 +119,48 @@ async function generateImageWithGemini(prompt: string): Promise<string> {
|
|||
}
|
||||
|
||||
// Gemini 2.0 Flash with native image generation (Nano Banana)
|
||||
// Uses Cloudflare Worker proxy to bypass geo-restrictions
|
||||
async function generateWithGemini2FlashImage(prompt: string, apiKey: string): Promise<string | null> {
|
||||
const response = await fetch(
|
||||
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=${apiKey}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contents: [
|
||||
{
|
||||
parts: [
|
||||
{
|
||||
text: `Generate an image: ${prompt}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
generationConfig: {
|
||||
responseModalities: ["TEXT", "IMAGE"],
|
||||
// Use the Cloudflare Worker proxy to route through US
|
||||
const proxyUrl = process.env.GEMINI_PROXY_URL || "https://gemini-proxy.jeffemmett.workers.dev";
|
||||
|
||||
const response = await fetch(proxyUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": apiKey,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "gemini-2.0-flash-exp",
|
||||
contents: [
|
||||
{
|
||||
parts: [
|
||||
{
|
||||
text: `Generate an image: ${prompt}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
],
|
||||
generationConfig: {
|
||||
responseModalities: ["TEXT", "IMAGE"],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error("Gemini 2.0 Flash API error:", response.status, errorText);
|
||||
console.error("Gemini proxy API error:", response.status, errorText);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Check for API errors in response
|
||||
if (data.error) {
|
||||
console.error("Gemini API error via proxy:", data.error);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract image from response
|
||||
const parts = data.candidates?.[0]?.content?.parts || [];
|
||||
for (const part of parts) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Cloudflare Worker: Gemini API Proxy
|
||||
* Routes requests through US region to bypass geo-restrictions
|
||||
*/
|
||||
|
||||
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}`;
|
||||
|
||||
const geminiResponse = await fetch(geminiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contents,
|
||||
generationConfig,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await geminiResponse.json();
|
||||
|
||||
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": "*",
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
name = "gemini-proxy"
|
||||
main = "gemini-proxy.js"
|
||||
compatibility_date = "2024-01-01"
|
||||
|
||||
# Workers run at edge globally, requests from US users will hit US edge
|
||||
[placement]
|
||||
mode = "smart"
|
||||
Loading…
Reference in New Issue