UNPKG

senangwebs-chatbot

Version:

Lightweight JavaScript library with OpenRouter API integration for AI-powered conversations.

165 lines (143 loc) 4.39 kB
/** * Cloudflare Workers Proxy for OpenRouter API * * Deploy this as a Cloudflare Worker to create a secure proxy. * Your API key is stored as an environment variable in Cloudflare. * * Setup: * 1. Create a new Cloudflare Worker * 2. Add environment variable: OPENROUTER_API_KEY * 3. Deploy this code * 4. Configure chatbot: data-swc-api-base-url="https://your-worker.workers.dev/chat" * * Deploy with Wrangler: * wrangler secret put OPENROUTER_API_KEY * wrangler deploy */ const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'; // CORS headers const corsHeaders = { 'Access-Control-Allow-Origin': '*', // Change to your domain in production 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }; /** * Handle incoming requests */ addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { // Handle CORS preflight if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } // Get URL path const url = new URL(request.url); // Health check endpoint if (url.pathname === '/health') { return new Response(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }), { headers: { 'Content-Type': 'application/json', ...corsHeaders } }); } // Chat endpoint if (url.pathname === '/chat' && request.method === 'POST') { return handleChatRequest(request); } // 404 for other paths return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: { 'Content-Type': 'application/json', ...corsHeaders } }); } /** * Handle chat completion request */ async function handleChatRequest(request) { try { // Get API key from environment const apiKey = OPENROUTER_API_KEY; if (!apiKey) { return jsonResponse({ error: 'API key not configured' }, 500); } // Parse request body const body = await request.json(); const { messages, model, stream, max_tokens, temperature } = body; // Validate request if (!messages || !Array.isArray(messages)) { return jsonResponse({ error: 'Invalid messages format' }, 400); } if (!model) { return jsonResponse({ error: 'Model is required' }, 400); } // Optional: Add rate limiting using Cloudflare KV // Optional: Add request logging using Cloudflare Analytics Engine // Optional: Add authentication using Cloudflare Access // Prepare request body const requestBody = { model, messages, stream: stream || false, max_tokens: max_tokens || 1000, temperature: temperature !== undefined ? temperature : 0.7 }; // Make request to OpenRouter const openrouterResponse = await fetch(`${OPENROUTER_BASE_URL}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': request.headers.get('Referer') || 'https://your-site.com', 'X-Title': 'SenangWebs Chatbot' }, body: JSON.stringify(requestBody) }); // Handle streaming response if (stream) { return new Response(openrouterResponse.body, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', ...corsHeaders } }); } // Handle non-streaming response const data = await openrouterResponse.json(); return new Response(JSON.stringify(data), { status: openrouterResponse.status, headers: { 'Content-Type': 'application/json', ...corsHeaders } }); } catch (error) { console.error('Proxy error:', error); return jsonResponse({ error: 'Internal server error', message: error.message }, 500); } } /** * Helper function to create JSON response */ function jsonResponse(data, status = 200) { return new Response(JSON.stringify(data), { status, headers: { 'Content-Type': 'application/json', ...corsHeaders } }); }