From 5bd03bdcaa2c3ef821cf2e6132e766a432bb24fe Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 5 Jan 2024 15:35:55 -0600 Subject: [PATCH] Support admin accounts endpoint (first pass) --- src/app.ts | 2 ++ src/controllers/api/admin.ts | 23 +++++++++++++++++++++ src/storages/types.ts | 2 ++ src/views/mastodon/accounts.ts | 4 ++-- src/views/mastodon/admin-accounts.ts | 30 ++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/controllers/api/admin.ts create mode 100644 src/views/mastodon/admin-accounts.ts diff --git a/src/app.ts b/src/app.ts index 0d38e34..bc1b372 100644 --- a/src/app.ts +++ b/src/app.ts @@ -35,6 +35,7 @@ import { updateCredentialsController, verifyCredentialsController, } from './controllers/api/accounts.ts'; +import { adminAccountsController } from './controllers/api/admin.ts'; import { appCredentialsController, createAppController } from './controllers/api/apps.ts'; import { blocksController } from './controllers/api/blocks.ts'; import { bookmarksController } from './controllers/api/bookmarks.ts'; @@ -185,6 +186,7 @@ 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.post('/api/v1/pleroma/admin/config', requireRole('admin'), updateConfigController); // Not (yet) implemented. diff --git a/src/controllers/api/admin.ts b/src/controllers/api/admin.ts new file mode 100644 index 0000000..46f7b29 --- /dev/null +++ b/src/controllers/api/admin.ts @@ -0,0 +1,23 @@ +import { type AppController } from '@/app.ts'; +import { Conf } from '@/config.ts'; +import { eventsDB } from '@/storages.ts'; +import { renderAdminAccount } from '@/views/mastodon/admin-accounts.ts'; + +const adminAccountsController: AppController = async (c) => { + const events = await eventsDB.getEvents([{ kinds: [30361], authors: [Conf.pubkey], limit: 20 }]); + const pubkeys = events.map((event) => event.tags.find(([name]) => name === 'd')?.[1]!); + const authors = await eventsDB.getEvents([{ kinds: [0], ids: pubkeys, limit: pubkeys.length }]); + + for (const event of events) { + const d = event.tags.find(([name]) => name === 'd')?.[1]; + event.d_author = authors.find((author) => author.pubkey === d); + } + + return c.json( + await Promise.all( + events.map((event) => renderAdminAccount(event)), + ), + ); +}; + +export { adminAccountsController }; diff --git a/src/storages/types.ts b/src/storages/types.ts index c89e9cc..dda2205 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -29,6 +29,8 @@ interface DittoEvent extends Event { author?: DittoEvent<0>; author_stats?: AuthorStats; event_stats?: EventStats; + d_author?: DittoEvent<0>; + user?: DittoEvent<30361>; } /** Storage interface for Nostr events. */ diff --git a/src/views/mastodon/accounts.ts b/src/views/mastodon/accounts.ts index 620a1b5..0030f3b 100644 --- a/src/views/mastodon/accounts.ts +++ b/src/views/mastodon/accounts.ts @@ -12,7 +12,7 @@ interface ToAccountOpts { } async function renderAccount( - event: Omit, 'id' | 'sig'>, + event: Omit, 'id' | 'sig'>, opts: ToAccountOpts = {}, ) { const { withSource = false } = opts; @@ -39,7 +39,7 @@ async function renderAccount( avatar: picture, avatar_static: picture, bot: false, - created_at: nostrDate(event.created_at).toISOString(), + created_at: user ? user.inserted_at.toISOString() : nostrDate(event.created_at).toISOString(), discoverable: true, display_name: name, emojis: renderEmojis(event), diff --git a/src/views/mastodon/admin-accounts.ts b/src/views/mastodon/admin-accounts.ts new file mode 100644 index 0000000..0a73a99 --- /dev/null +++ b/src/views/mastodon/admin-accounts.ts @@ -0,0 +1,30 @@ +import { DittoEvent } from '@/storages/types.ts'; +import { nostrDate } from '@/utils.ts'; + +import { accountFromPubkey, renderAccount } from './accounts.ts'; + +async function renderAdminAccount(event: DittoEvent<30361>) { + const d = event.tags.find(([name]) => name === 'd')?.[1]!; + const account = event.d_author ? await renderAccount({ ...event.d_author, user: event }) : await accountFromPubkey(d); + + return { + id: account.id, + username: event.tags.find(([name]) => name === 'name')?.[1]!, + domain: account.acct.split('@')[1] || null, + created_at: nostrDate(event.created_at).toISOString(), + email: '', + ip: null, + ips: [], + locale: '', + invite_request: null, + role: event.tags.find(([name]) => name === 'role')?.[1] || 'user', + confirmed: true, + approved: true, + disabled: false, + silenced: false, + suspended: false, + account, + }; +} + +export { renderAdminAccount };