aboutsummaryrefslogtreecommitdiffstats
path: root/names/server.js
diff options
context:
space:
mode:
Diffstat (limited to 'names/server.js')
-rw-r--r--names/server.js11
1 files changed, 9 insertions, 2 deletions
diff --git a/names/server.js b/names/server.js
index 104aa5e..dacbda6 100644
--- a/names/server.js
+++ b/names/server.js
@@ -15,7 +15,7 @@
//
// Deliberately dumb: one JSON state file, no accounts, no email.
-import { readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
+import { readFileSync, writeFileSync, renameSync, mkdirSync, openSync, fsyncSync, closeSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { verifyEvent, finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure';
import * as nip44 from 'nostr-tools/nip44';
@@ -65,14 +65,21 @@ const NAME_RE = /^[a-z0-9][a-z0-9._-]{0,29}$/;
const NEW_NAME_RE = /^[a-z0-9]{2,24}$/;
const NEW_NAME_ERR = 'names can only have letters and numbers (2–24)';
+// A missing file is a fresh registrar; an unreadable one is not. Starting
+// empty over a corrupt file would persist the empty state on top of it.
let state = (() => {
- try { return JSON.parse(readFileSync(STATE, 'utf8')); } catch { return { names: {} }; }
+ let raw;
+ try { raw = readFileSync(STATE, 'utf8'); } catch (e) { if (e.code === 'ENOENT') return { names: {} }; throw e; }
+ return JSON.parse(raw);
})();
function persist() {
mkdirSync(dirname(STATE), { recursive: true });
const tmp = STATE + '.tmp';
writeFileSync(tmp, JSON.stringify(state));
+ // flush before the rename, or a crash can leave a renamed-but-empty file
+ const fd = openSync(tmp, 'r'); fsyncSync(fd); closeSync(fd);
renameSync(tmp, STATE);
+ const dfd = openSync(dirname(STATE), 'r'); fsyncSync(dfd); closeSync(dfd);
}
// ---------------------------------------------------------------------------