Spaces:
Running
Running
Update server/Server.js
Browse files- server/Server.js +23 -44
server/Server.js
CHANGED
@@ -2,59 +2,47 @@ const express = require('express');
|
|
2 |
const path = require('node:path');
|
3 |
const { WebSocketServer, WebSocket } = require('ws');
|
4 |
const http = require('node:http');
|
5 |
-
require('dotenv').config();
|
6 |
|
7 |
const app = express();
|
8 |
const server = http.createServer(app);
|
9 |
|
10 |
-
// --- 👇 دامنه مجاز WebSocket (باید با ALLOWED_ORIGIN در App.tsx یکی باشد) 👇 ---
|
11 |
const ALLOWED_WEBSOCKET_ORIGIN = "https://aisada.ir"; // یا "http://aisada.ir"
|
12 |
-
// --- 👆 ---
|
13 |
|
14 |
-
// --- 👇 استفاده از verifyClient برای بررسی Origin قبل از نهایی کردن اتصال 👇 ---
|
15 |
const wss = new WebSocketServer({
|
16 |
-
server,
|
17 |
verifyClient: (info, cb) => {
|
18 |
-
// info.origin حاوی مقدار هدر Origin فرستاده شده توسط مرورگر است
|
19 |
const origin = info.origin;
|
20 |
-
|
21 |
-
|
22 |
if (origin === ALLOWED_WEBSOCKET_ORIGIN) {
|
23 |
console.log(`[WebSocket Verify] Origin allowed: ${origin}`);
|
24 |
-
cb(true);
|
25 |
} else {
|
26 |
-
// اگر origin تعریف نشده بود (مثلاً یک کلاینت غیرمرورگری) یا مطابقت نداشت
|
27 |
console.warn(`[WebSocket Verify] Forbidden origin: ${origin}. Rejecting connection.`);
|
28 |
-
|
29 |
-
cb(false, 403, 'Forbidden Origin'); // اتصال را با کد 403 رد کن
|
30 |
}
|
31 |
}
|
32 |
});
|
33 |
-
// --- 👆 ---
|
34 |
|
35 |
-
// Serve static files from the React app build directory
|
36 |
-
// این مسیر باید درست باشد نسبت به محل اجرای server.js
|
37 |
app.use(express.static(path.join(__dirname, '../build')));
|
38 |
|
39 |
-
// خواندن API Key از متغیرهای محیطی
|
40 |
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
41 |
-
|
42 |
if (!GEMINI_API_KEY) {
|
43 |
console.error('FATAL ERROR: GEMINI_API_KEY environment variable is not set!');
|
44 |
-
process.exit(1);
|
45 |
}
|
46 |
|
47 |
-
// تابع برای ایجاد اتصال WebSocket به Gemini (بدون تغییر نسبت به قبل)
|
48 |
const createGeminiWebSocket = (clientWs) => {
|
49 |
-
const geminiWsUrl = `wss://generativelanguage.googleapis.com/v1beta/models/gemini-pro:streamGenerateContent?key=${GEMINI_API_KEY}&alt=sse`;
|
50 |
-
|
51 |
-
|
52 |
-
const geminiWs = new WebSocket(geminiWsUrl); // از URL صحیح استفاده کنید
|
53 |
|
54 |
geminiWs.on('open', () => {
|
55 |
console.log('[Gemini Connect] Connected to Gemini API');
|
56 |
if (geminiWs.pendingSetup) {
|
57 |
-
|
|
|
58 |
geminiWs.send(JSON.stringify(geminiWs.pendingSetup));
|
59 |
geminiWs.pendingSetup = null;
|
60 |
}
|
@@ -62,14 +50,12 @@ const createGeminiWebSocket = (clientWs) => {
|
|
62 |
|
63 |
geminiWs.on('message', (data) => {
|
64 |
try {
|
65 |
-
|
66 |
-
//
|
67 |
if (clientWs.readyState === WebSocket.OPEN) {
|
68 |
-
// اگر بافر بود، مستقیم بفرست
|
69 |
if (Buffer.isBuffer(data)) {
|
70 |
clientWs.send(data, { binary: true });
|
71 |
} else {
|
72 |
-
// اگر متن بود، تبدیل به بافر و ارسال (مطابق کد قبلی)
|
73 |
const blob = Buffer.from(data.toString());
|
74 |
clientWs.send(blob, { binary: true });
|
75 |
}
|
@@ -85,9 +71,7 @@ const createGeminiWebSocket = (clientWs) => {
|
|
85 |
return geminiWs;
|
86 |
};
|
87 |
|
88 |
-
// رویداد connection حالا فقط برای درخواستهای مجاز اجرا میشود
|
89 |
wss.on('connection', (ws, req) => {
|
90 |
-
// req.headers.origin را برای لاگ استفاده میکنیم
|
91 |
const origin = req.headers.origin || 'N/A';
|
92 |
console.log(`[WebSocket Event] Client connected (Origin: ${origin})`);
|
93 |
let geminiWs = null;
|
@@ -96,12 +80,13 @@ wss.on('connection', (ws, req) => {
|
|
96 |
try {
|
97 |
const messageString = message.toString();
|
98 |
const data = JSON.parse(messageString);
|
99 |
-
|
|
|
100 |
|
101 |
if (data.setup) {
|
|
|
102 |
console.log('[Client Receive] Initializing Gemini connection...');
|
103 |
geminiWs = createGeminiWebSocket(ws);
|
104 |
-
// مدیریت پیام setup که قبل از باز شدن اتصال gemini میرسد
|
105 |
if (geminiWs.readyState !== WebSocket.OPEN) {
|
106 |
geminiWs.pendingSetup = data;
|
107 |
} else {
|
@@ -110,19 +95,17 @@ wss.on('connection', (ws, req) => {
|
|
110 |
return;
|
111 |
}
|
112 |
|
113 |
-
// ارسال سایر پیامها به Gemini
|
114 |
if (geminiWs && geminiWs.readyState === WebSocket.OPEN) {
|
115 |
geminiWs.send(JSON.stringify(data));
|
116 |
} else if (geminiWs) {
|
|
|
117 |
console.log('[Client Receive] Gemini connection not ready, waiting...');
|
118 |
-
// اینجا میتوان پیام را در صف گذاشت یا خطا داد
|
119 |
} else {
|
120 |
console.error('[Client Receive] No Gemini connection established. Closing client connection.');
|
121 |
-
ws.close(1011, 'Backend connection not ready');
|
122 |
}
|
123 |
} catch (error) {
|
124 |
-
console.error('[Client Receive] Error processing message:', error, message.toString().substring(0, 100) + '...');
|
125 |
-
// بستن اتصال در صورت خطای parse یا پردازش ناموفق
|
126 |
ws.close(1008, 'Message processing error');
|
127 |
}
|
128 |
});
|
@@ -133,18 +116,16 @@ wss.on('connection', (ws, req) => {
|
|
133 |
console.log('[WebSocket Event] Closing associated Gemini connection.');
|
134 |
geminiWs.close();
|
135 |
}
|
136 |
-
geminiWs = null;
|
137 |
});
|
138 |
|
139 |
ws.on('error', (error) => {
|
140 |
console.error('[WebSocket Event] Client WebSocket error:', error);
|
141 |
-
// بستن اتصال جمینی در صورت خطای کلاینت
|
142 |
if (geminiWs && geminiWs.readyState !== WebSocket.CLOSED && geminiWs.readyState !== WebSocket.CLOSING) {
|
143 |
console.log('[WebSocket Event] Closing associated Gemini connection due to client error.');
|
144 |
geminiWs.close();
|
145 |
}
|
146 |
geminiWs = null;
|
147 |
-
// خود ws معمولا به طور خودکار بسته میشود، اما برای اطمینان:
|
148 |
if (ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
|
149 |
ws.close(1011, 'Client error');
|
150 |
}
|
@@ -152,14 +133,12 @@ wss.on('connection', (ws, req) => {
|
|
152 |
|
153 |
});
|
154 |
|
155 |
-
// سرو کردن فایلهای استاتیک React (مسیر build)
|
156 |
app.get('*', (req, res) => {
|
157 |
-
res.sendFile(path.resolve(__dirname, '../build', 'index.html'));
|
158 |
});
|
159 |
|
160 |
-
// اجرای سرور
|
161 |
const PORT = process.env.PORT || 3001;
|
162 |
server.listen(PORT, () => {
|
163 |
console.log(`Server listening on port ${PORT}`);
|
164 |
-
console.log(`Allowed WebSocket Origin: ${ALLOWED_WEBSOCKET_ORIGIN}`);
|
165 |
});
|
|
|
2 |
const path = require('node:path');
|
3 |
const { WebSocketServer, WebSocket } = require('ws');
|
4 |
const http = require('node:http');
|
5 |
+
require('dotenv').config();
|
6 |
|
7 |
const app = express();
|
8 |
const server = http.createServer(app);
|
9 |
|
|
|
10 |
const ALLOWED_WEBSOCKET_ORIGIN = "https://aisada.ir"; // یا "http://aisada.ir"
|
|
|
11 |
|
|
|
12 |
const wss = new WebSocketServer({
|
13 |
+
server,
|
14 |
verifyClient: (info, cb) => {
|
|
|
15 |
const origin = info.origin;
|
16 |
+
// --- 👇 لاگ Verify را نگه میداریم چون مهم است 👇 ---
|
17 |
+
console.log(`[WebSocket Verify] Verifying origin: ${origin}`);
|
18 |
if (origin === ALLOWED_WEBSOCKET_ORIGIN) {
|
19 |
console.log(`[WebSocket Verify] Origin allowed: ${origin}`);
|
20 |
+
cb(true);
|
21 |
} else {
|
|
|
22 |
console.warn(`[WebSocket Verify] Forbidden origin: ${origin}. Rejecting connection.`);
|
23 |
+
cb(false, 403, 'Forbidden Origin');
|
|
|
24 |
}
|
25 |
}
|
26 |
});
|
|
|
27 |
|
|
|
|
|
28 |
app.use(express.static(path.join(__dirname, '../build')));
|
29 |
|
|
|
30 |
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
|
|
31 |
if (!GEMINI_API_KEY) {
|
32 |
console.error('FATAL ERROR: GEMINI_API_KEY environment variable is not set!');
|
33 |
+
process.exit(1);
|
34 |
}
|
35 |
|
|
|
36 |
const createGeminiWebSocket = (clientWs) => {
|
37 |
+
const geminiWsUrl = `wss://generativelanguage.googleapis.com/v1beta/models/gemini-pro:streamGenerateContent?key=${GEMINI_API_KEY}&alt=sse`;
|
38 |
+
console.log(`[Gemini Connect] Attempting connection to Gemini...`);
|
39 |
+
const geminiWs = new WebSocket(geminiWsUrl);
|
|
|
40 |
|
41 |
geminiWs.on('open', () => {
|
42 |
console.log('[Gemini Connect] Connected to Gemini API');
|
43 |
if (geminiWs.pendingSetup) {
|
44 |
+
// لاگ pendingSetup ممکن است حجیم باشد، خلاصهتر میکنیم
|
45 |
+
console.log('[Gemini Connect] Sending pending setup...');
|
46 |
geminiWs.send(JSON.stringify(geminiWs.pendingSetup));
|
47 |
geminiWs.pendingSetup = null;
|
48 |
}
|
|
|
50 |
|
51 |
geminiWs.on('message', (data) => {
|
52 |
try {
|
53 |
+
// --- 👇 لاگ دریافت از Gemini حذف شد 👇 ---
|
54 |
+
// console.log('[Gemini Receive] Received from Gemini');
|
55 |
if (clientWs.readyState === WebSocket.OPEN) {
|
|
|
56 |
if (Buffer.isBuffer(data)) {
|
57 |
clientWs.send(data, { binary: true });
|
58 |
} else {
|
|
|
59 |
const blob = Buffer.from(data.toString());
|
60 |
clientWs.send(blob, { binary: true });
|
61 |
}
|
|
|
71 |
return geminiWs;
|
72 |
};
|
73 |
|
|
|
74 |
wss.on('connection', (ws, req) => {
|
|
|
75 |
const origin = req.headers.origin || 'N/A';
|
76 |
console.log(`[WebSocket Event] Client connected (Origin: ${origin})`);
|
77 |
let geminiWs = null;
|
|
|
80 |
try {
|
81 |
const messageString = message.toString();
|
82 |
const data = JSON.parse(messageString);
|
83 |
+
// --- 👇 لاگ دریافت از Client حذف شد 👇 ---
|
84 |
+
// console.log('[Client Receive] Received from client');
|
85 |
|
86 |
if (data.setup) {
|
87 |
+
// لاگ setup را نگه میداریم چون مهم است
|
88 |
console.log('[Client Receive] Initializing Gemini connection...');
|
89 |
geminiWs = createGeminiWebSocket(ws);
|
|
|
90 |
if (geminiWs.readyState !== WebSocket.OPEN) {
|
91 |
geminiWs.pendingSetup = data;
|
92 |
} else {
|
|
|
95 |
return;
|
96 |
}
|
97 |
|
|
|
98 |
if (geminiWs && geminiWs.readyState === WebSocket.OPEN) {
|
99 |
geminiWs.send(JSON.stringify(data));
|
100 |
} else if (geminiWs) {
|
101 |
+
// لاگ انتظار را نگه میداریم
|
102 |
console.log('[Client Receive] Gemini connection not ready, waiting...');
|
|
|
103 |
} else {
|
104 |
console.error('[Client Receive] No Gemini connection established. Closing client connection.');
|
105 |
+
ws.close(1011, 'Backend connection not ready');
|
106 |
}
|
107 |
} catch (error) {
|
108 |
+
console.error('[Client Receive] Error processing message:', error, message.toString().substring(0, 100) + '...');
|
|
|
109 |
ws.close(1008, 'Message processing error');
|
110 |
}
|
111 |
});
|
|
|
116 |
console.log('[WebSocket Event] Closing associated Gemini connection.');
|
117 |
geminiWs.close();
|
118 |
}
|
119 |
+
geminiWs = null;
|
120 |
});
|
121 |
|
122 |
ws.on('error', (error) => {
|
123 |
console.error('[WebSocket Event] Client WebSocket error:', error);
|
|
|
124 |
if (geminiWs && geminiWs.readyState !== WebSocket.CLOSED && geminiWs.readyState !== WebSocket.CLOSING) {
|
125 |
console.log('[WebSocket Event] Closing associated Gemini connection due to client error.');
|
126 |
geminiWs.close();
|
127 |
}
|
128 |
geminiWs = null;
|
|
|
129 |
if (ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
|
130 |
ws.close(1011, 'Client error');
|
131 |
}
|
|
|
133 |
|
134 |
});
|
135 |
|
|
|
136 |
app.get('*', (req, res) => {
|
137 |
+
res.sendFile(path.resolve(__dirname, '../build', 'index.html'));
|
138 |
});
|
139 |
|
|
|
140 |
const PORT = process.env.PORT || 3001;
|
141 |
server.listen(PORT, () => {
|
142 |
console.log(`Server listening on port ${PORT}`);
|
143 |
+
console.log(`Allowed WebSocket Origin: ${ALLOWED_WEBSOCKET_ORIGIN}`);
|
144 |
});
|