wip
This commit is contained in:
parent
17a4d4fad9
commit
2a112ffe5b
|
|
@ -7,7 +7,7 @@
|
||||||
-
|
-
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
-
|
- Enhance: メモリ使用量を削減しました
|
||||||
|
|
||||||
|
|
||||||
## 2025.11.1
|
## 2025.11.1
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,6 @@
|
||||||
"ipaddr.js": "2.2.0",
|
"ipaddr.js": "2.2.0",
|
||||||
"is-svg": "5.1.0",
|
"is-svg": "5.1.0",
|
||||||
"js-yaml": "4.1.1",
|
"js-yaml": "4.1.1",
|
||||||
"jsdom": "26.1.0",
|
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.3",
|
"jsonld": "8.3.3",
|
||||||
"jsrsasign": "11.1.0",
|
"jsrsasign": "11.1.0",
|
||||||
|
|
@ -145,6 +144,7 @@
|
||||||
"nanoid": "5.1.6",
|
"nanoid": "5.1.6",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
|
"node-html-parser": "7.0.1",
|
||||||
"nodemailer": "7.0.10",
|
"nodemailer": "7.0.10",
|
||||||
"nsfwjs": "4.2.0",
|
"nsfwjs": "4.2.0",
|
||||||
"oauth": "0.10.2",
|
"oauth": "0.10.2",
|
||||||
|
|
@ -240,6 +240,7 @@
|
||||||
"fkill": "9.0.0",
|
"fkill": "9.0.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-mock": "29.7.0",
|
"jest-mock": "29.7.0",
|
||||||
|
"jsdom": "26.1.0",
|
||||||
"nodemon": "3.1.11",
|
"nodemon": "3.1.11",
|
||||||
"pid-port": "1.0.2",
|
"pid-port": "1.0.2",
|
||||||
"simple-oauth2": "5.1.0",
|
"simple-oauth2": "5.1.0",
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { JSDOM } from 'jsdom';
|
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
|
import * as htmlParser from 'node-html-parser';
|
||||||
import type { MiInstance } from '@/models/Instance.js';
|
import type { MiInstance } from '@/models/Instance.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
@ -15,7 +15,6 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import type { DOMWindow } from 'jsdom';
|
|
||||||
|
|
||||||
type NodeInfo = {
|
type NodeInfo = {
|
||||||
openRegistrations?: unknown;
|
openRegistrations?: unknown;
|
||||||
|
|
@ -59,7 +58,7 @@ export class FetchInstanceMetadataService {
|
||||||
return await this.redisClient.set(
|
return await this.redisClient.set(
|
||||||
`fetchInstanceMetadata:mutex:v2:${host}`, '1',
|
`fetchInstanceMetadata:mutex:v2:${host}`, '1',
|
||||||
'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
|
'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
|
@bindThis
|
||||||
private async fetchDom(instance: MiInstance): Promise<Document> {
|
private async fetchDom(instance: MiInstance): Promise<htmlParser.HTMLElement> {
|
||||||
this.logger.info(`Fetching HTML of ${instance.host} ...`);
|
this.logger.info(`Fetching HTML of ${instance.host} ...`);
|
||||||
|
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
|
|
||||||
const html = await this.httpRequestService.getHtml(url);
|
const html = await this.httpRequestService.getHtml(url);
|
||||||
|
|
||||||
const { window } = new JSDOM(html);
|
const doc = htmlParser.parse(html);
|
||||||
const doc = window.document;
|
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
@ -206,12 +204,12 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@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;
|
const url = 'https://' + instance.host;
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
|
// 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) {
|
if (href) {
|
||||||
return (new URL(href, url)).href;
|
return (new URL(href, url)).href;
|
||||||
|
|
@ -232,7 +230,7 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@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) {
|
if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
return (new URL(manifest.icons[0].src, url)).href;
|
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
|
// https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559
|
||||||
const href =
|
const href =
|
||||||
[
|
[
|
||||||
links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href,
|
links.find(link => link.attributes.rel === 'apple-touch-icon-precomposed')?.attributes.href,
|
||||||
links.find(link => link.relList.contains('apple-touch-icon'))?.href,
|
links.find(link => link.attributes.rel === 'apple-touch-icon')?.attributes.href,
|
||||||
links.find(link => link.relList.contains('icon'))?.href,
|
links.find(link => link.attributes.rel === 'icon')?.attributes.href,
|
||||||
]
|
]
|
||||||
.find(href => href);
|
.find(href => href);
|
||||||
|
|
||||||
|
|
@ -261,7 +259,7 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@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;
|
const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color;
|
||||||
|
|
||||||
if (themeColor) {
|
if (themeColor) {
|
||||||
|
|
@ -273,7 +271,7 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@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 (info && info.metadata) {
|
||||||
if (typeof info.metadata.nodeName === 'string') {
|
if (typeof info.metadata.nodeName === 'string') {
|
||||||
return info.metadata.nodeName;
|
return info.metadata.nodeName;
|
||||||
|
|
@ -298,7 +296,7 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@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 (info && info.metadata) {
|
||||||
if (typeof info.metadata.nodeDescription === 'string') {
|
if (typeof info.metadata.nodeDescription === 'string') {
|
||||||
return info.metadata.nodeDescription;
|
return info.metadata.nodeDescription;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import RE2 from 're2';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import ms from 'ms';
|
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 { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
||||||
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
||||||
import * as Acct from '@/misc/acct.js';
|
import * as Acct from '@/misc/acct.js';
|
||||||
|
|
@ -569,16 +569,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
try {
|
try {
|
||||||
const html = await this.httpRequestService.getHtml(url);
|
const html = await this.httpRequestService.getHtml(url);
|
||||||
|
|
||||||
const { window } = new JSDOM(html);
|
const doc = htmlParser.parse(html);
|
||||||
const doc: Document = window.document;
|
|
||||||
|
|
||||||
const myLink = `${this.config.url}/@${user.username}`;
|
const myLink = `${this.config.url}/@${user.username}`;
|
||||||
|
|
||||||
const aEls = Array.from(doc.getElementsByTagName('a'));
|
const aEls = Array.from(doc.getElementsByTagName('a'));
|
||||||
const linkEls = Array.from(doc.getElementsByTagName('link'));
|
const linkEls = Array.from(doc.getElementsByTagName('link'));
|
||||||
|
|
||||||
const includesMyLink = aEls.some(a => a.href === myLink);
|
const includesMyLink = aEls.some(a => a.attributes.href === myLink);
|
||||||
const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
|
const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.attributes.rel === 'me' && link.attributes.href === myLink);
|
||||||
|
|
||||||
if (includesMyLink || includesRelMeLinks) {
|
if (includesMyLink || includesRelMeLinks) {
|
||||||
await this.userProfilesRepository.createQueryBuilder('profile').update()
|
await this.userProfilesRepository.createQueryBuilder('profile').update()
|
||||||
|
|
@ -588,8 +587,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
})
|
})
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.close();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// なにもしない
|
// なにもしない
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import dns from 'node:dns/promises';
|
import dns from 'node:dns/promises';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { JSDOM } from 'jsdom';
|
import * as htmlParser from 'node-html-parser';
|
||||||
import httpLinkHeader from 'http-link-header';
|
import httpLinkHeader from 'http-link-header';
|
||||||
import ipaddr from 'ipaddr.js';
|
import ipaddr from 'ipaddr.js';
|
||||||
import oauth2orize, { type OAuth2, AuthorizationError, ValidateFunctionArity2, OAuth2Req, MiddlewareRequest } from 'oauth2orize';
|
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 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 name = id;
|
||||||
let logo: string | null = null;
|
let logo: string | null = null;
|
||||||
|
|
|
||||||
|
|
@ -273,9 +273,6 @@ importers:
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: 4.1.1
|
specifier: 4.1.1
|
||||||
version: 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:
|
json5:
|
||||||
specifier: 2.2.3
|
specifier: 2.2.3
|
||||||
version: 2.2.3
|
version: 2.2.3
|
||||||
|
|
@ -318,6 +315,9 @@ importers:
|
||||||
node-fetch:
|
node-fetch:
|
||||||
specifier: 3.3.2
|
specifier: 3.3.2
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
|
node-html-parser:
|
||||||
|
specifier: 7.0.1
|
||||||
|
version: 7.0.1
|
||||||
nodemailer:
|
nodemailer:
|
||||||
specifier: 7.0.10
|
specifier: 7.0.10
|
||||||
version: 7.0.10
|
version: 7.0.10
|
||||||
|
|
@ -598,6 +598,9 @@ importers:
|
||||||
jest-mock:
|
jest-mock:
|
||||||
specifier: 29.7.0
|
specifier: 29.7.0
|
||||||
version: 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:
|
nodemon:
|
||||||
specifier: 3.1.11
|
specifier: 3.1.11
|
||||||
version: 3.1.11
|
version: 3.1.11
|
||||||
|
|
@ -8669,6 +8672,9 @@ packages:
|
||||||
engines: {node: ^18.17.0 || >=20.5.0}
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
node-html-parser@7.0.1:
|
||||||
|
resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==}
|
||||||
|
|
||||||
node-int64@0.4.0:
|
node-int64@0.4.0:
|
||||||
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||||
|
|
||||||
|
|
@ -20199,6 +20205,11 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
node-html-parser@7.0.1:
|
||||||
|
dependencies:
|
||||||
|
css-select: 5.2.2
|
||||||
|
he: 1.2.0
|
||||||
|
|
||||||
node-int64@0.4.0: {}
|
node-int64@0.4.0: {}
|
||||||
|
|
||||||
node-releases@2.0.27: {}
|
node-releases@2.0.27: {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue