opex792 commited on
Commit
38b088f
·
verified ·
1 Parent(s): 204264d

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +77 -46
server.js CHANGED
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
5
  import fileUpload from 'express-fileupload';
6
  import path from 'path';
7
  import { fileURLToPath } from 'url';
8
- import fs from 'fs'; // Import the fs module
9
 
10
  const __filename = fileURLToPath(import.meta.url);
11
  const __dirname = path.dirname(__filename);
@@ -18,10 +18,15 @@ if (!ADMIN_PASSWORD) {
18
  process.exit(1);
19
  }
20
 
21
- // Define the directory for the database
22
- const DB_DIR = path.join(__dirname, 'data');
23
- // Define the full path to the database file
24
- const DB_PATH = path.join(DB_DIR, 'database.sqlite');
 
 
 
 
 
25
  const KEY_DEACTIVATION_THRESHOLD = 5;
26
  const KEY_COOLDOWN_SECONDS = 60;
27
 
@@ -30,11 +35,6 @@ app.use(express.json({ limit: '10mb' }));
30
  app.use(express.urlencoded({ extended: true }));
31
  app.use(fileUpload());
32
 
33
- // Ensure the database directory exists before initializing Sequelize
34
- if (!fs.existsSync(DB_DIR)) {
35
- fs.mkdirSync(DB_DIR, { recursive: true });
36
- }
37
-
38
  const sequelize = new Sequelize({
39
  dialect: 'sqlite',
40
  storage: DB_PATH,
@@ -81,21 +81,16 @@ GeminiKey.hasMany(RequestLog, { foreignKey: 'gemini_key_id' });
81
  RequestLog.belongsTo(GeminiKey, { foreignKey: 'gemini_key_id' });
82
 
83
  const GEMINI_DEFAULT_LIMITS = {
84
- 'gemini-2.5-flash-preview-05-20': { rpm: 10, rpd: 500, tpm: 250000, tpd: 500000 },
85
- 'gemini-2.5-flash-preview-tts': { rpm: 3, rpd: 15, tpm: 10000, tpd: 20000 },
86
- 'gemini-2.5-pro-experimental-03-25': { rpm: 5, rpd: 25, tpm: 250000, tpd: 1000000 },
87
- 'gemini-2.0-flash': { rpm: 15, rpd: 1500, tpm: 1000000, tpd: 2000000 },
88
- 'gemini-2.0-flash-experimental': { rpm: 10, rpd: 1000, tpm: 250000, tpd: 500000 },
89
- 'gemini-2.0-flash-lite': { rpm: 30, rpd: 1500, tpm: 1000000, tpd: 2000000 },
90
- 'gemini-1.5-flash': { rpm: 15, rpd: 500, tpm: 250000, tpd: 500000 },
91
- 'gemini-1.5-flash-8b': { rpm: 15, rpd: 500, tpm: 250000, tpd: 500000 },
92
- 'gemma-3': { rpm: 30, rpd: 14400, tpm: 15000, tpd: 360000 },
93
- 'gemma-3n': { rpm: 30, rpd: 14400, tpm: 15000, tpd: 360000 },
94
  'default': { rpm: 15, rpd: 1500, tpm: 1000000, tpd: 2000000 }
95
  };
96
 
97
  function getModelLimits(model) {
98
- return GEMINI_DEFAULT_LIMITS[model] || GEMINI_DEFAULT_LIMITS['default'];
 
 
99
  }
100
 
101
  function safeParseInt(value, defaultValue = 0) {
@@ -235,7 +230,7 @@ const adminAuth = (req, res, next) => {
235
  next();
236
  };
237
 
238
- app.post('/v1/chat/completions', authenticateServiceKey, async (req, res) => {
239
  if (!req.body || typeof req.body !== 'object') {
240
  return res.status(400).json({ error: 'Request body must be a valid JSON object.' });
241
  }
@@ -259,16 +254,52 @@ app.post('/v1/chat/completions', authenticateServiceKey, async (req, res) => {
259
  attemptedKeys.add(geminiKey.id);
260
 
261
  try {
262
- const geminiApiUrl = `https://generativelanguage.googleapis.com/v1beta/openai/chat/completions`;
263
- const response = await axios.post(geminiApiUrl, req.body, {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  headers: {
265
  'Content-Type': 'application/json',
266
- 'Authorization': `Bearer ${geminiKey.key}`
267
  },
268
  timeout: 45000
269
  });
270
 
271
- const responseData = response.data;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  geminiKey.error_count = 0;
273
  geminiKey.cooldown_until = null;
274
  geminiKey.last_used_at = new Date();
@@ -279,16 +310,16 @@ app.post('/v1/chat/completions', authenticateServiceKey, async (req, res) => {
279
  service_key_id: req.serviceKey.key,
280
  model_requested: model,
281
  request_body: JSON.stringify(req.body),
282
- response_body: JSON.stringify(responseData),
283
  status_code: response.status,
284
  is_success: true,
285
  processing_time_ms: Date.now() - startTime,
286
- prompt_tokens: responseData.usage?.prompt_tokens || 0,
287
- completion_tokens: responseData.usage?.completion_tokens || 0,
288
- total_tokens: responseData.usage?.total_tokens || 0,
289
  });
290
 
291
- return res.status(response.status).json(responseData);
292
 
293
  } catch (error) {
294
  const isTimeout = error.code === 'ECONNABORTED';
@@ -453,19 +484,21 @@ app.get('/admin/download/:table', adminAuth, async (req, res) => {
453
 
454
  app.get('/user/dashboard', authenticateServiceKey, async (req, res) => {
455
  const oneDayAgo = new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
456
- const usage = await RequestLog.findOne({
457
  attributes: [
458
  [sequelize.fn('COUNT', sequelize.col('id')), 'requests'],
459
  [sequelize.fn('SUM', sequelize.col('total_tokens')), 'tokens']
460
  ],
461
  where: {
462
  service_key_id: req.serviceKey.key,
463
- createdAt: { [Op.gte]: oneDayAgo }
464
- }
 
 
465
  });
466
 
467
- const requestsLastDay = usage?.get('requests') || 0;
468
- const tokensLastDay = usage?.get('tokens') || 0;
469
 
470
  res.send(`
471
  <html>
@@ -485,13 +518,12 @@ app.get('/user/dashboard', authenticateServiceKey, async (req, res) => {
485
  <li><b>Tokens per day:</b> ${req.serviceKey.tpd_limit}</li>
486
  </ul>
487
  <hr>
488
- <h3>API Usage</h3>
489
- <p>Use this service key in your requests:</p>
490
- <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto;">curl "http://localhost:7860/v1/chat/completions" \\
491
  -H "Content-Type: application/json" \\
492
  -H "Authorization: Bearer ${req.serviceKey.key}" \\
493
  -d '{
494
- "model": "gemini-2.0-flash",
495
  "messages": [
496
  {"role": "user", "content": "Hello!"}
497
  ]
@@ -515,7 +547,8 @@ app.get('/user/stats', authenticateServiceKey, async (req, res) => {
515
  ],
516
  where: {
517
  service_key_id: req.serviceKey.key,
518
- createdAt: { [Op.gte]: oneDayAgo }
 
519
  },
520
  replacements: { oneMinuteAgo, oneDayAgo }
521
  });
@@ -546,11 +579,11 @@ app.get('/user/stats', authenticateServiceKey, async (req, res) => {
546
  });
547
  });
548
 
549
- app.get('/v1/models', authenticateServiceKey, async (req, res) => {
550
  const models = Object.keys(GEMINI_DEFAULT_LIMITS).filter(model => model !== 'default').map(model => ({
551
  id: model,
552
  object: "model",
553
- created: 1677649963,
554
  owned_by: "google"
555
  }));
556
 
@@ -562,10 +595,8 @@ app.get('/v1/models', authenticateServiceKey, async (req, res) => {
562
 
563
  sequelize.sync({ force: false }).then(() => {
564
  console.log('Database initialized successfully.');
565
- app.listen(PORT, () => {
566
  console.log(`Gemini Proxy Rotator running on port ${PORT}`);
567
- console.log(`Admin panel: http://localhost:${PORT}/admin`);
568
- console.log(`Set ADMIN_PASSWORD environment variable for admin access`);
569
  });
570
  }).catch(error => {
571
  console.error('Failed to initialize database:', error);
 
5
  import fileUpload from 'express-fileupload';
6
  import path from 'path';
7
  import { fileURLToPath } from 'url';
8
+ import fs from 'fs';
9
 
10
  const __filename = fileURLToPath(import.meta.url);
11
  const __dirname = path.dirname(__filename);
 
18
  process.exit(1);
19
  }
20
 
21
+ // --- ИЗМЕНЕНИЕ ---
22
+ // Путь к базе данных теперь указывает на папку 'data'
23
+ const dataDir = path.join(__dirname, 'data');
24
+ if (!fs.existsSync(dataDir)) {
25
+ fs.mkdirSync(dataDir, { recursive: true });
26
+ }
27
+ const DB_PATH = path.join(dataDir, 'database.sqlite');
28
+ // --- КОНЕЦ ИЗМЕНЕНИЯ ---
29
+
30
  const KEY_DEACTIVATION_THRESHOLD = 5;
31
  const KEY_COOLDOWN_SECONDS = 60;
32
 
 
35
  app.use(express.urlencoded({ extended: true }));
36
  app.use(fileUpload());
37
 
 
 
 
 
 
38
  const sequelize = new Sequelize({
39
  dialect: 'sqlite',
40
  storage: DB_PATH,
 
81
  RequestLog.belongsTo(GeminiKey, { foreignKey: 'gemini_key_id' });
82
 
83
  const GEMINI_DEFAULT_LIMITS = {
84
+ 'gemini-1.5-flash-latest': { rpm: 15, rpd: 500, tpm: 250000, tpd: 500000 },
85
+ 'gemini-1.5-pro-latest': { rpm: 5, rpd: 25, tpm: 250000, tpd: 1000000 },
86
+ 'gemini-1.0-pro': { rpm: 15, rpd: 1500, tpm: 1000000, tpd: 2000000 },
 
 
 
 
 
 
 
87
  'default': { rpm: 15, rpd: 1500, tpm: 1000000, tpd: 2000000 }
88
  };
89
 
90
  function getModelLimits(model) {
91
+ // Ищем точное совпадение или совпадение по началу строки
92
+ const foundModel = Object.keys(GEMINI_DEFAULT_LIMITS).find(k => model.startsWith(k));
93
+ return GEMINI_DEFAULT_LIMITS[foundModel] || GEMINI_DEFAULT_LIMITS['default'];
94
  }
95
 
96
  function safeParseInt(value, defaultValue = 0) {
 
230
  next();
231
  };
232
 
233
+ app.post(['/v1/chat/completions', '/v1beta/chat/completions'], authenticateServiceKey, async (req, res) => {
234
  if (!req.body || typeof req.body !== 'object') {
235
  return res.status(400).json({ error: 'Request body must be a valid JSON object.' });
236
  }
 
254
  attemptedKeys.add(geminiKey.id);
255
 
256
  try {
257
+ const geminiApiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`;
258
+
259
+ // OpenAI to Gemini Translation
260
+ const geminiRequestBody = {
261
+ contents: req.body.messages.map(msg => ({
262
+ role: msg.role === 'assistant' ? 'model' : msg.role,
263
+ parts: [{ text: msg.content }]
264
+ })),
265
+ generationConfig: {
266
+ temperature: req.body.temperature,
267
+ topP: req.body.top_p,
268
+ maxOutputTokens: req.body.max_tokens,
269
+ }
270
+ };
271
+
272
+ const response = await axios.post(geminiApiUrl, geminiRequestBody, {
273
  headers: {
274
  'Content-Type': 'application/json',
275
+ 'x-goog-api-key': geminiKey.key
276
  },
277
  timeout: 45000
278
  });
279
 
280
+ const geminiResponse = response.data;
281
+
282
+ // Gemini to OpenAI Translation
283
+ const openAIResponse = {
284
+ id: `chatcmpl-${uuidv4()}`,
285
+ object: 'chat.completion',
286
+ created: Math.floor(Date.now() / 1000),
287
+ model: model,
288
+ choices: geminiResponse.candidates.map((candidate, index) => ({
289
+ index: index,
290
+ message: {
291
+ role: 'assistant',
292
+ content: candidate.content.parts[0].text
293
+ },
294
+ finish_reason: candidate.finishReason.toLowerCase()
295
+ })),
296
+ usage: {
297
+ prompt_tokens: geminiResponse.usageMetadata?.promptTokenCount || 0,
298
+ completion_tokens: geminiResponse.usageMetadata?.candidatesTokenCount || 0,
299
+ total_tokens: geminiResponse.usageMetadata?.totalTokenCount || 0
300
+ }
301
+ };
302
+
303
  geminiKey.error_count = 0;
304
  geminiKey.cooldown_until = null;
305
  geminiKey.last_used_at = new Date();
 
310
  service_key_id: req.serviceKey.key,
311
  model_requested: model,
312
  request_body: JSON.stringify(req.body),
313
+ response_body: JSON.stringify(openAIResponse),
314
  status_code: response.status,
315
  is_success: true,
316
  processing_time_ms: Date.now() - startTime,
317
+ prompt_tokens: openAIResponse.usage.prompt_tokens,
318
+ completion_tokens: openAIResponse.usage.completion_tokens,
319
+ total_tokens: openAIResponse.usage.total_tokens,
320
  });
321
 
322
+ return res.status(response.status).json(openAIResponse);
323
 
324
  } catch (error) {
325
  const isTimeout = error.code === 'ECONNABORTED';
 
484
 
485
  app.get('/user/dashboard', authenticateServiceKey, async (req, res) => {
486
  const oneDayAgo = new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
487
+ const usageResult = await RequestLog.findOne({
488
  attributes: [
489
  [sequelize.fn('COUNT', sequelize.col('id')), 'requests'],
490
  [sequelize.fn('SUM', sequelize.col('total_tokens')), 'tokens']
491
  ],
492
  where: {
493
  service_key_id: req.serviceKey.key,
494
+ createdAt: { [Op.gte]: oneDayAgo },
495
+ is_success: true
496
+ },
497
+ raw: true
498
  });
499
 
500
+ const requestsLastDay = safeParseInt(usageResult.requests);
501
+ const tokensLastDay = safeParseInt(usageResult.tokens);
502
 
503
  res.send(`
504
  <html>
 
518
  <li><b>Tokens per day:</b> ${req.serviceKey.tpd_limit}</li>
519
  </ul>
520
  <hr>
521
+ <h3>API Usage Example</h3>
522
+ <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto;">curl "YOUR_SPACE_URL/v1/chat/completions" \\
 
523
  -H "Content-Type: application/json" \\
524
  -H "Authorization: Bearer ${req.serviceKey.key}" \\
525
  -d '{
526
+ "model": "gemini-1.5-flash-latest",
527
  "messages": [
528
  {"role": "user", "content": "Hello!"}
529
  ]
 
547
  ],
548
  where: {
549
  service_key_id: req.serviceKey.key,
550
+ createdAt: { [Op.gte]: oneDayAgo },
551
+ is_success: true,
552
  },
553
  replacements: { oneMinuteAgo, oneDayAgo }
554
  });
 
579
  });
580
  });
581
 
582
+ app.get(['/v1/models', '/v1beta/models'], authenticateServiceKey, async (req, res) => {
583
  const models = Object.keys(GEMINI_DEFAULT_LIMITS).filter(model => model !== 'default').map(model => ({
584
  id: model,
585
  object: "model",
586
+ created: Math.floor(Date.now() / 1000),
587
  owned_by: "google"
588
  }));
589
 
 
595
 
596
  sequelize.sync({ force: false }).then(() => {
597
  console.log('Database initialized successfully.');
598
+ app.listen(PORT, '0.0.0.0', () => {
599
  console.log(`Gemini Proxy Rotator running on port ${PORT}`);
 
 
600
  });
601
  }).catch(error => {
602
  console.error('Failed to initialize database:', error);