From 3204b61f0b281adc089ebc1b7c04d73ec1b636c1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 23 Apr 2024 16:21:36 -0500 Subject: [PATCH] Add an API to manage admin relays --- src/app.ts | 6 +++- src/controllers/api/ditto.ts | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/controllers/api/ditto.ts diff --git a/src/app.ts b/src/app.ts index 28852dd..6dd8da6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -77,6 +77,7 @@ import { auth19, requirePubkey } from '@/middleware/auth19.ts'; import { auth98, requireProof, requireRole } from '@/middleware/auth98.ts'; import { cache } from '@/middleware/cache.ts'; import { csp } from '@/middleware/csp.ts'; +import { adminRelaysController } from '@/controllers/api/ditto.ts'; interface AppEnv extends HonoEnv { Variables: { @@ -187,11 +188,14 @@ app.get('/api/v1/favourites', requirePubkey, favouritesController); app.get('/api/v1/bookmarks', requirePubkey, bookmarksController); app.get('/api/v1/blocks', requirePubkey, blocksController); -app.get('/api/v1/admin/accounts', adminAccountsController); +app.get('/api/v1/admin/accounts', requireRole('admin'), adminAccountsController); app.get('/api/v1/pleroma/admin/config', requireRole('admin'), configController); app.post('/api/v1/pleroma/admin/config', requireRole('admin'), updateConfigController); app.delete('/api/v1/pleroma/admin/statuses/:id', requireRole('admin'), pleromaAdminDeleteStatusController); +app.get('/api/v1/admin/ditto/relays', requireRole('admin'), adminRelaysController); +app.put('/api/v1/admin/ditto/relays', requireRole('admin'), adminRelaysController); + // Not (yet) implemented. app.get('/api/v1/custom_emojis', emptyArrayController); app.get('/api/v1/filters', emptyArrayController); diff --git a/src/controllers/api/ditto.ts b/src/controllers/api/ditto.ts new file mode 100644 index 0000000..425dcfb --- /dev/null +++ b/src/controllers/api/ditto.ts @@ -0,0 +1,57 @@ +import { NostrEvent } from '@nostrify/nostrify'; +import { z } from 'zod'; + +import { AppController } from '@/app.ts'; +import { Conf } from '@/config.ts'; +import { eventsDB } from '@/storages.ts'; +import { AdminSigner } from '@/signers/AdminSigner.ts'; + +const relaySchema = z.object({ + url: z.string().url(), + read: z.boolean(), + write: z.boolean(), +}); + +type RelayEntity = z.infer; + +export const adminRelaysController: AppController = async (c) => { + const [event] = await eventsDB.query([ + { kinds: [10002], authors: [Conf.pubkey], limit: 1 }, + ]); + + if (!event) { + return c.json([]); + } + + return c.json(renderRelays(event)); +}; + +export const adminSetRelaysController: AppController = async (c) => { + const relays = relaySchema.array().parse(await c.req.json()); + + const event = await new AdminSigner().signEvent({ + kind: 10002, + tags: relays.map(({ url, read, write }) => ['r', url, read && write ? '' : read ? 'read' : 'write']), + content: '', + created_at: Math.floor(Date.now() / 1000), + }); + + await eventsDB.event(event); + + return c.json(renderRelays(event)); +}; + +/** Render Ditto API relays from a NIP-65 event. */ +function renderRelays(event: NostrEvent): RelayEntity[] { + return event.tags.reduce((acc, [name, url, marker]) => { + if (name === 'r') { + const relay: RelayEntity = { + url, + read: !marker || marker === 'read', + write: !marker || marker === 'write', + }; + acc.push(relay); + } + return acc; + }, [] as RelayEntity[]); +}