Spaces:
Running
Running
| // server/index.js | |
| const express = require('express'); | |
| const path = require('node:path'); | |
| const { WebSocketServer, WebSocket } = require('ws'); | |
| const http = require('node:http'); | |
| require('dotenv').config(); | |
| const app = express(); | |
| const server = http.createServer(app); | |
| const wss = new WebSocketServer({ server }); | |
| // بخش خواندن دستورالعملهای شخصیت از Secrets | |
| const instructionSecretNames = { | |
| default: 'PERSONALITY_DEFAULT', | |
| teacher: 'PERSONALITY_TEACHER', | |
| poetic: 'PERSONALITY_POETIC', | |
| funny: 'PERSONALITY_FUNNY', | |
| }; | |
| const personalityInstructions = {}; | |
| console.log('در حال خواندن دستورالعملهای شخصیت از Secrets...'); | |
| Object.keys(instructionSecretNames).forEach(key => { | |
| const secretName = instructionSecretNames[key]; | |
| const instruction = process.env[secretName]; | |
| if (instruction) { | |
| personalityInstructions[key] = instruction; | |
| console.log(`- دستورالعمل '${key}' با موفقیت خوانده شد.`); | |
| } else { | |
| personalityInstructions[key] = `دستورالعمل '${key}' از سرور بارگذاری نشد.`; | |
| console.warn(`** هشدار: Secret با نام '${secretName}' یافت نشد! **`); | |
| } | |
| }); | |
| // بخش مدیریت کلیدهای API (کد هوشمند شما بدون تغییر) | |
| const apiKeysEnv = process.env.ALL_GEMINI_API_KEYS; | |
| const apiKeys = apiKeysEnv ? apiKeysEnv.split(',').map(key => key.trim()).filter(key => key) : []; | |
| if (apiKeys.length === 0) { | |
| console.error('🔴 خطای حیاتی: Secret با نام ALL_GEMINI_API_KEYS یافت نشد!'); | |
| process.exit(1); | |
| } | |
| console.log(`✅ تعداد ${apiKeys.length} کلید API بارگذاری شد.`); | |
| let currentKeyIndex = 0; | |
| const getStartingKeyIndex = () => { | |
| const index = currentKeyIndex % apiKeys.length; | |
| currentKeyIndex++; | |
| return index; | |
| }; | |
| // توابع attachGeminiEventHandlers و tryConnectToGemini شما اینجا بدون تغییر قرار میگیرند. | |
| // ... (برای کوتاهی اینجا حذف شده، اما شما باید آنها را نگه دارید) ... | |
| function attachGeminiEventHandlers(clientWs, geminiWs, apiKeyUsed) { | |
| geminiWs.on('message', data => clientWs.readyState === WebSocket.OPEN && clientWs.send(data, { binary: true })); | |
| geminiWs.on('error', error => { | |
| console.error(`🔴 خطای WebSocket جیمینای (کلید ...${apiKeyUsed.slice(-4)}):`, error.message); | |
| clientWs.readyState === WebSocket.OPEN && clientWs.close(); | |
| }); | |
| geminiWs.on('close', code => { | |
| if (code !== 1000) console.log(`🟡 اتصال جیمینای بسته شد (کلید ...${apiKeyUsed.slice(-4)}). کد: ${code}`); | |
| clientWs.readyState === WebSocket.OPEN && clientWs.close(); | |
| }); | |
| } | |
| async function tryConnectToGemini(clientWs, setupData, startIndex, attemptCount = 0) { | |
| if (attemptCount >= apiKeys.length) { | |
| console.error(`🔴 تمام ${apiKeys.length} کلید API ناموفق بودند.`); | |
| if (clientWs.readyState === WebSocket.OPEN) clientWs.send(JSON.stringify({ error: "خطای داخلی سرور." })) && clientWs.close(); | |
| return null; | |
| } | |
| const keyIndexToTry = (startIndex + attemptCount) % apiKeys.length; | |
| const apiKey = apiKeys[keyIndexToTry]; | |
| return new Promise(resolve => { | |
| const geminiWs = new WebSocket(`wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContent?key=${apiKey}`); | |
| const timeout = setTimeout(() => { | |
| geminiWs.removeAllListeners() && geminiWs.close(); | |
| console.warn(`🟡 اتصال با کلید ...${apiKey.slice(-4)} زمانبر شد. امتحان کلید بعدی...`); | |
| resolve(tryConnectToGemini(clientWs, setupData, startIndex, attemptCount + 1)); | |
| }, 8000); | |
| geminiWs.on('open', () => { | |
| clearTimeout(timeout); | |
| console.log(`✅ اتصال با کلید اندیس ${keyIndexToTry} موفق بود.`); | |
| try { | |
| geminiWs.send(JSON.stringify(setupData)); | |
| } catch (e) { | |
| clientWs.close(); | |
| resolve(null); | |
| return; | |
| } | |
| attachGeminiEventHandlers(clientWs, geminiWs, apiKey); | |
| resolve(geminiWs); | |
| }); | |
| geminiWs.on('error', () => { | |
| clearTimeout(timeout); | |
| geminiWs.removeAllListeners(); | |
| console.warn(`🟡 اتصال با کلید ...${apiKey.slice(-4)} ناموفق بود. امتحان کلید بعدی...`); | |
| resolve(tryConnectToGemini(clientWs, setupData, startIndex, attemptCount + 1)); | |
| }); | |
| }); | |
| } | |
| // سرو کردن فایلهای استاتیک | |
| app.use(express.static(path.join(__dirname, '../build'))); | |
| // API Endpoint جدید برای ارسال دستورالعملها | |
| app.get('/api/instructions', (req, res) => res.json(personalityInstructions)); | |
| // مدیریت اتصال WebSocket کلاینت (بدون تغییر) | |
| wss.on('connection', ws => { | |
| const startingKeyIndex = getStartingKeyIndex(); | |
| console.log(`🔌 کلاینت جدید متصل شد. کلید شروع: اندیس ${startingKeyIndex}`); | |
| let geminiWs = null; | |
| ws.on('message', async message => { | |
| try { | |
| const data = JSON.parse(message.toString()); | |
| if (data.setup) { | |
| if (geminiWs) return; | |
| geminiWs = await tryConnectToGemini(ws, data, startingKeyIndex); | |
| } else if (geminiWs?.readyState === WebSocket.OPEN) { | |
| geminiWs.send(JSON.stringify(data)); | |
| } | |
| } catch (e) { /* خطا */ } | |
| }); | |
| ws.on('close', () => console.log(`🔌 کلاینت با کلید شروع اندیس ${startingKeyIndex} قطع شد.`) && geminiWs?.close()); | |
| ws.on('error', error => console.error(`🔴 خطای WebSocket کلاینت:`, error) && geminiWs?.close()); | |
| }); | |
| // رسیدگی به سایر درخواستها | |
| app.get('*', (req, res) => res.sendFile(path.join(__dirname, '../build', 'index.html'))); | |
| const PORT = process.env.PORT || 3001; | |
| server.listen(PORT, () => console.log(`🚀 سرور در حال اجرا روی پورت ${PORT}`)); |