diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index fe51082dd3f0..cd15721b0329 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -116,7 +116,7 @@ export class ApiCallService implements OnApplicationShutdown { return; } this.authenticateService.authenticate(token).then(([user, app]) => { - this.call(endpoint, user, app, body, null, request, reply).then((res) => { + this.call(endpoint, user, app, body, null, request).then((res) => { if (request.method === 'GET' && endpoint.meta.cacheSec && !token && !user) { reply.header('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); } @@ -168,7 +168,7 @@ export class ApiCallService implements OnApplicationShutdown { this.call(endpoint, user, app, fields, { name: multipartData.filename, path: path, - }, request, reply).then((res) => { + }, request).then((res) => { this.send(reply, res); }).catch((err: ApiError) => { this.#sendApiError(reply, err); @@ -239,7 +239,6 @@ export class ApiCallService implements OnApplicationShutdown { path: string; } | null, request: FastifyRequest<{ Body: Record | undefined, Querystring: Record }>, - reply: FastifyReply, ) { const isSecure = user != null && token == null; @@ -373,7 +372,7 @@ export class ApiCallService implements OnApplicationShutdown { } // API invoking - return await ep.exec(data, user, token, reply, file, request.ip, request.headers).catch((err: Error) => { + return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => { if (err instanceof ApiError || err instanceof AuthenticationError) { throw err; } else if (err instanceof IdentifiableError) { diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index 5be9cba10da8..68f3d4c0f7ce 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -10,7 +10,6 @@ import type { MiLocalUser } from '@/models/User.js'; import type { MiAccessToken } from '@/models/AccessToken.js'; import { ApiError } from './error.js'; import type { IEndpointMeta } from './endpoints.js'; -import type { FastifyReply } from 'fastify'; const Ajv = _Ajv.default; @@ -40,16 +39,16 @@ type File = { // TODO: paramsの型をT['params']のスキーマ定義から推論する type Executor = - (params: SchemaType, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, reply: FastifyReply, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record | null) => + (params: SchemaType, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record | null) => Promise>>; export abstract class Endpoint { - public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, reply: FastifyReply, file?: File, ip?: string | null, headers?: Record | null) => Promise; + public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record | null) => Promise; constructor(meta: T, paramDef: Ps, cb: Executor) { const validate = ajv.compile(paramDef); - this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, reply: FastifyReply, file?: File, ip?: string | null, headers?: Record | null) => { + this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record | null) => { let cleanup: undefined | (() => void) = undefined; if (meta.requireFile) { @@ -80,7 +79,7 @@ export abstract class Endpoint { return Promise.reject(err); } - return cb(params as SchemaType, user, token, reply, file, cleanup, ip, headers); + return cb(params as SchemaType, user, token, file, cleanup, ip, headers); }; } } diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index fc2e31571206..adcda30a7d88 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -13,7 +13,6 @@ export const meta = { tags: ['notes'], requireCredential: false, - allowGet: true, res: { type: 'object', @@ -44,18 +43,12 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private getterService: GetterService, ) { - super(meta, paramDef, async (ps, me, token, reply) => { + super(meta, paramDef, async (ps, me) => { const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); - if (["followers", "specified"].includes(note.visibility)) { - reply.header('Cache-Control', 'private, max-age=600'); - } else { - reply.header('Cache-Control', 'public'); - } - return await this.noteEntityService.pack(note, me, { detail: true, }); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index d5c3ef3b01c4..2447dcc44fe3 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -616,6 +616,25 @@ export class ClientServerService { } }); + fastify.get<{ Params: { note: string; } }>('/notes/:note.json', async (request, reply) => { + const note = await this.notesRepository.findOneBy({ + id: request.params.note, + visibility: In(['public', 'home']), + }); + if (note) { + try { + const _note = await this.noteEntityService.pack(note, null); + reply.header('Content-Type', 'application/json; charset=utf-8'); + reply.header('Cache-Control', 'public'); + return reply.send(_note); + } catch (err) { + return reply.status(500).send({ error: 'Internal Server Error' }); + } + } else { + return reply.status(404).send({ error: 'Data not found' }); + } + }); + // Page fastify.get<{ Params: { user: string; page: string; } }>('/@:user/pages/:page', async (request, reply) => { const { username, host } = Acct.parse(request.params.user); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index e1711cb2ada0..7edc6bde26cb 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -72,7 +72,9 @@ let tlNotesCount = 0; async function prepend(data) { let note = data; if (data.idOnly) { - note = await misskeyApiGet("notes/show", { noteId: data.id }); + const res = await fetch(`/notes/${data.id}.json`); + if (!res.ok) return; + note = await res.json(); } if (tlComponent.value == null) return;