@alizadefaig/pos-server 1.2.28 → 1.2.29
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/sync/config.js +1 -1
- package/app/sync/index.js +27 -25
- package/package.json +1 -1
package/app/sync/config.js
CHANGED
|
@@ -71,7 +71,7 @@ const intervals = () => ({
|
|
|
71
71
|
// модель M: как часто спрашивать у облака режим заведения (заодно это heartbeat кассы)
|
|
72
72
|
modePoll: num('OFFLINE_MODE_POLL_MS', 10000),
|
|
73
73
|
// выход терминала при недосланном: столько push досылает очередь, прежде чем токен отзовут
|
|
74
|
-
|
|
74
|
+
logoutDrain: num('OFFLINE_LOGOUT_DRAIN_MS', 30000),
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
const timeouts = () => ({
|
package/app/sync/index.js
CHANGED
|
@@ -100,9 +100,7 @@ const state = {
|
|
|
100
100
|
// чеки, которые облако просит перечитать (sync:pull)
|
|
101
101
|
pullIds: new Set(),
|
|
102
102
|
|
|
103
|
-
// выход терминала (logout):
|
|
104
|
-
// идёт отзыв токена — возврат на кассу (resume) в это время невозможен
|
|
105
|
-
revokeWaitFrom: null,
|
|
103
|
+
// выход терминала (logout): идёт отзыв токена — возврат на кассу (resume) в это время невозможен
|
|
106
104
|
revoking: false,
|
|
107
105
|
|
|
108
106
|
// сверка касса ↔ облако (reconcile.js): последний итог, расхождений подряд, сколько вылечено
|
|
@@ -386,10 +384,12 @@ const authoritativeEpoch = () => (
|
|
|
386
384
|
: null
|
|
387
385
|
);
|
|
388
386
|
|
|
389
|
-
|
|
387
|
+
// until — выход терминала: досылать, пока облако отвечает (без гистерезиса), не дольше этого срока.
|
|
388
|
+
const runPush = async ({until = null} = {}) => {
|
|
390
389
|
let rounds = 0;
|
|
391
390
|
reconcilePoison();
|
|
392
|
-
|
|
391
|
+
const going = () => (until === null ? state.online : state.oks > 0 && Date.now() < until);
|
|
392
|
+
while (refreshPending() > 0 && rounds < MAX_PUSH_ROUNDS && going()) {
|
|
393
393
|
rounds += 1;
|
|
394
394
|
const epoch = authoritativeEpoch();
|
|
395
395
|
let result;
|
|
@@ -1244,7 +1244,6 @@ const setSession = async (input) => {
|
|
|
1244
1244
|
// Фронт снова прислал сессию — значит, терминал вошёл обратно (resume).
|
|
1245
1245
|
delete state.session.parked;
|
|
1246
1246
|
}
|
|
1247
|
-
state.revokeWaitFrom = null;
|
|
1248
1247
|
return {success: true, mode: state.mode};
|
|
1249
1248
|
};
|
|
1250
1249
|
|
|
@@ -1260,7 +1259,6 @@ const clearSession = async () => {
|
|
|
1260
1259
|
}
|
|
1261
1260
|
stopComponents();
|
|
1262
1261
|
state.session = null;
|
|
1263
|
-
state.revokeWaitFrom = null;
|
|
1264
1262
|
await state.store.block((s) => kv.del(s, 'session'));
|
|
1265
1263
|
updateMode();
|
|
1266
1264
|
return {success: true};
|
|
@@ -1273,12 +1271,12 @@ const clearSession = async () => {
|
|
|
1273
1271
|
* (auth.logoutApp) и стирал сессию синка: без интернета выход падал, а недосланное
|
|
1274
1272
|
* ждало следующего входа, которого без интернета не бывает (app.auth — только облако).
|
|
1275
1273
|
*
|
|
1276
|
-
* Теперь выход «паркует» сессию: фронт свою авторизацию стирает, а синк токен
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1279
|
-
* (
|
|
1280
|
-
* следующим входом в это заведение
|
|
1281
|
-
* (resume) — это единственный вход без облака.
|
|
1274
|
+
* Теперь выход «паркует» сессию: фронт свою авторизацию стирает, а синк держит токен до
|
|
1275
|
+
* первого ответа облака. Облако ответило (гистерезис связи не ждём) — синк досылает очередь не
|
|
1276
|
+
* дольше OFFLINE_LOGOUT_DRAIN_MS и отзывает токен в любом случае, в том числе на мастере модели M
|
|
1277
|
+
* (заведение он после этого не обслуживает). Недосланное остаётся помеченным и уйдёт со
|
|
1278
|
+
* следующим входом в это заведение на этой кассе. До отзыва на кассу можно вернуться по PIN
|
|
1279
|
+
* сотрудника (resume) — это единственный вход без облака.
|
|
1282
1280
|
*/
|
|
1283
1281
|
|
|
1284
1282
|
const initNames = () => {
|
|
@@ -1324,8 +1322,11 @@ const revokeToken = async (client, data = {}, timeout = config.timeouts().api) =
|
|
|
1324
1322
|
}
|
|
1325
1323
|
};
|
|
1326
1324
|
|
|
1325
|
+
// Облако ответило на последнюю проверку связи: для выхода гистерезис не ждём.
|
|
1326
|
+
const cloudAnswers = () => state.oks > 0;
|
|
1327
|
+
|
|
1327
1328
|
const canRevokeParked = () => Boolean(state.session && state.session.parked && state.cloud)
|
|
1328
|
-
&&
|
|
1329
|
+
&& cloudAnswers() && !state.revoking;
|
|
1329
1330
|
|
|
1330
1331
|
/** Отзыв токена запаркованной сессии. Вернулись на кассу, пока шёл запрос, — сессию не трогаем. */
|
|
1331
1332
|
const revokeParked = async (timeout) => {
|
|
@@ -1347,16 +1348,20 @@ const revokeParked = async (timeout) => {
|
|
|
1347
1348
|
|
|
1348
1349
|
const runRevoke = async () => {
|
|
1349
1350
|
if (!canRevokeParked()) {
|
|
1350
|
-
state.revokeWaitFrom = null;
|
|
1351
1351
|
return;
|
|
1352
1352
|
}
|
|
1353
|
-
if (refreshPending() > 0) {
|
|
1354
|
-
|
|
1355
|
-
if (
|
|
1353
|
+
if (!isFollower() && hasInit() && refreshPending() > 0) {
|
|
1354
|
+
await step('logout push', () => runPush({until: Date.now() + config.intervals().logoutDrain}));
|
|
1355
|
+
if (!canRevokeParked()) {
|
|
1356
1356
|
return;
|
|
1357
1357
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1358
|
+
if (refreshPending() > 0) {
|
|
1359
|
+
console.warn(`[sync] logout: ${state.pending} unsent record(s) left — revoking the token anyway, `
|
|
1360
|
+
+ 'they wait for the next login to this company');
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (isServingAsMaster()) {
|
|
1364
|
+
console.warn('[sync] terminal logged out on the master — the company is not served after the token is revoked');
|
|
1360
1365
|
}
|
|
1361
1366
|
await revokeParked();
|
|
1362
1367
|
};
|
|
@@ -1376,7 +1381,7 @@ const runLogoutQueue = async () => {
|
|
|
1376
1381
|
};
|
|
1377
1382
|
|
|
1378
1383
|
const runLogout = async () => {
|
|
1379
|
-
if (
|
|
1384
|
+
if (cloudAnswers() && state.store && (kv.get(state.store, 'logout_queue') || []).length) {
|
|
1380
1385
|
await step('logout queue', runLogoutQueue);
|
|
1381
1386
|
}
|
|
1382
1387
|
await step('revoke', runRevoke);
|
|
@@ -1402,9 +1407,7 @@ const logout = async ({token} = {}) => {
|
|
|
1402
1407
|
if (canRevokeParked() && refreshPending() === 0 && await revokeParked(REVOKE_TIMEOUT_MS)) {
|
|
1403
1408
|
return {success: true, handled: true, revoked: true};
|
|
1404
1409
|
}
|
|
1405
|
-
|
|
1406
|
-
console.warn('[sync] terminal logged out on the master — the company is still served, token kept');
|
|
1407
|
-
}
|
|
1410
|
+
// Очередь не пуста или облако не ответило — досылка и отзыв идут в цикле сразу же.
|
|
1408
1411
|
schedule(0);
|
|
1409
1412
|
return {success: true, handled: true, parked: true, pending: state.pending, master: isServingAsMaster()};
|
|
1410
1413
|
};
|
|
@@ -1450,7 +1453,6 @@ const resume = async ({code} = {}) => {
|
|
|
1450
1453
|
throw busy();
|
|
1451
1454
|
}
|
|
1452
1455
|
delete session.parked;
|
|
1453
|
-
state.revokeWaitFrom = null;
|
|
1454
1456
|
await state.store.block((s) => kv.set(s, 'session', {...session}));
|
|
1455
1457
|
log('terminal logged back in by PIN');
|
|
1456
1458
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alizadefaig/pos-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.29",
|
|
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": {
|