UmbrelApps: community app store whatsnext
Electrum Gate, Evolu Relay en Atreides Assets, met de image-recepten in tools/ en de tests in tests/. Begonnen zonder voorgeschiedenis op 09-10-2026. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
85d85a1295
63 files changed
+13987
No files matched your search
@@ -0,0 +1,107 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// De staat op schijf: de allowlist en het opdrachtbestand van de statuspagina.
|
||||
//
|
||||
// Dit is de enige plek met bestanden erin. Het beleid zelf staat in `policy.js`
|
||||
// en weet hier niets van.
|
||||
//
|
||||
// Waar het staat: onder `${APP_DATA_DIR}/data/`, náást de database van de relay
|
||||
// en niet erin. De relay bezit zijn eigen SQLite en daar schrijven wij niet in.
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
import { createEmptyState, normalizeState } from './policy.js';
|
||||
|
||||
export const stateFileName = 'owners.json';
|
||||
export const commandFileName = 'command.json';
|
||||
|
||||
/**
|
||||
* Leest de allowlist.
|
||||
*
|
||||
* Drie uitkomsten, en de derde is de interessante:
|
||||
* - `new`: er is nog niets, dus een verse staat met de leerstand aan;
|
||||
* - `ok`: gelezen en begrepen;
|
||||
* - `corrupt`: er stond iets wat we niet begrijpen. Dan schuiven we het bestand
|
||||
* opzij en beginnen we met de leerstand **uit**.
|
||||
*
|
||||
* Die laatste keuze is bewust en volgt de regel die deze store al kent van
|
||||
* Electrum Gate: bij twijfel weigert de app. Een onleesbaar bestand betekent dat
|
||||
* we niet weten wie er toegelaten was; met de leerstand aan zou de eerstvolgende
|
||||
* die verbindt stilzwijgend de nieuwe eigenaar worden, en dat hoeft je eigen
|
||||
* apparaat niet te zijn. Dan liever een app die niemand toelaat en dat op de
|
||||
* pagina toont.
|
||||
*/
|
||||
export const readState = (dataDir, now) => {
|
||||
const file = join(dataDir, stateFileName);
|
||||
if (!existsSync(file)) return { state: createEmptyState(), status: 'new' };
|
||||
|
||||
let parsed = null;
|
||||
try {
|
||||
parsed = JSON.parse(readFileSync(file, 'utf8'));
|
||||
} catch {
|
||||
parsed = null;
|
||||
}
|
||||
|
||||
const state = parsed === null ? null : normalizeState(parsed);
|
||||
if (state !== null) return { state, status: 'ok' };
|
||||
|
||||
// Opzij schuiven en niet weggooien: als dit ooit gebeurt, wil je kunnen zien
|
||||
// wat erin stond.
|
||||
const aside = `${file}.corrupt-${now.replace(/[:.]/g, '-')}`;
|
||||
try {
|
||||
renameSync(file, aside);
|
||||
} catch {
|
||||
// Lukt het opzij schuiven niet, dan gaan we alsnog dicht. De melding hieronder
|
||||
// is dan het enige dat de gebruiker heeft, en dat is nog steeds beter dan
|
||||
// stilzwijgend iemand toelaten.
|
||||
}
|
||||
return { state: { ...createEmptyState(), learning: false }, status: 'corrupt' };
|
||||
};
|
||||
|
||||
/**
|
||||
* Schrijft de allowlist. Eerst naar een tijdelijk bestand en dan hernoemen, zodat
|
||||
* een onderbroken schrijfactie geen half bestand achterlaat: hernoemen binnen
|
||||
* dezelfde map is atomair.
|
||||
*/
|
||||
export const writeState = (dataDir, state) => {
|
||||
mkdirSync(dataDir, { recursive: true });
|
||||
const file = join(dataDir, stateFileName);
|
||||
const temporary = `${file}.tmp`;
|
||||
writeFileSync(temporary, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
|
||||
renameSync(temporary, file);
|
||||
};
|
||||
|
||||
/**
|
||||
* Haalt een opdracht van de statuspagina op en verwijdert het bestand meteen.
|
||||
*
|
||||
* Verwijderen vóór het uitvoeren is met opzet: een opdracht die het proces laat
|
||||
* struikelen mag niet bij elke ronde opnieuw worden geprobeerd.
|
||||
*/
|
||||
export const takeCommand = (dataDir) => {
|
||||
const file = join(dataDir, commandFileName);
|
||||
if (!existsSync(file)) return null;
|
||||
|
||||
let raw = null;
|
||||
try {
|
||||
raw = readFileSync(file, 'utf8');
|
||||
} catch {
|
||||
raw = null;
|
||||
}
|
||||
|
||||
try {
|
||||
unlinkSync(file);
|
||||
} catch {
|
||||
// Niets aan te doen; de volgende ronde probeert het opnieuw.
|
||||
}
|
||||
|
||||
if (raw === null) return null;
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/** De map waarin de staat hoort, afgeleid van het pad van de relay-database. */
|
||||
export const dataDirectoryFor = (relayDatabaseFile) => dirname(relayDatabaseFile);
|
||||
Reference in new issue
Block a user