qwen-api-proxy 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/bin/qwen-api-proxy.js +64 -53
- package/index.js +106 -20
- package/package.json +8 -2
- package/src/api/chat.js +71 -71
- package/src/api/chatHistory.js +19 -19
- package/src/api/fileUpload.js +23 -23
- package/src/api/imageGeneration.js +23 -23
- package/src/api/modelMapping.js +145 -153
- package/src/api/routes.js +148 -148
- package/src/api/tokenManager.js +93 -93
- package/src/browser/auth.js +13 -13
- package/src/browser/browser.js +9 -9
- package/src/browser/session.js +3 -3
- package/src/config.js +4 -4
- package/src/utils/accountSetup.js +14 -14
- package/src/utils/botSettings.js +14 -14
- package/src/utils/permissionChecker.js +157 -157
- package/src/utils/prompt.js +1 -1
- package/src/utils/proxy.js +11 -11
- package/src/utils/telegramBot.js +656 -654
- package/src/utils/telegramNotifier.js +7 -7
package/README.md
CHANGED
|
@@ -141,6 +141,8 @@ curl http://localhost:3264/api/chat/completions \
|
|
|
141
141
|
|
|
142
142
|
## Быстрый старт
|
|
143
143
|
|
|
144
|
+
> 🆕 **Новые пользователи?** Начните с [QUICK_START.md](QUICK_START.md) - пошаговое руководство с устранением常见ных ошибок
|
|
145
|
+
|
|
144
146
|
### 🌍 Кросс-платформенная поддержка
|
|
145
147
|
|
|
146
148
|
Этот проект полностью поддерживает работу на:
|
|
@@ -309,7 +311,7 @@ docker run -d \
|
|
|
309
311
|
-v $(pwd)/logs:/app/logs \
|
|
310
312
|
-v $(pwd)/uploads:/app/uploads \
|
|
311
313
|
-v $(pwd)/temp:/app/temp \
|
|
312
|
-
endykaufman/qwen-api-proxy:1.0.
|
|
314
|
+
endykaufman/qwen-api-proxy:1.0.13
|
|
313
315
|
|
|
314
316
|
# 3. Смотрим логи
|
|
315
317
|
docker logs -f qwen-proxy
|
|
@@ -318,7 +320,7 @@ docker logs -f qwen-proxy
|
|
|
318
320
|
### Доступные теги
|
|
319
321
|
|
|
320
322
|
- `latest` - последняя стабильная версия
|
|
321
|
-
- `1.0.
|
|
323
|
+
- `1.0.13` - текущая версия
|
|
322
324
|
- `1.0.x` - предыдущие версии
|
|
323
325
|
|
|
324
326
|
> **💡 Важно:** Перед первым запуском добавьте аккаунт через `npm run auth` или загрузите сессию через Telegram бота.
|
|
@@ -339,7 +341,7 @@ docker logs -f qwen-proxy
|
|
|
339
341
|
```yaml
|
|
340
342
|
services:
|
|
341
343
|
qwen-proxy:
|
|
342
|
-
image: endykaufman/qwen-api-proxy:1.0.
|
|
344
|
+
image: endykaufman/qwen-api-proxy:1.0.13
|
|
343
345
|
container_name: qwen-proxy
|
|
344
346
|
env_file:
|
|
345
347
|
- .env
|
|
@@ -394,7 +396,7 @@ docker run -d \
|
|
|
394
396
|
-v $(pwd)/logs:/app/logs \
|
|
395
397
|
-v $(pwd)/uploads:/app/uploads \
|
|
396
398
|
-v $(pwd)/temp:/app/temp \
|
|
397
|
-
endykaufman/qwen-api-proxy:1.0.
|
|
399
|
+
endykaufman/qwen-api-proxy:1.0.13
|
|
398
400
|
```
|
|
399
401
|
|
|
400
402
|
Файл `docker-compose.yml`:
|
|
@@ -403,7 +405,7 @@ docker run -d \
|
|
|
403
405
|
services:
|
|
404
406
|
qwen-proxy:
|
|
405
407
|
build: .
|
|
406
|
-
image: endykaufman/qwen-api-proxy:1.0.
|
|
408
|
+
image: endykaufman/qwen-api-proxy:1.0.13
|
|
407
409
|
container_name: qwen-proxy
|
|
408
410
|
env_file:
|
|
409
411
|
- .env # Автоматическая загрузка переменных
|
package/bin/qwen-api-proxy.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* qwen-api-proxy - CLI entry point for global installation
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* This script:
|
|
7
7
|
* 1. Sets up the working directory structure in the current location
|
|
8
8
|
* 2. Creates necessary directories (session, logs, uploads, temp)
|
|
@@ -21,7 +21,7 @@ const WORKING_DIR = process.cwd();
|
|
|
21
21
|
|
|
22
22
|
// Parse command-line arguments early to get custom directory
|
|
23
23
|
const args = process.argv.slice(2);
|
|
24
|
-
const dirFlag = args.find(arg => arg.startsWith('--dir='));
|
|
24
|
+
const dirFlag = args.find((arg) => arg.startsWith('--dir='));
|
|
25
25
|
const customDir = dirFlag ? dirFlag.split('=')[1] : null;
|
|
26
26
|
|
|
27
27
|
// Use custom directory if specified
|
|
@@ -47,8 +47,8 @@ function setupWorkingDirectory(targetDir = EFFECTIVE_DIR) {
|
|
|
47
47
|
];
|
|
48
48
|
|
|
49
49
|
console.log('📁 Setting up working directory...');
|
|
50
|
-
|
|
51
|
-
dirs.forEach(dir => {
|
|
50
|
+
|
|
51
|
+
dirs.forEach((dir) => {
|
|
52
52
|
const dirPath = path.join(targetDir, dir);
|
|
53
53
|
if (!fs.existsSync(dirPath)) {
|
|
54
54
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
@@ -71,16 +71,16 @@ function setupWorkingDirectory(targetDir = EFFECTIVE_DIR) {
|
|
|
71
71
|
function setupEnvFile(targetDir = EFFECTIVE_DIR) {
|
|
72
72
|
const envPath = path.join(targetDir, '.env');
|
|
73
73
|
const envExamplePath = path.join(targetDir, '.env.example');
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
if (!fs.existsSync(envPath)) {
|
|
76
76
|
// Copy .env.example from package if it exists in working dir, otherwise create basic
|
|
77
77
|
const packageEnvExample = path.join(PACKAGE_ROOT, '.env.example');
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
if (fs.existsSync(packageEnvExample)) {
|
|
80
80
|
fs.copyFileSync(packageEnvExample, envExamplePath);
|
|
81
81
|
console.log(' ✓ Created .env.example');
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
// Create empty .env if not exists
|
|
85
85
|
if (!fs.existsSync(envPath)) {
|
|
86
86
|
fs.writeFileSync(envPath, '# Qwen API Proxy Configuration\n# See .env.example for available options\n\n');
|
|
@@ -105,12 +105,12 @@ function setupGitignore(targetDir = EFFECTIVE_DIR) {
|
|
|
105
105
|
'session_backup_*'
|
|
106
106
|
];
|
|
107
107
|
const missingEntries = [];
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
// Check which entries are missing from .gitignore
|
|
110
110
|
if (fs.existsSync(gitignorePath)) {
|
|
111
111
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
112
|
-
const lines = content.split('\n').map(line => line.trim());
|
|
113
|
-
requiredEntries.forEach(entry => {
|
|
112
|
+
const lines = content.split('\n').map((line) => line.trim());
|
|
113
|
+
requiredEntries.forEach((entry) => {
|
|
114
114
|
if (!lines.includes(entry)) {
|
|
115
115
|
missingEntries.push(entry);
|
|
116
116
|
}
|
|
@@ -146,7 +146,7 @@ npm-debug.log*
|
|
|
146
146
|
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
147
147
|
console.log(' ✓ Created .gitignore');
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
return missingEntries;
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -155,7 +155,7 @@ npm-debug.log*
|
|
|
155
155
|
*/
|
|
156
156
|
function checkDependencies() {
|
|
157
157
|
const isWindows = process.platform === 'win32';
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
try {
|
|
160
160
|
// On Windows, check for zip.exe or 7z.exe
|
|
161
161
|
if (isWindows) {
|
|
@@ -195,25 +195,36 @@ function setup() {
|
|
|
195
195
|
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
196
196
|
console.log('║ Qwen API Proxy - Working Directory Setup ║');
|
|
197
197
|
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
console.log(`📍 Working directory: ${EFFECTIVE_DIR}\n`);
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
setupWorkingDirectory();
|
|
202
202
|
console.log();
|
|
203
203
|
setupEnvFile();
|
|
204
204
|
console.log();
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
const missingGitignoreDirs = setupGitignore();
|
|
207
207
|
console.log();
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
checkDependencies();
|
|
210
|
-
|
|
210
|
+
|
|
211
211
|
console.log('\n✅ Working directory setup complete!\n');
|
|
212
|
-
|
|
212
|
+
console.log('📝 Next steps for first-time users:');
|
|
213
|
+
console.log(' 1. Add an account: qwen-api-proxy (or npx qwen-api-proxy)');
|
|
214
|
+
console.log(' - Select "1 - Add new account"');
|
|
215
|
+
console.log(' - Browser will open for authentication');
|
|
216
|
+
console.log(' - Login to Qwen and press ENTER in console');
|
|
217
|
+
console.log(' 2. Create archive: qwen-api-proxy archive');
|
|
218
|
+
console.log(' 3. Start server: qwen-api-proxy');
|
|
219
|
+
console.log('\n💡 Alternative: Use Telegram bot to upload session files');
|
|
220
|
+
console.log(' - Configure TELEGRAM_BOT_TOKEN in .env file');
|
|
221
|
+
console.log(' - Send session archive to the bot');
|
|
222
|
+
console.log('\n');
|
|
223
|
+
|
|
213
224
|
// Show warnings for missing .gitignore entries
|
|
214
225
|
if (missingGitignoreDirs.length > 0) {
|
|
215
226
|
console.warn('⚠️ WARNING: The following entries are not in .gitignore:');
|
|
216
|
-
missingGitignoreDirs.forEach(entry => {
|
|
227
|
+
missingGitignoreDirs.forEach((entry) => {
|
|
217
228
|
console.warn(` - ${entry}`);
|
|
218
229
|
});
|
|
219
230
|
console.warn('\n Add them to .gitignore to prevent committing sensitive data!\n');
|
|
@@ -224,41 +235,41 @@ function setup() {
|
|
|
224
235
|
const needsSetup = args.includes('--setup') || !fs.existsSync(path.join(EFFECTIVE_DIR, 'session'));
|
|
225
236
|
|
|
226
237
|
// Parse command from arguments (already defined args above)
|
|
227
|
-
const command = args.find(arg => !arg.startsWith('-'));
|
|
238
|
+
const command = args.find((arg) => !arg.startsWith('-'));
|
|
228
239
|
|
|
229
240
|
// Handle 'init' command - manual setup only
|
|
230
241
|
if (command === 'init') {
|
|
231
242
|
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
232
243
|
console.log('║ Qwen API Proxy - Initialize Working Directory ║');
|
|
233
244
|
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
234
|
-
|
|
245
|
+
|
|
235
246
|
console.log(`📍 Working directory: ${EFFECTIVE_DIR}\n`);
|
|
236
|
-
|
|
247
|
+
|
|
237
248
|
setupWorkingDirectory(EFFECTIVE_DIR);
|
|
238
249
|
console.log();
|
|
239
250
|
setupEnvFile(EFFECTIVE_DIR);
|
|
240
251
|
console.log();
|
|
241
|
-
|
|
252
|
+
|
|
242
253
|
const missingGitignoreDirs = setupGitignore(EFFECTIVE_DIR);
|
|
243
254
|
console.log();
|
|
244
|
-
|
|
255
|
+
|
|
245
256
|
checkDependencies();
|
|
246
|
-
|
|
257
|
+
|
|
247
258
|
console.log('\n✅ Working directory initialized successfully!\n');
|
|
248
259
|
console.log('📝 Next steps:');
|
|
249
260
|
console.log(' 1. Edit .env file with your configuration');
|
|
250
261
|
console.log(' 2. Run: qwen-api-proxy');
|
|
251
262
|
console.log('\n');
|
|
252
|
-
|
|
263
|
+
|
|
253
264
|
// Show warnings for missing .gitignore entries
|
|
254
265
|
if (missingGitignoreDirs.length > 0) {
|
|
255
266
|
console.warn('⚠️ WARNING: The following entries are not in .gitignore:');
|
|
256
|
-
missingGitignoreDirs.forEach(entry => {
|
|
267
|
+
missingGitignoreDirs.forEach((entry) => {
|
|
257
268
|
console.warn(` - ${entry}`);
|
|
258
269
|
});
|
|
259
270
|
console.warn('\n Add them to .gitignore to prevent committing sensitive data!\n');
|
|
260
271
|
}
|
|
261
|
-
|
|
272
|
+
|
|
262
273
|
process.exit(0);
|
|
263
274
|
}
|
|
264
275
|
|
|
@@ -267,11 +278,11 @@ if (command === 'doctor') {
|
|
|
267
278
|
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
268
279
|
console.log('║ Qwen API Proxy - System Health Check ║');
|
|
269
280
|
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
270
|
-
|
|
281
|
+
|
|
271
282
|
const issues = [];
|
|
272
283
|
const warnings = [];
|
|
273
284
|
const ok = [];
|
|
274
|
-
|
|
285
|
+
|
|
275
286
|
// Check Node.js version
|
|
276
287
|
const nodeVersion = process.version;
|
|
277
288
|
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
@@ -280,7 +291,7 @@ if (command === 'doctor') {
|
|
|
280
291
|
} else {
|
|
281
292
|
issues.push(`Node.js ${nodeVersion} (✗ requires >= 18)`);
|
|
282
293
|
}
|
|
283
|
-
|
|
294
|
+
|
|
284
295
|
// Check zip command (cross-platform)
|
|
285
296
|
const isWindows = process.platform === 'win32';
|
|
286
297
|
let zipAvailable = false;
|
|
@@ -304,16 +315,16 @@ if (command === 'doctor') {
|
|
|
304
315
|
} catch (error) {
|
|
305
316
|
// zip not available
|
|
306
317
|
}
|
|
307
|
-
|
|
318
|
+
|
|
308
319
|
if (zipAvailable) {
|
|
309
320
|
ok.push('zip/7z command available');
|
|
310
321
|
} else {
|
|
311
322
|
warnings.push('zip/7z command not found (required for archive command)');
|
|
312
323
|
}
|
|
313
|
-
|
|
324
|
+
|
|
314
325
|
// Check working directory structure
|
|
315
326
|
const requiredDirs = ['session', 'session/accounts', 'session/history', 'logs', 'uploads', 'temp'];
|
|
316
|
-
requiredDirs.forEach(dir => {
|
|
327
|
+
requiredDirs.forEach((dir) => {
|
|
317
328
|
const dirPath = path.join(EFFECTIVE_DIR, dir);
|
|
318
329
|
if (fs.existsSync(dirPath)) {
|
|
319
330
|
ok.push(`${dir}/ directory exists`);
|
|
@@ -321,12 +332,12 @@ if (command === 'doctor') {
|
|
|
321
332
|
warnings.push(`${dir}/ directory missing`);
|
|
322
333
|
}
|
|
323
334
|
});
|
|
324
|
-
|
|
335
|
+
|
|
325
336
|
// Check .env file
|
|
326
337
|
const envPath = path.join(EFFECTIVE_DIR, '.env');
|
|
327
338
|
if (fs.existsSync(envPath)) {
|
|
328
339
|
ok.push('.env file exists');
|
|
329
|
-
|
|
340
|
+
|
|
330
341
|
// Check for common configurations
|
|
331
342
|
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
332
343
|
if (envContent.includes('TELEGRAM_BOT_TOKEN=') && !envContent.includes('TELEGRAM_BOT_TOKEN=your_bot_token_here')) {
|
|
@@ -337,7 +348,7 @@ if (command === 'doctor') {
|
|
|
337
348
|
} else {
|
|
338
349
|
warnings.push('.env file not found (run "qwen-api-proxy init" to create)');
|
|
339
350
|
}
|
|
340
|
-
|
|
351
|
+
|
|
341
352
|
// Check .gitignore file
|
|
342
353
|
const gitignorePath = path.join(EFFECTIVE_DIR, '.gitignore');
|
|
343
354
|
const requiredGitignoreEntries = [
|
|
@@ -349,16 +360,16 @@ if (command === 'doctor') {
|
|
|
349
360
|
'session_backup_*'
|
|
350
361
|
];
|
|
351
362
|
const missingGitignoreEntries = [];
|
|
352
|
-
|
|
363
|
+
|
|
353
364
|
if (fs.existsSync(gitignorePath)) {
|
|
354
365
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
355
|
-
const lines = content.split('\n').map(line => line.trim());
|
|
356
|
-
requiredGitignoreEntries.forEach(entry => {
|
|
366
|
+
const lines = content.split('\n').map((line) => line.trim());
|
|
367
|
+
requiredGitignoreEntries.forEach((entry) => {
|
|
357
368
|
if (!lines.includes(entry)) {
|
|
358
369
|
missingGitignoreEntries.push(entry);
|
|
359
370
|
}
|
|
360
371
|
});
|
|
361
|
-
|
|
372
|
+
|
|
362
373
|
if (missingGitignoreEntries.length === 0) {
|
|
363
374
|
ok.push('.gitignore properly configured');
|
|
364
375
|
} else {
|
|
@@ -367,11 +378,11 @@ if (command === 'doctor') {
|
|
|
367
378
|
} else {
|
|
368
379
|
warnings.push('.gitignore not found (run "qwen-api-proxy init" to create)');
|
|
369
380
|
}
|
|
370
|
-
|
|
381
|
+
|
|
371
382
|
// Check permissions
|
|
372
383
|
const testDirs = ['session', 'logs', 'uploads'];
|
|
373
384
|
let permissionsOk = true;
|
|
374
|
-
testDirs.forEach(dir => {
|
|
385
|
+
testDirs.forEach((dir) => {
|
|
375
386
|
const dirPath = path.join(EFFECTIVE_DIR, dir);
|
|
376
387
|
if (fs.existsSync(dirPath)) {
|
|
377
388
|
try {
|
|
@@ -387,7 +398,7 @@ if (command === 'doctor') {
|
|
|
387
398
|
if (permissionsOk) {
|
|
388
399
|
ok.push('Directory permissions OK');
|
|
389
400
|
}
|
|
390
|
-
|
|
401
|
+
|
|
391
402
|
// Check session data
|
|
392
403
|
const tokensPath = path.join(EFFECTIVE_DIR, 'session', 'tokens.json');
|
|
393
404
|
if (fs.existsSync(tokensPath)) {
|
|
@@ -404,7 +415,7 @@ if (command === 'doctor') {
|
|
|
404
415
|
} else {
|
|
405
416
|
warnings.push('No accounts configured yet');
|
|
406
417
|
}
|
|
407
|
-
|
|
418
|
+
|
|
408
419
|
// Check disk space (cross-platform)
|
|
409
420
|
try {
|
|
410
421
|
if (isWindows) {
|
|
@@ -456,21 +467,21 @@ if (command === 'doctor') {
|
|
|
456
467
|
} catch (error) {
|
|
457
468
|
// df/wmic not available, skip
|
|
458
469
|
}
|
|
459
|
-
|
|
470
|
+
|
|
460
471
|
// Print results
|
|
461
472
|
console.log('✅ OK:');
|
|
462
|
-
ok.forEach(msg => console.log(` ✓ ${msg}`));
|
|
463
|
-
|
|
473
|
+
ok.forEach((msg) => console.log(` ✓ ${msg}`));
|
|
474
|
+
|
|
464
475
|
if (warnings.length > 0) {
|
|
465
476
|
console.log('\n⚠️ Warnings:');
|
|
466
|
-
warnings.forEach(msg => console.log(` ⚠ ${msg}`));
|
|
477
|
+
warnings.forEach((msg) => console.log(` ⚠ ${msg}`));
|
|
467
478
|
}
|
|
468
|
-
|
|
479
|
+
|
|
469
480
|
if (issues.length > 0) {
|
|
470
481
|
console.log('\n❌ Issues:');
|
|
471
|
-
issues.forEach(msg => console.log(` ✗ ${msg}`));
|
|
482
|
+
issues.forEach((msg) => console.log(` ✗ ${msg}`));
|
|
472
483
|
}
|
|
473
|
-
|
|
484
|
+
|
|
474
485
|
console.log('\n' + '='.repeat(60));
|
|
475
486
|
if (issues.length === 0 && warnings.length === 0) {
|
|
476
487
|
console.log('🎉 All checks passed! System is healthy.');
|
|
@@ -480,7 +491,7 @@ if (command === 'doctor') {
|
|
|
480
491
|
console.log(`✗ ${issues.length} issue(s) found - please fix before running`);
|
|
481
492
|
}
|
|
482
493
|
console.log('='.repeat(60) + '\n');
|
|
483
|
-
|
|
494
|
+
|
|
484
495
|
process.exit(issues.length > 0 ? 1 : 0);
|
|
485
496
|
}
|
|
486
497
|
|
package/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { checkPermissions } from './src/utils/permissionChecker.js';
|
|
|
15
15
|
import fs from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { execSync } from 'child_process';
|
|
18
|
-
import { SESSION_DIR } from './src/config.js';
|
|
18
|
+
import { SESSION_DIR, ACCOUNTS_DIR } from './src/config.js';
|
|
19
19
|
|
|
20
20
|
const app = express();
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ if (Number.isNaN(port) || port <= 0 || port > 65535) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function toBoolean(value) {
|
|
30
|
-
if (typeof value !== 'string') return false;
|
|
30
|
+
if (typeof value !== 'string') {return false;}
|
|
31
31
|
return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -42,7 +42,7 @@ function ensureNonInteractiveTokens() {
|
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
44
|
const now = Date.now();
|
|
45
|
-
const validTokens = tokens.filter(t => (!t.resetAt || new Date(t.resetAt).getTime() <= now) && !t.invalid);
|
|
45
|
+
const validTokens = tokens.filter((t) => (!t.resetAt || new Date(t.resetAt).getTime() <= now) && !t.invalid);
|
|
46
46
|
if (!validTokens.length) {
|
|
47
47
|
logWarn('⚠️ Все аккаунты недоступны. Сервер работает в режиме Telegram бота.');
|
|
48
48
|
logWarn('📦 Отправьте архив с сессиями через Telegram бот для обновления аккаунтов.');
|
|
@@ -75,7 +75,7 @@ app.use((req, res, next) => {
|
|
|
75
75
|
res.header('Access-Control-Allow-Origin', '*');
|
|
76
76
|
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
77
77
|
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
|
78
|
-
if (req.method === 'OPTIONS') return res.sendStatus(200);
|
|
78
|
+
if (req.method === 'OPTIONS') {return res.sendStatus(200);}
|
|
79
79
|
next();
|
|
80
80
|
});
|
|
81
81
|
|
|
@@ -133,6 +133,30 @@ function createSessionArchive() {
|
|
|
133
133
|
throw new Error('Папка сессии пуста. Сначала выполните авторизацию.');
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// Проверяем, есть ли реальные данные (accounts, tokens.json, и т.д.)
|
|
137
|
+
const hasAccounts = fs.existsSync(path.join(sessionPath, 'accounts')) &&
|
|
138
|
+
fs.readdirSync(path.join(sessionPath, 'accounts')).length > 0;
|
|
139
|
+
const hasTokens = fs.existsSync(path.join(sessionPath, 'tokens.json'));
|
|
140
|
+
|
|
141
|
+
if (!hasAccounts && !hasTokens) {
|
|
142
|
+
throw new Error('Папка сессии не содержит данных аккаунтов.');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Показываем статистику
|
|
146
|
+
console.log('\n📊 Найденные данные:');
|
|
147
|
+
if (hasAccounts) {
|
|
148
|
+
const accounts = fs.readdirSync(path.join(sessionPath, 'accounts'));
|
|
149
|
+
console.log(` ✓ Аккаунты: ${accounts.length} (${accounts.join(', ')})`);
|
|
150
|
+
}
|
|
151
|
+
if (hasTokens) {
|
|
152
|
+
try {
|
|
153
|
+
const tokens = JSON.parse(fs.readFileSync(path.join(sessionPath, 'tokens.json'), 'utf8'));
|
|
154
|
+
console.log(` ✓ Токены: ${Array.isArray(tokens) ? tokens.length : 0} записей`);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.log(' ⚠ tokens.json не удалось прочитать');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
// Создаем ZIP архив (cross-platform)
|
|
137
161
|
let command;
|
|
138
162
|
if (isWindows) {
|
|
@@ -188,29 +212,29 @@ function validateGitignore() {
|
|
|
188
212
|
'session_backup',
|
|
189
213
|
'session_backup_*'
|
|
190
214
|
];
|
|
191
|
-
|
|
215
|
+
|
|
192
216
|
if (!fs.existsSync(gitignorePath)) {
|
|
193
217
|
logWarn('⚠️ Файл .gitignore не найден в корневой директории');
|
|
194
218
|
logWarn(' Рекомендуется создать .gitignore для защиты чувствительных данных');
|
|
195
219
|
return false;
|
|
196
220
|
}
|
|
197
|
-
|
|
221
|
+
|
|
198
222
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
199
|
-
const lines = content.split('\n').map(line => line.trim());
|
|
223
|
+
const lines = content.split('\n').map((line) => line.trim());
|
|
200
224
|
const missingEntries = [];
|
|
201
|
-
|
|
202
|
-
requiredEntries.forEach(entry => {
|
|
225
|
+
|
|
226
|
+
requiredEntries.forEach((entry) => {
|
|
203
227
|
if (!lines.includes(entry)) {
|
|
204
228
|
missingEntries.push(entry);
|
|
205
229
|
}
|
|
206
230
|
});
|
|
207
|
-
|
|
231
|
+
|
|
208
232
|
if (missingEntries.length > 0) {
|
|
209
233
|
logWarn(`⚠️ .gitignore отсутствует: ${missingEntries.join(', ')}`);
|
|
210
234
|
logWarn(' Добавьте эти записи в .gitignore для защиты данных');
|
|
211
235
|
return false;
|
|
212
236
|
}
|
|
213
|
-
|
|
237
|
+
|
|
214
238
|
logInfo('✅ .gitignore проверен - все записи на месте');
|
|
215
239
|
return true;
|
|
216
240
|
}
|
|
@@ -234,6 +258,68 @@ async function handleCLICommand() {
|
|
|
234
258
|
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
235
259
|
|
|
236
260
|
try {
|
|
261
|
+
// Проверяем, есть ли уже аккаунты
|
|
262
|
+
const tokensPath = path.join(process.cwd(), SESSION_DIR, 'tokens.json');
|
|
263
|
+
const accountsPath = path.join(process.cwd(), SESSION_DIR, ACCOUNTS_DIR);
|
|
264
|
+
|
|
265
|
+
let hasAccounts = false;
|
|
266
|
+
|
|
267
|
+
if (fs.existsSync(tokensPath)) {
|
|
268
|
+
try {
|
|
269
|
+
const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf8'));
|
|
270
|
+
if (Array.isArray(tokens) && tokens.length > 0) {
|
|
271
|
+
hasAccounts = true;
|
|
272
|
+
}
|
|
273
|
+
} catch (e) {
|
|
274
|
+
// tokens.json corrupted
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!hasAccounts && fs.existsSync(accountsPath)) {
|
|
279
|
+
const accounts = fs.readdirSync(accountsPath);
|
|
280
|
+
if (accounts.length > 0) {
|
|
281
|
+
hasAccounts = true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Если аккаунтов нет, предлагаем добавить
|
|
286
|
+
if (!hasAccounts) {
|
|
287
|
+
console.log('📝 Аккаунты не найдены. Необходимо добавить аккаунт перед созданием архива.\n');
|
|
288
|
+
console.log('🔹 Вариант 1: Добавить аккаунт сейчас (откроется браузер)');
|
|
289
|
+
console.log('🔹 Вариант 2: Отправить файл сессии через Telegram бота');
|
|
290
|
+
console.log('🔹 Вариант 3: Выйти и запустить вручную: npx qwen-api-proxy\n');
|
|
291
|
+
|
|
292
|
+
const { prompt } = await import('./src/utils/prompt.js');
|
|
293
|
+
const choice = await prompt('Ваш выбор (1/2/3, Enter = 1): ');
|
|
294
|
+
|
|
295
|
+
if (choice === '2') {
|
|
296
|
+
console.log('\n💡 Для использования Telegram бота:');
|
|
297
|
+
console.log(' 1. Добавьте TELEGRAM_BOT_TOKEN в .env файл');
|
|
298
|
+
console.log(' 2. Запустите: npx qwen-api-proxy');
|
|
299
|
+
console.log(' 3. Отправьте файл сессии боту\n');
|
|
300
|
+
process.exit(0);
|
|
301
|
+
} else if (choice === '3') {
|
|
302
|
+
console.log('\n👋 Выход. Запустите "npx qwen-api-proxy" для добавления аккаунта.\n');
|
|
303
|
+
process.exit(0);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// choice === '1' или Enter - добавляем аккаунт
|
|
307
|
+
console.log('\n🔐 Запуск браузера для добавления аккаунта...\n');
|
|
308
|
+
|
|
309
|
+
const { addAccountInteractive } = await import('./src/utils/accountSetup.js');
|
|
310
|
+
const accountId = await addAccountInteractive();
|
|
311
|
+
|
|
312
|
+
if (!accountId) {
|
|
313
|
+
console.log('\n❌ Аккаунт не был добавлен. Архив не создан.\n');
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
console.log(`\n✅ Аккаунт ${accountId} успешно добавлен!\n`);
|
|
318
|
+
} else {
|
|
319
|
+
console.log('✅ Найден существующий аккаунт\n');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Теперь создаем архив
|
|
237
323
|
const archivePath = createSessionArchive();
|
|
238
324
|
console.log('\n🎉 ГОТОВО!');
|
|
239
325
|
console.log(`📄 Архив: ${archivePath}`);
|
|
@@ -320,7 +406,7 @@ async function startServer() {
|
|
|
320
406
|
console.log('4 - Удалить аккаунт');
|
|
321
407
|
|
|
322
408
|
let choice = await prompt('Ваш выбор (Enter = 3): ');
|
|
323
|
-
if (!choice) choice = '3';
|
|
409
|
+
if (!choice) {choice = '3';}
|
|
324
410
|
|
|
325
411
|
if (choice === '1') {
|
|
326
412
|
await addAccountInteractive();
|
|
@@ -328,9 +414,9 @@ async function startServer() {
|
|
|
328
414
|
const { reloginAccountInteractive } = await import('./src/utils/accountSetup.js');
|
|
329
415
|
await reloginAccountInteractive();
|
|
330
416
|
} else if (choice === '3') {
|
|
331
|
-
const hasValidToken = tokens.some(t => {
|
|
332
|
-
if (t.invalid) return false;
|
|
333
|
-
if (!t.resetAt) return true;
|
|
417
|
+
const hasValidToken = tokens.some((t) => {
|
|
418
|
+
if (t.invalid) {return false;}
|
|
419
|
+
if (!t.resetAt) {return true;}
|
|
334
420
|
return new Date(t.resetAt).getTime() <= Date.now();
|
|
335
421
|
});
|
|
336
422
|
if (!tokens.length || !hasValidToken) {
|
|
@@ -422,7 +508,7 @@ async function startServer() {
|
|
|
422
508
|
logInfo('🔍 Начинаем загрузку списка моделей...');
|
|
423
509
|
const apiModels = await fetchModelsFromAPI();
|
|
424
510
|
logDebug(`fetchModelsFromAPI вернул: ${apiModels ? apiModels.length + ' моделей' : 'null'}`);
|
|
425
|
-
|
|
511
|
+
|
|
426
512
|
if (apiModels && apiModels.length > 0) {
|
|
427
513
|
logInfo(`✅ Загружено ${apiModels.length} моделей с Qwen API`);
|
|
428
514
|
logDebug(`Первые 5 моделей: ${apiModels.slice(0, 5).join(', ')}`);
|
|
@@ -433,11 +519,11 @@ async function startServer() {
|
|
|
433
519
|
const fileModels = getAvailableModelsFromFile();
|
|
434
520
|
logDebug(`getAvailableModelsFromFile вернул: ${fileModels ? fileModels.length + ' моделей' : 'null'}`);
|
|
435
521
|
}
|
|
436
|
-
|
|
522
|
+
|
|
437
523
|
const defaultModel = getDefaultModel();
|
|
438
524
|
logInfo(`🎯 Модель по умолчанию: ${defaultModel}`);
|
|
439
525
|
logDebug(`getDefaultModel() вернул: ${defaultModel}`);
|
|
440
|
-
|
|
526
|
+
|
|
441
527
|
getApiKeys();
|
|
442
528
|
});
|
|
443
529
|
} catch (err) {
|
|
@@ -451,10 +537,10 @@ async function startServer() {
|
|
|
451
537
|
}
|
|
452
538
|
|
|
453
539
|
// Проверяем CLI команды перед запуском сервера
|
|
454
|
-
handleCLICommand().then(hasCommand => {
|
|
540
|
+
handleCLICommand().then((hasCommand) => {
|
|
455
541
|
if (!hasCommand) {
|
|
456
542
|
// Нет команды CLI, запускаем сервер как обычно
|
|
457
|
-
startServer().catch(async error => {
|
|
543
|
+
startServer().catch(async (error) => {
|
|
458
544
|
logError('Ошибка при запуске сервера', error);
|
|
459
545
|
await shutdownBrowser();
|
|
460
546
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwen-api-proxy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Proxy server for accessing Qwen API through browser emulation with OpenAI-compatible API and Telegram bot",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"start": "node index.js",
|
|
46
46
|
"archive": "node index.js archive",
|
|
47
|
+
"lint": "eslint .",
|
|
48
|
+
"lint:fix": "eslint . --fix",
|
|
47
49
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
48
50
|
"test:cross-platform": "node test-cross-platform.js",
|
|
49
51
|
"check-permissions": "node scripts/checkPermissions.js",
|
|
@@ -82,5 +84,9 @@
|
|
|
82
84
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
83
85
|
"undici": "^6.21.1",
|
|
84
86
|
"winston": "^3.17.0"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"eslint": "^9.39.4",
|
|
90
|
+
"eslint-plugin-import": "^2.32.0"
|
|
85
91
|
}
|
|
86
|
-
}
|
|
92
|
+
}
|