aboutsummaryrefslogtreecommitdiffstats
path: root/feed/service.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-01 06:55:14 -0700
committerAdam Soltys <[email protected]>2026-10-01 06:55:14 -0700
commit5d441ebbce6066c3636168ec5332ae9c2bdb8607 (patch)
tree7e70c783fc0be40eccb09af7499db586c5410526 /feed/service.js
parent981ff1c7f89b27ff3d7c2957e211365baff0a359 (diff)
downloadcoinosv3-5d441ebbce6066c3636168ec5332ae9c2bdb8607.tar.gz
coinosv3-5d441ebbce6066c3636168ec5332ae9c2bdb8607.zip
Serve a shared snapshot for the public feed
Build the default Popular feed once on the server and let browsers verify and paint its signed events before slow media finishes. Keep custom relays, pagination, filtering, and relay fallback on their existing paths.\n\nAdd stale-while-refresh persistence, service-worker bypass, development integration, and unit/browser coverage.
Diffstat (limited to 'feed/service.js')
-rw-r--r--feed/service.js29
1 files changed, 29 insertions, 0 deletions
diff --git a/feed/service.js b/feed/service.js
new file mode 100644
index 0000000..304b40f
--- /dev/null
+++ b/feed/service.js
@@ -0,0 +1,29 @@
+import { SimplePool } from 'nostr-tools/pool';
+import { readFile, mkdir, writeFile, rename } from 'node:fs/promises';
+import { dirname } from 'node:path';
+import { buildSnapshot, createSnapshotCache, PUBLIC_FEED_RELAYS } from './cache.js';
+
+export async function startFeedCache() {
+ const file = process.env.FEED_CACHE_FILE || new URL('../db/public-feed.json', import.meta.url).pathname;
+ let initial = null;
+ try {
+ const data = JSON.parse(await readFile(file, 'utf8'));
+ if (data.version === 1 && Number.isFinite(data.generatedAt) && Array.isArray(data.notes) && data.notes.length && Array.isArray(data.profiles)) initial = data;
+ } catch {}
+ // SimplePool verifies signatures and matches filters before accepting events.
+ const pool = new SimplePool();
+ const cache = createSnapshotCache({
+ initial,
+ build: () => buildSnapshot((filter) => pool.querySync(PUBLIC_FEED_RELAYS, filter, { maxWait: 4500 })),
+ save: async (data) => {
+ await mkdir(dirname(file), { recursive: true });
+ await writeFile(file + '.tmp', JSON.stringify(data));
+ await rename(file + '.tmp', file);
+ },
+ onError: (e) => console.warn('[public feed]', e.message),
+ });
+ cache.read(); // warm before the first visitor, reusing the persisted snapshot
+ const timer = setInterval(() => cache.read(), 15_000);
+ timer.unref();
+ return cache;
+}