qwen-api-proxy 1.0.13 → 1.0.15
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 +5 -5
- package/package.json +1 -1
- package/src/api/tokenManager.js +61 -0
- package/src/utils/telegramBot.js +41 -15
package/README.md
CHANGED
|
@@ -311,7 +311,7 @@ docker run -d \
|
|
|
311
311
|
-v $(pwd)/logs:/app/logs \
|
|
312
312
|
-v $(pwd)/uploads:/app/uploads \
|
|
313
313
|
-v $(pwd)/temp:/app/temp \
|
|
314
|
-
endykaufman/qwen-api-proxy:1.0.
|
|
314
|
+
endykaufman/qwen-api-proxy:1.0.15
|
|
315
315
|
|
|
316
316
|
# 3. Смотрим логи
|
|
317
317
|
docker logs -f qwen-proxy
|
|
@@ -320,7 +320,7 @@ docker logs -f qwen-proxy
|
|
|
320
320
|
### Доступные теги
|
|
321
321
|
|
|
322
322
|
- `latest` - последняя стабильная версия
|
|
323
|
-
- `1.0.
|
|
323
|
+
- `1.0.15` - текущая версия
|
|
324
324
|
- `1.0.x` - предыдущие версии
|
|
325
325
|
|
|
326
326
|
> **💡 Важно:** Перед первым запуском добавьте аккаунт через `npm run auth` или загрузите сессию через Telegram бота.
|
|
@@ -341,7 +341,7 @@ docker logs -f qwen-proxy
|
|
|
341
341
|
```yaml
|
|
342
342
|
services:
|
|
343
343
|
qwen-proxy:
|
|
344
|
-
image: endykaufman/qwen-api-proxy:1.0.
|
|
344
|
+
image: endykaufman/qwen-api-proxy:1.0.15
|
|
345
345
|
container_name: qwen-proxy
|
|
346
346
|
env_file:
|
|
347
347
|
- .env
|
|
@@ -396,7 +396,7 @@ docker run -d \
|
|
|
396
396
|
-v $(pwd)/logs:/app/logs \
|
|
397
397
|
-v $(pwd)/uploads:/app/uploads \
|
|
398
398
|
-v $(pwd)/temp:/app/temp \
|
|
399
|
-
endykaufman/qwen-api-proxy:1.0.
|
|
399
|
+
endykaufman/qwen-api-proxy:1.0.15
|
|
400
400
|
```
|
|
401
401
|
|
|
402
402
|
Файл `docker-compose.yml`:
|
|
@@ -405,7 +405,7 @@ docker run -d \
|
|
|
405
405
|
services:
|
|
406
406
|
qwen-proxy:
|
|
407
407
|
build: .
|
|
408
|
-
image: endykaufman/qwen-api-proxy:1.0.
|
|
408
|
+
image: endykaufman/qwen-api-proxy:1.0.15
|
|
409
409
|
container_name: qwen-proxy
|
|
410
410
|
env_file:
|
|
411
411
|
- .env # Автоматическая загрузка переменных
|
package/package.json
CHANGED
package/src/api/tokenManager.js
CHANGED
|
@@ -27,6 +27,60 @@ export function hasCookies(accountId) {
|
|
|
27
27
|
return fs.existsSync(cookiesPath);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Извлекает JWT токен из cookies.json
|
|
32
|
+
* @param {string} accountId - ID аккаунта
|
|
33
|
+
* @returns {string|null} - JWT токен или null
|
|
34
|
+
*/
|
|
35
|
+
function extractTokenFromCookies(accountId) {
|
|
36
|
+
try {
|
|
37
|
+
const cookiesPath = path.join(ACCOUNTS_PATH, accountId, 'cookies.json');
|
|
38
|
+
if (!fs.existsSync(cookiesPath)) {return null;}
|
|
39
|
+
|
|
40
|
+
const cookies = JSON.parse(fs.readFileSync(cookiesPath, 'utf8'));
|
|
41
|
+
|
|
42
|
+
// Ищем cookie с именем 'token'
|
|
43
|
+
const tokenCookie = cookies.find((cookie) => cookie.name === 'token');
|
|
44
|
+
|
|
45
|
+
if (tokenCookie && tokenCookie.value) {
|
|
46
|
+
return tokenCookie.value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// Тихо возвращаем null - это не критичная операция
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Восстанавливает token.txt из cookies.json если token.txt отсутствует
|
|
58
|
+
* @param {string} accountId - ID аккаунта
|
|
59
|
+
* @returns {boolean} - true если токен был восстановлен
|
|
60
|
+
*/
|
|
61
|
+
function restoreTokenFromFile(accountId) {
|
|
62
|
+
const tokenPath = path.join(ACCOUNTS_PATH, accountId, 'token.txt');
|
|
63
|
+
|
|
64
|
+
// Если token.txt уже существует, ничего не делаем
|
|
65
|
+
if (fs.existsSync(tokenPath)) {return false;}
|
|
66
|
+
|
|
67
|
+
// Пытаемся извлечь токен из cookies.json
|
|
68
|
+
const token = extractTokenFromCookies(accountId);
|
|
69
|
+
|
|
70
|
+
if (token) {
|
|
71
|
+
try {
|
|
72
|
+
fs.writeFileSync(tokenPath, token, 'utf8');
|
|
73
|
+
logInfo(`✅ ${accountId}: Токен восстановлен из cookies.json`);
|
|
74
|
+
return true;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logWarn(`⚠️ ${accountId}: Не удалось создать token.txt: ${error.message}`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
30
84
|
/**
|
|
31
85
|
* Декодирует JWT токен и извлекает время истечения
|
|
32
86
|
* @param {string} token - JWT токен
|
|
@@ -69,6 +123,13 @@ export function loadTokens() {
|
|
|
69
123
|
try {
|
|
70
124
|
const tokens = JSON.parse(fs.readFileSync(TOKENS_FILE, 'utf8'));
|
|
71
125
|
|
|
126
|
+
// Автоматически восстанавливаем token.txt из cookies.json если нужно
|
|
127
|
+
tokens.forEach((token) => {
|
|
128
|
+
if (token.id) {
|
|
129
|
+
restoreTokenFromFile(token.id);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
72
133
|
// Добавляем expiryTime для каждого токена, если его нет
|
|
73
134
|
return tokens.map((token) => {
|
|
74
135
|
if (!token.expiryTime && token.token) {
|
package/src/utils/telegramBot.js
CHANGED
|
@@ -1567,27 +1567,55 @@ async function extractZip(zipPath, sessionPath, chatId) {
|
|
|
1567
1567
|
const zip = new AdmZip(zipPath);
|
|
1568
1568
|
const zipEntries = zip.getEntries();
|
|
1569
1569
|
|
|
1570
|
+
// Нормализуем пути: заменяем обратные слеши на прямые (Windows -> Unix)
|
|
1571
|
+
const normalizedEntries = zipEntries.map((entry) => ({
|
|
1572
|
+
...entry,
|
|
1573
|
+
normalizedPath: entry.entryName.replace(/\\/g, '/')
|
|
1574
|
+
}));
|
|
1575
|
+
|
|
1570
1576
|
// Для отладки: показываем первые несколько записей в архиве
|
|
1571
|
-
const firstEntries =
|
|
1577
|
+
const firstEntries = normalizedEntries.slice(0, 20).map((e) => e.normalizedPath);
|
|
1572
1578
|
logInfo('📂 Первые 20 записей в ZIP архиве:');
|
|
1573
1579
|
logInfo(firstEntries.join('\n'));
|
|
1574
1580
|
|
|
1575
|
-
// Проверяем что есть папка session
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
)
|
|
1581
|
+
// Проверяем что есть папка session (с нормализованными путями)
|
|
1582
|
+
// Поддерживаем разные варианты:
|
|
1583
|
+
// 1. "session/" - папка session в корне
|
|
1584
|
+
// 2. "session" - ровно папка session (без слеша)
|
|
1585
|
+
// 3. "session/accounts/..." - содержимое session
|
|
1586
|
+
const hasSessionFolder = normalizedEntries.some((entry) => {
|
|
1587
|
+
const p = entry.normalizedPath;
|
|
1588
|
+
// Точное совпадение "session" или "session/"
|
|
1589
|
+
if (p === 'session' || p === 'session/') {return true;}
|
|
1590
|
+
// Начинается с "session/"
|
|
1591
|
+
if (p.startsWith('session/')) {return true;}
|
|
1592
|
+
// Для ZIP созданных на Windows может быть "session\" который уже нормализован
|
|
1593
|
+
return false;
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
if (hasSessionFolder) {
|
|
1597
|
+
logInfo('✅ Найдена папка session в корне архива');
|
|
1598
|
+
// Логируем пример найденного пути для отладки
|
|
1599
|
+
const sampleEntry = normalizedEntries.find((e) => {
|
|
1600
|
+
const p = e.normalizedPath;
|
|
1601
|
+
return p === 'session' || p === 'session/' || p.startsWith('session/');
|
|
1602
|
+
});
|
|
1603
|
+
if (sampleEntry) {
|
|
1604
|
+
logInfo(`📂 Пример: ${sampleEntry.normalizedPath}`);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1579
1607
|
|
|
1580
1608
|
if (!hasSessionFolder) {
|
|
1581
1609
|
// Пытаемся найти session в любом месте архива
|
|
1582
|
-
const sessionEntries =
|
|
1583
|
-
e.
|
|
1610
|
+
const sessionEntries = normalizedEntries.filter((e) =>
|
|
1611
|
+
e.normalizedPath.includes('session') || e.normalizedPath.includes('Session')
|
|
1584
1612
|
);
|
|
1585
1613
|
|
|
1586
1614
|
if (sessionEntries.length > 0) {
|
|
1587
1615
|
logInfo('🔍 Найдены записи содержащие \'session\':');
|
|
1588
|
-
logInfo(sessionEntries.slice(0, 10).map((e) => e.
|
|
1616
|
+
logInfo(sessionEntries.slice(0, 10).map((e) => e.normalizedPath).join('\n'));
|
|
1589
1617
|
reject(new Error(
|
|
1590
|
-
`Архив содержит '${sessionEntries[0].
|
|
1618
|
+
`Архив содержит '${sessionEntries[0].normalizedPath}', но не содержит 'session/' в корне. ` +
|
|
1591
1619
|
'Переместите папку session/ в корень архива'
|
|
1592
1620
|
));
|
|
1593
1621
|
} else {
|
|
@@ -1596,8 +1624,6 @@ async function extractZip(zipPath, sessionPath, chatId) {
|
|
|
1596
1624
|
return;
|
|
1597
1625
|
}
|
|
1598
1626
|
|
|
1599
|
-
logInfo('✅ Найдена папка session в корне архива');
|
|
1600
|
-
|
|
1601
1627
|
// Создаем папку session если не существует
|
|
1602
1628
|
if (!fs.existsSync(sessionPath)) {
|
|
1603
1629
|
fs.mkdirSync(sessionPath, { recursive: true });
|
|
@@ -1607,10 +1633,10 @@ async function extractZip(zipPath, sessionPath, chatId) {
|
|
|
1607
1633
|
let successCount = 0;
|
|
1608
1634
|
let errorCount = 0;
|
|
1609
1635
|
|
|
1610
|
-
|
|
1611
|
-
if (entry.
|
|
1636
|
+
normalizedEntries.forEach((entry) => {
|
|
1637
|
+
if (entry.normalizedPath.startsWith('session/')) {
|
|
1612
1638
|
try {
|
|
1613
|
-
const relativePath = entry.
|
|
1639
|
+
const relativePath = entry.normalizedPath.substring('session/'.length);
|
|
1614
1640
|
if (relativePath) {
|
|
1615
1641
|
const targetPath = path.join(sessionPath, relativePath);
|
|
1616
1642
|
|
|
@@ -1631,7 +1657,7 @@ async function extractZip(zipPath, sessionPath, chatId) {
|
|
|
1631
1657
|
}
|
|
1632
1658
|
} catch (err) {
|
|
1633
1659
|
errorCount++;
|
|
1634
|
-
logWarn(`⚠️ Ошибка распаковки ${entry.
|
|
1660
|
+
logWarn(`⚠️ Ошибка распаковки ${entry.normalizedPath}: ${err.message}`);
|
|
1635
1661
|
}
|
|
1636
1662
|
}
|
|
1637
1663
|
});
|