From 4db8b2d4fbb983bd895812fe98cea3d1342c7966 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 15 Aug 2024 16:59:39 -0500 Subject: [PATCH] Throw when creating a replaceable event that doesn't exist yet --- src/controllers/api/accounts.ts | 76 +++++++++++++++++---------------- src/controllers/error.ts | 6 ++- src/utils/api.ts | 27 ++++++++---- 3 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 7d9e7641..9c635565 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -9,7 +9,7 @@ import { booleanParamSchema, fileSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { uploadFile } from '@/utils/upload.ts'; import { nostrNow } from '@/utils.ts'; -import { createEvent, paginated, parseBody, updateListEvent } from '@/utils/api.ts'; +import { createEvent, paginated, parseBody, updateEvent, updateListEvent } from '@/utils/api.ts'; import { extractIdentifier, lookupAccount, lookupPubkey } from '@/utils/lookup.ts'; import { renderAccounts, renderEventAccounts, renderStatuses } from '@/views.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; @@ -269,45 +269,49 @@ const updateCredentialsController: AppController = async (c) => { return c.json(result.error, 422); } - const author = await getAuthor(pubkey); - const meta = author ? n.json().pipe(n.metadata()).catch({}).parse(author.content) : {}; + const event = await updateEvent( + { kinds: [0], authors: [pubkey], limit: 1 }, + async (prev) => { + const meta = n.json().pipe(n.metadata()).catch({}).parse(prev.content); + const { + avatar: avatarFile, + header: headerFile, + display_name, + note, + nip05, + lud16, + website, + bot, + } = result.data; - const { - avatar: avatarFile, - header: headerFile, - display_name, - note, - nip05, - lud16, - website, - bot, - } = result.data; + const [avatar, header] = await Promise.all([ + avatarFile ? uploadFile(c, avatarFile, { pubkey }) : undefined, + headerFile ? uploadFile(c, headerFile, { pubkey }) : undefined, + ]); - const [avatar, header] = await Promise.all([ - avatarFile ? uploadFile(c, avatarFile, { pubkey }) : undefined, - headerFile ? uploadFile(c, headerFile, { pubkey }) : undefined, - ]); + meta.name = display_name ?? meta.name; + meta.about = note ?? meta.about; + meta.picture = avatar?.url ?? meta.picture; + meta.banner = header?.url ?? meta.banner; + meta.nip05 = nip05 ?? meta.nip05; + meta.lud16 = lud16 ?? meta.lud16; + meta.website = website ?? meta.website; + meta.bot = bot ?? meta.bot; - meta.name = display_name ?? meta.name; - meta.about = note ?? meta.about; - meta.picture = avatar?.url ?? meta.picture; - meta.banner = header?.url ?? meta.banner; - meta.nip05 = nip05 ?? meta.nip05; - meta.lud16 = lud16 ?? meta.lud16; - meta.website = website ?? meta.website; - meta.bot = bot ?? meta.bot; + if (avatarFile === '') delete meta.picture; + if (headerFile === '') delete meta.banner; + if (nip05 === '') delete meta.nip05; + if (lud16 === '') delete meta.lud16; + if (website === '') delete meta.website; - if (avatarFile === '') delete meta.picture; - if (headerFile === '') delete meta.banner; - if (nip05 === '') delete meta.nip05; - if (lud16 === '') delete meta.lud16; - if (website === '') delete meta.website; - - const event = await createEvent({ - kind: 0, - content: JSON.stringify(meta), - tags: [], - }, c); + return { + kind: 0, + content: JSON.stringify(meta), + tags: [], + }; + }, + c, + ); const account = await renderAccount(event, { withSource: true }); const settingsStore = result.data.pleroma_settings_store; diff --git a/src/controllers/error.ts b/src/controllers/error.ts index c6a9bd1e..f7806db8 100644 --- a/src/controllers/error.ts +++ b/src/controllers/error.ts @@ -3,7 +3,11 @@ import { HTTPException } from '@hono/hono/http-exception'; export const errorHandler: ErrorHandler = (err, c) => { if (err instanceof HTTPException) { - return c.json({ error: err.message }, err.status); + if (err.res) { + return err.res; + } else { + return c.json({ error: err.message }, err.status); + } } if (err.message === 'canceling statement due to statement timeout') { diff --git a/src/utils/api.ts b/src/utils/api.ts index 1ac61942..b3b5a8b1 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -46,27 +46,38 @@ interface UpdateEventFilter extends NostrFilter { limit: 1; } -/** Fetch existing event, update it, then publish the new event. */ +/** Update a replaceable event, or throw if no event exists yet. */ async function updateEvent( filter: UpdateEventFilter, - fn: (prev: NostrEvent | undefined) => E, + fn: (prev: NostrEvent) => E | Promise, c: AppContext, ): Promise { const store = await Storages.db(); - const [prev] = await store.query([filter], { signal: c.req.raw.signal }); - return createEvent(fn(prev), c); + + const [prev] = await store.query( + [filter], + { signal: c.req.raw.signal }, + ); + + if (prev) { + return createEvent(await fn(prev), c); + } else { + throw new HTTPException(422, { + message: 'No event to update', + }); + } } -/** Fetch existing event, update its tags, then publish the new event. */ +/** Update a replaceable list event, or throw if no event exists yet. */ function updateListEvent( filter: UpdateEventFilter, fn: (tags: string[][]) => string[][], c: AppContext, ): Promise { - return updateEvent(filter, (prev) => ({ + return updateEvent(filter, ({ content, tags }) => ({ kind: filter.kinds[0], - content: prev?.content ?? '', - tags: fn(prev?.tags ?? []), + content, + tags: fn(tags), }), c); }