This commit is contained in:
syuilo 2025-11-29 09:43:55 +09:00
parent 17a4d4fad9
commit 2a112ffe5b
6 changed files with 37 additions and 30 deletions

View File

@ -7,7 +7,7 @@
-
### Server
-
- Enhance: メモリ使用量を削減しました
## 2025.11.1

View File

@ -130,7 +130,6 @@
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
"js-yaml": "4.1.1",
"jsdom": "26.1.0",
"json5": "2.2.3",
"jsonld": "8.3.3",
"jsrsasign": "11.1.0",
@ -145,6 +144,7 @@
"nanoid": "5.1.6",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"node-html-parser": "7.0.1",
"nodemailer": "7.0.10",
"nsfwjs": "4.2.0",
"oauth": "0.10.2",
@ -240,6 +240,7 @@
"fkill": "9.0.0",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"jsdom": "26.1.0",
"nodemon": "3.1.11",
"pid-port": "1.0.2",
"simple-oauth2": "5.1.0",

View File

@ -5,9 +5,9 @@
import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common';
import { JSDOM } from 'jsdom';
import tinycolor from 'tinycolor2';
import * as Redis from 'ioredis';
import * as htmlParser from 'node-html-parser';
import type { MiInstance } from '@/models/Instance.js';
import type Logger from '@/logger.js';
import { DI } from '@/di-symbols.js';
@ -15,7 +15,6 @@ import { LoggerService } from '@/core/LoggerService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { DOMWindow } from 'jsdom';
type NodeInfo = {
openRegistrations?: unknown;
@ -59,7 +58,7 @@ export class FetchInstanceMetadataService {
return await this.redisClient.set(
`fetchInstanceMetadata:mutex:v2:${host}`, '1',
'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
'GET' // 古い値を返すなかったらnull
'GET', // 古い値を返すなかったらnull
);
}
@ -181,15 +180,14 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchDom(instance: MiInstance): Promise<Document> {
private async fetchDom(instance: MiInstance): Promise<htmlParser.HTMLElement> {
this.logger.info(`Fetching HTML of ${instance.host} ...`);
const url = 'https://' + instance.host;
const html = await this.httpRequestService.getHtml(url);
const { window } = new JSDOM(html);
const doc = window.document;
const doc = htmlParser.parse(html);
return doc;
}
@ -206,12 +204,12 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchFaviconUrl(instance: MiInstance, doc: Document | null): Promise<string | null> {
private async fetchFaviconUrl(instance: MiInstance, doc: htmlParser.HTMLElement | null): Promise<string | null> {
const url = 'https://' + instance.host;
if (doc) {
// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.relList.contains('icon'))?.href;
const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.attributes.rel === 'icon')?.attributes.href;
if (href) {
return (new URL(href, url)).href;
@ -232,7 +230,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchIconUrl(instance: MiInstance, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async fetchIconUrl(instance: MiInstance, doc: htmlParser.HTMLElement | null, manifest: Record<string, any> | null): Promise<string | null> {
if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
const url = 'https://' + instance.host;
return (new URL(manifest.icons[0].src, url)).href;
@ -246,9 +244,9 @@ export class FetchInstanceMetadataService {
// https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559
const href =
[
links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href,
links.find(link => link.relList.contains('apple-touch-icon'))?.href,
links.find(link => link.relList.contains('icon'))?.href,
links.find(link => link.attributes.rel === 'apple-touch-icon-precomposed')?.attributes.href,
links.find(link => link.attributes.rel === 'apple-touch-icon')?.attributes.href,
links.find(link => link.attributes.rel === 'icon')?.attributes.href,
]
.find(href => href);
@ -261,7 +259,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getThemeColor(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async getThemeColor(info: NodeInfo | null, doc: htmlParser.HTMLElement | null, manifest: Record<string, any> | null): Promise<string | null> {
const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color;
if (themeColor) {
@ -273,7 +271,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getSiteName(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async getSiteName(info: NodeInfo | null, doc: htmlParser.HTMLElement | null, manifest: Record<string, any> | null): Promise<string | null> {
if (info && info.metadata) {
if (typeof info.metadata.nodeName === 'string') {
return info.metadata.nodeName;
@ -298,7 +296,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getDescription(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async getDescription(info: NodeInfo | null, doc: htmlParser.HTMLElement | null, manifest: Record<string, any> | null): Promise<string | null> {
if (info && info.metadata) {
if (typeof info.metadata.nodeDescription === 'string') {
return info.metadata.nodeDescription;

View File

@ -7,7 +7,7 @@ import RE2 from 're2';
import * as mfm from 'mfm-js';
import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { JSDOM } from 'jsdom';
import * as htmlParser from 'node-html-parser';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
import { extractHashtags } from '@/misc/extract-hashtags.js';
import * as Acct from '@/misc/acct.js';
@ -569,16 +569,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
const html = await this.httpRequestService.getHtml(url);
const { window } = new JSDOM(html);
const doc: Document = window.document;
const doc = htmlParser.parse(html);
const myLink = `${this.config.url}/@${user.username}`;
const aEls = Array.from(doc.getElementsByTagName('a'));
const linkEls = Array.from(doc.getElementsByTagName('link'));
const includesMyLink = aEls.some(a => a.href === myLink);
const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
const includesMyLink = aEls.some(a => a.attributes.href === myLink);
const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.attributes.rel === 'me' && link.attributes.href === myLink);
if (includesMyLink || includesRelMeLinks) {
await this.userProfilesRepository.createQueryBuilder('profile').update()
@ -588,8 +587,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
})
.execute();
}
window.close();
} catch (err) {
// なにもしない
}

View File

@ -6,7 +6,7 @@
import dns from 'node:dns/promises';
import { fileURLToPath } from 'node:url';
import { Inject, Injectable } from '@nestjs/common';
import { JSDOM } from 'jsdom';
import * as htmlParser from 'node-html-parser';
import httpLinkHeader from 'http-link-header';
import ipaddr from 'ipaddr.js';
import oauth2orize, { type OAuth2, AuthorizationError, ValidateFunctionArity2, OAuth2Req, MiddlewareRequest } from 'oauth2orize';
@ -120,9 +120,9 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
}
const text = await res.text();
const fragment = JSDOM.fragment(text);
const fragment = htmlParser.parse(text);
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
redirectUris.push(...[...fragment.querySelectorAll('link[rel=redirect_uri][href]')].map(el => el.attributes.href));
let name = id;
let logo: string | null = null;

View File

@ -273,9 +273,6 @@ importers:
js-yaml:
specifier: 4.1.1
version: 4.1.1
jsdom:
specifier: 26.1.0
version: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
json5:
specifier: 2.2.3
version: 2.2.3
@ -318,6 +315,9 @@ importers:
node-fetch:
specifier: 3.3.2
version: 3.3.2
node-html-parser:
specifier: 7.0.1
version: 7.0.1
nodemailer:
specifier: 7.0.10
version: 7.0.10
@ -598,6 +598,9 @@ importers:
jest-mock:
specifier: 29.7.0
version: 29.7.0
jsdom:
specifier: 26.1.0
version: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
nodemon:
specifier: 3.1.11
version: 3.1.11
@ -8669,6 +8672,9 @@ packages:
engines: {node: ^18.17.0 || >=20.5.0}
hasBin: true
node-html-parser@7.0.1:
resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==}
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
@ -20199,6 +20205,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
node-html-parser@7.0.1:
dependencies:
css-select: 5.2.2
he: 1.2.0
node-int64@0.4.0: {}
node-releases@2.0.27: {}