From 85a13d8bd7a130e8f0d0d6ffadb5af86965cecf1 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Sat, 30 Sep 2023 19:07:40 +0200 Subject: [PATCH] [mastodon-client] GET /statuses/:id/history --- .../src/server/api/mastodon/converters.ts | 9 ++++ .../server/api/mastodon/endpoints/status.ts | 32 ++++++------- .../api/mastodon/entities/status_edit.ts | 1 - .../src/server/api/mastodon/helpers/note.ts | 45 ++++++++++++++++++- 4 files changed, 67 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts index 74ca8efdf..c6fed386a 100644 --- a/packages/backend/src/server/api/mastodon/converters.ts +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -75,6 +75,15 @@ export function convertStatus(status: MastodonEntity.Status) { return status; } +export function convertStatusEdit(edit: MastodonEntity.StatusEdit) { + edit.account = convertAccount(edit.account); + edit.media_attachments = edit.media_attachments.map((attachment) => + convertAttachment(attachment), + ); + if (edit.poll) edit.poll = convertPoll(edit.poll); + return edit; +} + export function convertConversation(conversation: MastodonEntity.Conversation) { conversation.id = convertId(conversation.id, IdType.MastodonId); conversation.accounts = conversation.accounts.map(convertAccount); diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index f44f4a838..43b3d56e4 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -3,7 +3,7 @@ import { getClient } from "../index.js"; import querystring from "node:querystring"; import qs from "qs"; import { convertId, IdType } from "../../index.js"; -import { convertAccount, convertPoll, convertStatus, } from "../converters.js"; +import { convertAccount, convertPoll, convertStatus, convertStatusEdit, } from "../converters.js"; import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { getNote } from "@/server/api/common/getters.js"; import authenticate from "@/server/api/authenticate.js"; @@ -183,14 +183,20 @@ export function setupEndpointsStatus(router: Router): void { router.get<{ Params: { id: string } }>( "/v1/statuses/:id/history", async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getStatusHistory( - convertId(ctx.params.id, IdType.IceshrimpId), - ); - ctx.body = data.data.map((account) => convertAccount(account)); + const auth = await authenticate(ctx.headers.authorization, null); + const user = auth[0] ?? null; + + const id = convertId(ctx.params.id, IdType.IceshrimpId); + const note = await getNote(id, user).catch(_ => null); + + if (note === null) { + ctx.status = 404; + return; + } + + const res = await NoteHelpers.getNoteEditHistory(note); + ctx.body = res.map(p => convertStatusEdit(p)); } catch (e: any) { console.error(e); ctx.status = 401; @@ -205,11 +211,6 @@ export function setupEndpointsStatus(router: Router): void { const auth = await authenticate(ctx.headers.authorization, null); const user = auth[0] ?? null; - if (!user) { - ctx.status = 401; - return; - } - const id = convertId(ctx.params.id, IdType.IceshrimpId); const note = await getNote(id, user).catch(_ => null); @@ -238,11 +239,6 @@ export function setupEndpointsStatus(router: Router): void { const auth = await authenticate(ctx.headers.authorization, null); const user = auth[0] ?? null; - if (!user) { - ctx.status = 401; - return; - } - const id = convertId(ctx.params.id, IdType.IceshrimpId); const note = await getNote(id, user).catch(_ => null); diff --git a/packages/backend/src/server/api/mastodon/entities/status_edit.ts b/packages/backend/src/server/api/mastodon/entities/status_edit.ts index 98405ad5c..dfffa8e4e 100644 --- a/packages/backend/src/server/api/mastodon/entities/status_edit.ts +++ b/packages/backend/src/server/api/mastodon/entities/status_edit.ts @@ -12,7 +12,6 @@ namespace MastodonEntity { export type StatusEdit = { account: Account; content: string; - plain_content: string | null; created_at: string; emojis: Emoji[]; sensitive: boolean; diff --git a/packages/backend/src/server/api/mastodon/helpers/note.ts b/packages/backend/src/server/api/mastodon/helpers/note.ts index 0196e8c50..1e8a104ea 100644 --- a/packages/backend/src/server/api/mastodon/helpers/note.ts +++ b/packages/backend/src/server/api/mastodon/helpers/note.ts @@ -1,7 +1,7 @@ import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; import { DriveFiles, - Metas, + Metas, NoteEdits, NoteFavorites, NoteReactions, Notes, @@ -33,6 +33,8 @@ import { awaitAll } from "@/prelude/await-all.js"; import { IsNull } from "typeorm"; import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js"; import mfm from "mfm-js"; +import { FileConverter } from "@/server/api/mastodon/converters/file.js"; +import { toHtml } from "@/mfm/to-html.js"; export class NoteHelpers { public static async getDefaultReaction(): Promise { @@ -163,6 +165,47 @@ export class NoteHelpers { }); } + public static async getNoteEditHistory(note: Note): Promise { + if (!note.updatedAt) return []; + const cache = UserHelpers.getFreshAccountCache(); + const account = Promise.resolve(note.user ?? await UserHelpers.getUserCached(note.userId, cache)) + .then(p => UserConverter.encode(p, cache)); + const edits = await NoteEdits.find({where: {noteId: note.id}, order: {id: "ASC"}}); + const history: Promise[] = []; + if (edits.length < 1) return []; + + const curr = { + id: note.id, + noteId: note.id, + note: note, + text: note.text, + cw: note.cw, + fileIds: note.fileIds, + updatedAt: note.updatedAt + } + + edits.push(curr); + + let lastDate = note.createdAt; + for (const edit of edits) { + const files = DriveFiles.packMany(edit.fileIds); + const item = { + account: account, + content: toHtml(mfm.parse(edit.text ?? ''), JSON.parse(note.mentionedRemoteUsers)) ?? '', + created_at: lastDate.toISOString(), + emojis: [], + sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false), + spoiler_text: edit.cw ?? '', + poll: null, + media_attachments: files.then(files => files.length > 0 ? files.map((f) => FileConverter.encode(f)) : []) + }; + lastDate = edit.updatedAt; + history.unshift(awaitAll(item)); + } + + return Promise.all(history); + } + public static async getNoteRebloggedBy(note: Note, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40): Promise> { if (limit > 80) limit = 80; const query = PaginationHelpers.makePaginationQuery(