enhance(backend): headタグ内にrel=alternateの指定のあるlinkタグがある場合、記述されたURLを参照して照会できるように (#14371)

* signedGet時にhttpかつalternate属性のlinkがある場合に一回だけfollowして照会する

* Fix: validation position

* Fix import

* Fix tagname

* Update CHANGELOG

* Fix code style

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
taichan 2024-08-17 15:12:23 +09:00 committed by GitHub
parent fd744f44c1
commit 9fbc1b7f7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 2 deletions

View file

@ -17,6 +17,7 @@
- Fix: 特定の条件下でノートの削除ボタンが出ないのを修正 - Fix: 特定の条件下でノートの削除ボタンが出ないのを修正
### Server ### Server
- enhance: 照会時にURLがhtmlかつheadタグ内に`rel="alternate"`, `type="application/activity+json"`の`link`タグがある場合に追ってリンク先を照会できるように
- Enhance: 凍結されたアカウントのフォローリクエストを表示しないように - Enhance: 凍結されたアカウントのフォローリクエストを表示しないように
- Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374 - Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374
- 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。 - 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。

View file

@ -6,6 +6,7 @@
import * as crypto from 'node:crypto'; import * as crypto from 'node:crypto';
import { URL } from 'node:url'; import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Window } from 'happy-dom';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
@ -180,7 +181,8 @@ export class ApRequestService {
* @param url URL to fetch * @param url URL to fetch
*/ */
@bindThis @bindThis
public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> { public async signedGet(url: string, user: { id: MiUser['id'] }, followAlternate?: boolean): Promise<unknown> {
const _followAlternate = followAlternate ?? true;
const keypair = await this.userKeypairService.getUserKeypair(user.id); const keypair = await this.userKeypairService.getUserKeypair(user.id);
const req = ApRequestCreator.createSignedGet({ const req = ApRequestCreator.createSignedGet({
@ -198,9 +200,27 @@ export class ApRequestService {
headers: req.request.headers, headers: req.request.headers,
}, { }, {
throwErrorWhenResponseNotOk: true, throwErrorWhenResponseNotOk: true,
validators: [validateContentTypeSetAsActivityPub],
}); });
//#region リクエスト先がhtmlかつactivity+jsonへのalternate linkタグがあるとき
if (res.headers.get('Content-type')?.startsWith('text/html;') && _followAlternate === true) {
const html = await res.text();
const window = new Window();
const document = window.document;
document.documentElement.innerHTML = html;
const alternate = document.querySelector('head > link[rel="alternate"][type="application/activity+json"]');
if (alternate) {
const href = alternate.getAttribute('href');
if (href) {
return await this.signedGet(href, user, false);
}
}
}
//#endregion
validateContentTypeSetAsActivityPub(res);
return await res.json(); return await res.json();
} }
} }