mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Submissions persistence: publish to nos.lol + admin re-broadcast mirror
Submissions are signed by throwaway ephemeral keys and public relays prune events from pubkeys with no social graph, so the queue quietly evaporated. - Forms: also publish to nos.lol (better retention, already in the app's query set) alongside snort/damus/primal. - submissionFacade: on each fetch, re-broadcast the still-pending signed events back to our relays (publishEventToRelays) so pruning doesn't drop them. Best-effort, fire-and-forget — never blocks or fails the fetch; no-ops when no relay pool is available. - test: fetch still returns the pending queue when a pool is present. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
d091ce8bac
commit
5eb7545a3f
7 files changed
+51
-1
No files matched your search
@@ -284,6 +284,7 @@
|
|||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,7 @@
|
|||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,7 @@
|
|||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,7 @@
|
|||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
|||||||
@@ -226,6 +226,7 @@
|
|||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
SUBMISSION_KIND,
|
SUBMISSION_KIND,
|
||||||
parseSubmissionEvent,
|
parseSubmissionEvent,
|
||||||
} from "./submissionService.js";
|
} from "./submissionService.js";
|
||||||
|
import { publishEventToRelays } from "../nostrPublish.js";
|
||||||
import { devLogger } from "../utils/logger.js";
|
import { devLogger } from "../utils/logger.js";
|
||||||
|
|
||||||
export const RESOLVED_LIST_KIND = 30000;
|
export const RESOLVED_LIST_KIND = 30000;
|
||||||
@@ -33,6 +34,28 @@ function getManagerAndRelays(client) {
|
|||||||
return { manager, relays };
|
return { manager, relays };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep pending submissions alive. They're authored by throwaway ephemeral keys
|
||||||
|
// and public relays prune events from pubkeys with no social graph, so the queue
|
||||||
|
// quietly evaporates over time. A signed event can be re-published by anyone, so
|
||||||
|
// on each fetch we re-broadcast the still-pending events back to our relays to
|
||||||
|
// refresh them. Best-effort and fire-and-forget — never blocks or fails a fetch.
|
||||||
|
function mirrorPendingSubmissions(rawEvents, client) {
|
||||||
|
try {
|
||||||
|
const pool = client?.pool;
|
||||||
|
const relays = Array.isArray(client?.relays) ? client.relays : [];
|
||||||
|
if (!pool || !relays.length || !Array.isArray(rawEvents) || !rawEvents.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const event of rawEvents) {
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => publishEventToRelays(pool, relays, event))
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// best effort — mirroring must never break the submissions view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function newestByCreatedAt(events) {
|
function newestByCreatedAt(events) {
|
||||||
let newest = null;
|
let newest = null;
|
||||||
for (const event of Array.isArray(events) ? events : []) {
|
for (const event of Array.isArray(events) ? events : []) {
|
||||||
@@ -93,7 +116,16 @@ export async function fetchPendingSubmissions({
|
|||||||
byApplicant.set(key, submission);
|
byApplicant.set(key, submission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...byApplicant.values()];
|
const pending = [...byApplicant.values()];
|
||||||
|
|
||||||
|
// Refresh the still-pending events on our relays so they don't get pruned.
|
||||||
|
const pendingIds = new Set(pending.map((submission) => submission.eventId));
|
||||||
|
const rawPending = (Array.isArray(subEvents) ? subEvents : []).filter(
|
||||||
|
(event) => event && pendingIds.has(event.id),
|
||||||
|
);
|
||||||
|
mirrorPendingSubmissions(rawPending, client);
|
||||||
|
|
||||||
|
return pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -94,6 +94,19 @@ test("fetchPendingSubmissions returns pending, excludes resolved, dedupes by app
|
|||||||
assert.equal(alice.id, "s2", "newest application for alice wins");
|
assert.equal(alice.id, "s2", "newest application for alice wins");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("fetchPendingSubmissions still returns pending when a pool is present (mirror is best-effort)", async () => {
|
||||||
|
const events = [
|
||||||
|
submissionEvent({ id: "m1", applicant: "npub-x", created_at: 100 }),
|
||||||
|
];
|
||||||
|
const client = mockClient({ events });
|
||||||
|
// Presence of a pool triggers the re-broadcast mirror path; any publish
|
||||||
|
// failure is swallowed and must not affect the returned queue.
|
||||||
|
client.pool = {};
|
||||||
|
const pending = await fetchPendingSubmissions({ adminHex: ADMIN, client });
|
||||||
|
assert.equal(pending.length, 1);
|
||||||
|
assert.equal(pending[0].applicant, "npub-x");
|
||||||
|
});
|
||||||
|
|
||||||
test("fetchPendingSubmissions returns [] for a blank admin or no relays", async () => {
|
test("fetchPendingSubmissions returns [] for a blank admin or no relays", async () => {
|
||||||
assert.deepEqual(await fetchPendingSubmissions({ adminHex: "", client: mockClient({}) }), []);
|
assert.deepEqual(await fetchPendingSubmissions({ adminHex: "", client: mockClient({}) }), []);
|
||||||
const noRelays = { ...mockClient({}), relays: [] };
|
const noRelays = { ...mockClient({}), relays: [] };
|
||||||
|
|||||||
Reference in new issue
Block a user