Files
log/workers/ollama-proxy.js
2025-06-14 13:17:08 +09:00

93 lines
2.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Cloudflare Worker for secure Ollama proxy
export default {
async fetch(request, env, ctx) {
// CORS preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': 'https://log.syui.ai',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-User-Token',
'Access-Control-Max-Age': '86400',
}
});
}
// Verify origin
const origin = request.headers.get('Origin');
const referer = request.headers.get('Referer');
// 許可されたオリジンのみ
const allowedOrigins = [
'https://log.syui.ai',
'https://log.pages.dev' // Cloudflare Pages preview
];
if (!origin || !allowedOrigins.some(allowed => origin.startsWith(allowed))) {
return new Response('Forbidden', { status: 403 });
}
// ユーザー認証トークン検証(オプション)
const userToken = request.headers.get('X-User-Token');
if (env.REQUIRE_AUTH && !userToken) {
return new Response('Unauthorized', { status: 401 });
}
// リクエストボディを取得
const body = await request.json();
// プロンプトサイズ制限
if (body.prompt && body.prompt.length > 1000) {
return new Response(JSON.stringify({
error: 'Prompt too long. Maximum 1000 characters.'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// レート制限CF Workers KV使用
if (env.RATE_LIMITER) {
const clientIP = request.headers.get('CF-Connecting-IP');
const rateLimitKey = `rate:${clientIP}`;
const currentCount = await env.RATE_LIMITER.get(rateLimitKey) || 0;
if (currentCount >= 20) {
return new Response(JSON.stringify({
error: 'Rate limit exceeded. Try again later.'
}), {
status: 429,
headers: { 'Content-Type': 'application/json' }
});
}
// カウント増加1時間TTL
await env.RATE_LIMITER.put(rateLimitKey, currentCount + 1, {
expirationTtl: 3600
});
}
// Ollamaへプロキシ
const ollamaResponse = await fetch(env.OLLAMA_API_URL || 'https://ollama.syui.ai/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 内部認証ヘッダー(必要に応じて)
'X-Internal-Token': env.OLLAMA_INTERNAL_TOKEN || ''
},
body: JSON.stringify(body)
});
// レスポンスを返す
const responseData = await ollamaResponse.text();
return new Response(responseData, {
status: ollamaResponse.status,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': origin,
'Cache-Control': 'no-store'
}
});
}
};