Testclient: Aankloppen heet Testen, en een weigering stopt de herhaallus
Voorstel van de gebruiker, en het tweede deel loste meer op dan een knop. Een verbinding die geweigerd wordt bleef eindeloos opnieuw proberen, en elke poging is een regel in de weigerlijst van de relay; zo liep die lijst tijdens deze sessie ook vol. - klop heet test op de opdrachtregel en Testen op de pagina. - Verbinden gaat uit zodra een test zegt dat de eigenaar geweigerd wordt, met de uitkomst in het rood naast de naam. Onbekend telt als toegestaan, want anders moet je eerst testen om iets te mogen en is dat zelf ook een poging. Blob schrijven gaat door dezelfde poort en schrijft dan lokaal verder. - Een lopende verbinding stopt zichzelf: na drie mislukte sockets wordt er precies een keer getest, en bij een weigering gaat de verbinding eruit. Is het geen weigering, dan blijft hij staan, want dan is opnieuw proberen juist goed. De melding in een dialoogvenster is eruit; de uitkomst staat naast de naam en in het log en die blijven staan. In de browser nagekeken: testen geeft 401, de regel verschijnt rood, en Verbinden is uitgegrijsd voor de geteste eigenaar terwijl de ongeteste hem houdt. Suite: 478 goed, 0 fout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,8 +43,10 @@
|
||||
|
||||
8. **`sync` en het bedieningsvlak weten niet of een verbinding er nog is.** Er staat "verbonden" zodra de
|
||||
instantie gemaakt is, en Evolu meldt niet dat de relay weggevallen is (hij probeert het gewoon opnieuw).
|
||||
Voor de proeven is `klop` het antwoord, maar op de pagina staat een groen bolletje dat meer belooft dan
|
||||
het weet. Evolu heeft een `SyncState`; uitzoeken of daar iets bruikbaars in zit.
|
||||
**Deels opgelost op 09-09-2026:** het bedieningsvlak telt mislukte sockets en test na drie mislukkingen
|
||||
één keer, zodat een geweigerde eigenaar niet eindeloos blijft kloppen. Wat er nog niet is, is een groen
|
||||
bolletje dat werkelijk "er staat nu een verbinding" betekent in plaats van "we hebben er een geopend".
|
||||
Evolu heeft een `SyncState`; uitzoeken of daar iets bruikbaars in zit.
|
||||
|
||||
9. **Twee toegelaten eigenaars zijn uit de allowlist van de relay verdwenen (09-09-2026), en we weten
|
||||
niet waarom.** Ze gaven allebei 101 en even later allebei 401, met "turned away" op de statuspagina.
|
||||
|
||||
@@ -88,5 +88,26 @@ kort daarvoor nog twee keer 101 gaf.
|
||||
|
||||
**Daarmee ligt de vraag bij de relay en niet meer bij de cliënt: twee eigenaars die toegelaten waren zijn
|
||||
uit de allowlist verdwenen.** Wat er op de statuspagina van staat is "turned away". Wat dit kan zijn staat
|
||||
in [OPEN.md](OPEN.md) punt 10; het antwoord zit in de logregels van de relay-container en die kan alleen de
|
||||
in [OPEN.md](OPEN.md) punt 9; het antwoord zit in de logregels van de relay-container en die kan alleen de
|
||||
gebruiker lezen.
|
||||
|
||||
## 09-09-2026 - "Aankloppen" heet nu "Testen", en een weigering stopt de lus
|
||||
|
||||
Voorstel van de gebruiker, en het tweede deel loste meer op dan een knop. Een verbinding die geweigerd
|
||||
wordt bleef namelijk eindeloos opnieuw proberen, en élke poging is een regel in de weigerlijst van de
|
||||
relay; dat is precies hoe die lijst tijdens deze sessie volliep. Drie wijzigingen:
|
||||
|
||||
- **de naam.** `klop` heet `test` op de opdrachtregel en "Testen" op de pagina. Het verschil met verbinden
|
||||
staat er nu ook uitgelegd: testen is één vraag met een statuscode als antwoord, verbinden is een gesprek
|
||||
dat Evolu eindeloos blijft herstellen;
|
||||
- **"Verbinden" gaat uit zodra een test zegt dat de eigenaar geweigerd wordt**, met de uitkomst in het rood
|
||||
naast de naam. Onbekend telt als toegestaan, en dat is met opzet: eerst moeten testen om iets te mogen zou
|
||||
betekenen dat je altijd een poging in de weigerlijst zet voordat je iets kunt doen. "Blob schrijven" gaat
|
||||
door dezelfde poort en schrijft dan lokaal verder;
|
||||
- **een lopende verbinding stopt zichzelf.** Na drie mislukte sockets wordt er precies één keer getest; is
|
||||
het een weigering, dan gaat de verbinding eruit. Is het geen weigering, dan blijft hij staan, want dan is
|
||||
het een relay die even weg is en daar is opnieuw proberen juist goed voor.
|
||||
|
||||
De melding in een dialoogvenster is eruit: de uitkomst staat naast de naam en in het log, en die blijven
|
||||
staan. In de browser nagekeken: testen geeft 401, de regel verschijnt in het rood, en "Verbinden" is
|
||||
uitgegrijsd voor de geteste eigenaar terwijl de ongeteste hem gewoon houdt.
|
||||
|
||||
@@ -42,9 +42,11 @@
|
||||
- [x] **Een blob schrijven met een instelbare omvang, en teruglezen.** `schrijf` en `lees`, met
|
||||
`--bytes` en `--lokaal`
|
||||
- [x] **De losse WebSocket-poging die de HTTP-statuscode wél te zien krijgt:** `src/probe.js` en de
|
||||
opdracht `klop`. Met `node:http` en niet met de WebSocket van Node, want die geeft je bij een
|
||||
weigering een `error` en geen 401. Daarmee is [OPEN.md](OPEN.md) punt 1 opgelost vóórdat het een
|
||||
probleem werd
|
||||
opdracht `test` (tot 09-09-2026 `klop` geheten, hernoemd op voorstel van de gebruiker). Met
|
||||
`node:http` en niet met de WebSocket van Node, want die geeft je bij een weigering een `error` en
|
||||
geen 401. Daarmee is [OPEN.md](OPEN.md) punt 1 opgelost vóórdat het een probleem werd
|
||||
- [x] **`spiegel <naam>`**, de enige proef die van de cliëntkant kan bewijzen dat er werkelijk iets op de
|
||||
relay staat: dezelfde eigenaar in een lege database ernaast
|
||||
|
||||
## Fase 3 - het bedieningsvlak (af, 09-09-2026)
|
||||
|
||||
@@ -57,6 +59,10 @@
|
||||
nodig is, waarschuwen als `.env` ontbreekt, openen de browser en houden het venster open bij een fout
|
||||
- [x] **In de browser nagekeken** (09-09-2026): een blob schrijven en teruglezen werkt vanuit de pagina,
|
||||
de gegevens zijn dezelfde als die van de opdrachtregel, en licht en donker kloppen allebei
|
||||
- [x] **"Verbinden" gaat uit na een weigering** (09-09-2026, voorstel van de gebruiker), en een lopende
|
||||
verbinding die blijft falen test zichzelf één keer en stopt bij een weigering. Dat laatste is de
|
||||
eigenlijke opbrengst: zonder die rem vult één geweigerde verbinding de weigerlijst van de relay met
|
||||
pogingen, en dat is tijdens deze sessie ook gebeurd
|
||||
|
||||
## Fase 4 - de proeven
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export const OPDRACHTEN = {
|
||||
lijst: { argumenten: [], uitleg: 'Toont de eigenaars in het register' },
|
||||
schrijf: { argumenten: ['naam', 'label'], uitleg: 'Schrijft een blob weg voor deze eigenaar' },
|
||||
lees: { argumenten: ['naam'], uitleg: 'Toont de blobs van deze eigenaar' },
|
||||
klop: { argumenten: ['naam'], uitleg: 'Klopt aan bij de relay en toont de HTTP-status' },
|
||||
test: { argumenten: ['naam'], uitleg: 'Test of de relay deze eigenaar toelaat, zonder te verbinden' },
|
||||
spiegel: { argumenten: ['naam'], uitleg: 'Haalt deze eigenaar op in een lege database, via de relay' },
|
||||
sync: { argumenten: ['naam'], uitleg: 'Verbindt en blijft draaien tot Ctrl-C' },
|
||||
vergeet: { argumenten: ['naam'], uitleg: 'Haalt een eigenaar uit het register' },
|
||||
|
||||
@@ -146,10 +146,10 @@ const opdrachten = {
|
||||
}
|
||||
},
|
||||
|
||||
klop: async ({ naam }) => {
|
||||
test: async ({ naam }) => {
|
||||
eisRelay();
|
||||
const { regel } = haalEigenaarOp(naam);
|
||||
console.log(`Aankloppen bij ${config.relayUrl} als ${regel.ownerId}`);
|
||||
console.log(`Testen bij ${config.relayUrl} als ${regel.ownerId}`);
|
||||
const uitkomst = await klopAan(config.relayUrl, regel.ownerId);
|
||||
console.log(` status ${uitkomst.status ?? '-'}`);
|
||||
console.log(` uitkomst ${uitkomst.uitleg}`);
|
||||
|
||||
@@ -175,6 +175,13 @@
|
||||
: 'niet verbonden';
|
||||
rij.append(toestand);
|
||||
|
||||
if (eigenaar.test) {
|
||||
const test = document.createElement('span');
|
||||
test.className = eigenaar.test.geaccepteerd ? 'melding' : 'melding fout';
|
||||
test.textContent = `getest ${tijd(eigenaar.test.tijd)}: ${eigenaar.test.status ?? '-'} ${eigenaar.test.uitleg}`;
|
||||
rij.append(test);
|
||||
}
|
||||
|
||||
const duw = document.createElement('div');
|
||||
duw.className = 'duw';
|
||||
|
||||
@@ -184,14 +191,26 @@
|
||||
omvang.style.width = '6em';
|
||||
omvang.addEventListener('input', () => omvangPer.set(eigenaar.naam, omvang.value));
|
||||
|
||||
duw.append(
|
||||
knop('Aankloppen', async () => {
|
||||
const uit = await roep('klop', { naam: eigenaar.naam });
|
||||
alert(`status ${uit.status ?? '-'}: ${uit.uitleg}`);
|
||||
}),
|
||||
eigenaar.verbonden
|
||||
// Verbinden gaat uit zodra een test zegt dat deze eigenaar geweigerd
|
||||
// wordt. Niet uit netheid: Evolu blijft bij een weigering eindeloos
|
||||
// opnieuw proberen, en élke poging is een regel in de weigerlijst van de
|
||||
// relay. Onbekend telt als toegestaan, want anders moet je eerst testen
|
||||
// om iets te mogen, en dat is zelf ook een poging.
|
||||
const verbindKnop = eigenaar.verbonden
|
||||
? knop('Verbreken', () => roep('verbreek', { naam: eigenaar.naam }))
|
||||
: knop('Verbinden', () => roep('verbind', { naam: eigenaar.naam })),
|
||||
: knop('Verbinden', () => roep('verbind', { naam: eigenaar.naam }));
|
||||
|
||||
if (!eigenaar.verbonden && !eigenaar.magVerbinden) {
|
||||
verbindKnop.disabled = true;
|
||||
verbindKnop.title = 'De relay weigert deze eigenaar. Laat hem toe op de statuspagina van de app en test opnieuw.';
|
||||
}
|
||||
|
||||
duw.append(
|
||||
// Geen melding in een dialoogvenster: de uitkomst komt naast de naam te
|
||||
// staan en in het log, en die blijven staan. Een dialoog moet je
|
||||
// wegklikken en is daarna weg.
|
||||
knop('Testen', () => roep('test', { naam: eigenaar.naam })),
|
||||
verbindKnop,
|
||||
omvang,
|
||||
knop('Blob schrijven', () =>
|
||||
roep('schrijf', { naam: eigenaar.naam, label: `blob ${tijd(new Date().toISOString())}`, bytes: omvang.value }),
|
||||
|
||||
@@ -28,6 +28,27 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
const open = new Map();
|
||||
const log = [];
|
||||
|
||||
/**
|
||||
* De laatste uitkomst van "Testen", per eigenaar.
|
||||
*
|
||||
* Hierop wordt de knop "Verbinden" uitgezet. Onbekend telt als toegestaan, en
|
||||
* dat is met opzet: anders moet je eerst testen voordat je iets mag, en een
|
||||
* test is zelf een poging die in de weigerlijst van de relay belandt.
|
||||
*/
|
||||
const laatsteTest = new Map();
|
||||
|
||||
/**
|
||||
* Hoeveel sockets er achter elkaar mislukt zijn, per eigenaar.
|
||||
*
|
||||
* Evolu is local-first en blijft eindeloos opnieuw proberen. Dat is bij een
|
||||
* relay die even weg is precies goed, maar bij een eigenaar die geweigerd
|
||||
* wordt is elke poging een regel in de weigerlijst van de relay. Vandaar de
|
||||
* teller: na een paar mislukkingen wordt er één keer getest, en bij een
|
||||
* weigering gaat de verbinding eruit in plaats van door te blijven kloppen.
|
||||
*/
|
||||
const socketFouten = new Map();
|
||||
const FOUTEN_VOOR_TEST = 3;
|
||||
|
||||
const meld = (tekst) => {
|
||||
log.unshift({ tijd: new Date().toISOString(), tekst });
|
||||
if (log.length > MAX_LOG) log.length = MAX_LOG;
|
||||
@@ -48,6 +69,33 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
meld(`${naam}: defect uit een worker, zie de terminal`);
|
||||
console.error(defect);
|
||||
},
|
||||
onWebSocket: (bericht) => {
|
||||
if (bericht.wat === 'open') {
|
||||
socketFouten.set(naam, 0);
|
||||
meld(`${naam}: socket open`);
|
||||
return;
|
||||
}
|
||||
if (bericht.wat !== 'dicht') return;
|
||||
|
||||
const aantal = (socketFouten.get(naam) ?? 0) + 1;
|
||||
socketFouten.set(naam, aantal);
|
||||
if (aantal !== FOUTEN_VOOR_TEST) return;
|
||||
|
||||
// Precies één keer testen, bij de derde mislukking. Daarna weten we of
|
||||
// dit een weigering is of een relay die even weg is, en dat scheelt de
|
||||
// relay een weigerlijst vol pogingen.
|
||||
meld(`${naam}: ${aantal} sockets mislukt (${bericht.detail}), wordt getest`);
|
||||
void (async () => {
|
||||
const uitkomst = await klopAan(config.relayUrl, regel.ownerId);
|
||||
laatsteTest.set(naam, { ...uitkomst, tijd: new Date().toISOString() });
|
||||
if (uitkomst.geaccepteerd) {
|
||||
meld(`${naam}: de relay laat hem toe, dus dit is iets anders dan een weigering`);
|
||||
return;
|
||||
}
|
||||
meld(`${naam}: geweigerd (${uitkomst.uitleg}), verbinding wordt verbroken`);
|
||||
await verbreek(naam);
|
||||
})();
|
||||
},
|
||||
});
|
||||
|
||||
const ingang = { store, sinds: new Date().toISOString(), metRelay };
|
||||
@@ -60,6 +108,7 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
const ingang = open.get(naam);
|
||||
if (!ingang) return;
|
||||
open.delete(naam);
|
||||
socketFouten.delete(naam);
|
||||
await ingang.store.sluit();
|
||||
meld(`${naam} losgekoppeld`);
|
||||
};
|
||||
@@ -76,6 +125,7 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
blobs = null;
|
||||
}
|
||||
}
|
||||
const test = laatsteTest.get(regel.naam) ?? null;
|
||||
eigenaars.push({
|
||||
naam: regel.naam,
|
||||
ownerId: regel.ownerId,
|
||||
@@ -85,6 +135,11 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
sinds: ingang?.sinds ?? null,
|
||||
transportUrl: ingang?.store.transportUrl ?? null,
|
||||
blobs,
|
||||
test,
|
||||
// Onbekend telt als toegestaan: eerst moeten testen om te mogen
|
||||
// verbinden zou betekenen dat je altijd een poging in de weigerlijst
|
||||
// van de relay zet voordat je iets kunt doen.
|
||||
magVerbinden: test === null || test.geaccepteerd === true,
|
||||
});
|
||||
}
|
||||
return { relayUrl: config.relayUrl, eigenaars, log };
|
||||
@@ -115,7 +170,12 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
if (!Number.isInteger(omvang) || omvang < 1 || omvang > 8 * 1024 * 1024) {
|
||||
throw new Error('bytes moet tussen 1 en 8388608 liggen.');
|
||||
}
|
||||
const ingang = open.get(naam) ?? (await verbind(naam, Boolean(config.relayUrl)));
|
||||
// Schrijven opent zo nodig zelf een verbinding, dus dezelfde poort als bij
|
||||
// de knop "Verbinden": een geweigerde eigenaar gaat lokaal verder in
|
||||
// plaats van de weigerlijst van de relay te vullen.
|
||||
const test = laatsteTest.get(naam);
|
||||
const geweigerd = test != null && test.geaccepteerd === false;
|
||||
const ingang = open.get(naam) ?? (await verbind(naam, Boolean(config.relayUrl) && !geweigerd));
|
||||
const body = new Uint8Array(omvang);
|
||||
for (let i = 0; i < omvang; i += 1) body[i] = i % 256;
|
||||
const geschreven = await ingang.store.schrijf(label || 'blob', body);
|
||||
@@ -135,17 +195,19 @@ export const startUi = async ({ config, register, databasesIn }) => {
|
||||
};
|
||||
},
|
||||
|
||||
klop: async ({ naam }) => {
|
||||
test: async ({ naam }) => {
|
||||
const regel = register.zoek(naam);
|
||||
if (!regel) throw new Error(`Geen eigenaar "${naam}".`);
|
||||
if (!config.relayUrl) throw new Error('Geen relay-adres. Zet RELAY_URL in .env.');
|
||||
const uitkomst = await klopAan(config.relayUrl, regel.ownerId);
|
||||
meld(`${naam}: aankloppen gaf ${uitkomst.status ?? '-'}, ${uitkomst.uitleg}`);
|
||||
laatsteTest.set(naam, { ...uitkomst, tijd: new Date().toISOString() });
|
||||
meld(`${naam}: testen gaf ${uitkomst.status ?? '-'}, ${uitkomst.uitleg}`);
|
||||
return uitkomst;
|
||||
},
|
||||
|
||||
vergeet: async ({ naam }) => {
|
||||
await verbreek(naam);
|
||||
laatsteTest.delete(naam);
|
||||
const weg = register.vergeet(naam);
|
||||
meld(`${weg.naam} uit het register gehaald`);
|
||||
return { naam: weg.naam };
|
||||
|
||||
Reference in New Issue
Block a user