Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(backend): フィードのノートのMFMはHTMLにレンダーしてから返す #14006

Merged
merged 8 commits into from
Jun 22, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
### Server
- チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
- Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
- Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
Expand Down
6 changes: 5 additions & 1 deletion packages/backend/src/server/web/FeedService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { MfmService } from "@/core/MfmService.js";
import { parse as mfmParse } from 'mfm-js';

@Injectable()
export class FeedService {
Expand All @@ -33,6 +35,7 @@ export class FeedService {
private userEntityService: UserEntityService,
private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
private mfmService: MfmService,
) {
}

Expand Down Expand Up @@ -76,13 +79,14 @@ export class FeedService {
id: In(note.fileIds),
}) : [];
const file = files.find(file => file.type.startsWith('image/'));
const text = note.text;

feed.addItem({
title: `New note by ${author.name}`,
link: `${this.config.url}/notes/${note.id}`,
date: this.idService.parse(note.id).date,
description: note.cw ?? undefined,
content: note.text ?? undefined,
content: text ? this.mfmService.toHtml(mfmParse(text), JSON.parse(note.mentionedRemoteUsers)) ?? undefined : undefined,
image: file ? this.driveFileEntityService.getPublicUrl(file) : undefined,
});
}
Expand Down
17 changes: 17 additions & 0 deletions packages/backend/test/e2e/fetch-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,23 @@ describe('Webリソース', () => {
path: path('nonexisting'),
status: 404,
}));

describe(' has entry such ', () => {
beforeEach(() => {
post(alice, { text: "**a**" })
});

test('MFMを含まない。', async () => {
const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
const _body: unknown = content.body;
// JSONフィードのときは改めて文字列化する
const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;

if (body.includes("**a**")) {
throw new Error("MFM shouldn't be included");
}
});
})
});

describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/backend/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/val
import { entities } from '../src/postgres.js';
import { loadConfig } from '../src/config.js';
import type * as misskey from 'misskey-js';
import { type Response } from 'node-fetch';

export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';

Expand Down Expand Up @@ -454,7 +455,7 @@ export type SimpleGetResponse = {
type: string | null,
location: string | null
};
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined): Promise<SimpleGetResponse> => {
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined, bodyExtractor: (res: Response) => Promise<string | null> = _ => Promise.resolve(null)): Promise<SimpleGetResponse> => {
const res = await relativeFetch(path, {
headers: {
Accept: accept,
Expand Down Expand Up @@ -482,7 +483,7 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
const body =
jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() :
htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) :
null;
await bodyExtractor(res);

return {
status: res.status,
Expand Down
Loading