aboutsummaryrefslogtreecommitdiffstats
path: root/names/server.js
diff options
context:
space:
mode:
Diffstat (limited to 'names/server.js')
-rw-r--r--names/server.js33
1 files changed, 13 insertions, 20 deletions
diff --git a/names/server.js b/names/server.js
index 9d94b51..c4ed650 100644
--- a/names/server.js
+++ b/names/server.js
@@ -13,10 +13,10 @@
// WHAT THIS SERVICE LEARNS: name ↔ nostr pubkey ↔ payment URI. All of it is
// public by construction (DNS is public). It cannot spend anything.
//
-// Deliberately dumb: one JSON state file, no accounts, no email.
+// Deliberately dumb: one SQLite file (store.js), no accounts, no email.
-import { readFileSync, writeFileSync, renameSync, mkdirSync, openSync, fsyncSync, closeSync } from 'node:fs';
-import { dirname, join } from 'node:path';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
import { verifyEvent, finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure';
import * as nip44 from 'nostr-tools/nip44';
import { wrapManyEvents } from 'nostr-tools/nip17';
@@ -34,10 +34,12 @@ import { decodeNoffer } from '../coinosv3/src/noffer.js';
import { decodeBolt11 } from '../coinosv3/src/ark/lightning.js';
import { decodeOffer } from '../coinosv3/src/bolt12.js';
import { registrationIdentity } from './ownership.js';
+import { openStore } from './store.js';
const CFG = JSON.parse(readFileSync(process.env.NAMES_CONFIG
|| join(import.meta.dir, 'config.json'), 'utf8'));
-// { port, domains: { '<domain>': { zoneId } }, domain (default), cfEmail, cfKey, stateFile,
+// { port, domains: { '<domain>': { zoneId } }, domain (default), cfEmail, cfKey,
+// dbFile (SQLite state), stateFile (the old JSON state, imported once),
// ln: { kind:'socket', path } — the ASP's CLN, for per-name bolt12 offers,
// forwarder: { mnemonic, ark, esplora, network } — a small float ark wallet
// that forwards settled offer payments to the name's ark address }
@@ -46,6 +48,7 @@ const DOMAIN = CFG.domain || 'coinos.io'; // the default domain for claims
const PUBLIC_BASE = CFG.publicBase || 'https://names.coinos.io';
const DOMAINS = CFG.domains || { [DOMAIN]: { zoneId: CFG.zoneId } };
const STATE = CFG.stateFile || join(import.meta.dir, 'data', 'names.json');
+const DB = CFG.dbFile || STATE.replace(/\.json$/, '') + '.db';
const TTL = 300; // record TTL: BIP-353 wallets cache by this
const log = (...a) => console.log(new Date().toISOString(), ...a);
@@ -65,22 +68,12 @@ 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 = (() => {
- 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);
-}
+// State lives in SQLite: each persist() is one transaction that rewrites only
+// the records that changed. On the first start the old JSON state file is
+// imported (a corrupt one stops startup rather than importing nothing).
+const store = openStore(DB, { legacyJson: STATE });
+const state = store.state;
+function persist() { store.persist(); }
// ---------------------------------------------------------------------------
// Cloudflare DNS