From 15810c1935a48e35df8d33993a53a170a320bd41 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 8 Jan 2024 16:53:46 -0600 Subject: [PATCH] Configure server from the kind 0 event of the admin user --- src/config.ts | 4 ---- src/controllers/api/instance.ts | 15 ++++++++++----- src/controllers/nostr/relay-info.ts | 15 ++++++++++----- src/schemas/nostr.ts | 10 ++++++++++ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/config.ts b/src/config.ts index 8c2626a..2f98c1e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -58,10 +58,6 @@ const Conf = { get postCharLimit() { return Number(Deno.env.get('POST_CHAR_LIMIT') || 5000); }, - /** Admin contact to expose through various endpoints. This information is public. */ - get adminEmail() { - return Deno.env.get('ADMIN_EMAIL') || 'webmaster@localhost'; - }, /** S3 media storage configuration. */ s3: { get endPoint() { diff --git a/src/controllers/api/instance.ts b/src/controllers/api/instance.ts index ebc3269..01f1a5b 100644 --- a/src/controllers/api/instance.ts +++ b/src/controllers/api/instance.ts @@ -1,17 +1,22 @@ import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; +import { jsonServerMetaSchema } from '@/schemas/nostr.ts'; +import { eventsDB } from '@/storages.ts'; -const instanceController: AppController = (c) => { +const instanceController: AppController = async (c) => { const { host, protocol } = Conf.url; + const [event] = await eventsDB.filter([{ kinds: [0], authors: [Conf.pubkey], limit: 1 }]); + const meta = jsonServerMetaSchema.parse(event?.content); + /** Protocol to use for WebSocket URLs, depending on the protocol of the `LOCAL_DOMAIN`. */ const wsProtocol = protocol === 'http:' ? 'ws:' : 'wss:'; return c.json({ uri: host, - title: 'Ditto', - description: 'Nostr and the Fediverse', - short_description: 'Nostr and the Fediverse', + title: meta.name ?? 'Ditto', + description: meta.about ?? 'Nostr and the Fediverse', + short_description: meta.tagline ?? 'Nostr and the Fediverse', registrations: Conf.registrations, max_toot_chars: Conf.postCharLimit, configuration: { @@ -49,7 +54,7 @@ const instanceController: AppController = (c) => { streaming_api: `${wsProtocol}//${host}`, }, version: '0.0.0 (compatible; Ditto 0.0.1)', - email: Conf.adminEmail, + email: meta.email ?? `postmaster@${host}`, nostr: { pubkey: Conf.pubkey, relay: `${wsProtocol}//${host}/relay`, diff --git a/src/controllers/nostr/relay-info.ts b/src/controllers/nostr/relay-info.ts index 9c16cdd..60c2f8b 100644 --- a/src/controllers/nostr/relay-info.ts +++ b/src/controllers/nostr/relay-info.ts @@ -1,13 +1,18 @@ import { AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; +import { jsonServerMetaSchema } from '@/schemas/nostr.ts'; +import { eventsDB } from '@/storages.ts'; + +const relayInfoController: AppController = async (c) => { + const [event] = await eventsDB.filter([{ kinds: [0], authors: [Conf.pubkey], limit: 1 }]); + const meta = jsonServerMetaSchema.parse(event?.content); -const relayInfoController: AppController = (c) => { return c.json({ - name: 'Ditto', - description: 'Nostr and the Fediverse.', + name: meta.name ?? 'Ditto', + description: meta.about ?? 'Nostr and the Fediverse.', pubkey: Conf.pubkey, - contact: `mailto:${Conf.adminEmail}`, - supported_nips: [1, 5, 9, 11, 16, 45, 46, 98], + contact: `mailto:${meta.email ?? `postmaster@${Conf.url.host}`}`, + supported_nips: [1, 5, 9, 11, 16, 45, 50, 46, 98], software: 'Ditto', version: '0.0.0', limitation: { diff --git a/src/schemas/nostr.ts b/src/schemas/nostr.ts index 7fb888c..1e8c0af 100644 --- a/src/schemas/nostr.ts +++ b/src/schemas/nostr.ts @@ -85,6 +85,12 @@ const mediaDataSchema = z.object({ width: z.number().int().positive().optional().catch(undefined), }); +/** Kind 0 content schema for the Ditto server admin user. */ +const serverMetaSchema = metaContentSchema.extend({ + tagline: z.string().optional().catch(undefined), + email: z.string().optional().catch(undefined), +}); + /** Media data from `"media"` tags. */ type MediaData = z.infer; @@ -94,6 +100,9 @@ const jsonMetaContentSchema = jsonSchema.pipe(metaContentSchema).catch({}); /** Parses media data from a JSON string. */ const jsonMediaDataSchema = jsonSchema.pipe(mediaDataSchema).catch({}); +/** Parses server admin meta from a JSON string. */ +const jsonServerMetaSchema = jsonSchema.pipe(serverMetaSchema).catch({}); + /** NIP-11 Relay Information Document. */ const relayInfoDocSchema = z.object({ name: z.string().transform((val) => val.slice(0, 30)).optional().catch(undefined), @@ -130,6 +139,7 @@ export { filterSchema, jsonMediaDataSchema, jsonMetaContentSchema, + jsonServerMetaSchema, type MediaData, mediaDataSchema, metaContentSchema,