@mmmbuto/nexuscrew 0.8.48 → 0.8.50
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/frontend/dist/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-title" content="NexusCrew" />
|
|
12
12
|
<link rel="manifest" href="/manifest.json" />
|
|
13
13
|
<title>NexusCrew</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-kj-l-RDc.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-43DFO1EH.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.8.
|
|
1
|
+
{"version":"0.8.50"}
|
|
@@ -96,4 +96,28 @@ function summarizeStops(results) {
|
|
|
96
96
|
return { stoppedAny, quarantinedAny, allClosed: stoppedAny && !quarantinedAny };
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
// Esito di un giro di verifica del pool. `verified` richiede TUTTI gli slot:
|
|
100
|
+
// e' una scelta deliberata, perche' rotateRotatableReverse si rifiuta di
|
|
101
|
+
// commutare dentro un pool non interamente provato. La conseguenza pero' va
|
|
102
|
+
// detta, non dedotta: finche' il pool non e' verificato la rotazione
|
|
103
|
+
// automatica resta SPENTA e il watcher non viene nemmeno armato. Un pool
|
|
104
|
+
// "unverifiable" non e' un'etichetta descrittiva, e' autoriparazione assente.
|
|
105
|
+
function summarizePoolVerification(results, slotCount) {
|
|
106
|
+
const list = Array.isArray(results) ? results : [];
|
|
107
|
+
const total = Number.isSafeInteger(slotCount) && slotCount > 0 ? slotCount : 0;
|
|
108
|
+
const verifiedSlots = [...new Set(list.filter((r) => r && r.proven === true)
|
|
109
|
+
.map((r) => r.slot).filter(Number.isSafeInteger))].sort((a, b) => a - b);
|
|
110
|
+
const failures = list.filter((r) => r && r.proven !== true)
|
|
111
|
+
.map((r) => ({ slot: r.slot, code: typeof r.code === 'string' && /^[a-z0-9-]{1,64}$/.test(r.code) ? r.code : 'reverse-slot-verify-failed' }));
|
|
112
|
+
// Zero slot dichiarati non e' "tutto verificato": senza un pool da provare
|
|
113
|
+
// non c'e' nulla da cui ruotare.
|
|
114
|
+
const verified = total > 0 && verifiedSlots.length === total;
|
|
115
|
+
return {
|
|
116
|
+
verifiedSlots,
|
|
117
|
+
failures,
|
|
118
|
+
verification: verified ? 'verified' : 'unverifiable',
|
|
119
|
+
rotationActive: verified,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { LEASE_MS, GRACE_MS, nextReadySlot, prepareRotation, abortPrepared, commitRotation, settleGrace, quarantineSlot, stopWasDemonstrated, summarizeStops, summarizePoolVerification };
|
package/lib/server.js
CHANGED
|
@@ -187,9 +187,20 @@ function createServer(opts = {}) {
|
|
|
187
187
|
const key = reverseKey(active.port, expectedGeneration);
|
|
188
188
|
const existing = entries.get(key);
|
|
189
189
|
if (existing) return existing;
|
|
190
|
+
// Il listener prova la PROPRIA identita' a chi compone la sfida, e l'altro
|
|
191
|
+
// capo la costruisce con l'id di QUESTA installazione (probeReverseOwner usa
|
|
192
|
+
// `peer.nodeId`, cioe' noi visti da lui). `node.nodeId` e' invece l'id del
|
|
193
|
+
// nodo REMOTO — lo store lo impone, rifiutando come self-reference una voce
|
|
194
|
+
// il cui nodeId eguagli il proprio. Registrarlo faceva firmare due tuple
|
|
195
|
+
// diverse ai due capi: la prova non tornava mai e Share si spegneva con un
|
|
196
|
+
// errore definitivo, non ritentabile.
|
|
197
|
+
const localInstanceId = (nodesStore.loadStore(nodesPath) || {}).nodeId || null;
|
|
198
|
+
// Fail-closed: senza identita' locale il listener nascerebbe con una prova
|
|
199
|
+
// che nessuno puo' validare. Meglio non aprirlo che aprirlo indimostrabile.
|
|
200
|
+
if (!localInstanceId) return null;
|
|
190
201
|
const listener = await reverseSlotListeners.open({
|
|
191
202
|
nodeName: node.name, remotePort: active.port, generation: expectedGeneration,
|
|
192
|
-
instanceId:
|
|
203
|
+
instanceId: localInstanceId, secret: node.acceptToken,
|
|
193
204
|
});
|
|
194
205
|
let launched = nodesTunnel.startReverseForward({
|
|
195
206
|
home: cfg.home || os.homedir(), node, remotePort: active.port, generation: expectedGeneration,
|
|
@@ -256,6 +267,10 @@ function createServer(opts = {}) {
|
|
|
256
267
|
async function verifyRotatablePool(node) {
|
|
257
268
|
const pool = node?.reversePool;
|
|
258
269
|
if (!pool || !node.shared || !node.token || !node.nodeId) return { verified: false, code: 'reverse-pool-not-ready' };
|
|
270
|
+
// Fermarsi al primo slot che non si prova nasconde quali altri fossero
|
|
271
|
+
// sani: l'esito e' identico (servono tutti per 'verified'), ma la diagnosi
|
|
272
|
+
// no. Chi guarda deve poter distinguere "un solo slot non risponde" da
|
|
273
|
+
// "non risponde nessuno", perche' sono due guasti diversi.
|
|
259
274
|
const proven = [];
|
|
260
275
|
for (let slot = 0; slot < pool.slots.length; slot += 1) {
|
|
261
276
|
const candidate = pool.slots[slot];
|
|
@@ -264,9 +279,9 @@ function createServer(opts = {}) {
|
|
|
264
279
|
await startRotatableReverse(node, { slot, generation: candidate.generation });
|
|
265
280
|
temporary = slot !== pool.activeSlot;
|
|
266
281
|
await federation.verifyHubPoolSlot({ node, slot, generation: candidate.generation, fetchImpl: healthFetch });
|
|
267
|
-
proven.push(slot);
|
|
268
|
-
} catch (
|
|
269
|
-
|
|
282
|
+
proven.push({ slot, proven: true });
|
|
283
|
+
} catch (error) {
|
|
284
|
+
proven.push({ slot, proven: false, code: error && error.code });
|
|
270
285
|
} finally {
|
|
271
286
|
if (temporary) await stopRotatableReverse(node.name, { remotePort: candidate.port, generation: candidate.generation });
|
|
272
287
|
}
|
|
@@ -274,15 +289,27 @@ function createServer(opts = {}) {
|
|
|
274
289
|
const current = nodesStore.loadStoreStrict(nodesPath);
|
|
275
290
|
const fresh = nodesStore.getNode(current, node.name);
|
|
276
291
|
if (!fresh?.reversePool) return { verified: false, code: 'reverse-pool-missing' };
|
|
277
|
-
const
|
|
278
|
-
const
|
|
292
|
+
const outcome = reverseRotation.summarizePoolVerification(proven, fresh.reversePool.slots.length);
|
|
293
|
+
const { verifiedSlots, verification, failures } = outcome;
|
|
279
294
|
const updatedPool = { ...fresh.reversePool, verifiedSlots, verification };
|
|
280
295
|
nodesStore.atomicWriteStore(nodesPath, nodesStore.setNodeReversePool(current, fresh.name, updatedPool));
|
|
281
296
|
diagnostics.record(verification === 'verified' ? 'info' : 'warn', 'reverse-pool',
|
|
282
297
|
verification === 'verified' ? 'REVERSE_POOL_VERIFIED' : 'REVERSE_POOL_UNVERIFIABLE',
|
|
283
|
-
verification === 'verified' ? 'Reverse pool verified' : 'Reverse pool could not be fully verified',
|
|
298
|
+
verification === 'verified' ? 'Reverse pool verified' : 'Reverse pool could not be fully verified',
|
|
299
|
+
{ node: fresh.name, verifiedSlots: verifiedSlots.length, slots: fresh.reversePool.slots.length,
|
|
300
|
+
...(failures.length ? { failedSlots: failures } : {}) });
|
|
301
|
+
// Un pool non verificato non e' solo un'etichetta: la rotazione automatica
|
|
302
|
+
// RIFIUTA di partire senza tutti gli slot provati (scelta deliberata, vedi
|
|
303
|
+
// rotateRotatableReverse) e il watcher non viene nemmeno armato. Detto
|
|
304
|
+
// altrimenti: l'autoriparazione del canale reverse resta SPENTA, e finora
|
|
305
|
+
// nessuno lo diceva. Va dichiarato, non dedotto da un aggettivo.
|
|
306
|
+
if (verification !== 'verified') {
|
|
307
|
+
diagnostics.record('warn', 'reverse-pool', 'REVERSE_POOL_ROTATION_INACTIVE',
|
|
308
|
+
'Automatic reverse rotation is not active: the pool is not fully verified',
|
|
309
|
+
{ node: fresh.name, verifiedSlots: verifiedSlots.length, slots: fresh.reversePool.slots.length });
|
|
310
|
+
}
|
|
284
311
|
if (verification === 'verified') ensureReverseWatcher(fresh.name);
|
|
285
|
-
return { verified: verification === 'verified', verification, verifiedSlots };
|
|
312
|
+
return { verified: verification === 'verified', verification, verifiedSlots, ...(failures.length ? { failures } : {}) };
|
|
286
313
|
}
|
|
287
314
|
async function settleRotatableGrace(name, generation) {
|
|
288
315
|
const current = nodesStore.loadStoreStrict(nodesPath);
|