diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts index 1537047..8befa56 100644 --- a/src/controllers/nostr/relay.ts +++ b/src/controllers/nostr/relay.ts @@ -15,12 +15,14 @@ import type { Event, Filter } from '@/deps.ts'; /** Limit of events returned per-filter. */ const FILTER_LIMIT = 100; +/** NIP-01 relay to client message. */ type RelayMsg = | ['EVENT', string, Event] | ['NOTICE', string] | ['EOSE', string] | ['OK', string, boolean, string]; +/** Set up the Websocket connection. */ function connectStream(socket: WebSocket) { socket.onmessage = (e) => { const result = jsonSchema.pipe(clientMsgSchema).safeParse(e.data); @@ -31,6 +33,7 @@ function connectStream(socket: WebSocket) { } }; + /** Handle client message. */ function handleMsg(msg: ClientMsg) { switch (msg[0]) { case 'REQ': @@ -45,6 +48,7 @@ function connectStream(socket: WebSocket) { } } + /** Handle REQ. Start a subscription. */ async function handleReq([_, sub, ...filters]: ClientREQ) { for (const event of await eventsDB.getFilters(prepareFilters(filters))) { send(['EVENT', sub, event]); @@ -52,20 +56,28 @@ function connectStream(socket: WebSocket) { send(['EOSE', sub]); } + /** Handle EVENT. Store the event. */ async function handleEvent([_, event]: ClientEVENT) { try { + // This will store it (if eligible) and run other side-effects. await pipeline.handleEvent(event); send(['OK', event.id, true, '']); - } catch (_e) { - send(['OK', event.id, false, 'blocked: only registered users can post']); + } catch (e) { + if (e instanceof pipeline.RelayError) { + send(['OK', event.id, false, e.message]); + } else { + send(['OK', event.id, false, 'error: something went wrong']); + } } } + /** Handle CLOSE. Close the subscription. */ function handleClose([_, _sub]: ClientCLOSE) { // TODO: ??? return; } + /** Send a message back to the client. */ function send(msg: RelayMsg) { return socket.send(JSON.stringify(msg)); } diff --git a/src/pipeline.ts b/src/pipeline.ts index 00d3eb9..af72b79 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -12,9 +12,9 @@ import { isRelay, nostrDate } from '@/utils.ts'; */ async function handleEvent(event: Event): Promise { await Promise.all([ - trackHashtags(event), storeEvent(event), trackRelays(event), + trackHashtags(event), ]); } @@ -22,6 +22,8 @@ async function handleEvent(event: Event): Promise { async function storeEvent(event: Event): Promise { if (await findUser({ pubkey: event.pubkey }) || await isLocallyFollowed(event.pubkey)) { await eventsDB.insertEvent(event).catch(console.warn); + } else { + return Promise.reject(new RelayError('blocked', 'only registered users can post')); } } @@ -61,4 +63,11 @@ function trackRelays(event: Event) { return addRelays([...relays]); } -export { handleEvent }; +/** NIP-20 command line result. */ +class RelayError extends Error { + constructor(prefix: 'duplicate' | 'pow' | 'blocked' | 'rate-limited' | 'invalid' | 'error', message: string) { + super(`${prefix}: ${message}`); + } +} + +export { handleEvent, RelayError };