diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index f7839f8..0ae754e 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -1,4 +1,5 @@ import { type AppController } from '@/app.ts'; +import { Conf } from '@/config.ts'; import { type Event, ISO6391, z } from '@/deps.ts'; import { getAncestors, getDescendants, getEvent } from '@/queries.ts'; import { toStatus } from '@/transformers/nostr-to-mastoapi.ts'; @@ -40,45 +41,46 @@ const createStatusController: AppController = async (c) => { const body = await parseBody(c.req.raw); const result = createStatusSchema.safeParse(body); - if (result.success) { - const { data } = result; - - if (data.visibility !== 'public') { - return c.json({ error: 'Only posting publicly is supported.' }, 422); - } - - if (data.poll) { - return c.json({ error: 'Polls are not yet supported.' }, 422); - } - - if (data.media_ids?.length) { - return c.json({ error: 'Media uploads are not yet supported.' }, 422); - } - - const tags: string[][] = []; - - if (data.in_reply_to_id) { - tags.push(['e', data.in_reply_to_id, 'reply']); - } - - if (data.sensitive && data.spoiler_text) { - tags.push(['content-warning', data.spoiler_text]); - } else if (data.sensitive) { - tags.push(['content-warning']); - } else if (data.spoiler_text) { - tags.push(['subject', data.spoiler_text]); - } - - const event = await createEvent({ - kind: 1, - content: data.status ?? '', - tags, - }, c); - - return c.json(await toStatus(event, c.get('pubkey'))); - } else { + if (!result.success) { return c.json({ error: 'Bad request', schema: result.error }, 400); } + + const { data } = result; + + if (data.visibility !== 'public') { + return c.json({ error: 'Only posting publicly is supported.' }, 422); + } + + if (data.poll) { + return c.json({ error: 'Polls are not yet supported.' }, 422); + } + + const tags: string[][] = []; + + if (data.in_reply_to_id) { + tags.push(['e', data.in_reply_to_id, 'reply']); + } + + if (data.sensitive && data.spoiler_text) { + tags.push(['content-warning', data.spoiler_text]); + } else if (data.sensitive) { + tags.push(['content-warning']); + } else if (data.spoiler_text) { + tags.push(['subject', data.spoiler_text]); + } + + for (const cid of data.media_ids ?? []) { + const url = new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString(); + tags.push(['media', url]); + } + + const event = await createEvent({ + kind: 1, + content: data.status ?? '', + tags, + }, c); + + return c.json(await toStatus(event, c.get('pubkey'))); }; const contextController: AppController = async (c) => { diff --git a/src/note.ts b/src/note.ts index 82688b6..928c436 100644 --- a/src/note.ts +++ b/src/note.ts @@ -54,7 +54,7 @@ function parseNoteContent(content: string): ParsedNoteContent { interface MediaLink { url: string; - mimeType: string; + mimeType?: string; } function getMediaLinks(links: Link[]): MediaLink[] { diff --git a/src/transformers/nostr-to-mastoapi.ts b/src/transformers/nostr-to-mastoapi.ts index aa0b231..5a3d3a0 100644 --- a/src/transformers/nostr-to-mastoapi.ts +++ b/src/transformers/nostr-to-mastoapi.ts @@ -118,7 +118,6 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) { ]; const { html, links, firstUrl } = parseNoteContent(event.content); - const mediaLinks = getMediaLinks(links); const [mentions, card, repliesCount, reblogsCount, favouritesCount, [repostEvent], [reactionEvent]] = await Promise .all([ @@ -140,6 +139,12 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) { const cw = event.tags.find(isCWTag); const subject = event.tags.find((tag) => tag[0] === 'subject'); + const mediaLinks = getMediaLinks(links); + + const media = event.tags + .filter((tag) => tag[0] === 'media') + .map((tag) => ({ url: tag[1], mimeType: tag[2] || undefined })); + return { id: event.id, account, @@ -161,7 +166,7 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) { bookmarked: false, reblog: null, application: null, - media_attachments: mediaLinks.map(renderAttachment), + media_attachments: mediaLinks.concat(media).map(renderAttachment), mentions, tags: [], emojis: toEmojis(event), @@ -187,7 +192,7 @@ function buildInlineRecipients(mentions: Mention[]): string { const attachmentTypeSchema = z.enum(['image', 'video', 'gifv', 'audio', 'unknown']).catch('unknown'); -function renderAttachment({ url, mimeType }: MediaLink) { +function renderAttachment({ url, mimeType = '' }: MediaLink) { const [baseType, _subType] = mimeType.split('/'); const type = attachmentTypeSchema.parse(baseType);