Merge branch 'develop' into feat-1714

This commit is contained in:
かっこかり
2024-07-25 15:16:23 +09:00
committed by GitHub
86 changed files with 960 additions and 1210 deletions

View File

@@ -3,10 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as crypto from 'node:crypto';
import { IncomingMessage } from 'node:http';
import { Inject, Injectable } from '@nestjs/common';
import fastifyAccepts from '@fastify/accepts';
import { verifyDigestHeader, parseRequestSignature } from '@misskey-dev/node-http-message-signatures';
import httpSignature from '@peertube/http-signature';
import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
import accepts from 'accepts';
import vary from 'vary';
@@ -30,17 +31,12 @@ import { IActivity } from '@/core/activitypub/type.js';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
import type { FindOptionsWhere } from 'typeorm';
import { LoggerService } from '@/core/LoggerService.js';
import Logger from '@/logger.js';
const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
@Injectable()
export class ActivityPubServerService {
private logger: Logger;
private inboxLogger: Logger;
constructor(
@Inject(DI.config)
private config: Config,
@@ -75,11 +71,8 @@ export class ActivityPubServerService {
private queueService: QueueService,
private userKeypairService: UserKeypairService,
private queryService: QueryService,
private loggerService: LoggerService,
) {
//this.createServer = this.createServer.bind(this);
this.logger = this.loggerService.getLogger('server-ap', 'gray');
this.inboxLogger = this.logger.createSubLogger('inbox', 'gray');
}
@bindThis
@@ -107,44 +100,70 @@ export class ActivityPubServerService {
}
@bindThis
private async inbox(request: FastifyRequest, reply: FastifyReply) {
if (request.body == null) {
this.inboxLogger.warn('request body is empty');
reply.code(400);
return;
}
private inbox(request: FastifyRequest, reply: FastifyReply) {
let signature;
let signature: ReturnType<typeof parseRequestSignature>;
const verifyDigest = await verifyDigestHeader(request.raw, request.rawBody || '', true);
if (verifyDigest !== true) {
this.inboxLogger.warn('digest verification failed');
try {
signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
} catch (e) {
reply.code(401);
return;
}
try {
signature = parseRequestSignature(request.raw, {
requiredInputs: {
draft: ['(request-target)', 'digest', 'host', 'date'],
},
});
} catch (err) {
this.inboxLogger.warn('signature header parsing failed', { err });
if (signature.params.headers.indexOf('host') === -1
|| request.headers.host !== this.config.host) {
// Host not specified or not match.
reply.code(401);
return;
}
if (typeof request.body === 'object' && 'signature' in request.body) {
// LD SignatureがあればOK
this.queueService.inbox(request.body as IActivity, null);
reply.code(202);
if (signature.params.headers.indexOf('digest') === -1) {
// Digest not found.
reply.code(401);
} else {
const digest = request.headers.digest;
if (typeof digest !== 'string') {
// Huh?
reply.code(401);
return;
}
this.inboxLogger.warn('signature header parsing failed and LD signature not found');
reply.code(401);
return;
const re = /^([a-zA-Z0-9\-]+)=(.+)$/;
const match = digest.match(re);
if (match == null) {
// Invalid digest
reply.code(401);
return;
}
const algo = match[1].toUpperCase();
const digestValue = match[2];
if (algo !== 'SHA-256') {
// Unsupported digest algorithm
reply.code(401);
return;
}
if (request.rawBody == null) {
// Bad request
reply.code(400);
return;
}
const hash = crypto.createHash('sha256').update(request.rawBody).digest('base64');
if (hash !== digestValue) {
// Invalid digest
reply.code(401);
return;
}
}
this.queueService.inbox(request.body as IActivity, signature);
reply.code(202);
}
@@ -621,7 +640,7 @@ export class ActivityPubServerService {
if (this.userEntityService.isLocalUser(user)) {
reply.header('Cache-Control', 'public, max-age=180');
this.setResponseType(request, reply);
return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair.publicKey)));
return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
} else {
reply.code(400);
return;

View File

@@ -94,13 +94,6 @@ export class NodeinfoServerService {
localComments: 0,
},
metadata: {
/**
* '00': Draft, RSA only
* '01': Draft, Ed25519 suported
* '11': RFC 9421, Ed25519 supported
*/
httpMessageSignaturesImplementationLevel: '01',
nodeName: meta.name,
nodeDescription: meta.description,
nodeAdmins: [{

View File

@@ -56,8 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const res = [] as [string, number][];
for (const job of jobs) {
const signature = job.data.signature ? 'version' in job.data.signature ? job.data.signature.value : job.data.signature : null;
const host = signature ? Array.isArray(signature) ? 'TODO' : new URL(signature.keyId).host : new URL(job.data.activity.actor).host;
const host = new URL(job.data.signature.keyId).host;
if (res.find(x => x[0] === host)) {
res.find(x => x[0] === host)![1]++;
} else {

View File

@@ -14,6 +14,7 @@ import { CacheService } from '@/core/CacheService.js';
import { MiFollowing, MiUserProfile } from '@/models/_.js';
import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js';
import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
import type { JsonObject } from '@/misc/json-value.js';
import type { ChannelsService } from './ChannelsService.js';
import type { EventEmitter } from 'events';
import type Channel from './channel.js';
@@ -28,7 +29,7 @@ export default class Connection {
private wsConnection: WebSocket.WebSocket;
public subscriber: StreamEventEmitter;
private channels: Channel[] = [];
private subscribingNotes: any = {};
private subscribingNotes: Partial<Record<string, number>> = {};
private cachedNotes: Packed<'Note'>[] = [];
public userProfile: MiUserProfile | null = null;
public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
@@ -101,7 +102,7 @@ export default class Connection {
*/
@bindThis
private async onWsConnectionMessage(data: WebSocket.RawData) {
let obj: Record<string, any>;
let obj: JsonObject;
try {
obj = JSON.parse(data.toString());
@@ -111,6 +112,8 @@ export default class Connection {
const { type, body } = obj;
if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
switch (type) {
case 'readNotification': this.onReadNotification(body); break;
case 'subNote': this.onSubscribeNote(body); break;
@@ -151,7 +154,7 @@ export default class Connection {
}
@bindThis
private readNote(body: any) {
private readNote(body: JsonObject) {
const id = body.id;
const note = this.cachedNotes.find(n => n.id === id);
@@ -163,7 +166,7 @@ export default class Connection {
}
@bindThis
private onReadNotification(payload: any) {
private onReadNotification(payload: JsonObject) {
this.notificationService.readAllNotification(this.user!.id);
}
@@ -171,16 +174,14 @@ export default class Connection {
* 投稿購読要求時
*/
@bindThis
private onSubscribeNote(payload: any) {
if (!payload.id) return;
private onSubscribeNote(payload: JsonObject) {
if (!payload.id || typeof payload.id !== 'string') return;
if (this.subscribingNotes[payload.id] == null) {
this.subscribingNotes[payload.id] = 0;
}
const current = this.subscribingNotes[payload.id] ?? 0;
const updated = current + 1;
this.subscribingNotes[payload.id] = updated;
this.subscribingNotes[payload.id]++;
if (this.subscribingNotes[payload.id] === 1) {
if (updated === 1) {
this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
}
}
@@ -189,11 +190,14 @@ export default class Connection {
* 投稿購読解除要求時
*/
@bindThis
private onUnsubscribeNote(payload: any) {
if (!payload.id) return;
private onUnsubscribeNote(payload: JsonObject) {
if (!payload.id || typeof payload.id !== 'string') return;
this.subscribingNotes[payload.id]--;
if (this.subscribingNotes[payload.id] <= 0) {
const current = this.subscribingNotes[payload.id];
if (current == null) return;
const updated = current - 1;
this.subscribingNotes[payload.id] = updated;
if (updated <= 0) {
delete this.subscribingNotes[payload.id];
this.subscriber.off(`noteStream:${payload.id}`, this.onNoteStreamMessage);
}
@@ -212,17 +216,22 @@ export default class Connection {
* チャンネル接続要求時
*/
@bindThis
private onChannelConnectRequested(payload: any) {
private onChannelConnectRequested(payload: JsonObject) {
const { channel, id, params, pong } = payload;
this.connectChannel(id, params, channel, pong);
if (typeof id !== 'string') return;
if (typeof channel !== 'string') return;
if (typeof pong !== 'boolean' && typeof pong !== 'undefined' && pong !== null) return;
if (typeof params !== 'undefined' && (typeof params !== 'object' || params === null || Array.isArray(params))) return;
this.connectChannel(id, params, channel, pong ?? undefined);
}
/**
* チャンネル切断要求時
*/
@bindThis
private onChannelDisconnectRequested(payload: any) {
private onChannelDisconnectRequested(payload: JsonObject) {
const { id } = payload;
if (typeof id !== 'string') return;
this.disconnectChannel(id);
}
@@ -230,7 +239,7 @@ export default class Connection {
* クライアントにメッセージ送信
*/
@bindThis
public sendMessageToWs(type: string, payload: any) {
public sendMessageToWs(type: string, payload: JsonObject) {
this.wsConnection.send(JSON.stringify({
type: type,
body: payload,
@@ -241,7 +250,7 @@ export default class Connection {
* チャンネルに接続
*/
@bindThis
public connectChannel(id: string, params: any, channel: string, pong = false) {
public connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) {
const channelService = this.channelsService.getChannelService(channel);
if (channelService.requireCredential && this.user == null) {
@@ -288,7 +297,11 @@ export default class Connection {
* @param data メッセージ
*/
@bindThis
private onChannelMessageRequested(data: any) {
private onChannelMessageRequested(data: JsonObject) {
if (typeof data.id !== 'string') return;
if (typeof data.type !== 'string') return;
if (typeof data.body === 'undefined') return;
const channel = this.channels.find(c => c.id === data.id);
if (channel != null && channel.onMessage != null) {
channel.onMessage(data.type, data.body);

View File

@@ -8,6 +8,7 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { Packed } from '@/misc/json-schema.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
import type Connection from './Connection.js';
/**
@@ -81,10 +82,12 @@ export default abstract class Channel {
this.connection = connection;
}
public send(payload: { type: string, body: JsonValue }): void
public send(type: string, payload: JsonValue): void
@bindThis
public send(typeOrPayload: any, payload?: any) {
const type = payload === undefined ? typeOrPayload.type : typeOrPayload;
const body = payload === undefined ? typeOrPayload.body : payload;
public send(typeOrPayload: { type: string, body: JsonValue } | string, payload?: JsonValue) {
const type = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).type : (typeOrPayload as string);
const body = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).body : payload;
this.connection.sendMessageToWs('channel', {
id: this.id,
@@ -93,11 +96,11 @@ export default abstract class Channel {
});
}
public abstract init(params: any): void;
public abstract init(params: JsonObject): void;
public dispose?(): void;
public onMessage?(type: string, body: any): void;
public onMessage?(type: string, body: JsonValue): void;
}
export type MiChannelService<T extends boolean> = {

View File

@@ -5,6 +5,7 @@
import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class AdminChannel extends Channel {
@@ -14,7 +15,7 @@ class AdminChannel extends Channel {
public static kind = 'read:admin:stream';
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
// Subscribe admin stream
this.subscriber.on(`adminStream:${this.user!.id}`, data => {
this.send(data);

View File

@@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class AntennaChannel extends Channel {
@@ -27,8 +28,9 @@ class AntennaChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.antennaId = params.antennaId as string;
public async init(params: JsonObject) {
if (typeof params.antennaId !== 'string') return;
this.antennaId = params.antennaId;
// Subscribe stream
this.subscriber.on(`antennaStream:${this.antennaId}`, this.onEvent);

View File

@@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class ChannelChannel extends Channel {
@@ -27,8 +28,9 @@ class ChannelChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.channelId = params.channelId as string;
public async init(params: JsonObject) {
if (typeof params.channelId !== 'string') return;
this.channelId = params.channelId;
// Subscribe stream
this.subscriber.on('notesStream', this.onNote);

View File

@@ -5,6 +5,7 @@
import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class DriveChannel extends Channel {
@@ -14,7 +15,7 @@ class DriveChannel extends Channel {
public static kind = 'read:account';
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
// Subscribe drive stream
this.subscriber.on(`driveStream:${this.user!.id}`, data => {
this.send(data);

View File

@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class GlobalTimelineChannel extends Channel {
@@ -32,12 +33,12 @@ class GlobalTimelineChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.gtlAvailable) return;
this.withRenotes = params.withRenotes ?? true;
this.withFiles = params.withFiles ?? false;
this.withRenotes = !!(params.withRenotes ?? true);
this.withFiles = !!(params.withFiles ?? false);
// Subscribe events
this.subscriber.on('notesStream', this.onNote);

View File

@@ -9,6 +9,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class HashtagChannel extends Channel {
@@ -28,11 +29,11 @@ class HashtagChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
if (!Array.isArray(params.q)) return;
if (!params.q.every(x => Array.isArray(x) && x.every(y => typeof y === 'string'))) return;
this.q = params.q;
if (this.q == null) return;
// Subscribe stream
this.subscriber.on('notesStream', this.onNote);
}

View File

@@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class HomeTimelineChannel extends Channel {
@@ -29,9 +30,9 @@ class HomeTimelineChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.withRenotes = params.withRenotes ?? true;
this.withFiles = params.withFiles ?? false;
public async init(params: JsonObject) {
this.withRenotes = !!(params.withRenotes ?? true);
this.withFiles = !!(params.withFiles ?? false);
this.subscriber.on('notesStream', this.onNote);
}

View File

@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class HybridTimelineChannel extends Channel {
@@ -34,13 +35,13 @@ class HybridTimelineChannel extends Channel {
}
@bindThis
public async init(params: any): Promise<void> {
public async init(params: JsonObject): Promise<void> {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.ltlAvailable) return;
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withRenotes = !!(params.withRenotes ?? true);
this.withReplies = !!(params.withReplies ?? false);
this.withFiles = !!(params.withFiles ?? false);
// Subscribe events
this.subscriber.on('notesStream', this.onNote);

View File

@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class LocalTimelineChannel extends Channel {
@@ -33,13 +34,13 @@ class LocalTimelineChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.ltlAvailable) return;
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withRenotes = !!(params.withRenotes ?? true);
this.withReplies = !!(params.withReplies ?? false);
this.withFiles = !!(params.withFiles ?? false);
// Subscribe events
this.subscriber.on('notesStream', this.onNote);

View File

@@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class MainChannel extends Channel {
@@ -25,7 +26,7 @@ class MainChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
// Subscribe main stream channel
this.subscriber.on(`mainStream:${this.user!.id}`, async data => {
switch (data.type) {

View File

@@ -6,6 +6,7 @@
import Xev from 'xev';
import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
const ev = new Xev();
@@ -22,19 +23,22 @@ class QueueStatsChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
ev.addListener('queueStats', this.onStats);
}
@bindThis
private onStats(stats: any) {
private onStats(stats: JsonObject) {
this.send('stats', stats);
}
@bindThis
public onMessage(type: string, body: any) {
public onMessage(type: string, body: JsonValue) {
switch (type) {
case 'requestLog':
if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
if (typeof body.id !== 'string') return;
if (typeof body.length !== 'number') return;
ev.once(`queueStatsLog:${body.id}`, statsLog => {
this.send('statsLog', statsLog);
});

View File

@@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { ReversiService } from '@/core/ReversiService.js';
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class ReversiGameChannel extends Channel {
@@ -28,25 +29,41 @@ class ReversiGameChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.gameId = params.gameId as string;
public async init(params: JsonObject) {
if (typeof params.gameId !== 'string') return;
this.gameId = params.gameId;
this.subscriber.on(`reversiGameStream:${this.gameId}`, this.send);
}
@bindThis
public onMessage(type: string, body: any) {
public onMessage(type: string, body: JsonValue) {
switch (type) {
case 'ready': this.ready(body); break;
case 'updateSettings': this.updateSettings(body.key, body.value); break;
case 'cancel': this.cancelGame(); break;
case 'putStone': this.putStone(body.pos, body.id); break;
case 'ready':
if (typeof body !== 'boolean') return;
this.ready(body);
break;
case 'updateSettings':
if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
if (typeof body.key !== 'string') return;
if (typeof body.value !== 'object' || body.value === null || Array.isArray(body.value)) return;
this.updateSettings(body.key, body.value);
break;
case 'cancel':
this.cancelGame();
break;
case 'putStone':
if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
if (typeof body.pos !== 'number') return;
if (typeof body.id !== 'string') return;
this.putStone(body.pos, body.id);
break;
case 'claimTimeIsUp': this.claimTimeIsUp(); break;
}
}
@bindThis
private async updateSettings(key: string, value: any) {
private async updateSettings(key: string, value: JsonObject) {
if (this.user == null) return;
this.reversiService.updateSettings(this.gameId!, this.user, key, value);

View File

@@ -5,6 +5,7 @@
import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class ReversiChannel extends Channel {
@@ -21,7 +22,7 @@ class ReversiChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
this.subscriber.on(`reversiStream:${this.user!.id}`, this.send);
}

View File

@@ -8,6 +8,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class RoleTimelineChannel extends Channel {
@@ -28,8 +29,9 @@ class RoleTimelineChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.roleId = params.roleId as string;
public async init(params: JsonObject) {
if (typeof params.roleId !== 'string') return;
this.roleId = params.roleId;
this.subscriber.on(`roleTimelineStream:${this.roleId}`, this.onEvent);
}

View File

@@ -6,6 +6,7 @@
import Xev from 'xev';
import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
const ev = new Xev();
@@ -22,19 +23,20 @@ class ServerStatsChannel extends Channel {
}
@bindThis
public async init(params: any) {
public async init(params: JsonObject) {
ev.addListener('serverStats', this.onStats);
}
@bindThis
private onStats(stats: any) {
private onStats(stats: JsonObject) {
this.send('stats', stats);
}
@bindThis
public onMessage(type: string, body: any) {
public onMessage(type: string, body: JsonValue) {
switch (type) {
case 'requestLog':
if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
ev.once(`serverStatsLog:${body.id}`, statsLog => {
this.send('statsLog', statsLog);
});

View File

@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
import Channel, { type MiChannelService } from '../channel.js';
class UserListChannel extends Channel {
@@ -36,10 +37,11 @@ class UserListChannel extends Channel {
}
@bindThis
public async init(params: any) {
this.listId = params.listId as string;
this.withFiles = params.withFiles ?? false;
this.withRenotes = params.withRenotes ?? true;
public async init(params: JsonObject) {
if (typeof params.listId !== 'string') return;
this.listId = params.listId;
this.withFiles = !!(params.withFiles ?? false);
this.withRenotes = !!(params.withRenotes ?? true);
// Check existence and owner
const listExist = await this.userListsRepository.exists({