From c3b8ad1c283ea10c6d99249278b7d019a7ffb246 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 7 Apr 2023 21:37:16 -0500 Subject: [PATCH] Scaffold db, handler, query --- src/db.ts | 7 +++++++ src/deps.ts | 7 +++---- src/gossip.ts | 21 +++++++++++++++++++++ src/handler.ts | 19 +++++++++++++++++++ src/query.ts | 7 +++++++ src/schema.ts | 20 +++++++++++++++++++- 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 src/db.ts create mode 100644 src/gossip.ts create mode 100644 src/handler.ts create mode 100644 src/query.ts diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..022a952 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,7 @@ +import { lmdb } from '@/deps.ts'; + +const db = lmdb.open('db', {}); + +const gossipDB = db.openDB('gossip', { dupSort: true }); + +export { db, gossipDB }; diff --git a/src/deps.ts b/src/deps.ts index 8385391..edc893a 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,7 +1,6 @@ -import { Context, Hono, validator } from 'https://deno.land/x/hono@v3.0.2/mod.ts'; -export { Hono, validator }; +export { type Context, Hono, validator } from 'https://deno.land/x/hono@v3.0.2/mod.ts'; export { cors } from 'https://deno.land/x/hono@v3.0.2/middleware.ts'; export { z } from 'https://deno.land/x/zod@v3.20.5/mod.ts'; export { Author, RelayPool } from 'https://dev.jspm.io/nostr-relaypool@0.5.3'; -export { getEventHash, getPublicKey, nip19, signEvent } from 'npm:nostr-tools@^1.7.4'; -export type { Context }; +export { type Filter, getEventHash, getPublicKey, nip19, signEvent } from 'npm:nostr-tools@^1.7.4'; +export { default as lmdb } from 'npm:lmdb'; diff --git a/src/gossip.ts b/src/gossip.ts new file mode 100644 index 0000000..7ccb218 --- /dev/null +++ b/src/gossip.ts @@ -0,0 +1,21 @@ +import { parseRelay } from './schema.ts'; + +import type { Event } from './event.ts'; + +/** Gets relays which pertain to the author from the event. */ +function getAuthorRelays(event: Event): URL[] { + const relays: string[] = []; + + switch (event.kind) { + case 10002: + event.tags.forEach((tag) => tag[0] === 'r' && relays.push(tag[1])); + break; + case 2: + relays.push(event.content); + break; + } + + return relays.map(parseRelay).filter((r): r is URL => !!r); +} + +export { getAuthorRelays }; diff --git a/src/handler.ts b/src/handler.ts new file mode 100644 index 0000000..ad75b10 --- /dev/null +++ b/src/handler.ts @@ -0,0 +1,19 @@ +import { gossipDB } from '@/db.ts'; + +import { getAuthorRelays } from './gossip.ts'; + +import type { Event } from './event.ts'; + +function handleEvent(event: Event): void { + handleRelays(event); +} + +/** Add author relays into the database. */ +function handleRelays(event: Event): Promise { + const relays = getAuthorRelays(event); + return Promise.all( + relays.map((relay) => gossipDB.put(event.pubkey, relay.toString())), + ); +} + +export { handleEvent }; diff --git a/src/query.ts b/src/query.ts new file mode 100644 index 0000000..4b4d062 --- /dev/null +++ b/src/query.ts @@ -0,0 +1,7 @@ +import type { Filter } from '@/deps.ts'; + +function query(_filter: Filter, _relays?: URL[]) { + // TODO +} + +export { query }; diff --git a/src/schema.ts b/src/schema.ts index 18b0e8b..1f3dda8 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -22,5 +22,23 @@ const metaContentSchema = z.object({ type MetaContent = z.infer; -export { jsonSchema, metaContentSchema }; +/** It's like `safeParse` except it returns `T` on success and `undefined` on fail. */ +function parseish(schema: z.ZodType, value: unknown): T | undefined { + const result = schema.safeParse(value); + return result.success ? result.data : undefined; +} + +const parseRelay = (relay: string | URL) => parseish(relaySchema, relay); + +const relaySchema = z.custom((relay) => { + if (typeof relay !== 'string') return false; + try { + const { protocol } = new URL(relay); + return protocol === 'wss:' || protocol === 'ws:'; + } catch (_e) { + return false; + } +}); + +export { jsonSchema, metaContentSchema, parseRelay, relaySchema }; export type { MetaContent };