Skip to content

Commit ac12ab8

Browse files
fix(backend): フィードのノートのMFMはHTMLにレンダーしてから返す (#14006)
* fix(backend): フィードのノートのMFMはHTMLにレンダーしてから返す (test wip) * chore: beforeEachを使う? * fix: プレーンテキストにフォールバックしてMFMが含まれていないか調べる方針を実装 * fix: application/jsonだとパースされるのでその作用をキャンセル * build: fix lint error * docs: update CHANGELOG.md --------- Co-authored-by: syuilo <[email protected]>
1 parent ef205fb commit ac12ab8

File tree

4 files changed

+26
-3
lines changed

4 files changed

+26
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
### Server
1313
- チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
1414
- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
15+
- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
1516
- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
1617
- Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
1718
- Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正

packages/backend/src/server/web/FeedService.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
1414
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
1515
import { bindThis } from '@/decorators.js';
1616
import { IdService } from '@/core/IdService.js';
17+
import { MfmService } from "@/core/MfmService.js";
18+
import { parse as mfmParse } from 'mfm-js';
1719

1820
@Injectable()
1921
export class FeedService {
@@ -33,6 +35,7 @@ export class FeedService {
3335
private userEntityService: UserEntityService,
3436
private driveFileEntityService: DriveFileEntityService,
3537
private idService: IdService,
38+
private mfmService: MfmService,
3639
) {
3740
}
3841

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

8084
feed.addItem({
8185
title: `New note by ${author.name}`,
8286
link: `${this.config.url}/notes/${note.id}`,
8387
date: this.idService.parse(note.id).date,
8488
description: note.cw ?? undefined,
85-
content: note.text ?? undefined,
89+
content: text ? this.mfmService.toHtml(mfmParse(text), JSON.parse(note.mentionedRemoteUsers)) ?? undefined : undefined,
8690
image: file ? this.driveFileEntityService.getPublicUrl(file) : undefined,
8791
});
8892
}

packages/backend/test/e2e/fetch-resource.ts

+17
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ describe('Webリソース', () => {
153153
path: path('nonexisting'),
154154
status: 404,
155155
}));
156+
157+
describe(' has entry such ', () => {
158+
beforeEach(() => {
159+
post(alice, { text: "**a**" })
160+
});
161+
162+
test('MFMを含まない。', async () => {
163+
const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
164+
const _body: unknown = content.body;
165+
// JSONフィードのときは改めて文字列化する
166+
const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
167+
168+
if (body.includes("**a**")) {
169+
throw new Error("MFM shouldn't be included");
170+
}
171+
});
172+
})
156173
});
157174

158175
describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {

packages/backend/test/utils.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/val
1717
import { entities } from '../src/postgres.js';
1818
import { loadConfig } from '../src/config.js';
1919
import type * as misskey from 'misskey-js';
20+
import { type Response } from 'node-fetch';
2021

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

@@ -454,7 +455,7 @@ export type SimpleGetResponse = {
454455
type: string | null,
455456
location: string | null
456457
};
457-
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined): Promise<SimpleGetResponse> => {
458+
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined, bodyExtractor: (res: Response) => Promise<string | null> = _ => Promise.resolve(null)): Promise<SimpleGetResponse> => {
458459
const res = await relativeFetch(path, {
459460
headers: {
460461
Accept: accept,
@@ -482,7 +483,7 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
482483
const body =
483484
jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() :
484485
htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) :
485-
null;
486+
await bodyExtractor(res);
486487

487488
return {
488489
status: res.status,

0 commit comments

Comments
 (0)