Ezmary's picture
Update server/index.js
0ded4c9 verified
// 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}`));