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}`)); |