diff --git a/deno.lock b/deno.lock index c9cbfdc..a4506d2 100644 --- a/deno.lock +++ b/deno.lock @@ -6,6 +6,10 @@ "jsr:@nostrify/nostrify@^0.30.0": "jsr:@nostrify/nostrify@0.30.0", "jsr:@nostrify/types@^0.30.0": "jsr:@nostrify/types@0.30.0", "jsr:@std/encoding@^0.224.1": "jsr:@std/encoding@0.224.3", + "jsr:@std/fmt@^1.0.0-rc.1": "jsr:@std/fmt@1.0.0", + "jsr:@std/fs@^1.0.0-rc.5": "jsr:@std/fs@1.0.1", + "jsr:@std/io@^0.224.3": "jsr:@std/io@0.224.4", + "jsr:@std/log@0.224.5": "jsr:@std/log@0.224.5", "npm:@scure/bip32@^1.4.0": "npm:@scure/bip32@1.4.0", "npm:@scure/bip39@^1.3.0": "npm:@scure/bip39@1.3.0", "npm:lru-cache@^10.2.0": "npm:lru-cache@10.4.3", @@ -36,6 +40,23 @@ }, "@std/encoding@0.224.3": { "integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf" + }, + "@std/fmt@1.0.0": { + "integrity": "8a95c9fdbb61559418ccbc0f536080cf43341655e1444f9d375a66886ceaaa3d" + }, + "@std/fs@1.0.1": { + "integrity": "d6914ca2c21abe591f733b31dbe6331e446815e513e2451b3b9e472daddfefcb" + }, + "@std/io@0.224.4": { + "integrity": "bce1151765e4e70e376039fd72c71672b4d4aae363878a5ee3e58361b81197ec" + }, + "@std/log@0.224.5": { + "integrity": "4612a45189438441bbd923a4cad1cce5c44c6c4a039195a3e8d831ce38894eee", + "dependencies": [ + "jsr:@std/fmt@^1.0.0-rc.1", + "jsr:@std/fs@^1.0.0-rc.5", + "jsr:@std/io@^0.224.3" + ] } }, "npm": { diff --git a/deps.ts b/deps.ts index 9f02635..02201f4 100644 --- a/deps.ts +++ b/deps.ts @@ -2,4 +2,8 @@ export * as nostrToolsRelay from "npm:nostr-tools/relay"; export * as nostrTools from "npm:nostr-tools"; export * as cliffy from "https://deno.land/x/cliffy@v0.25.7/mod.ts"; export * as nostrify from "jsr:@nostrify/nostrify@^0.30.0"; -export * as queue from "jsr:@henrygd/queue@1.0.6"; +import { newQueue as newQueueImport } from "jsr:@henrygd/queue@1.0.6"; +export const newQueue = newQueueImport; +// import * as logImport from "jsr:@std/log@0.224.5"; +// export const log = logImport.getLogger("nostroots-server"); +export * as logPackage from "jsr:@std/log@0.224.5"; diff --git a/log.ts b/log.ts new file mode 100644 index 0000000..7a1fb28 --- /dev/null +++ b/log.ts @@ -0,0 +1,21 @@ +import { logPackage } from "./deps.ts"; + +logPackage.setup({ + handlers: { + console: new logPackage.ConsoleHandler("DEBUG", { + formatter: (record) => + `${record.datetime.toISOString()} [${record.levelName}] ${ + record.msg + } ${JSON.stringify(record.args)}`, + useColors: true, + }), + }, + loggers: { + default: { + level: "DEBUG", + handlers: ["console"], + }, + }, +}); + +export const log = logPackage.getLogger(); diff --git a/main.ts b/main.ts index 762b747..8e3eb10 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,5 @@ import { cliffy, nostrTools } from "./deps.ts"; +import { log } from "./log.ts"; import { repost } from "./validation/repost.ts"; function getOrCreatePrivateKey(maybePrivateKeyNsec?: string) { @@ -12,7 +13,7 @@ function getOrCreatePrivateKey(maybePrivateKeyNsec?: string) { const key = nostrTools.generateSecretKey(); const nsec = nostrTools.nip19.nsecEncode(key); - console.log(`#2yrJza Using random nsec ${nsec}`); + log.info(`#2yrJza Using random nsec ${nsec}`); return key; } @@ -31,7 +32,7 @@ await new cliffy.Command() const privateKey = getOrCreatePrivateKey(options.privateKeyNsec); const maxAgeMinutes = options.maxAgeMinutes; - console.log(`#PnFUPS Startup isDev ${isDev}`); + log.debug(`#PnFUPS Startup isDev ${isDev}`); repost(privateKey, isDev, maxAgeMinutes); }) diff --git a/validation/repost.ts b/validation/repost.ts index 49fc0d3..3856be3 100644 --- a/validation/repost.ts +++ b/validation/repost.ts @@ -10,6 +10,9 @@ import { } from "../common/constants.ts"; import { DEV_PUBKEY } from "../common/constants.ts"; import { validateEvent } from "./validate.ts"; +import { newQueue } from "../deps.ts"; +import { nostrTools } from "../deps.ts"; +import { log } from "../log.ts"; async function getRelayPool(isDev: true | undefined) { const relays = isDev ? DEV_RELAYS : DEFAULT_RELAYS; @@ -39,9 +42,9 @@ async function publishEvent( relayPool: nostrify.NPool, event: nostrify.NostrEvent ) { - console.log("Publishing event…"); + log.debug("#aSmTVL Publishing event…"); await relayPool.event(event); - console.log("Event published."); + log.info("#p26tur Event published."); } /** @@ -102,11 +105,32 @@ function createFilter( return [baseFilter]; } +function processEventFactoryFactory( + relayPool: nostrify.NPool, + privateKey: Uint8Array +) { + return function processEventFactory(event: nostrify.NostrEvent) { + return async function () { + log.debug(`#C1NJbQ Got event`, event); + + const isEventValid = await validateEvent(relayPool, event); + if (!isEventValid) { + log.info(`#u0Prc5 Discarding invalid event ${event.id}`); + return; + } + const repostedEvent = await generateRepostedEvent(event, privateKey); + publishEvent(relayPool, repostedEvent); + }; + }; +} + export async function repost( privateKey: Uint8Array, isDev: true | undefined, maxAgeMinutes: number | undefined ) { + log.debug(`#BmseJH Startup`); + const relayPool = await getRelayPool(isDev); const filter = createFilter(isDev, maxAgeMinutes); @@ -115,16 +139,13 @@ export async function repost( const signal = controller.signal; const subscription = relayPool.req(filter, { signal }); + const queue = newQueue(3); + const processEventFactory = processEventFactoryFactory(relayPool, privateKey); + for await (const msg of subscription) { if (msg[0] === "EVENT") { const event = msg[2]; - const isEventValid = await validateEvent(relayPool, event); - if (!isEventValid) { - console.info(`Discarding event…`); - return; - } - const repostedEvent = await generateRepostedEvent(event, privateKey); - publishEvent(relayPool, repostedEvent); + queue.add(processEventFactory(event)); } else if (msg[0] === "EOSE") { if (isDev) { globalThis.setTimeout(() => { diff --git a/validation/validate.ts b/validation/validate.ts index aa79ca4..89ed132 100644 --- a/validation/validate.ts +++ b/validation/validate.ts @@ -5,6 +5,7 @@ import { import { MINIMUM_TRUSTROOTS_USERNAME_LENGTH } from "../common/constants.ts"; import { MAP_NOTE_KIND } from "../common/constants.ts"; import { nostrify, nostrTools } from "../deps.ts"; +import { log } from "../log.ts"; import { Profile } from "../types.ts"; async function getKindZeroEvent(relayPool: nostrify.NPool, pubKey: string) { @@ -30,7 +31,7 @@ async function getKindZeroEvent(relayPool: nostrify.NPool, pubKey: string) { } function getProfileFromEvent(event: nostrTools.Event): Profile | undefined { - console.log("kindZeroEvent", event); + log.debug("#GHg51j kindZeroEvent", event); try { const profile = JSON.parse(event.content); @@ -79,7 +80,7 @@ async function getNip5PubKey( */ export async function validateEvent( relayPool: nostrify.NPool, - event: nostrTools.Event + event: nostrify.NostrEvent ) { if (event.kind !== MAP_NOTE_KIND) { return false; @@ -93,33 +94,33 @@ export async function validateEvent( const kindZeroEvent = await getKindZeroEvent(relayPool, event.pubkey); if (typeof kindZeroEvent === "undefined") { - console.log("#Kmf59M Skipping event with no kind zero event", { event }); + log.debug("#Kmf59M Skipping event with no kind zero event", { event }); return false; } const profile = getProfileFromEvent(kindZeroEvent); if (typeof profile === "undefined") { - console.log("#pd4X7C Skipping event with invalid profile", { event }); + log.debug("#pd4X7C Skipping event with invalid profile", { event }); return false; } const { trustrootsUsername } = profile; - console.log(`Checking username ${trustrootsUsername}`); + log.debug(`#yUtER5 Checking username ${trustrootsUsername}`); const nip5PubKey = await getNip5PubKey(trustrootsUsername); if (typeof nip5PubKey !== "string") { - console.log("#b0gWmE Failed to get string nip5 pubkey", { event }); + log.debug("#b0gWmE Failed to get string nip5 pubkey", { event }); return false; } if (event.pubkey !== nip5PubKey) { - console.log("#dtKr5H Event failed nip5 validation", { event }); + log.debug("#dtKr5H Event failed nip5 validation", { event }); return false; } - console.log("#lpglLu Event passed validation", event); + log.debug("#lpglLu Event passed validation", event); return true; }