├── .env ├── README.md ├── _routes.json └── functions └── [[path]].js /.env: -------------------------------------------------------------------------------- 1 | # 项目名称会决定你的域名 2 | # 如果你的项目名是 cfgapi,域名将会是 cfgapi.pages.dev 3 | PROJECT_NAME=cfgapi 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 注意事项 2 | 2025-01-05更新 3 | * [请切换至AI-TEST版本使用](https://github.com/TAisBUG/CFGAPI/tree/AI) 4 | 5 | -------------------------------------------------------------------------------- /_routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "include": ["/v1beta/*"], 4 | "exclude": [] 5 | } 6 | -------------------------------------------------------------------------------- /functions/[[path]].js: -------------------------------------------------------------------------------- 1 | export async function onRequest(context) { 2 | try { 3 | const TELEGRAPH_URL = 'https://generativelanguage.googleapis.com'; 4 | const request = context.request; 5 | const url = new URL(request.url); 6 | 7 | const newUrl = new URL(url.pathname + url.search, TELEGRAPH_URL); 8 | 9 | const providedApiKeys = url.searchParams.get('key'); 10 | 11 | if (!providedApiKeys) { 12 | return new Response('API key is missing.', { status: 400 }); 13 | } 14 | 15 | const apiKeyArray = providedApiKeys.split(';').map(key => key.trim()).filter(key => key !== ''); 16 | 17 | if (apiKeyArray.length === 0) { 18 | return new Response('Valid API key is missing.', { status: 400 }); 19 | } 20 | 21 | const selectedApiKey = apiKeyArray[Math.floor(Math.random() * apiKeyArray.length)]; 22 | newUrl.searchParams.set('key', selectedApiKey); 23 | 24 | const modifiedRequest = new Request(newUrl.toString(), { 25 | headers: request.headers, 26 | method: request.method, 27 | body: request.body, 28 | redirect: 'follow' 29 | }); 30 | 31 | const response = await fetch(modifiedRequest); 32 | 33 | if (!response.ok) { 34 | const errorBody = await response.text(); 35 | return new Response(`API request failed: ${errorBody}`, { status: response.status }); 36 | } 37 | 38 | // 检查是否是 SSE 流 39 | if (response.headers.get('content-type')?.includes('text/event-stream')) { 40 | const reader = response.body.getReader(); 41 | const encoder = new TextEncoder(); 42 | const decoder = new TextDecoder(); 43 | 44 | let lastContent = null; // 存储上一次的内容 45 | let buffer = ''; // 用于处理跨块的数据 46 | 47 | const stream = new ReadableStream({ 48 | async start(controller) { 49 | try { 50 | while (true) { 51 | const {done, value} = await reader.read(); 52 | 53 | if (done) { 54 | // 处理缓冲区中剩余的数据 55 | if (buffer) { 56 | if (buffer.startsWith('data: ')) { 57 | const data = buffer.slice(6); 58 | if (data !== '[DONE]') { 59 | try { 60 | const parsedData = JSON.parse(data); 61 | const content = extractContent(parsedData); 62 | if (!lastContent || !isRepeatContent(content, lastContent)) { 63 | controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsedData)}\n\n`)); 64 | } 65 | } catch (e) { 66 | controller.enqueue(encoder.encode(`data: ${data}\n\n`)); 67 | } 68 | } 69 | } 70 | } 71 | controller.close(); 72 | break; 73 | } 74 | 75 | buffer += decoder.decode(value); 76 | const lines = buffer.split('\n'); 77 | 78 | // 保留最后一行,因为它可能是不完整的 79 | buffer = lines.pop() || ''; 80 | 81 | for (const line of lines) { 82 | if (line.startsWith('data: ')) { 83 | const data = line.slice(6); 84 | if (data === '[DONE]') { 85 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 86 | continue; 87 | } 88 | 89 | try { 90 | const parsedData = JSON.parse(data); 91 | const content = extractContent(parsedData); 92 | 93 | // 检查是否是重复内容 94 | if (lastContent && isRepeatContent(content, lastContent)) { 95 | continue; // 跳过重复内容 96 | } 97 | 98 | // 更新最后发送的内容 99 | lastContent = content; 100 | 101 | // 发送数据 102 | controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsedData)}\n\n`)); 103 | } catch (e) { 104 | // 如果解析失败,仍然发送原始数据 105 | controller.enqueue(encoder.encode(`data: ${data}\n\n`)); 106 | } 107 | } 108 | } 109 | } 110 | } catch (error) { 111 | controller.error(error); 112 | } 113 | } 114 | }); 115 | 116 | return new Response(stream, { 117 | headers: { 118 | 'Content-Type': 'text/event-stream', 119 | 'Access-Control-Allow-Origin': '*', 120 | 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 121 | 'Access-Control-Allow-Headers': 'Content-Type' 122 | } 123 | }); 124 | } 125 | 126 | // 非流式响应直接返回 127 | const modifiedResponse = new Response(response.body, response); 128 | modifiedResponse.headers.set('Access-Control-Allow-Origin', '*'); 129 | modifiedResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 130 | modifiedResponse.headers.set('Access-Control-Allow-Headers', 'Content-Type'); 131 | 132 | return modifiedResponse; 133 | 134 | } catch (error) { 135 | console.error('Proxy error:', error); 136 | return new Response('An error occurred: ' + error.message, { 137 | status: 500, 138 | headers: { 139 | 'Access-Control-Allow-Origin': '*', 140 | 'Content-Type': 'text/plain' 141 | } 142 | }); 143 | } 144 | } 145 | 146 | // 从响应数据中提取实际内容 147 | function extractContent(parsedData) { 148 | try { 149 | return parsedData.candidates?.[0]?.content?.parts?.[0]?.text || ''; 150 | } catch (e) { 151 | return JSON.stringify(parsedData); 152 | } 153 | } 154 | 155 | // 检查是否是重复内容 156 | function isRepeatContent(currentContent, lastContent) { 157 | if (!currentContent || !lastContent) return false; 158 | return lastContent.endsWith(currentContent); 159 | } 160 | 161 | // 处理 OPTIONS 请求 162 | export async function onRequestOptions() { 163 | return new Response(null, { 164 | status: 204, 165 | headers: { 166 | 'Access-Control-Allow-Origin': '*', 167 | 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 168 | 'Access-Control-Allow-Headers': 'Content-Type', 169 | 'Access-Control-Max-Age': '86400' 170 | } 171 | }); 172 | } 173 | --------------------------------------------------------------------------------