@alizadefaig/pos-server 1.2.25 → 1.2.26
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/app/proxy.js +10 -1
- package/package.json +1 -1
- package/routes/common/tax.js +6 -0
- package/server.js +41 -3
- package/tests/usbDiagnose.js +59 -0
package/app/proxy.js
CHANGED
|
@@ -41,10 +41,19 @@ const loadProxy = (app) => {
|
|
|
41
41
|
headers: req.headers,
|
|
42
42
|
method: req.method,
|
|
43
43
|
url: req.url,
|
|
44
|
-
body: req.
|
|
44
|
+
body: req.body,
|
|
45
45
|
params: req.params,
|
|
46
46
|
}
|
|
47
47
|
})
|
|
48
|
+
// Cloudflare/Traefik присылают запрос как `Transfer-Encoding: chunked` (без Content-Length).
|
|
49
|
+
// body-parser (express.json/urlencoded/multer) уже вычитал тело (readableLength === 0),
|
|
50
|
+
// а fixRequestBody допишет его и выставит Content-Length. Если при этом оставить
|
|
51
|
+
// скопированный `Transfer-Encoding: chunked`, в апстрим уйдут ОБА заголовка (TE + CL) —
|
|
52
|
+
// nginx отклоняет такой запрос как 400 Bad Request (RFC 7230, защита от request smuggling).
|
|
53
|
+
// Снимаем chunked, чтобы осталась только корректная рамка по Content-Length.
|
|
54
|
+
if (req.readableLength === 0 && req.body && Object.keys(req.body).length > 0) {
|
|
55
|
+
proxyReq.removeHeader('transfer-encoding');
|
|
56
|
+
}
|
|
48
57
|
return fixRequestBody(proxyReq, req);
|
|
49
58
|
},
|
|
50
59
|
proxyRes: (proxyRes, req, res, options) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alizadefaig/pos-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.26",
|
|
4
4
|
"description": "Server in express js with Proxy to Main server if need and will work with Electron JS",
|
|
5
5
|
"homepage": "https://gitlab.com/faigalizade/pos-server#readme",
|
|
6
6
|
"bugs": {
|
package/routes/common/tax.js
CHANGED
|
@@ -22,6 +22,12 @@ const proxy = createProxyMiddleware({
|
|
|
22
22
|
},
|
|
23
23
|
on: {
|
|
24
24
|
proxyReq: (proxyReq, req, res, options) => {
|
|
25
|
+
// Тело уже распарсено body-parser'ом (нужно для req.body.url) и будет пересобрано
|
|
26
|
+
// fixRequestBody с Content-Length. Снимаем chunked, чтобы в апстрим не ушли
|
|
27
|
+
// Transfer-Encoding и Content-Length одновременно (иначе 400 Bad Request).
|
|
28
|
+
if (req.readableLength === 0 && req.body && Object.keys(req.body).length > 0) {
|
|
29
|
+
proxyReq.removeHeader('transfer-encoding');
|
|
30
|
+
}
|
|
25
31
|
return fixRequestBody(proxyReq, req);
|
|
26
32
|
},
|
|
27
33
|
}
|
package/server.js
CHANGED
|
@@ -14,9 +14,47 @@ const {getLogger} = require("./app/logger/Log");
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
const upload = multer();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
// Локальные роуты Node, которым нужно РАСПАРСЕННОЕ тело. Всё остальное проксируется в апстрим
|
|
19
|
+
// и должно стримиться СЫРЫМ потоком: если буферизовать тело body-parser'ом, а затем пересобирать
|
|
20
|
+
// его через fixRequestBody, то под chunked-трафиком Cloudflare/Traefik фрейминг ломается
|
|
21
|
+
// (Transfer-Encoding: chunked + добавленный Content-Length одновременно) → nginx апстрима рубит
|
|
22
|
+
// запрос голой 400 Bad Request. Сырой стрим проходит в апстрим как есть — без пересборки.
|
|
23
|
+
const LOCAL_BODY_ROUTES = new Set([
|
|
24
|
+
'/health',
|
|
25
|
+
'/init',
|
|
26
|
+
'/socket',
|
|
27
|
+
'/socket/request',
|
|
28
|
+
'/api/printers.get',
|
|
29
|
+
'/api/printers.getUsbPrinters',
|
|
30
|
+
'/api/printers.print',
|
|
31
|
+
]);
|
|
32
|
+
const isLocalBodyRoute = (req) => {
|
|
33
|
+
const p = req.path;
|
|
34
|
+
if (LOCAL_BODY_ROUTES.has(p)) return true;
|
|
35
|
+
// tax-прокси достаёт целевой URL из тела запроса (req.body.url) — ему тело нужно распарсенным
|
|
36
|
+
return p === '/tax' || p.startsWith('/tax/');
|
|
37
|
+
};
|
|
38
|
+
const bodyParsers = [
|
|
39
|
+
express.json({limit: '500mb'}),
|
|
40
|
+
express.urlencoded({extended: true, limit: '500mb'}),
|
|
41
|
+
upload.any(),
|
|
42
|
+
];
|
|
43
|
+
app.use((req, res, next) => {
|
|
44
|
+
// Проксируемые запросы НЕ буферизуем — отдаём сырой поток дальше (в http-proxy-middleware).
|
|
45
|
+
if (!isLocalBodyRoute(req)) {
|
|
46
|
+
return next();
|
|
47
|
+
}
|
|
48
|
+
// Локальные роуты — прогоняем парсеры тела последовательно, как раньше.
|
|
49
|
+
let index = 0;
|
|
50
|
+
const run = (err) => {
|
|
51
|
+
if (err) return next(err);
|
|
52
|
+
const middleware = bodyParsers[index++];
|
|
53
|
+
if (!middleware) return next();
|
|
54
|
+
middleware(req, res, run);
|
|
55
|
+
};
|
|
56
|
+
run();
|
|
57
|
+
});
|
|
20
58
|
app.use(cors({
|
|
21
59
|
origin: '*',
|
|
22
60
|
allowedHeaders: '*',
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Диагностика: почему USB-принтер не виден на macOS.
|
|
2
|
+
// Запуск (с подключённым принтером): node tests/usbDiagnose.js
|
|
3
|
+
const usb = require('usb');
|
|
4
|
+
|
|
5
|
+
(async () => {
|
|
6
|
+
const IFACE_PRINTER = 0x07;
|
|
7
|
+
const list = usb.getDeviceList();
|
|
8
|
+
console.log(`Всего USB-устройств: ${list.length}\n`);
|
|
9
|
+
|
|
10
|
+
for (const d of list) {
|
|
11
|
+
const dd = d.deviceDescriptor;
|
|
12
|
+
const id = `vid=0x${dd.idVendor.toString(16)} (${dd.idVendor}) pid=0x${dd.idProduct.toString(16)} (${dd.idProduct})`;
|
|
13
|
+
let classes = [];
|
|
14
|
+
try {
|
|
15
|
+
classes = d.configDescriptor.interfaces.flat().map(i => i.bInterfaceClass);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
console.log(`${id} — configDescriptor НЕДОСТУПЕН: ${e.message}`);
|
|
18
|
+
console.log(' → findPrinter() отфильтрует это устройство, оно НЕ попадёт в список\n');
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const isPrinter = classes.includes(IFACE_PRINTER);
|
|
22
|
+
console.log(`${id} ifaceClasses=[${classes.join(',')}] ${isPrinter ? '← ПРИНТЕР (класс 7)' : ''}`);
|
|
23
|
+
if (!isPrinter && classes.some(c => c === 0xff || c === 0x02 || c === 0x0a)) {
|
|
24
|
+
console.log(' ⚠ vendor-specific/CDC класс — findPrinter() такой НЕ находит (ищет только класс 7)');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// пробуем открыть и снять claim — то, что делает printers.get / печать
|
|
28
|
+
try {
|
|
29
|
+
d.open();
|
|
30
|
+
console.log(' open(): OK');
|
|
31
|
+
try {
|
|
32
|
+
const name = await new Promise((res, rej) =>
|
|
33
|
+
d.getStringDescriptor(dd.iProduct, (e, s) => e ? rej(e) : res(s)));
|
|
34
|
+
console.log(` имя: ${name}`);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.log(` getStringDescriptor: ОШИБКА ${e.message} — уронит весь /printers.get (нет try/catch)`);
|
|
37
|
+
}
|
|
38
|
+
if (isPrinter) {
|
|
39
|
+
for (const iface of d.interfaces) {
|
|
40
|
+
try {
|
|
41
|
+
console.log(` iface#${iface.interfaceNumber} kernelDriverActive=${iface.isKernelDriverActive()}`);
|
|
42
|
+
} catch (e) { console.log(` isKernelDriverActive: ${e.message}`); }
|
|
43
|
+
try {
|
|
44
|
+
iface.claim();
|
|
45
|
+
console.log(` iface#${iface.interfaceNumber} claim(): OK (печать возможна)`);
|
|
46
|
+
iface.release(() => {});
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.log(` iface#${iface.interfaceNumber} claim(): ОШИБКА ${e.message}`);
|
|
49
|
+
console.log(' → интерфейс держит драйвер macOS (CUPS/printer-class); detachKernelDriver на macOS не работает');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
d.close();
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.log(` open(): ОШИБКА ${e.message} — исключение синхронное, колбэк в printers.js его НЕ ловит → /printers.get падает 500`);
|
|
56
|
+
}
|
|
57
|
+
console.log('');
|
|
58
|
+
}
|
|
59
|
+
})();
|