diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-09-24 18:21:31 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-24 18:21:31 +0900 |
| commit | f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa (patch) | |
| tree | 0f6f098cbb282e4b6619152b14b9e6f57e6b448f /packages/backend/src | |
| parent | Merge pull request #11384 from misskey-dev/develop (diff) | |
| parent | 2023.9.0 (diff) | |
| download | misskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.tar.gz misskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.tar.bz2 misskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.zip | |
Merge pull request #11874 from misskey-dev/develop
Release: 2023.9.0
Diffstat (limited to 'packages/backend/src')
782 files changed, 10055 insertions, 5444 deletions
diff --git a/packages/backend/src/@types/hcaptcha.d.ts b/packages/backend/src/@types/hcaptcha.d.ts index afed587560..43e67dd340 100644 --- a/packages/backend/src/@types/hcaptcha.d.ts +++ b/packages/backend/src/@types/hcaptcha.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module 'hcaptcha' { interface IVerifyResponse { success: boolean; diff --git a/packages/backend/src/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts index f2f9bfcc31..1f3b48aa54 100644 --- a/packages/backend/src/@types/http-signature.d.ts +++ b/packages/backend/src/@types/http-signature.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module '@peertube/http-signature' { import type { IncomingMessage, ClientRequest } from 'node:http'; diff --git a/packages/backend/src/@types/os-utils.d.ts b/packages/backend/src/@types/os-utils.d.ts index 390df17d39..8c44232c14 100644 --- a/packages/backend/src/@types/os-utils.d.ts +++ b/packages/backend/src/@types/os-utils.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module 'os-utils' { type FreeCommandCallback = (usedmem: number) => void; diff --git a/packages/backend/src/@types/package.json.d.ts b/packages/backend/src/@types/package.json.d.ts index abe5fae687..197b4b6bf0 100644 --- a/packages/backend/src/@types/package.json.d.ts +++ b/packages/backend/src/@types/package.json.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module '*/package.json' { interface IRepository { type: string; diff --git a/packages/backend/src/@types/probe-image-size.d.ts b/packages/backend/src/@types/probe-image-size.d.ts index 416e819acb..4d312cba34 100644 --- a/packages/backend/src/@types/probe-image-size.d.ts +++ b/packages/backend/src/@types/probe-image-size.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module 'probe-image-size' { import type { ReadStream } from 'node:fs'; diff --git a/packages/backend/src/@types/redis-lock.d.ts b/packages/backend/src/@types/redis-lock.d.ts index 9242656a98..c607d600d8 100644 --- a/packages/backend/src/@types/redis-lock.d.ts +++ b/packages/backend/src/@types/redis-lock.d.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + declare module 'redis-lock' { import type Redis from 'ioredis'; diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts index 4caf4c3e96..9f1ee9fcaa 100644 --- a/packages/backend/src/GlobalModule.ts +++ b/packages/backend/src/GlobalModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { setTimeout } from 'node:timers/promises'; import { Global, Inject, Module } from '@nestjs/common'; import * as Redis from 'ioredis'; diff --git a/packages/backend/src/MainModule.ts b/packages/backend/src/MainModule.ts index fc568e883e..90aba0cc91 100644 --- a/packages/backend/src/MainModule.ts +++ b/packages/backend/src/MainModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { ServerModule } from '@/server/ServerModule.js'; import { GlobalModule } from '@/GlobalModule.js'; diff --git a/packages/backend/src/NestLogger.ts b/packages/backend/src/NestLogger.ts index 448098b831..e18e9e88a7 100644 --- a/packages/backend/src/NestLogger.ts +++ b/packages/backend/src/NestLogger.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { LoggerService } from '@nestjs/common'; import Logger from '@/logger.js'; diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts index 3995545d7f..4783a2b2da 100644 --- a/packages/backend/src/boot/common.ts +++ b/packages/backend/src/boot/common.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { NestFactory } from '@nestjs/core'; import { ChartManagementService } from '@/core/chart/ChartManagementService.js'; import { QueueProcessorService } from '@/queue/QueueProcessorService.js'; import { NestLogger } from '@/NestLogger.js'; import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js'; -import { JanitorService } from '@/daemons/JanitorService.js'; import { QueueStatsService } from '@/daemons/QueueStatsService.js'; import { ServerStatsService } from '@/daemons/ServerStatsService.js'; import { ServerService } from '@/server/ServerService.js'; @@ -20,7 +24,6 @@ export async function server() { if (process.env.NODE_ENV !== 'test') { app.get(ChartManagementService).start(); - app.get(JanitorService).start(); app.get(QueueStatsService).start(); app.get(ServerStatsService).start(); } diff --git a/packages/backend/src/boot/index.ts b/packages/backend/src/boot/entry.ts index f4daf30690..fc8fc2ffb4 100644 --- a/packages/backend/src/boot/index.ts +++ b/packages/backend/src/boot/entry.ts @@ -1,3 +1,7 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ /** * Misskey Entry Point! diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index c253f697f7..a45ea2bb8f 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; @@ -5,7 +10,6 @@ import * as os from 'node:os'; import cluster from 'node:cluster'; import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; -import semver from 'semver'; import Logger from '@/logger.js'; import { loadConfig } from '@/config.js'; import type { Config } from '@/config.js'; @@ -64,21 +68,34 @@ export async function masterMain() { process.exit(1); } - if (envOption.onlyServer) { - await server(); - } else if (envOption.onlyQueue) { - await jobQueue(); - } else { - await server(); - } - bootLogger.succ('Misskey initialized'); - if (!envOption.disableClustering) { + if (envOption.disableClustering) { + if (envOption.onlyServer) { + await server(); + } else if (envOption.onlyQueue) { + await jobQueue(); + } else { + await server(); + await jobQueue(); + } + } else { + if (envOption.onlyServer) { + // nop + } else if (envOption.onlyQueue) { + // nop + } else { + await server(); + } + await spawnWorkers(config.clusterLimit); } - bootLogger.succ(config.socket ? `Now listening on socket ${config.socket} on ${config.url}` : `Now listening on port ${config.port} on ${config.url}`, null, true); + if (envOption.onlyQueue) { + bootLogger.succ('Queue started', null, true); + } else { + bootLogger.succ(config.socket ? `Now listening on socket ${config.socket} on ${config.url}` : `Now listening on port ${config.port} on ${config.url}`, null, true); + } } function showEnvironment(): void { diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index ab75aaa572..0399c9fe5c 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import cluster from 'node:cluster'; import { envOption } from '@/env.js'; import { jobQueue, server } from './common.js'; diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 253975096e..abbfdfed8f 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -1,5 +1,6 @@ -/** - * Config loader +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only */ import * as fs from 'node:fs'; @@ -18,11 +19,9 @@ type RedisOptionsSource = Partial<RedisOptions> & { }; /** - * ユーザーが設定する必要のある情報 + * 設定ファイルの型 */ -export type Source = { - repository_url?: string; - feedback_url?: string; +type Source = { url: string; port?: number; socket?: string; @@ -65,12 +64,11 @@ export type Source = { maxFileSize?: number; - accesslog?: string; - clusterLimit?: number; id: string; + outgoingAddress?: string; outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual'; deliverJobConcurrency?: number; @@ -87,12 +85,63 @@ export type Source = { videoThumbnailGenerator?: string; signToActivityPubGet?: boolean; + + perChannelMaxNoteCacheCount?: number; + perUserNotificationsMaxCount?: number; + deactivateAntennaThreshold?: number; }; -/** - * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 - */ -export type Mixin = { +export type Config = { + url: string; + port: number; + socket: string | undefined; + chmodSocket: string | undefined; + disableHsts: boolean | undefined; + db: { + host: string; + port: number; + db: string; + user: string; + pass: string; + disableCache?: boolean; + extra?: { [x: string]: string }; + }; + dbReplications: boolean | undefined; + dbSlaves: { + host: string; + port: number; + db: string; + user: string; + pass: string; + }[] | undefined; + meilisearch: { + host: string; + port: string; + apiKey: string; + ssl?: boolean; + index: string; + scope?: 'local' | 'global' | string[]; + } | undefined; + proxy: string | undefined; + proxySmtp: string | undefined; + proxyBypassHosts: string[] | undefined; + allowedPrivateNetworks: string[] | undefined; + maxFileSize: number | undefined; + clusterLimit: number | undefined; + id: string; + outgoingAddress: string | undefined; + outgoingAddressFamily: 'ipv4' | 'ipv6' | 'dual' | undefined; + deliverJobConcurrency: number | undefined; + inboxJobConcurrency: number | undefined; + relashionshipJobConcurrency: number | undefined; + deliverJobPerSec: number | undefined; + inboxJobPerSec: number | undefined; + relashionshipJobPerSec: number | undefined; + deliverJobMaxAttempts: number | undefined; + inboxJobMaxAttempts: number | undefined; + proxyRemoteFiles: boolean | undefined; + signToActivityPubGet: boolean | undefined; + version: string; host: string; hostname: string; @@ -111,10 +160,11 @@ export type Mixin = { redis: RedisOptions & RedisOptionsSource; redisForPubsub: RedisOptions & RedisOptionsSource; redisForJobQueue: RedisOptions & RedisOptionsSource; + perChannelMaxNoteCacheCount: number; + perUserNotificationsMaxCount: number; + deactivateAntennaThreshold: number; }; -export type Config = Source & Mixin; - const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -132,7 +182,7 @@ const path = process.env.MISSKEY_CONFIG_YML ? resolve(dir, 'test.yml') : resolve(dir, 'default.yml'); -export function loadConfig() { +export function loadConfig(): Config { const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json'); const clientManifest = clientManifestExists ? @@ -140,43 +190,72 @@ export function loadConfig() { : { 'src/_boot_.ts': { file: 'src/_boot_.ts' } }; const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; - const mixin = {} as Mixin; - const url = tryCreateUrl(config.url); - - config.url = url.origin; - - config.port = config.port ?? parseInt(process.env.PORT ?? '', 10); - - mixin.version = meta.version; - mixin.host = url.host; - mixin.hostname = url.hostname; - mixin.scheme = url.protocol.replace(/:$/, ''); - mixin.wsScheme = mixin.scheme.replace('http', 'ws'); - mixin.wsUrl = `${mixin.wsScheme}://${mixin.host}`; - mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`; - mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`; - mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`; - mixin.userAgent = `Misskey/${meta.version} (${config.url})`; - mixin.clientEntry = clientManifest['src/_boot_.ts']; - mixin.clientManifestExists = clientManifestExists; + const version = meta.version; + const host = url.host; + const hostname = url.hostname; + const scheme = url.protocol.replace(/:$/, ''); + const wsScheme = scheme.replace('http', 'ws'); const externalMediaProxy = config.mediaProxy ? config.mediaProxy.endsWith('/') ? config.mediaProxy.substring(0, config.mediaProxy.length - 1) : config.mediaProxy : null; - const internalMediaProxy = `${mixin.scheme}://${mixin.host}/proxy`; - mixin.mediaProxy = externalMediaProxy ?? internalMediaProxy; - mixin.externalMediaProxyEnabled = externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy; + const internalMediaProxy = `${scheme}://${host}/proxy`; + const redis = convertRedisOptions(config.redis, host); - mixin.videoThumbnailGenerator = config.videoThumbnailGenerator ? - config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator - : null; - - mixin.redis = convertRedisOptions(config.redis, mixin.host); - mixin.redisForPubsub = config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, mixin.host) : mixin.redis; - mixin.redisForJobQueue = config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, mixin.host) : mixin.redis; - - return Object.assign(config, mixin); + return { + version, + url: url.origin, + port: config.port ?? parseInt(process.env.PORT ?? '', 10), + socket: config.socket, + chmodSocket: config.chmodSocket, + disableHsts: config.disableHsts, + host, + hostname, + scheme, + wsScheme, + wsUrl: `${wsScheme}://${host}`, + apiUrl: `${scheme}://${host}/api`, + authUrl: `${scheme}://${host}/auth`, + driveUrl: `${scheme}://${host}/files`, + db: config.db, + dbReplications: config.dbReplications, + dbSlaves: config.dbSlaves, + meilisearch: config.meilisearch, + redis, + redisForPubsub: config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, host) : redis, + redisForJobQueue: config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, host) : redis, + id: config.id, + proxy: config.proxy, + proxySmtp: config.proxySmtp, + proxyBypassHosts: config.proxyBypassHosts, + allowedPrivateNetworks: config.allowedPrivateNetworks, + maxFileSize: config.maxFileSize, + clusterLimit: config.clusterLimit, + outgoingAddress: config.outgoingAddress, + outgoingAddressFamily: config.outgoingAddressFamily, + deliverJobConcurrency: config.deliverJobConcurrency, + inboxJobConcurrency: config.inboxJobConcurrency, + relashionshipJobConcurrency: config.relashionshipJobConcurrency, + deliverJobPerSec: config.deliverJobPerSec, + inboxJobPerSec: config.inboxJobPerSec, + relashionshipJobPerSec: config.relashionshipJobPerSec, + deliverJobMaxAttempts: config.deliverJobMaxAttempts, + inboxJobMaxAttempts: config.inboxJobMaxAttempts, + proxyRemoteFiles: config.proxyRemoteFiles, + signToActivityPubGet: config.signToActivityPubGet, + mediaProxy: externalMediaProxy ?? internalMediaProxy, + externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy, + videoThumbnailGenerator: config.videoThumbnailGenerator ? + config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator + : null, + userAgent: `Misskey/${version} (${config.url})`, + clientEntry: clientManifest['src/_boot_.ts'], + clientManifestExists: clientManifestExists, + perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000, + perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 300, + deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), + }; } function tryCreateUrl(url: string) { @@ -192,7 +271,7 @@ function convertRedisOptions(options: RedisOptionsSource, host: string): RedisOp ...options, password: options.pass, prefix: options.prefix ?? host, - family: options.family == null ? 0 : options.family, + family: options.family ?? 0, keyPrefix: `${options.prefix ?? host}:`, db: options.db ?? 0, }; diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index ee1a9a3093..716a8de382 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const MAX_NOTE_TEXT_LENGTH = 3000; export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 111fcfd734..ec1d013922 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -1,11 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull, In, MoreThan, Not } from 'typeorm'; import { bindThis } from '@/decorators.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; -import type { BlockingsRepository, FollowingsRepository, InstancesRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js'; +import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; +import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/_.js'; import type { RelationshipJobData, ThinUser } from '@/queue/types.js'; import { IdService } from '@/core/IdService.js'; @@ -26,9 +30,6 @@ import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; @Injectable() export class AccountMoveService { constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -70,12 +71,12 @@ export class AccountMoveService { * After delivering Move activity, its local followers unfollow the old account and then follow the new one. */ @bindThis - public async moveFromLocal(src: LocalUser, dst: LocalUser | RemoteUser): Promise<unknown> { + public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> { const srcUri = this.userEntityService.getUserUri(src); const dstUri = this.userEntityService.getUserUri(dst); // add movedToUri to indicate that the user has moved - const update = {} as Partial<LocalUser>; + const update = {} as Partial<MiLocalUser>; update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri]; update.movedToUri = dstUri; update.movedAt = new Date(); @@ -113,7 +114,7 @@ export class AccountMoveService { } @bindThis - public async postMoveProcess(src: User, dst: User): Promise<void> { + public async postMoveProcess(src: MiUser, dst: MiUser): Promise<void> { // Copy blockings and mutings, and update lists try { await Promise.all([ @@ -212,7 +213,7 @@ export class AccountMoveService { * @returns Promise<void> */ @bindThis - public async updateLists(src: ThinUser, dst: User): Promise<void> { + public async updateLists(src: ThinUser, dst: MiUser): Promise<void> { // Return if there is no list to be updated. const oldJoinings = await this.userListJoiningsRepository.find({ where: { @@ -259,7 +260,7 @@ export class AccountMoveService { } @bindThis - private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: User): Promise<void> { + private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: MiUser): Promise<void> { if (localFollowerIds.length === 0) return; // Set the old account's following and followers counts to 0. @@ -300,11 +301,11 @@ export class AccountMoveService { */ @bindThis public async validateAlsoKnownAs( - dst: LocalUser | RemoteUser, - check: (oldUser: LocalUser | RemoteUser | null, newUser: LocalUser | RemoteUser) => boolean | Promise<boolean> = () => true, + dst: MiLocalUser | MiRemoteUser, + check: (oldUser: MiLocalUser | MiRemoteUser | null, newUser: MiLocalUser | MiRemoteUser) => boolean | Promise<boolean> = () => true, instant = false, - ): Promise<LocalUser | RemoteUser | null> { - let resultUser: LocalUser | RemoteUser | null = null; + ): Promise<MiLocalUser | MiRemoteUser | null> { + let resultUser: MiLocalUser | MiRemoteUser | null = null; if (this.userEntityService.isRemoteUser(dst)) { if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) { diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index b146fc66be..664700ea6b 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/User.js'; +import type { UsersRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; @@ -12,9 +16,6 @@ import { bindThis } from '@/decorators.js'; @Injectable() export class AccountUpdateService { constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -26,7 +27,7 @@ export class AccountUpdateService { } @bindThis - public async publishToFollowers(userId: User['id']) { + public async publishToFollowers(userId: MiUser['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 9e223f1492..1b8718335b 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { UserProfilesRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { NotificationService } from '@/core/NotificationService.js'; @@ -80,14 +85,12 @@ export const ACHIEVEMENT_TYPES = [ 'setNameToSyuilo', 'cookieClicked', 'brainDiver', + 'smashTestNotificationButton', ] as const; @Injectable() export class AchievementService { constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, @@ -97,7 +100,7 @@ export class AchievementService { @bindThis public async create( - userId: User['id'], + userId: MiUser['id'], type: typeof ACHIEVEMENT_TYPES[number], ): Promise<void> { if (!ACHIEVEMENT_TYPES.includes(type)) return; diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index c0596446dd..4e876495a6 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -1,12 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import * as nsfw from 'nsfwjs'; import si from 'systeminformation'; import { Mutex } from 'async-mutex'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; const _filename = fileURLToPath(import.meta.url); @@ -21,8 +24,6 @@ export class AiService { private modelLoadMutex: Mutex = new Mutex(); constructor( - @Inject(DI.config) - private config: Config, ) { } diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts new file mode 100644 index 0000000000..2b4877788a --- /dev/null +++ b/packages/backend/src/core/AnnouncementService.ts @@ -0,0 +1,205 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { MiUser } from '@/models/User.js'; +import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead } from '@/models/_.js'; +import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; +import { IdService } from '@/core/IdService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; + +@Injectable() +export class AnnouncementService { + constructor( + @Inject(DI.announcementsRepository) + private announcementsRepository: AnnouncementsRepository, + + @Inject(DI.announcementReadsRepository) + private announcementReadsRepository: AnnouncementReadsRepository, + + private idService: IdService, + private globalEventService: GlobalEventService, + private moderationLogService: ModerationLogService, + ) { + } + + @bindThis + public async getReads(userId: MiUser['id']): Promise<MiAnnouncementRead[]> { + return this.announcementReadsRepository.findBy({ + userId: userId, + }); + } + + @bindThis + public async getUnreadAnnouncements(user: MiUser): Promise<MiAnnouncement[]> { + const readsQuery = this.announcementReadsRepository.createQueryBuilder('read') + .select('read.announcementId') + .where('read.userId = :userId', { userId: user.id }); + + const q = this.announcementsRepository.createQueryBuilder('announcement') + .where('announcement.isActive = true') + .andWhere(new Brackets(qb => { + qb.orWhere('announcement.userId = :userId', { userId: user.id }); + qb.orWhere('announcement.userId IS NULL'); + })) + .andWhere(new Brackets(qb => { + qb.orWhere('announcement.forExistingUsers = false'); + qb.orWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt }); + })) + .andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`); + + q.setParameters(readsQuery.getParameters()); + + return q.getMany(); + } + + @bindThis + public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { + const announcement = await this.announcementsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + updatedAt: null, + title: values.title, + text: values.text, + imageUrl: values.imageUrl, + icon: values.icon, + display: values.display, + forExistingUsers: values.forExistingUsers, + needConfirmationToRead: values.needConfirmationToRead, + userId: values.userId, + }).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0])); + + const packed = (await this.packMany([announcement]))[0]; + + if (values.userId) { + this.globalEventService.publishMainStream(values.userId, 'announcementCreated', { + announcement: packed, + }); + + if (moderator) { + this.moderationLogService.log(moderator, 'createUserAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + userId: values.userId, + }); + } + } else { + this.globalEventService.publishBroadcastStream('announcementCreated', { + announcement: packed, + }); + + if (moderator) { + this.moderationLogService.log(moderator, 'createGlobalAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } + } + + return { + raw: announcement, + packed: packed, + }; + } + + @bindThis + public async update(announcement: MiAnnouncement, values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<void> { + await this.announcementsRepository.update(announcement.id, { + updatedAt: new Date(), + title: values.title, + text: values.text, + /* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */ + imageUrl: values.imageUrl || null, + display: values.display, + icon: values.icon, + forExistingUsers: values.forExistingUsers, + needConfirmationToRead: values.needConfirmationToRead, + isActive: values.isActive, + }); + + const after = await this.announcementsRepository.findOneByOrFail({ id: announcement.id }); + + if (moderator) { + if (announcement.userId) { + this.moderationLogService.log(moderator, 'updateUserAnnouncement', { + announcementId: announcement.id, + before: announcement, + after: after, + }); + } else { + this.moderationLogService.log(moderator, 'updateGlobalAnnouncement', { + announcementId: announcement.id, + before: announcement, + after: after, + }); + } + } + } + + @bindThis + public async delete(announcement: MiAnnouncement, moderator?: MiUser): Promise<void> { + await this.announcementsRepository.delete(announcement.id); + + if (moderator) { + if (announcement.userId) { + this.moderationLogService.log(moderator, 'deleteUserAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } else { + this.moderationLogService.log(moderator, 'deleteGlobalAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } + } + } + + @bindThis + public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> { + try { + await this.announcementReadsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + announcementId: announcementId, + userId: user.id, + }); + } catch (e) { + return; + } + + if ((await this.getUnreadAnnouncements(user)).length === 0) { + this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements'); + } + } + + @bindThis + public async packMany( + announcements: MiAnnouncement[], + me?: { id: MiUser['id'] } | null | undefined, + options?: { + reads?: MiAnnouncementRead[]; + }, + ): Promise<Packed<'Announcement'>[]> { + const reads = me ? (options?.reads ?? await this.getReads(me.id)) : []; + return announcements.map(announcement => ({ + id: announcement.id, + createdAt: announcement.createdAt.toISOString(), + updatedAt: announcement.updatedAt?.toISOString() ?? null, + text: announcement.text, + title: announcement.title, + imageUrl: announcement.imageUrl, + icon: announcement.icon, + display: announcement.display, + needConfirmationToRead: announcement.needConfirmationToRead, + forYou: announcement.userId === me?.id, + isRead: reads.some(read => read.announcementId === announcement.id), + })); + } +} diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 9310fd8b52..841ce4b84a 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -1,18 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { Antenna } from '@/models/entities/Antenna.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { User } from '@/models/entities/User.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; -import { IdService } from '@/core/IdService.js'; -import { isUserRelated } from '@/misc/is-user-related.js'; +import type { MiAntenna } from '@/models/Antenna.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiUser } from '@/models/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { PushNotificationService } from '@/core/PushNotificationService.js'; import * as Acct from '@/misc/acct.js'; import type { Packed } from '@/misc/json-schema.js'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository, NotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { AntennasRepository, UserListJoiningsRepository } from '@/models/_.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { StreamMessages } from '@/server/api/stream/types.js'; @@ -21,7 +21,7 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class AntennaService implements OnApplicationShutdown { private antennasFetched: boolean; - private antennas: Antenna[]; + private antennas: MiAntenna[]; constructor( @Inject(DI.redis) @@ -30,12 +30,6 @@ export class AntennaService implements OnApplicationShutdown { @Inject(DI.redisForSub) private redisForSub: Redis.Redis, - @Inject(DI.mutingsRepository) - private mutingsRepository: MutingsRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, @@ -43,11 +37,7 @@ export class AntennaService implements OnApplicationShutdown { private userListJoiningsRepository: UserListJoiningsRepository, private utilityService: UtilityService, - private idService: IdService, private globalEventService: GlobalEventService, - private pushNotificationService: PushNotificationService, - private noteEntityService: NoteEntityService, - private antennaEntityService: AntennaEntityService, ) { this.antennasFetched = false; this.antennas = []; @@ -86,7 +76,7 @@ export class AntennaService implements OnApplicationShutdown { } @bindThis - public async addNoteToAntennas(note: Note, noteUser: { id: User['id']; username: string; host: string | null; }): Promise<void> { + public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<void> { const antennas = await this.getAntennas(); const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const))); const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna); @@ -109,7 +99,7 @@ export class AntennaService implements OnApplicationShutdown { // NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている @bindThis - public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }): Promise<boolean> { + public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<boolean> { if (note.visibility === 'specified') return false; if (note.visibility === 'followers') return false; @@ -129,6 +119,12 @@ export class AntennaService implements OnApplicationShutdown { return this.utilityService.getFullApAccount(username, host).toLowerCase(); }); if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; + } else if (antenna.src === 'users_blacklist') { + const accts = antenna.users.map(x => { + const { username, host } = Acct.parse(x); + return this.utilityService.getFullApAccount(username, host).toLowerCase(); + }); + if (accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; } const keywords = antenna.keywords diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 6ccaec26ba..7a1293a6de 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { promisify } from 'node:util'; import { Inject, Injectable } from '@nestjs/common'; import redisLock from 'redis-lock'; diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index cd6b68e721..6ca684d53c 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; -import type { LocalUser, User } from '@/models/entities/User.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -11,11 +16,11 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class CacheService implements OnApplicationShutdown { - public userByIdCache: MemoryKVCache<User, User | string>; - public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null, string | null>; - public localUserByIdCache: MemoryKVCache<LocalUser>; - public uriPersonCache: MemoryKVCache<User | null, string | null>; - public userProfileCache: RedisKVCache<UserProfile>; + public userByIdCache: MemoryKVCache<MiUser, MiUser | string>; + public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>; + public localUserByIdCache: MemoryKVCache<MiLocalUser>; + public uriPersonCache: MemoryKVCache<MiUser | null, string | null>; + public userProfileCache: RedisKVCache<MiUserProfile>; public userMutingsCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>; public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ @@ -55,14 +60,14 @@ export class CacheService implements OnApplicationShutdown { ) { //this.onMessage = this.onMessage.bind(this); - const localUserByIdCache = new MemoryKVCache<LocalUser>(1000 * 60 * 60 * 6 /* 6h */); + const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */); this.localUserByIdCache = localUserByIdCache; // ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する - const userByIdCache = new MemoryKVCache<User, User | string>(1000 * 60 * 60 * 6 /* 6h */, { + const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, { toMapConverter: user => { if (user.host === null) { - localUserByIdCache.set(user.id, user as LocalUser); + localUserByIdCache.set(user.id, user as MiLocalUser); return user.id; } @@ -72,7 +77,7 @@ export class CacheService implements OnApplicationShutdown { }); this.userByIdCache = userByIdCache; - this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null, string | null>(Infinity, { + this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, { toMapConverter: user => { if (user === null) return null; @@ -81,7 +86,7 @@ export class CacheService implements OnApplicationShutdown { }, fromMapConverter: id => id === null ? null : localUserByIdCache.get(id), }); - this.uriPersonCache = new MemoryKVCache<User | null, string | null>(Infinity, { + this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, { toMapConverter: user => { if (user === null) return null; @@ -91,7 +96,7 @@ export class CacheService implements OnApplicationShutdown { fromMapConverter: id => id === null ? null : userByIdCache.get(id), }); - this.userProfileCache = new RedisKVCache<UserProfile>(this.redisClient, 'userProfile', { + this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }), @@ -173,7 +178,7 @@ export class CacheService implements OnApplicationShutdown { break; } case 'userTokenRegenerated': { - const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as LocalUser; + const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as MiLocalUser; this.localUserByNativeTokenCache.delete(body.oldToken); this.localUserByNativeTokenCache.set(body.newToken, user); break; @@ -192,7 +197,7 @@ export class CacheService implements OnApplicationShutdown { } @bindThis - public findUserById(userId: User['id']) { + public findUserById(userId: MiUser['id']) { return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId })); } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index 10cfdba254..f64196f4fc 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/ClipService.ts b/packages/backend/src/core/ClipService.ts new file mode 100644 index 0000000000..3d9982e80f --- /dev/null +++ b/packages/backend/src/core/ClipService.ts @@ -0,0 +1,159 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { QueryFailedError } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { ClipsRepository, MiNote, MiClip, ClipNotesRepository, NotesRepository } from '@/models/_.js'; +import { bindThis } from '@/decorators.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; +import type { MiLocalUser } from '@/models/User.js'; + +@Injectable() +export class ClipService { + public static NoSuchNoteError = class extends Error {}; + public static NoSuchClipError = class extends Error {}; + public static AlreadyAddedError = class extends Error {}; + public static TooManyClipNotesError = class extends Error {}; + public static TooManyClipsError = class extends Error {}; + + constructor( + @Inject(DI.clipsRepository) + private clipsRepository: ClipsRepository, + + @Inject(DI.clipNotesRepository) + private clipNotesRepository: ClipNotesRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private roleService: RoleService, + private idService: IdService, + ) { + } + + @bindThis + public async create(me: MiLocalUser, name: string, isPublic: boolean, description: string | null): Promise<MiClip> { + const currentCount = await this.clipsRepository.countBy({ + userId: me.id, + }); + if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) { + throw new ClipService.TooManyClipsError(); + } + + const clip = await this.clipsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + userId: me.id, + name: name, + isPublic: isPublic, + description: description, + }).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0])); + + return clip; + } + + @bindThis + public async update(me: MiLocalUser, clipId: MiClip['id'], name: string | undefined, isPublic: boolean | undefined, description: string | null | undefined): Promise<void> { + const clip = await this.clipsRepository.findOneBy({ + id: clipId, + userId: me.id, + }); + + if (clip == null) { + throw new ClipService.NoSuchClipError(); + } + + await this.clipsRepository.update(clip.id, { + name: name, + description: description, + isPublic: isPublic, + }); + } + + @bindThis + public async delete(me: MiLocalUser, clipId: MiClip['id']): Promise<void> { + const clip = await this.clipsRepository.findOneBy({ + id: clipId, + userId: me.id, + }); + + if (clip == null) { + throw new ClipService.NoSuchClipError(); + } + + await this.clipsRepository.delete(clip.id); + } + + @bindThis + public async addNote(me: MiLocalUser, clipId: MiClip['id'], noteId: MiNote['id']): Promise<void> { + const clip = await this.clipsRepository.findOneBy({ + id: clipId, + userId: me.id, + }); + + if (clip == null) { + throw new ClipService.NoSuchClipError(); + } + + const currentCount = await this.clipNotesRepository.countBy({ + clipId: clip.id, + }); + if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) { + throw new ClipService.TooManyClipNotesError(); + } + + try { + await this.clipNotesRepository.insert({ + id: this.idService.genId(), + noteId: noteId, + clipId: clip.id, + }); + } catch (e: unknown) { + if (e instanceof QueryFailedError) { + if (isDuplicateKeyValueError(e)) { + throw new ClipService.AlreadyAddedError(); + } else if (e.driverError.detail.includes('is not present in table "note".')) { + throw new ClipService.NoSuchNoteError(); + } + } + + throw e; + } + + this.clipsRepository.update(clip.id, { + lastClippedAt: new Date(), + }); + + this.notesRepository.increment({ id: noteId }, 'clippedCount', 1); + } + + @bindThis + public async removeNote(me: MiLocalUser, clipId: MiClip['id'], noteId: MiNote['id']): Promise<void> { + const clip = await this.clipsRepository.findOneBy({ + id: clipId, + userId: me.id, + }); + + if (clip == null) { + throw new ClipService.NoSuchClipError(); + } + + const note = await this.notesRepository.findOneBy({ id: noteId }); + + if (note == null) { + throw new ClipService.NoSuchNoteError(); + } + + await this.clipNotesRepository.delete({ + noteId: noteId, + clipId: clip.id, + }); + + this.notesRepository.decrement({ id: noteId }, 'clippedCount', 1); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index c7c98b3bdd..78333e70a5 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -1,7 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { AccountMoveService } from './AccountMoveService.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; +import { AnnouncementService } from './AnnouncementService.js'; import { AntennaService } from './AntennaService.js'; import { AppLockService } from './AppLockService.js'; import { AchievementService } from './AchievementService.js'; @@ -37,7 +43,7 @@ import { RelayService } from './RelayService.js'; import { RoleService } from './RoleService.js'; import { S3Service } from './S3Service.js'; import { SignupService } from './SignupService.js'; -import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js'; +import { WebAuthnService } from './WebAuthnService.js'; import { UserBlockingService } from './UserBlockingService.js'; import { CacheService } from './CacheService.js'; import { UserFollowingService } from './UserFollowingService.js'; @@ -45,12 +51,14 @@ import { UserKeypairService } from './UserKeypairService.js'; import { UserListService } from './UserListService.js'; import { UserMutingService } from './UserMutingService.js'; import { UserSuspendService } from './UserSuspendService.js'; +import { UserAuthService } from './UserAuthService.js'; import { VideoProcessingService } from './VideoProcessingService.js'; import { WebhookService } from './WebhookService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; import { UtilityService } from './UtilityService.js'; import { FileInfoService } from './FileInfoService.js'; import { SearchService } from './SearchService.js'; +import { ClipService } from './ClipService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -125,6 +133,7 @@ const $LoggerService: Provider = { provide: 'LoggerService', useExisting: Logger const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService }; const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService }; const $AiService: Provider = { provide: 'AiService', useExisting: AiService }; +const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService }; const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService }; const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService }; const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService }; @@ -161,7 +170,7 @@ const $RelayService: Provider = { provide: 'RelayService', useExisting: RelaySer const $RoleService: Provider = { provide: 'RoleService', useExisting: RoleService }; const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; -const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService }; +const $WebAuthnService: Provider = { provide: 'WebAuthnService', useExisting: WebAuthnService }; const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService }; const $CacheService: Provider = { provide: 'CacheService', useExisting: CacheService }; const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService }; @@ -169,11 +178,13 @@ const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisti const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService }; const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService }; const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService }; +const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService }; const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService }; const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService }; const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService }; const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService }; const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService }; +const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -252,6 +263,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AccountMoveService, AccountUpdateService, AiService, + AnnouncementService, AntennaService, AppLockService, AchievementService, @@ -288,7 +300,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting RoleService, S3Service, SignupService, - TwoFactorAuthenticationService, + WebAuthnService, UserBlockingService, CacheService, UserFollowingService, @@ -296,11 +308,13 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListService, UserMutingService, UserSuspendService, + UserAuthService, VideoProcessingService, WebhookService, UtilityService, FileInfoService, SearchService, + ClipService, ChartLoggerService, FederationChart, NotesChart, @@ -372,6 +386,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AccountMoveService, $AccountUpdateService, $AiService, + $AnnouncementService, $AntennaService, $AppLockService, $AchievementService, @@ -408,7 +423,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $RoleService, $S3Service, $SignupService, - $TwoFactorAuthenticationService, + $WebAuthnService, $UserBlockingService, $CacheService, $UserFollowingService, @@ -416,11 +431,13 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListService, $UserMutingService, $UserSuspendService, + $UserAuthService, $VideoProcessingService, $WebhookService, $UtilityService, $FileInfoService, $SearchService, + $ClipService, $ChartLoggerService, $FederationChart, $NotesChart, @@ -493,6 +510,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AccountMoveService, AccountUpdateService, AiService, + AnnouncementService, AntennaService, AppLockService, AchievementService, @@ -529,7 +547,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting RoleService, S3Service, SignupService, - TwoFactorAuthenticationService, + WebAuthnService, UserBlockingService, CacheService, UserFollowingService, @@ -537,11 +555,13 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListService, UserMutingService, UserSuspendService, + UserAuthService, VideoProcessingService, WebhookService, UtilityService, FileInfoService, SearchService, + ClipService, FederationChart, NotesChart, UsersChart, @@ -612,6 +632,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AccountMoveService, $AccountUpdateService, $AiService, + $AnnouncementService, $AntennaService, $AppLockService, $AchievementService, @@ -648,7 +669,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $RoleService, $S3Service, $SignupService, - $TwoFactorAuthenticationService, + $WebAuthnService, $UserBlockingService, $CacheService, $UserFollowingService, @@ -656,11 +677,13 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListService, $UserMutingService, $UserSuspendService, + $UserAuthService, $VideoProcessingService, $WebhookService, $UtilityService, $FileInfoService, $SearchService, + $ClipService, $FederationChart, $NotesChart, $UsersChart, diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index cef664bf0b..3419d0b497 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -1,13 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { IsNull, DataSource } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; -import { User } from '@/models/entities/User.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; +import { MiUser } from '@/models/User.js'; +import { MiUserProfile } from '@/models/UserProfile.js'; import { IdService } from '@/core/IdService.js'; -import { UserKeypair } from '@/models/entities/UserKeypair.js'; -import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import { MiUserKeypair } from '@/models/UserKeypair.js'; +import { MiUsedUsername } from '@/models/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; import { bindThis } from '@/decorators.js'; @@ -23,7 +28,7 @@ export class CreateSystemUserService { } @bindThis - public async createSystemUser(username: string): Promise<User> { + public async createSystemUser(username: string): Promise<MiUser> { const password = randomUUID(); // Generate hash of password @@ -35,18 +40,18 @@ export class CreateSystemUserService { const keyPair = await genRsaKeyPair(); - let account!: User; + let account!: MiUser; // Start transaction await this.db.transaction(async transactionalEntityManager => { - const exist = await transactionalEntityManager.findOneBy(User, { + const exist = await transactionalEntityManager.findOneBy(MiUser, { usernameLower: username.toLowerCase(), host: IsNull(), }); if (exist) throw new Error('the user is already exists'); - account = await transactionalEntityManager.insert(User, { + account = await transactionalEntityManager.insert(MiUser, { id: this.idService.genId(), createdAt: new Date(), username: username, @@ -57,21 +62,21 @@ export class CreateSystemUserService { isLocked: true, isExplorable: false, isBot: true, - }).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0])); + }).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0])); - await transactionalEntityManager.insert(UserKeypair, { + await transactionalEntityManager.insert(MiUserKeypair, { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, userId: account.id, }); - await transactionalEntityManager.insert(UserProfile, { + await transactionalEntityManager.insert(MiUserProfile, { userId: account.id, autoAcceptFollowed: false, password: hash, }); - await transactionalEntityManager.insert(UsedUsername, { + await transactionalEntityManager.insert(MiUsedUsername, { createdAt: new Date(), username: username.toLowerCase(), }); diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 661d956bd6..b14a8666e6 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -1,55 +1,55 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; -import { DataSource, In, IsNull } from 'typeorm'; +import { In, IsNull } from 'typeorm'; import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; -import type { EmojisRepository, Role } from '@/models/index.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiEmoji } from '@/models/Emoji.js'; +import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; -import type { Config } from '@/config.js'; import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/server/api/stream/types.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @Injectable() export class CustomEmojiService implements OnApplicationShutdown { - private cache: MemoryKVCache<Emoji | null>; - public localEmojisCache: RedisSingleCache<Map<string, Emoji>>; + private cache: MemoryKVCache<MiEmoji | null>; + public localEmojisCache: RedisSingleCache<Map<string, MiEmoji>>; constructor( @Inject(DI.redis) private redisClient: Redis.Redis, - @Inject(DI.config) - private config: Config, - - @Inject(DI.db) - private db: DataSource, - @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, private utilityService: UtilityService, private idService: IdService, private emojiEntityService: EmojiEntityService, + private moderationLogService: ModerationLogService, private globalEventService: GlobalEventService, ) { - this.cache = new MemoryKVCache<Emoji | null>(1000 * 60 * 60 * 12); + this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12); - this.localEmojisCache = new RedisSingleCache<Map<string, Emoji>>(this.redisClient, 'localEmojis', { + this.localEmojisCache = new RedisSingleCache<Map<string, MiEmoji>>(this.redisClient, 'localEmojis', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60 * 3, // 3m fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))), toRedisConverter: (value) => JSON.stringify(Array.from(value.values())), fromRedisConverter: (value) => { if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す) - return new Map(JSON.parse(value).map((x: Serialized<Emoji>) => [x.name, { + return new Map(JSON.parse(value).map((x: Serialized<MiEmoji>) => [x.name, { ...x, updatedAt: x.updatedAt ? new Date(x.updatedAt) : null, }])); @@ -59,7 +59,7 @@ export class CustomEmojiService implements OnApplicationShutdown { @bindThis public async add(data: { - driveFile: DriveFile; + driveFile: MiDriveFile; name: string; category: string | null; aliases: string[]; @@ -67,8 +67,8 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; - roleIdsThatCanBeUsedThisEmojiAsReaction: Role['id'][]; - }): Promise<Emoji> { + roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; + }, moderator?: MiUser): Promise<MiEmoji> { const emoji = await this.emojisRepository.insert({ id: this.idService.genId(), updatedAt: new Date(), @@ -91,22 +91,29 @@ export class CustomEmojiService implements OnApplicationShutdown { this.globalEventService.publishBroadcastStream('emojiAdded', { emoji: await this.emojiEntityService.packDetailed(emoji.id), }); + + if (moderator) { + this.moderationLogService.log(moderator, 'addCustomEmoji', { + emojiId: emoji.id, + emoji: emoji, + }); + } } return emoji; } @bindThis - public async update(id: Emoji['id'], data: { - driveFile?: DriveFile; + public async update(id: MiEmoji['id'], data: { + driveFile?: MiDriveFile; name?: string; category?: string | null; aliases?: string[]; license?: string | null; isSensitive?: boolean; localOnly?: boolean; - roleIdsThatCanBeUsedThisEmojiAsReaction?: Role['id'][]; - }): Promise<void> { + roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; + }, moderator?: MiUser): Promise<void> { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); @@ -142,10 +149,18 @@ export class CustomEmojiService implements OnApplicationShutdown { emoji: updated, }); } + + if (moderator) { + this.moderationLogService.log(moderator, 'updateCustomEmoji', { + emojiId: emoji.id, + before: emoji, + after: updated, + }); + } } @bindThis - public async addAliasesBulk(ids: Emoji['id'][], aliases: string[]) { + public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { const emojis = await this.emojisRepository.findBy({ id: In(ids), }); @@ -165,7 +180,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async setAliasesBulk(ids: Emoji['id'][], aliases: string[]) { + public async setAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { await this.emojisRepository.update({ id: In(ids), }, { @@ -181,7 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async removeAliasesBulk(ids: Emoji['id'][], aliases: string[]) { + public async removeAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { const emojis = await this.emojisRepository.findBy({ id: In(ids), }); @@ -201,7 +216,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async setCategoryBulk(ids: Emoji['id'][], category: string | null) { + public async setCategoryBulk(ids: MiEmoji['id'][], category: string | null) { await this.emojisRepository.update({ id: In(ids), }, { @@ -217,7 +232,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async setLicenseBulk(ids: Emoji['id'][], license: string | null) { + public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) { await this.emojisRepository.update({ id: In(ids), }, { @@ -233,7 +248,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async delete(id: Emoji['id']) { + public async delete(id: MiEmoji['id'], moderator?: MiUser) { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); await this.emojisRepository.delete(emoji.id); @@ -243,16 +258,30 @@ export class CustomEmojiService implements OnApplicationShutdown { this.globalEventService.publishBroadcastStream('emojiDeleted', { emojis: [await this.emojiEntityService.packDetailed(emoji)], }); + + if (moderator) { + this.moderationLogService.log(moderator, 'deleteCustomEmoji', { + emojiId: emoji.id, + emoji: emoji, + }); + } } @bindThis - public async deleteBulk(ids: Emoji['id'][]) { + public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { const emojis = await this.emojisRepository.findBy({ id: In(ids), }); for (const emoji of emojis) { await this.emojisRepository.delete(emoji.id); + + if (moderator) { + this.moderationLogService.log(moderator, 'deleteCustomEmoji', { + emojiId: emoji.id, + emoji: emoji, + }); + } } this.localEmojisCache.refresh(); diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 3a0592441b..570bd440e4 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -14,7 +18,6 @@ export class DeleteAccountService { private userSuspendService: UserSuspendService, private queueService: QueueService, - private globalEventService: GlobalEventService, ) { } diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 1ad7c0137b..5474272b00 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import * as stream from 'node:stream/promises'; import { Inject, Injectable } from '@nestjs/common'; diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 355e5e8c0d..366205f586 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; @@ -6,12 +11,12 @@ import { sharpBmp } from 'sharp-read-bmp'; import { IsNull } from 'typeorm'; import { DeleteObjectCommandInput, PutObjectCommandInput, NoSuchKey } from '@aws-sdk/client-s3'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import Logger from '@/logger.js'; -import type { RemoteUser, User } from '@/models/entities/User.js'; +import type { MiRemoteUser, MiUser } from '@/models/User.js'; import { MetaService } from '@/core/MetaService.js'; -import { DriveFile } from '@/models/entities/DriveFile.js'; +import { MiDriveFile } from '@/models/DriveFile.js'; import { IdService } from '@/core/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; @@ -22,7 +27,7 @@ import { VideoProcessingService } from '@/core/VideoProcessingService.js'; import { ImageProcessingService } from '@/core/ImageProcessingService.js'; import type { IImage } from '@/core/ImageProcessingService.js'; import { QueueService } from '@/core/QueueService.js'; -import type { DriveFolder } from '@/models/entities/DriveFolder.js'; +import type { MiDriveFolder } from '@/models/DriveFolder.js'; import { createTemp } from '@/misc/create-temp.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; @@ -37,10 +42,11 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { correctFilename } from '@/misc/correct-filename.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; type AddFileArgs = { /** User who wish to add file */ - user: { id: User['id']; host: User['host'] } | null; + user: { id: MiUser['id']; host: MiUser['host'] } | null; /** File path */ path: string; /** Name */ @@ -68,8 +74,8 @@ type AddFileArgs = { type UploadFromUrlArgs = { url: string; - user: { id: User['id']; host: User['host'] } | null; - folderId?: DriveFolder['id'] | null; + user: { id: MiUser['id']; host: MiUser['host'] } | null; + folderId?: MiDriveFolder['id'] | null; uri?: string | null; sensitive?: boolean; force?: boolean; @@ -81,6 +87,9 @@ type UploadFromUrlArgs = { @Injectable() export class DriveService { + public static NoSuchFolderError = class extends Error {}; + public static InvalidFileNameError = class extends Error {}; + public static CannotUnmarkSensitiveError = class extends Error {}; private registerLogger: Logger; private downloaderLogger: Logger; private deleteLogger: Logger; @@ -114,6 +123,7 @@ export class DriveService { private globalEventService: GlobalEventService, private queueService: QueueService, private roleService: RoleService, + private moderationLogService: ModerationLogService, private driveChart: DriveChart, private perUserDriveChart: PerUserDriveChart, private instanceChart: InstanceChart, @@ -133,7 +143,7 @@ export class DriveService { * @param size Size for original */ @bindThis - private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> { + private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<MiDriveFile> { // thunbnail, webpublic を必要なら生成 const alts = await this.generateAlts(path, type, !file.uri); @@ -327,7 +337,7 @@ export class DriveService { this.registerLogger.debug('web image not created (not an required image)'); } } catch (err) { - this.registerLogger.warn('web image not created (an error occured)', err as Error); + this.registerLogger.warn('web image not created (an error occurred)', err as Error); } } else { if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)'); @@ -346,7 +356,7 @@ export class DriveService { thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 422); } } catch (err) { - this.registerLogger.warn('thumbnail not created (an error occured)', err as Error); + this.registerLogger.warn('thumbnail not created (an error occurred)', err as Error); } // #endregion thumbnail @@ -400,7 +410,7 @@ export class DriveService { // Expire oldest file (without avatar or banner) of remote user @bindThis - private async expireOldFile(user: RemoteUser, driveCapacity: number) { + private async expireOldFile(user: MiRemoteUser, driveCapacity: number) { const q = this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) .andWhere('file.isLink = FALSE'); @@ -446,7 +456,7 @@ export class DriveService { requestIp = null, requestHeaders = null, ext = null, - }: AddFileArgs): Promise<DriveFile> { + }: AddFileArgs): Promise<MiDriveFile> { let skipNsfwCheck = false; const instance = await this.metaService.fetch(); const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw; @@ -515,7 +525,7 @@ export class DriveService { if (isLocalUser) { throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); } - await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser, driveCapacity - info.size); + await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as MiRemoteUser, driveCapacity - info.size); } } //#endregion @@ -553,7 +563,7 @@ export class DriveService { const folder = await fetchFolder(); - let file = new DriveFile(); + let file = new MiDriveFile(); file.id = this.idService.genId(); file.createdAt = new Date(); file.userId = user ? user.id : null; @@ -569,9 +579,7 @@ export class DriveService { file.maybePorn = info.porn; file.isSensitive = user ? this.userEntityService.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : - (sensitive !== null && sensitive !== undefined) - ? sensitive - : false + sensitive ?? false : false; if (info.sensitive && profile!.autoSensitive) file.isSensitive = true; @@ -611,7 +619,7 @@ export class DriveService { file = await this.driveFilesRepository.findOneBy({ uri: file.uri!, userId: user ? user.id : IsNull(), - }) as DriveFile; + }) as MiDriveFile; } else { this.registerLogger.error(err as Error); throw err; @@ -645,7 +653,58 @@ export class DriveService { } @bindThis - public async deleteFile(file: DriveFile, isExpired = false) { + public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) { + const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw; + + if (values.name && !this.driveFileEntityService.validateFileName(file.name)) { + throw new DriveService.InvalidFileNameError(); + } + + if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) { + throw new DriveService.CannotUnmarkSensitiveError(); + } + + if (values.folderId != null) { + const folder = await this.driveFoldersRepository.findOneBy({ + id: values.folderId, + userId: file.userId!, + }); + + if (folder == null) { + throw new DriveService.NoSuchFolderError(); + } + } + + await this.driveFilesRepository.update(file.id, values); + + const fileObj = await this.driveFileEntityService.pack(file.id, { self: true }); + + // Publish fileUpdated event + if (file.userId) { + this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj); + } + + if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) { + if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) { + if (values.isSensitive) { + this.moderationLogService.log(updater, 'markSensitiveDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } else { + this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } + } + } + + return fileObj; + } + + @bindThis + public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -668,11 +727,11 @@ export class DriveService { } } - this.deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired, deleter); } @bindThis - public async deleteFileSync(file: DriveFile, isExpired = false) { + public async deleteFileSync(file: MiDriveFile, isExpired = false, deleter?: MiUser) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -699,11 +758,11 @@ export class DriveService { await Promise.all(promises); } - this.deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired, deleter); } @bindThis - private async deletePostProcess(file: DriveFile, isExpired = false) { + private async deletePostProcess(file: MiDriveFile, isExpired = false, deleter?: MiUser) { // リモートファイル期限切れ削除後は直リンクにする if (isExpired && file.userHost !== null && file.uri != null) { this.driveFilesRepository.update(file.id, { @@ -730,6 +789,17 @@ export class DriveService { this.instanceChart.updateDrive(file, false); } } + + if (file.userId) { + this.globalEventService.publishDriveStream(file.userId, 'fileDeleted', file.id); + } + + if (deleter && await this.roleService.isModerator(deleter) && (file.userId !== deleter.id)) { + this.moderationLogService.log(deleter, 'deleteDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } } @bindThis @@ -766,7 +836,7 @@ export class DriveService { comment = null, requestIp = null, requestHeaders = null, - }: UploadFromUrlArgs): Promise<DriveFile> { + }: UploadFromUrlArgs): Promise<MiDriveFile> { // Create temp file const [path, cleanup] = await createTemp(); diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index a04e9c1225..c9da3f77c0 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as nodemailer from 'nodemailer'; import { Inject, Injectable } from '@nestjs/common'; import { validate as validateEmail } from 'deep-email-validator'; @@ -5,7 +10,7 @@ import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index a762038942..61806583c6 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { InstancesRepository } from '@/models/index.js'; -import type { Instance } from '@/models/entities/Instance.js'; +import type { InstancesRepository } from '@/models/_.js'; +import type { MiInstance } from '@/models/Instance.js'; import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; @@ -10,7 +15,7 @@ import { bindThis } from '@/decorators.js'; @Injectable() export class FederatedInstanceService implements OnApplicationShutdown { - public federatedInstanceCache: RedisKVCache<Instance | null>; + public federatedInstanceCache: RedisKVCache<MiInstance | null>; constructor( @Inject(DI.redis) @@ -22,7 +27,7 @@ export class FederatedInstanceService implements OnApplicationShutdown { private utilityService: UtilityService, private idService: IdService, ) { - this.federatedInstanceCache = new RedisKVCache<Instance | null>(this.redisClient, 'federatedInstance', { + this.federatedInstanceCache = new RedisKVCache<MiInstance | null>(this.redisClient, 'federatedInstance', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60 * 3, // 3m fetcher: (key) => this.instancesRepository.findOneBy({ host: key }), @@ -41,7 +46,7 @@ export class FederatedInstanceService implements OnApplicationShutdown { } @bindThis - public async fetch(host: string): Promise<Instance> { + public async fetch(host: string): Promise<MiInstance> { host = this.utilityService.toPuny(host); const cached = await this.federatedInstanceCache.get(host); @@ -65,7 +70,7 @@ export class FederatedInstanceService implements OnApplicationShutdown { } @bindThis - public async update(id: Instance['id'], data: Partial<Instance>): Promise<void> { + public async update(id: MiInstance['id'], data: Partial<MiInstance>): Promise<void> { const result = await this.instancesRepository.createQueryBuilder().update() .set(data) .where('id = :id', { id }) diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 88706f1a48..682acef15b 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + 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 type { Instance } from '@/models/entities/Instance.js'; +import type { MiInstance } from '@/models/Instance.js'; import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; import { LoggerService } from '@/core/LoggerService.js'; @@ -57,7 +62,7 @@ export class FetchInstanceMetadataService { } @bindThis - public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { + public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> { const host = instance.host; // Acquire mutex to ensure no parallel runs if (!await this.tryLock(host)) return; @@ -70,9 +75,9 @@ export class FetchInstanceMetadataService { return; } } - + this.logger.info(`Fetching metadata of ${instance.host} ...`); - + const [info, dom, manifest] = await Promise.all([ this.fetchNodeinfo(instance).catch(() => null), this.fetchDom(instance).catch(() => null), @@ -103,7 +108,7 @@ export class FetchInstanceMetadataService { if (name) updates.name = name; if (description) updates.description = description; - if (icon || favicon) updates.iconUrl = (icon && !icon.includes('data:image/png;base64')) ? icon : favicon; + if (icon ?? favicon) updates.iconUrl = (icon && !icon.includes('data:image/png;base64')) ? icon : favicon; if (favicon) updates.faviconUrl = favicon; if (themeColor) updates.themeColor = themeColor; @@ -118,7 +123,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> { + private async fetchNodeinfo(instance: MiInstance): Promise<NodeInfo> { this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); try { @@ -137,10 +142,10 @@ export class FetchInstanceMetadataService { const links = wellknown.links as any[]; - const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); - const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); - const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); - const link = lnik2_1 ?? lnik2_0 ?? lnik1_0; + const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); + const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); + const link2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); + const link = link2_1 ?? link2_0 ?? link1_0; if (link == null) { throw new Error('No nodeinfo link provided'); @@ -162,7 +167,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchDom(instance: Instance): Promise<DOMWindow['document']> { + private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> { this.logger.info(`Fetching HTML of ${instance.host} ...`); const url = 'https://' + instance.host; @@ -176,7 +181,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> { + private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> { const url = 'https://' + instance.host; const manifestUrl = url + '/manifest.json'; @@ -187,7 +192,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> { + private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> { const url = 'https://' + instance.host; if (doc) { @@ -213,7 +218,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { + private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | 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; diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index 1028d3760e..fdea59a8ad 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import * as crypto from 'node:crypto'; import { join } from 'node:path'; diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 19d9370083..4bc4f54c21 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { UserList } from '@/models/entities/UserList.js'; -import type { Antenna } from '@/models/entities/Antenna.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiUserList } from '@/models/UserList.js'; +import type { MiAntenna } from '@/models/Antenna.js'; import type { StreamChannels, AdminStreamTypes, @@ -20,7 +25,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; -import { Role } from '@/models/index.js'; +import { MiRole } from '@/models/_.js'; @Injectable() export class GlobalEventService { @@ -56,17 +61,17 @@ export class GlobalEventService { } @bindThis - public publishMainStream<K extends keyof MainStreamTypes>(userId: User['id'], type: K, value?: MainStreamTypes[K]): void { + public publishMainStream<K extends keyof MainStreamTypes>(userId: MiUser['id'], type: K, value?: MainStreamTypes[K]): void { this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishDriveStream<K extends keyof DriveStreamTypes>(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void { + public publishDriveStream<K extends keyof DriveStreamTypes>(userId: MiUser['id'], type: K, value?: DriveStreamTypes[K]): void { this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void { + public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: MiNote['id'], type: K, value?: NoteStreamTypes[K]): void { this.publish(`noteStream:${noteId}`, type, { id: noteId, body: value, @@ -74,17 +79,17 @@ export class GlobalEventService { } @bindThis - public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void { + public publishUserListStream<K extends keyof UserListStreamTypes>(listId: MiUserList['id'], type: K, value?: UserListStreamTypes[K]): void { this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void { + public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: MiAntenna['id'], type: K, value?: AntennaStreamTypes[K]): void { this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: Role['id'], type: K, value?: RoleTimelineStreamTypes[K]): void { + public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: MiRole['id'], type: K, value?: RoleTimelineStreamTypes[K]): void { this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value); } @@ -94,7 +99,7 @@ export class GlobalEventService { } @bindThis - public publishAdminStream<K extends keyof AdminStreamTypes>(userId: User['id'], type: K, value?: AdminStreamTypes[K]): void { + public publishAdminStream<K extends keyof AdminStreamTypes>(userId: MiUser['id'], type: K, value?: AdminStreamTypes[K]): void { this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); } } diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 851e42e7ba..c72c7460ff 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -1,19 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { IdService } from '@/core/IdService.js'; -import type { Hashtag } from '@/models/entities/Hashtag.js'; -import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; +import type { MiHashtag } from '@/models/Hashtag.js'; +import type { HashtagsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @Injectable() export class HashtagService { constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.hashtagsRepository) private hashtagsRepository: HashtagsRepository, @@ -23,14 +25,14 @@ export class HashtagService { } @bindThis - public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { + public async updateHashtags(user: { id: MiUser['id']; host: MiUser['host']; }, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag); } } @bindThis - public async updateUsertags(user: User, tags: string[]) { + public async updateUsertags(user: MiUser, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag, true, true); } @@ -41,7 +43,7 @@ export class HashtagService { } @bindThis - public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { + public async updateHashtag(user: { id: MiUser['id']; host: MiUser['host']; }, tag: string, isUserAttached = false, inc = true) { tag = normalizeForSearch(tag); const index = await this.hashtagsRepository.findOneBy({ name: tag }); @@ -121,7 +123,7 @@ export class HashtagService { attachedLocalUsersCount: this.userEntityService.isLocalUser(user) ? 1 : 0, attachedRemoteUserIds: this.userEntityService.isRemoteUser(user) ? [user.id] : [], attachedRemoteUsersCount: this.userEntityService.isRemoteUser(user) ? 1 : 0, - } as Hashtag); + } as MiHashtag); } else { this.hashtagsRepository.insert({ id: this.idService.genId(), @@ -138,7 +140,7 @@ export class HashtagService { attachedLocalUsersCount: 0, attachedRemoteUserIds: [], attachedRemoteUsersCount: 0, - } as Hashtag); + } as MiHashtag); } } } diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 3bb999ff8b..73bb3dc7e9 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as http from 'node:http'; import * as https from 'node:https'; import * as net from 'node:net'; @@ -48,12 +53,14 @@ export class HttpRequestService { keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup as unknown as net.LookupFunction, + localAddress: config.outgoingAddress, }); this.https = new https.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup as unknown as net.LookupFunction, + localAddress: config.outgoingAddress, }); const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128); @@ -66,6 +73,7 @@ export class HttpRequestService { maxFreeSockets: 256, scheduling: 'lifo', proxy: config.proxy, + localAddress: config.outgoingAddress, }) : this.http; @@ -77,6 +85,7 @@ export class HttpRequestService { maxFreeSockets: 256, scheduling: 'lifo', proxy: config.proxy, + localAddress: config.outgoingAddress, }) : this.https; } @@ -88,7 +97,7 @@ export class HttpRequestService { */ @bindThis public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { - if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { + if (bypassProxy || (this.config.proxyBypassHosts ?? []).includes(url.hostname)) { return url.protocol === 'http:' ? this.http : this.https; } else { return url.protocol === 'http:' ? this.httpAgent : this.httpsAgent; diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 4d129407cb..06c58ad8a1 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -1,8 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { ulid } from 'ulid'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { genAid, parseAid } from '@/misc/id/aid.js'; +import { genAidx, parseAidx } from '@/misc/id/aidx.js'; import { genMeid, parseMeid } from '@/misc/id/meid.js'; import { genMeidg, parseMeidg } from '@/misc/id/meidg.js'; import { genObjectId, parseObjectId } from '@/misc/id/object-id.js'; @@ -26,6 +32,7 @@ export class IdService { switch (this.method) { case 'aid': return genAid(date); + case 'aidx': return genAidx(date); case 'meid': return genMeid(date); case 'meidg': return genMeidg(date); case 'ulid': return ulid(date.getTime()); @@ -38,6 +45,7 @@ export class IdService { public parse(id: string): { date: Date; } { switch (this.method) { case 'aid': return parseAid(id); + case 'aidx': return parseAidx(id); case 'objectid': return parseObjectId(id); case 'meid': return parseMeid(id); case 'meidg': return parseMeidg(id); diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index 3246475d12..8e800eb8f5 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -1,7 +1,10 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import sharp from 'sharp'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; export type IImage = { data: Buffer; @@ -45,8 +48,6 @@ import { Readable } from 'node:stream'; @Injectable() export class ImageProcessingService { constructor( - @Inject(DI.config) - private config: Config, ) { } diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index 2e047dc5c1..b40fd46291 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { LocalUser } from '@/models/entities/User.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { MiLocalUser } from '@/models/User.js'; +import type { UsersRepository } from '@/models/_.js'; import { MemorySingleCache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; @@ -11,7 +16,7 @@ const ACTOR_USERNAME = 'instance.actor' as const; @Injectable() export class InstanceActorService { - private cache: MemorySingleCache<LocalUser>; + private cache: MemorySingleCache<MiLocalUser>; constructor( @Inject(DI.usersRepository) @@ -19,24 +24,24 @@ export class InstanceActorService { private createSystemUserService: CreateSystemUserService, ) { - this.cache = new MemorySingleCache<LocalUser>(Infinity); + this.cache = new MemorySingleCache<MiLocalUser>(Infinity); } @bindThis - public async getInstanceActor(): Promise<LocalUser> { + public async getInstanceActor(): Promise<MiLocalUser> { const cached = this.cache.get(); if (cached) return cached; const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, - }) as LocalUser | undefined; + }) as MiLocalUser | undefined; if (user) { this.cache.set(user); return user; } else { - const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser; + const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as MiLocalUser; this.cache.set(created); return created; } diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 7c03af7de7..22129bb348 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import * as Path from 'node:path'; import { fileURLToPath } from 'node:url'; diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index 14df9aa40c..46b000ee63 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -1,6 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import type { KEYWORD } from 'color-convert/conversions.js'; @@ -8,8 +11,6 @@ import type { KEYWORD } from 'color-convert/conversions.js'; @Injectable() export class LoggerService { constructor( - @Inject(DI.config) - private config: Config, ) { } diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index aae0a9134b..00e1e3c1fc 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; -import { Meta } from '@/models/entities/Meta.js'; +import { MiMeta } from '@/models/Meta.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { bindThis } from '@/decorators.js'; import { StreamMessages } from '@/server/api/stream/types.js'; @@ -10,8 +15,8 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class MetaService implements OnApplicationShutdown { - private cache: Meta | undefined; - private intervalId: NodeJS.Timer; + private cache: MiMeta | undefined; + private intervalId: NodeJS.Timeout; constructor( @Inject(DI.redisForSub) @@ -54,12 +59,12 @@ export class MetaService implements OnApplicationShutdown { } @bindThis - public async fetch(noCache = false): Promise<Meta> { + public async fetch(noCache = false): Promise<MiMeta> { if (!noCache && this.cache) return this.cache; return await this.db.transaction(async transactionalEntityManager => { // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する - const metas = await transactionalEntityManager.find(Meta, { + const metas = await transactionalEntityManager.find(MiMeta, { order: { id: 'DESC', }, @@ -74,13 +79,13 @@ export class MetaService implements OnApplicationShutdown { // metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う const saved = await transactionalEntityManager .upsert( - Meta, + MiMeta, { id: 'x', }, ['id'], ) - .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); + .then((x) => transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0])); this.cache = saved; return saved; @@ -89,9 +94,9 @@ export class MetaService implements OnApplicationShutdown { } @bindThis - public async update(data: Partial<Meta>): Promise<Meta> { + public async update(data: Partial<MiMeta>): Promise<MiMeta> { const updated = await this.db.transaction(async transactionalEntityManager => { - const metas = await transactionalEntityManager.find(Meta, { + const metas = await transactionalEntityManager.find(MiMeta, { order: { id: 'DESC', }, @@ -100,9 +105,9 @@ export class MetaService implements OnApplicationShutdown { const meta = metas[0]; if (meta) { - await transactionalEntityManager.update(Meta, meta.id, data); + await transactionalEntityManager.update(MiMeta, meta.id, data); - const metas = await transactionalEntityManager.find(Meta, { + const metas = await transactionalEntityManager.find(MiMeta, { order: { id: 'DESC', }, @@ -110,7 +115,7 @@ export class MetaService implements OnApplicationShutdown { return metas[0]; } else { - return await transactionalEntityManager.save(Meta, data); + return await transactionalEntityManager.save(MiMeta, data); } }); diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 38aaa84524..b275d1b142 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; @@ -5,7 +10,7 @@ import { Window } from 'happy-dom'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; -import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; +import type { IMentionedRemoteUsers } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; import type * as mfm from 'mfm-js'; diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 80e8cb9e52..f7f9063d92 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { ModerationLogsRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { ModerationLogsRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { IdService } from '@/core/IdService.js'; import { bindThis } from '@/decorators.js'; +import { ModerationLogPayloads, moderationLogTypes } from '@/types.js'; @Injectable() export class ModerationLogService { @@ -16,13 +22,13 @@ export class ModerationLogService { } @bindThis - public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) { + public async log<T extends typeof moderationLogTypes[number]>(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) { await this.moderationLogsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: moderator.id, type: type, - info: info ?? {}, + info: (info as any) ?? {}, }); } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 648ff76483..972319ddcf 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { setImmediate } from 'node:timers/promises'; import * as mfm from 'mfm-js'; import { In, DataSource } from 'typeorm'; @@ -7,22 +12,22 @@ import RE2 from 're2'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; -import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; -import { Note } from '@/models/entities/Note.js'; -import type { ChannelFollowingsRepository, ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { App } from '@/models/entities/App.js'; +import type { IMentionedRemoteUsers } from '@/models/Note.js'; +import { MiNote } from '@/models/Note.js'; +import type { ChannelsRepository, FollowingsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiApp } from '@/models/App.js'; import { concat } from '@/misc/prelude/array.js'; import { IdService } from '@/core/IdService.js'; -import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; -import type { IPoll } from '@/models/entities/Poll.js'; -import { Poll } from '@/models/entities/Poll.js'; +import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js'; +import type { IPoll } from '@/models/Poll.js'; +import { MiPoll } from '@/models/Poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; -import type { Channel } from '@/models/entities/Channel.js'; +import type { MiChannel } from '@/models/Channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MemorySingleCache } from '@/misc/cache.js'; -import type { UserProfile } from '@/models/entities/UserProfile.js'; +import type { MiUserProfile } from '@/models/UserProfile.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; @@ -49,23 +54,23 @@ import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; -const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); +const mutedWordsCache = new MemorySingleCache<{ userId: MiUserProfile['userId']; mutedWords: MiUserProfile['mutedWords']; }[]>(1000 * 60 * 5); type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; class NotificationManager { - private notifier: { id: User['id']; }; - private note: Note; + private notifier: { id: MiUser['id']; }; + private note: MiNote; private queue: { - target: LocalUser['id']; + target: MiLocalUser['id']; reason: NotificationType; }[]; constructor( private mutingsRepository: MutingsRepository, private notificationService: NotificationService, - notifier: { id: User['id']; }, - note: Note, + notifier: { id: MiUser['id']; }, + note: MiNote, ) { this.notifier = notifier; this.note = note; @@ -73,7 +78,7 @@ class NotificationManager { } @bindThis - public push(notifiee: LocalUser['id'], reason: NotificationType) { + public push(notifiee: MiLocalUser['id'], reason: NotificationType) { // 自分自身へは通知しない if (this.notifier.id === notifiee) return; @@ -114,32 +119,32 @@ class NotificationManager { } type MinimumUser = { - id: User['id']; - host: User['host']; - username: User['username']; - uri: User['uri']; + id: MiUser['id']; + host: MiUser['host']; + username: MiUser['username']; + uri: MiUser['uri']; }; type Option = { createdAt?: Date | null; name?: string | null; text?: string | null; - reply?: Note | null; - renote?: Note | null; - files?: DriveFile[] | null; + reply?: MiNote | null; + renote?: MiNote | null; + files?: MiDriveFile[] | null; poll?: IPoll | null; localOnly?: boolean | null; - reactionAcceptance?: Note['reactionAcceptance']; + reactionAcceptance?: MiNote['reactionAcceptance']; cw?: string | null; visibility?: string; visibleUsers?: MinimumUser[] | null; - channel?: Channel | null; + channel?: MiChannel | null; apMentions?: MinimumUser[] | null; apHashtags?: string[] | null; apEmojis?: string[] | null; uri?: string | null; url?: string | null; - app?: App | null; + app?: MiApp | null; }; @Injectable() @@ -177,12 +182,12 @@ export class NoteCreateService implements OnApplicationShutdown { @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, - @Inject(DI.channelFollowingsRepository) - private channelFollowingsRepository: ChannelFollowingsRepository, - @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: NoteThreadMutingsRepository, + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private idService: IdService, @@ -209,12 +214,12 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis public async create(user: { - id: User['id']; - username: User['username']; - host: User['host']; - createdAt: User['createdAt']; - isBot: User['isBot']; - }, data: Option, silent = false): Promise<Note> { + id: MiUser['id']; + username: MiUser['username']; + host: MiUser['host']; + createdAt: MiUser['createdAt']; + isBot: MiUser['isBot']; + }, data: Option, silent = false): Promise<MiNote> { // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -332,7 +337,7 @@ export class NoteCreateService implements OnApplicationShutdown { if (data.channel) { this.redisClient.xadd( `channelTimeline:${data.channel.id}`, - 'MAXLEN', '~', '1000', + 'MAXLEN', '~', this.config.perChannelMaxNoteCacheCount.toString(), '*', 'note', note.id); } @@ -346,8 +351,8 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { - const insert = new Note({ + private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { + const insert = new MiNote({ id: this.idService.genId(data.createdAt!), createdAt: data.createdAt!, fileIds: data.files ? data.files.map(file => file.id) : [], @@ -362,7 +367,7 @@ export class NoteCreateService implements OnApplicationShutdown { name: data.name, text: data.text, hasPoll: data.poll != null, - cw: data.cw == null ? null : data.cw, + cw: data.cw ?? null, tags: tags.map(tag => normalizeForSearch(tag)), emojis, userId: user.id, @@ -397,7 +402,7 @@ export class NoteCreateService implements OnApplicationShutdown { const url = profile != null ? profile.url : null; return { uri: u.uri, - url: url == null ? undefined : url, + url: url ?? undefined, username: u.username, host: u.host, } as IMentionedRemoteUsers[0]; @@ -409,9 +414,9 @@ export class NoteCreateService implements OnApplicationShutdown { if (insert.hasPoll) { // Start transaction await this.db.transaction(async transactionalEntityManager => { - await transactionalEntityManager.insert(Note, insert); + await transactionalEntityManager.insert(MiNote, insert); - const poll = new Poll({ + const poll = new MiPoll({ noteId: insert.id, choices: data.poll!.choices, expiresAt: data.poll!.expiresAt, @@ -422,7 +427,7 @@ export class NoteCreateService implements OnApplicationShutdown { userHost: user.host, }); - await transactionalEntityManager.insert(Poll, poll); + await transactionalEntityManager.insert(MiPoll, poll); }); } else { await this.notesRepository.insert(insert); @@ -444,12 +449,12 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private async postNoteCreated(note: Note, user: { - id: User['id']; - username: User['username']; - host: User['host']; - createdAt: User['createdAt']; - isBot: User['isBot']; + private async postNoteCreated(note: MiNote, user: { + id: MiUser['id']; + username: MiUser['username']; + host: MiUser['host']; + createdAt: MiUser['createdAt']; + isBot: MiUser['isBot']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { const meta = await this.metaService.fetch(); @@ -503,6 +508,20 @@ export class NoteCreateService implements OnApplicationShutdown { this.saveReply(data.reply, note); } + if (data.reply == null) { + this.followingsRepository.findBy({ + followeeId: user.id, + notify: 'normal', + }).then(followings => { + for (const following of followings) { + this.notificationService.createNotification(following.followerId, 'note', { + notifierId: user.id, + noteId: note.id, + }); + } + }); + } + // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき if (data.renote && (await this.noteEntityService.countSameRenotes(user.id, data.renote.id, note.id) === 0)) { if (!user.isBot) this.incRenoteCount(data.renote); @@ -623,7 +642,7 @@ export class NoteCreateService implements OnApplicationShutdown { // メンションされたリモートユーザーに配送 for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) { - dm.addDirectRecipe(u as RemoteUser); + dm.addDirectRecipe(u as MiRemoteUser); } // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 @@ -701,7 +720,7 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private incRenoteCount(renote: Note) { + private incRenoteCount(renote: MiNote) { this.notesRepository.createQueryBuilder().update() .set({ renoteCount: () => '"renoteCount" + 1', @@ -712,7 +731,7 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { + private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const isThreadMuted = await this.noteThreadMutingsRepository.exist({ where: { @@ -744,12 +763,12 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private saveReply(reply: Note, note: Note) { + private saveReply(reply: MiNote, note: MiNote) { this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); } @bindThis - private async renderNoteOrRenoteActivity(data: Option, note: Note) { + private async renderNoteOrRenoteActivity(data: Option, note: MiNote) { if (data.localOnly) return null; const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) @@ -760,14 +779,14 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private index(note: Note) { + private index(note: MiNote) { if (note.text == null && note.cw == null) return; this.searchService.indexNote(note); } @bindThis - private incNotesCountOfUser(user: { id: User['id']; }) { + private incNotesCountOfUser(user: { id: MiUser['id']; }) { this.usersRepository.createQueryBuilder().update() .set({ updatedAt: new Date(), @@ -778,13 +797,13 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> { + private async extractMentionedUsers(user: { host: MiUser['host']; }, tokens: mfm.MfmNode[]): Promise<MiUser[]> { if (tokens == null) return []; const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), - ))).filter(x => x != null) as User[]; + ))).filter(x => x != null) as MiUser[]; // Drop duplicate users mentionedUsers = mentionedUsers.filter((u, i, self) => diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index f77ea8aab4..c99f92b9cb 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; -import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; -import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; -import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; +import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js'; +import type { MiNote, IMentionedRemoteUsers } from '@/models/Note.js'; +import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/_.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; @@ -18,6 +23,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; @Injectable() export class NoteDeleteService { @@ -43,6 +49,7 @@ export class NoteDeleteService { private apDeliverManagerService: ApDeliverManagerService, private metaService: MetaService, private searchService: SearchService, + private moderationLogService: ModerationLogService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private instanceChart: InstanceChart, @@ -53,7 +60,7 @@ export class NoteDeleteService { * @param user 投稿者 * @param note 投稿 */ - async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; isBot: User['isBot']; }, note: Note, quiet = false) { + async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) { const deletedAt = new Date(); const cascadingNotes = await this.findCascadingNotes(note); @@ -74,7 +81,7 @@ export class NoteDeleteService { //#region ローカルの投稿なら削除アクティビティを配送 if (this.userEntityService.isLocalUser(user) && !note.localOnly) { - let renote: Note | null = null; + let renote: MiNote | null = null; // if deletd note is renote if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { @@ -126,11 +133,19 @@ export class NoteDeleteService { id: note.id, userId: user.id, }); + + if (deleter && (note.userId !== deleter.id)) { + this.moderationLogService.log(deleter, 'deleteNote', { + noteId: note.id, + noteUserId: note.userId, + note: note, + }); + } } @bindThis - private async findCascadingNotes(note: Note): Promise<Note[]> { - const recursive = async (noteId: string): Promise<Note[]> => { + private async findCascadingNotes(note: MiNote): Promise<MiNote[]> { + const recursive = async (noteId: string): Promise<MiNote[]> => { const query = this.notesRepository.createQueryBuilder('note') .where('note.replyId = :noteId', { noteId }) .orWhere(new Brackets(q => { @@ -146,13 +161,13 @@ export class NoteDeleteService { ].flat(); }; - const cascadingNotes: Note[] = await recursive(note.id); + const cascadingNotes: MiNote[] = await recursive(note.id); return cascadingNotes; } @bindThis - private async getMentionedRemoteUsers(note: Note) { + private async getMentionedRemoteUsers(note: MiNote) { const where = [] as any[]; // mention / reply / dm @@ -174,11 +189,11 @@ export class NoteDeleteService { return await this.usersRepository.find({ where, - }) as RemoteUser[]; + }) as MiRemoteUser[]; } @bindThis - private async deliverToConcerned(user: { id: LocalUser['id']; host: null; }, note: Note, content: any) { + private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); const remoteUsers = await this.getMentionedRemoteUsers(note); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index 3a9f832ac0..147554ee9a 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/_.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { IdService } from '@/core/IdService.js'; -import type { UserNotePining } from '@/models/entities/UserNotePining.js'; +import type { MiUserNotePining } from '@/models/UserNotePining.js'; import { RelayService } from '@/core/RelayService.js'; import type { Config } from '@/config.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -44,7 +49,7 @@ export class NotePiningService { * @param noteId */ @bindThis - public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { + public async addPinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) { // Fetch pinee const note = await this.notesRepository.findOneBy({ id: noteId, @@ -70,7 +75,7 @@ export class NotePiningService { createdAt: new Date(), userId: user.id, noteId: note.id, - } as UserNotePining); + } as MiUserNotePining); // Deliver to remote followers if (this.userEntityService.isLocalUser(user)) { @@ -84,7 +89,7 @@ export class NotePiningService { * @param noteId */ @bindThis - public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { + public async removePinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) { // Fetch unpinee const note = await this.notesRepository.findOneBy({ id: noteId, @@ -107,7 +112,7 @@ export class NotePiningService { } @bindThis - public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { + public async deliverPinnedChange(userId: MiUser['id'], noteId: MiNote['id'], isAddition: boolean) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 52e9bd369a..422e0192cf 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -1,13 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { setTimeout } from 'node:timers/promises'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNote } from '@/models/Note.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/index.js'; +import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; @Injectable() @@ -30,7 +35,7 @@ export class NoteReadService implements OnApplicationShutdown { } @bindThis - public async insertNoteUnread(userId: User['id'], note: Note, params: { + public async insertNoteUnread(userId: MiUser['id'], note: MiNote, params: { // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse isSpecified: boolean; isMentioned: boolean; @@ -79,11 +84,11 @@ export class NoteReadService implements OnApplicationShutdown { @bindThis public async read( - userId: User['id'], - notes: (Note | Packed<'Note'>)[], + userId: MiUser['id'], + notes: (MiNote | Packed<'Note'>)[], ): Promise<void> { - const readMentions: (Note | Packed<'Note'>)[] = []; - const readSpecifiedNotes: (Note | Packed<'Note'>)[] = []; + const readMentions: (MiNote | Packed<'Note'>)[] = []; + const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = []; for (const note of notes) { if (note.mentions && note.mentions.includes(userId)) { diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 8e25f82284..258ae44f7d 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -1,38 +1,39 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { setTimeout } from 'node:timers/promises'; import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; -import type { Notification } from '@/models/entities/Notification.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import type { UsersRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNotification } from '@/models/Notification.js'; import { bindThis } from '@/decorators.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; +import type { Config } from '@/config.js'; @Injectable() export class NotificationService implements OnApplicationShutdown { #shutdownController = new AbortController(); constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.redis) private redisClient: Redis.Redis, @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - @Inject(DI.mutingsRepository) - private mutingsRepository: MutingsRepository, - private notificationEntityService: NotificationEntityService, - private userEntityService: UserEntityService, private idService: IdService, private globalEventService: GlobalEventService, private pushNotificationService: PushNotificationService, @@ -42,7 +43,7 @@ export class NotificationService implements OnApplicationShutdown { @bindThis public async readAllNotification( - userId: User['id'], + userId: MiUser['id'], force = false, ) { const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`); @@ -64,17 +65,17 @@ export class NotificationService implements OnApplicationShutdown { } @bindThis - private postReadAllNotifications(userId: User['id']) { + private postReadAllNotifications(userId: MiUser['id']) { this.globalEventService.publishMainStream(userId, 'readAllNotifications'); this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); } @bindThis public async createNotification( - notifieeId: User['id'], - type: Notification['type'], - data: Partial<Notification>, - ): Promise<Notification | null> { + notifieeId: MiUser['id'], + type: MiNotification['type'], + data: Partial<MiNotification>, + ): Promise<MiNotification | null> { const profile = await this.cacheService.userProfileCache.fetch(notifieeId); const isMuted = profile.mutingNotificationTypes.includes(type); if (isMuted) return null; @@ -95,11 +96,11 @@ export class NotificationService implements OnApplicationShutdown { createdAt: new Date(), type: type, ...data, - } as Notification; + } as MiNotification; const redisIdPromise = this.redisClient.xadd( `notificationTimeline:${notifieeId}`, - 'MAXLEN', '~', '300', + 'MAXLEN', '~', this.config.perUserNotificationsMaxCount.toString(), '*', 'data', JSON.stringify(notification)); @@ -129,7 +130,7 @@ export class NotificationService implements OnApplicationShutdown { // TODO: locale ファイルをクライアント用とサーバー用で分けたい @bindThis - private async emailNotificationFollow(userId: User['id'], follower: User) { + private async emailNotificationFollow(userId: MiUser['id'], follower: MiUser) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; @@ -141,7 +142,7 @@ export class NotificationService implements OnApplicationShutdown { } @bindThis - private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { + private async emailNotificationReceiveFollowRequest(userId: MiUser['id'], follower: MiUser) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index be19400052..940aa98347 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, User } from '@/models/index.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, MiUser } from '@/models/_.js'; +import type { MiNote } from '@/models/Note.js'; import { RelayService } from '@/core/RelayService.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -37,7 +42,7 @@ export class PollService { } @bindThis - public async vote(user: User, note: Note, choice: number) { + public async vote(user: MiUser, note: MiNote, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); if (poll == null) throw new Error('poll not found'); @@ -87,7 +92,7 @@ export class PollService { } @bindThis - public async deliverQuestionUpdate(noteId: Note['id']) { + public async deliverQuestionUpdate(noteId: MiNote['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 780e56ef10..b1bc60701b 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; -import type { LocalUser } from '@/models/entities/User.js'; +import type { UsersRepository } from '@/models/_.js'; +import type { MiLocalUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; @@ -16,9 +21,9 @@ export class ProxyAccountService { } @bindThis - public async fetch(): Promise<LocalUser | null> { + public async fetch(): Promise<MiLocalUser | null> { const meta = await this.metaService.fetch(); if (meta.proxyAccountId == null) return null; - return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as LocalUser; + return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as MiLocalUser; } } diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index e1c3d3943c..40d1deceeb 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import push from 'web-push'; import * as Redis from 'ioredis'; @@ -5,7 +10,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Packed } from '@/misc/json-schema.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; -import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js'; +import type { MiSwSubscription, SwSubscriptionsRepository } from '@/models/_.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; import { RedisKVCache } from '@/misc/cache.js'; @@ -43,7 +48,7 @@ function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: Pus @Injectable() export class PushNotificationService implements OnApplicationShutdown { - private subscriptionsCache: RedisKVCache<SwSubscription[]>; + private subscriptionsCache: RedisKVCache<MiSwSubscription[]>; constructor( @Inject(DI.config) @@ -57,7 +62,7 @@ export class PushNotificationService implements OnApplicationShutdown { private metaService: MetaService, ) { - this.subscriptionsCache = new RedisKVCache<SwSubscription[]>(this.redisClient, 'userSwSubscriptions', { + this.subscriptionsCache = new RedisKVCache<MiSwSubscription[]>(this.redisClient, 'userSwSubscriptions', { lifetime: 1000 * 60 * 60 * 1, // 1h memoryCacheLifetime: 1000 * 60 * 3, // 3m fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }), diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 435d5d2389..9145726f86 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets, ObjectLiteral } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/index.js'; +import type { MiUser } from '@/models/User.js'; +import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import type { SelectQueryBuilder } from 'typeorm'; @@ -64,7 +69,7 @@ export class QueryService { // ここでいうBlockedは被Blockedの意 @bindThis - public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); @@ -87,7 +92,7 @@ export class QueryService { } @bindThis - public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockeeId') .where('blocking.blockerId = :blockerId', { blockerId: me.id }); @@ -104,7 +109,7 @@ export class QueryService { } @bindThis - public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { + public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void { if (me == null) { q.andWhere('note.channelId IS NULL'); } else { @@ -126,7 +131,7 @@ export class QueryService { } @bindThis - public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') .select('muted.noteId') .where('muted.userId = :userId', { userId: me.id }); @@ -137,7 +142,7 @@ export class QueryService { } @bindThis - public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') .select('threadMuted.threadId') .where('threadMuted.userId = :userId', { userId: me.id }); @@ -152,7 +157,7 @@ export class QueryService { } @bindThis - public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User): void { + public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: MiUser): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); @@ -197,7 +202,7 @@ export class QueryService { } @bindThis - public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); @@ -208,7 +213,7 @@ export class QueryService { } @bindThis - public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<User, 'id'> | null): void { + public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<MiUser, 'id'> | null): void { if (me == null) { q.andWhere(new Brackets(qb => { qb .where('note.replyId IS NULL') // 返信ではない @@ -234,7 +239,7 @@ export class QueryService { } @bindThis - public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { + public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { q.andWhere(new Brackets(qb => { qb @@ -274,7 +279,7 @@ export class QueryService { } @bindThis - public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { + public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void { const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting') .select('renote_muting.muteeId') .where('renote_muting.muterId = :muterId', { muterId: me.id }); diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts index 3384ca4577..4444dc9787 100644 --- a/packages/backend/src/core/QueueModule.ts +++ b/packages/backend/src/core/QueueModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { setTimeout } from 'node:timers/promises'; import { Inject, Module, OnApplicationShutdown } from '@nestjs/common'; import * as Bull from 'bullmq'; diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 546b4cee1b..d8c7250034 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import type { IActivity } from '@/core/activitypub/type.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiWebhook, webhookEventTypes } from '@/models/Webhook.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -232,7 +237,7 @@ export class QueueService { } @bindThis - public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { + public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) { return this.dbQueue.add('importFollowing', { user: { id: user.id }, fileId: fileId, @@ -249,7 +254,7 @@ export class QueueService { } @bindThis - public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { + public createImportMutingJob(user: ThinUser, fileId: MiDriveFile['id']) { return this.dbQueue.add('importMuting', { user: { id: user.id }, fileId: fileId, @@ -260,7 +265,7 @@ export class QueueService { } @bindThis - public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { + public createImportBlockingJob(user: ThinUser, fileId: MiDriveFile['id']) { return this.dbQueue.add('importBlocking', { user: { id: user.id }, fileId: fileId, @@ -293,7 +298,7 @@ export class QueueService { } @bindThis - public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { + public createImportUserListsJob(user: ThinUser, fileId: MiDriveFile['id']) { return this.dbQueue.add('importUserLists', { user: { id: user.id }, fileId: fileId, @@ -304,7 +309,7 @@ export class QueueService { } @bindThis - public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { + public createImportCustomEmojisJob(user: ThinUser, fileId: MiDriveFile['id']) { return this.dbQueue.add('importCustomEmojis', { user: { id: user.id }, fileId: fileId, @@ -407,7 +412,7 @@ export class QueueService { } @bindThis - public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { + public webhookDeliver(webhook: MiWebhook, type: typeof webhookEventTypes[number], content: unknown) { const data = { type, content, diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 4b01b6af7e..d9bde502c8 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; +import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/_.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { RemoteUser, User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiRemoteUser, MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { IdService } from '@/core/IdService.js'; -import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { MiNoteReaction } from '@/models/NoteReaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { NotificationService } from '@/core/NotificationService.js'; @@ -90,7 +95,7 @@ export class ReactionService { } @bindThis - public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, _reaction?: string | null) { + public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) { // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); @@ -141,7 +146,7 @@ export class ReactionService { } } - const record: NoteReaction = { + const record: MiNoteReaction = { id: this.idService.genId(), createdAt: new Date(), noteId: note.id, @@ -226,7 +231,7 @@ export class ReactionService { const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as RemoteUser); + dm.addDirectRecipe(reactee as MiRemoteUser); } if (['public', 'home', 'followers'].includes(note.visibility)) { @@ -234,7 +239,7 @@ export class ReactionService { } else if (note.visibility === 'specified') { const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id }))); for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) { - dm.addDirectRecipe(u as RemoteUser); + dm.addDirectRecipe(u as MiRemoteUser); } } @@ -244,7 +249,7 @@ export class ReactionService { } @bindThis - public async delete(user: { id: User['id']; host: User['host']; isBot: User['isBot']; }, note: Note) { + public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote) { // if already unreacted const exist = await this.noteReactionsRepository.findOneBy({ noteId: note.id, @@ -284,7 +289,7 @@ export class ReactionService { const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as RemoteUser); + dm.addDirectRecipe(reactee as MiRemoteUser); } dm.addFollowersRecipe(); dm.execute(); diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index c0113a21d7..7171bf84c5 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { LocalUser, User } from '@/models/entities/User.js'; -import type { RelaysRepository, UsersRepository } from '@/models/index.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; +import type { RelaysRepository, UsersRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { MemorySingleCache } from '@/misc/cache.js'; -import type { Relay } from '@/models/entities/Relay.js'; +import type { MiRelay } from '@/models/Relay.js'; import { QueueService } from '@/core/QueueService.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @@ -16,7 +21,7 @@ const ACTOR_USERNAME = 'relay.actor' as const; @Injectable() export class RelayService { - private relaysCache: MemorySingleCache<Relay[]>; + private relaysCache: MemorySingleCache<MiRelay[]>; constructor( @Inject(DI.usersRepository) @@ -30,24 +35,24 @@ export class RelayService { private createSystemUserService: CreateSystemUserService, private apRendererService: ApRendererService, ) { - this.relaysCache = new MemorySingleCache<Relay[]>(1000 * 60 * 10); + this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10); } @bindThis - private async getRelayActor(): Promise<LocalUser> { + private async getRelayActor(): Promise<MiLocalUser> { const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, }); - if (user) return user as LocalUser; + if (user) return user as MiLocalUser; const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME); - return created as LocalUser; + return created as MiLocalUser; } @bindThis - public async addRelay(inbox: string): Promise<Relay> { + public async addRelay(inbox: string): Promise<MiRelay> { const relay = await this.relaysRepository.insert({ id: this.idService.genId(), inbox, @@ -82,7 +87,7 @@ export class RelayService { } @bindThis - public async listRelay(): Promise<Relay[]> { + public async listRelay(): Promise<MiRelay[]> { const relays = await this.relaysRepository.find(); return relays; } @@ -106,7 +111,7 @@ export class RelayService { } @bindThis - public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> { + public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> { if (activity == null) return; const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({ diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts index 3d45605836..5d13988ed7 100644 --- a/packages/backend/src/core/RemoteLoggerService.ts +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index e19a245e18..75c5f14aa4 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import chalk from 'chalk'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; +import type { UsersRepository } from '@/models/_.js'; +import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -35,7 +40,7 @@ export class RemoteUserResolveService { } @bindThis - public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> { + public async resolveUser(username: string, host: string | null): Promise<MiLocalUser | MiRemoteUser> { const usernameLower = username.toLowerCase(); if (host == null) { @@ -46,7 +51,7 @@ export class RemoteUserResolveService { } else { return u; } - }) as LocalUser; + }) as MiLocalUser; } host = this.utilityService.toPuny(host); @@ -59,10 +64,10 @@ export class RemoteUserResolveService { } else { return u; } - }) as LocalUser; + }) as MiLocalUser; } - const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null; + const user = await this.usersRepository.findOneBy({ usernameLower, host }) as MiRemoteUser | null; const acctLower = `${usernameLower}@${host}`; @@ -81,7 +86,7 @@ export class RemoteUserResolveService { } else { return u; } - })) as LocalUser; + })) as MiLocalUser; } } @@ -127,7 +132,7 @@ export class RemoteUserResolveService { if (u == null) { throw new Error('user not found'); } else { - return u as LocalUser | RemoteUser; + return u as MiLocalUser | MiRemoteUser; } }); } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d065b460c6..dea6dc68cd 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -1,18 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import { In } from 'typeorm'; -import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; +import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js'; import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; import { CacheService } from '@/core/CacheService.js'; -import type { RoleCondFormulaValue } from '@/models/entities/Role.js'; +import type { RoleCondFormulaValue } from '@/models/Role.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { StreamMessages } from '@/server/api/stream/types.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import type { Packed } from '@/misc/json-schema.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @@ -66,8 +72,8 @@ export const DEFAULT_POLICIES: RolePolicies = { @Injectable() export class RoleService implements OnApplicationShutdown { - private rolesCache: MemorySingleCache<Role[]>; - private roleAssignmentByUserIdCache: MemoryKVCache<RoleAssignment[]>; + private rolesCache: MemorySingleCache<MiRole[]>; + private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>; public static AlreadyAssignedError = class extends Error {}; public static NotAssignedError = class extends Error {}; @@ -93,11 +99,12 @@ export class RoleService implements OnApplicationShutdown { private userEntityService: UserEntityService, private globalEventService: GlobalEventService, private idService: IdService, + private moderationLogService: ModerationLogService, ) { //this.onMessage = this.onMessage.bind(this); - this.rolesCache = new MemorySingleCache<Role[]>(1000 * 60 * 60 * 1); - this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(1000 * 60 * 60 * 1); + this.rolesCache = new MemorySingleCache<MiRole[]>(1000 * 60 * 60 * 1); + this.roleAssignmentByUserIdCache = new MemoryKVCache<MiRoleAssignment[]>(1000 * 60 * 60 * 1); this.redisForSub.on('message', this.onMessage); } @@ -168,7 +175,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - private evalCond(user: User, value: RoleCondFormulaValue): boolean { + private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean { try { switch (value.type) { case 'and': { @@ -220,7 +227,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserAssigns(userId: User['id']) { + public async getUserAssigns(userId: MiUser['id']) { const now = Date.now(); let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); // 期限切れのロールを除外 @@ -229,7 +236,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserRoles(userId: User['id']) { + public async getUserRoles(userId: MiUser['id']) { const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const assigns = await this.getUserAssigns(userId); const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); @@ -242,7 +249,7 @@ export class RoleService implements OnApplicationShutdown { * 指定ユーザーのバッジロール一覧取得 */ @bindThis - public async getUserBadgeRoles(userId: User['id']) { + public async getUserBadgeRoles(userId: MiUser['id']) { const now = Date.now(); let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); // 期限切れのロールを除外 @@ -261,7 +268,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> { + public async getUserPolicies(userId: MiUser['id'] | null): Promise<RolePolicies> { const meta = await this.metaService.fetch(); const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; @@ -309,19 +316,19 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async isModerator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> { + public async isModerator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> { if (user == null) return false; return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator); } @bindThis - public async isAdministrator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> { + public async isAdministrator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> { if (user == null) return false; return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator); } @bindThis - public async isExplorable(role: { id: Role['id']} | null): Promise<boolean> { + public async isExplorable(role: { id: MiRole['id']} | null): Promise<boolean> { if (role == null) return false; const check = await this.rolesRepository.findOneBy({ id: role.id }); if (check == null) return false; @@ -329,7 +336,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> { + public async getModeratorIds(includeAdmins = true): Promise<MiUser['id'][]> { const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator); const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ @@ -340,7 +347,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getModerators(includeAdmins = true): Promise<User[]> { + public async getModerators(includeAdmins = true): Promise<MiUser[]> { const ids = await this.getModeratorIds(includeAdmins); const users = ids.length > 0 ? await this.usersRepository.findBy({ id: In(ids), @@ -349,7 +356,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getAdministratorIds(): Promise<User['id'][]> { + public async getAdministratorIds(): Promise<MiUser['id'][]> { const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const administratorRoles = roles.filter(r => r.isAdministrator); const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ @@ -360,7 +367,7 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getAdministrators(): Promise<User[]> { + public async getAdministrators(): Promise<MiUser[]> { const ids = await this.getAdministratorIds(); const users = ids.length > 0 ? await this.usersRepository.findBy({ id: In(ids), @@ -369,9 +376,11 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async assign(userId: User['id'], roleId: Role['id'], expiresAt: Date | null = null): Promise<void> { + public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise<void> { const now = new Date(); + const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); + const existing = await this.roleAssignmentsRepository.findOneBy({ roleId: roleId, userId: userId, @@ -401,10 +410,19 @@ export class RoleService implements OnApplicationShutdown { }); this.globalEventService.publishInternalEvent('userRoleAssigned', created); + + if (moderator) { + this.moderationLogService.log(moderator, 'assignRole', { + roleId: roleId, + roleName: role.name, + userId: userId, + expiresAt: expiresAt ? expiresAt.toISOString() : null, + }); + } } @bindThis - public async unassign(userId: User['id'], roleId: Role['id']): Promise<void> { + public async unassign(userId: MiUser['id'], roleId: MiRole['id'], moderator?: MiUser): Promise<void> { const now = new Date(); const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId }); @@ -425,6 +443,15 @@ export class RoleService implements OnApplicationShutdown { }); this.globalEventService.publishInternalEvent('userRoleUnassigned', existing); + + if (moderator) { + const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); + this.moderationLogService.log(moderator, 'unassignRole', { + roleId: roleId, + roleName: role.name, + userId: userId, + }); + } } @bindThis @@ -447,6 +474,39 @@ export class RoleService implements OnApplicationShutdown { } @bindThis + public async update(role: MiRole, params: Partial<MiRole>, moderator?: MiUser): Promise<void> { + const date = new Date(); + await this.rolesRepository.update(role.id, { + updatedAt: date, + ...params, + }); + + const updated = await this.rolesRepository.findOneByOrFail({ id: role.id }); + this.globalEventService.publishInternalEvent('roleUpdated', updated); + + if (moderator) { + this.moderationLogService.log(moderator, 'updateRole', { + roleId: role.id, + before: role, + after: updated, + }); + } + } + + @bindThis + public async delete(role: MiRole, moderator?: MiUser): Promise<void> { + await this.rolesRepository.delete({ id: role.id }); + this.globalEventService.publishInternalEvent('roleDeleted', role); + + if (moderator) { + this.moderationLogService.log(moderator, 'deleteRole', { + roleId: role.id, + role: role, + }); + } + } + + @bindThis public dispose(): void { this.redisForSub.off('message', this.onMessage); this.roleAssignmentByUserIdCache.dispose(); diff --git a/packages/backend/src/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index 01ce12ffdd..df0991539d 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/S3Service.ts @@ -1,13 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import * as http from 'node:http'; import * as https from 'node:https'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { Upload } from '@aws-sdk/lib-storage'; -import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { Meta } from '@/models/entities/Meta.js'; +import { NodeHttpHandler, NodeHttpHandlerOptions } from '@smithy/node-http-handler'; +import type { MiMeta } from '@/models/Meta.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3'; @@ -15,15 +18,12 @@ import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/c @Injectable() export class S3Service { constructor( - @Inject(DI.config) - private config: Config, - private httpRequestService: HttpRequestService, ) { } @bindThis - public getS3Client(meta: Meta): S3Client { + public getS3Client(meta: MiMeta): S3Client { const u = meta.objectStorageEndpoint ? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}` : `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent @@ -50,7 +50,7 @@ export class S3Service { } @bindThis - public async upload(meta: Meta, input: PutObjectCommandInput) { + public async upload(meta: MiMeta, input: PutObjectCommandInput) { const client = this.getS3Client(meta); return new Upload({ client, @@ -62,7 +62,7 @@ export class S3Service { } @bindThis - public delete(meta: Meta, input: DeleteObjectCommandInput) { + public delete(meta: MiMeta, input: DeleteObjectCommandInput) { const client = this.getS3Client(meta); return client.send(new DeleteObjectCommand(input)); } diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 392799da75..3ef321dd32 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; -import { Note } from '@/models/entities/Note.js'; -import { User } from '@/models/index.js'; -import type { NotesRepository } from '@/models/index.js'; +import { MiNote } from '@/models/Note.js'; +import { MiUser } from '@/models/_.js'; +import type { NotesRepository } from '@/models/_.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { QueryService } from '@/core/QueryService.js'; import { IdService } from '@/core/IdService.js'; @@ -20,6 +25,8 @@ type Q = { op: '<', k: K, v: number } | { op: '>=', k: K, v: number } | { op: '<=', k: K, v: number } | + { op: 'is null', k: K} | + { op: 'is not null', k: K} | { op: 'and', qs: Q[] } | { op: 'or', qs: Q[] } | { op: 'not', q: Q }; @@ -45,6 +52,8 @@ function compileQuery(q: Q): string { case '<=': return `(${q.k} <= ${compileValue(q.v)})`; case 'and': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' AND ') })`; case 'or': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' OR ') })`; + case 'is null': return `(${q.k} IS NULL)`; + case 'is not null': return `(${q.k} IS NOT NULL)`; case 'not': return `(NOT ${compileQuery(q.q)})`; default: throw new Error('unrecognized query operator'); } @@ -100,7 +109,7 @@ export class SearchService { } @bindThis - public async indexNote(note: Note): Promise<void> { + public async indexNote(note: MiNote): Promise<void> { if (note.text == null && note.cw == null) return; if (!['home', 'public'].includes(note.visibility)) return; @@ -136,7 +145,7 @@ export class SearchService { } @bindThis - public async unindexNote(note: Note): Promise<void> { + public async unindexNote(note: MiNote): Promise<void> { if (!['home', 'public'].includes(note.visibility)) return; if (this.meilisearch) { @@ -145,15 +154,15 @@ export class SearchService { } @bindThis - public async searchNote(q: string, me: User | null, opts: { - userId?: Note['userId'] | null; - channelId?: Note['channelId'] | null; + public async searchNote(q: string, me: MiUser | null, opts: { + userId?: MiNote['userId'] | null; + channelId?: MiNote['channelId'] | null; host?: string | null; }, pagination: { - untilId?: Note['id']; - sinceId?: Note['id']; + untilId?: MiNote['id']; + sinceId?: MiNote['id']; limit?: number; - }): Promise<Note[]> { + }): Promise<MiNote[]> { if (this.meilisearch) { const filter: Q = { op: 'and', @@ -165,7 +174,7 @@ export class SearchService { if (opts.channelId) filter.qs.push({ op: '=', k: 'channelId', v: opts.channelId }); if (opts.host) { if (opts.host === '.') { - // TODO: Meilisearchが2023/05/07現在値がNULLかどうかのクエリが書けない + filter.qs.push({ op: 'is null', k: 'userHost' }); } else { filter.qs.push({ op: '=', k: 'userHost', v: opts.host }); } @@ -199,6 +208,14 @@ export class SearchService { .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); + if (opts.host) { + if (opts.host === '.') { + query.andWhere('user.host IS NULL'); + } else { + query.andWhere('user.host = :host', { host: opts.host }); + } + } + this.queryService.generateVisibilityQuery(query, me); if (me) this.queryService.generateMutedUserQuery(query, me); if (me) this.queryService.generateBlockedUserQuery(query, me); diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 070a9a9e3e..dfec0cfcfe 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -1,15 +1,19 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { generateKeyPair } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { DataSource, IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import { User } from '@/models/entities/User.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; +import type { UsedUsernamesRepository, UsersRepository } from '@/models/_.js'; +import { MiUser } from '@/models/User.js'; +import { MiUserProfile } from '@/models/UserProfile.js'; import { IdService } from '@/core/IdService.js'; -import { UserKeypair } from '@/models/entities/UserKeypair.js'; -import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import { MiUserKeypair } from '@/models/UserKeypair.js'; +import { MiUsedUsername } from '@/models/UsedUsername.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -23,9 +27,6 @@ export class SignupService { @Inject(DI.db) private db: DataSource, - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -42,9 +43,9 @@ export class SignupService { @bindThis public async signup(opts: { - username: User['username']; + username: MiUser['username']; password?: string | null; - passwordHash?: UserProfile['password'] | null; + passwordHash?: MiUserProfile['password'] | null; host?: string | null; ignorePreservedUsernames?: boolean; }) { @@ -107,18 +108,18 @@ export class SignupService { err ? rej(err) : res([publicKey, privateKey]), )); - let account!: User; + let account!: MiUser; // Start transaction await this.db.transaction(async transactionalEntityManager => { - const exist = await transactionalEntityManager.findOneBy(User, { + const exist = await transactionalEntityManager.findOneBy(MiUser, { usernameLower: username.toLowerCase(), host: IsNull(), }); if (exist) throw new Error(' the username is already used'); - account = await transactionalEntityManager.save(new User({ + account = await transactionalEntityManager.save(new MiUser({ id: this.idService.genId(), createdAt: new Date(), username: username, @@ -128,19 +129,19 @@ export class SignupService { isRoot: isTheFirstUser, })); - await transactionalEntityManager.save(new UserKeypair({ + await transactionalEntityManager.save(new MiUserKeypair({ publicKey: keyPair[0], privateKey: keyPair[1], userId: account.id, })); - await transactionalEntityManager.save(new UserProfile({ + await transactionalEntityManager.save(new MiUserProfile({ userId: account.id, autoAcceptFollowed: true, password: hash, })); - await transactionalEntityManager.save(new UsedUsername({ + await transactionalEntityManager.save(new MiUsedUsername({ createdAt: new Date(), username: username.toLowerCase(), })); diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts deleted file mode 100644 index d4cf186a24..0000000000 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ /dev/null @@ -1,445 +0,0 @@ -import * as crypto from 'node:crypto'; -import { Inject, Injectable } from '@nestjs/common'; -import * as jsrsasign from 'jsrsasign'; -import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import { bindThis } from '@/decorators.js'; - -const ECC_PRELUDE = Buffer.from([0x04]); -const NULL_BYTE = Buffer.from([0]); -const PEM_PRELUDE = Buffer.from( - '3059301306072a8648ce3d020106082a8648ce3d030107034200', - 'hex', -); - -// Android Safetynet attestations are signed with this cert: -const GSR2 = `-----BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 -MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL -v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 -eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq -tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd -C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa -zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB -mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH -V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n -bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG -3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs -J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO -291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS -ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd -AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE-----\n`; - -function base64URLDecode(source: string) { - return Buffer.from(source.replace(/\-/g, '+').replace(/_/g, '/'), 'base64'); -} - -function getCertSubject(certificate: string) { - const subjectCert = new jsrsasign.X509(); - subjectCert.readCertPEM(certificate); - - const subjectString = subjectCert.getSubjectString(); - const subjectFields = subjectString.slice(1).split('/'); - - const fields = {} as Record<string, string>; - for (const field of subjectFields) { - const eqIndex = field.indexOf('='); - fields[field.substring(0, eqIndex)] = field.substring(eqIndex + 1); - } - - return fields; -} - -function verifyCertificateChain(certificates: string[]) { - let valid = true; - - for (let i = 0; i < certificates.length; i++) { - const Cert = certificates[i]; - const certificate = new jsrsasign.X509(); - certificate.readCertPEM(Cert); - - const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1]; - - const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); - if (certStruct == null) throw new Error('certStruct is null'); - - const algorithm = certificate.getSignatureAlgorithmField(); - const signatureHex = certificate.getSignatureValueHex(); - - // Verify against CA - const Signature = new jsrsasign.KJUR.crypto.Signature({ alg: algorithm }); - Signature.init(CACert); - Signature.updateHex(certStruct); - valid = valid && !!Signature.verify(signatureHex); // true if CA signed the certificate - } - - return valid; -} - -function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { - if (pemBuffer.length === 65 && pemBuffer[0] === 0x04) { - pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91); - type = 'PUBLIC KEY'; - } - const cert = pemBuffer.toString('base64'); - - const keyParts = []; - const max = Math.ceil(cert.length / 64); - let start = 0; - for (let i = 0; i < max; i++) { - keyParts.push(cert.substring(start, start + 64)); - start += 64; - } - - return ( - `-----BEGIN ${type}-----\n` + - keyParts.join('\n') + - `\n-----END ${type}-----\n` - ); -} - -@Injectable() -export class TwoFactorAuthenticationService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - ) { - } - - @bindThis - public hash(data: Buffer) { - return crypto - .createHash('sha256') - .update(data) - .digest(); - } - - @bindThis - public verifySignin({ - publicKey, - authenticatorData, - clientDataJSON, - clientData, - signature, - challenge, - }: { - publicKey: Buffer, - authenticatorData: Buffer, - clientDataJSON: Buffer, - clientData: any, - signature: Buffer, - challenge: string - }) { - if (clientData.type !== 'webauthn.get') { - throw new Error('type is not webauthn.get'); - } - - if (this.hash(clientData.challenge).toString('hex') !== challenge) { - throw new Error('challenge mismatch'); - } - if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw new Error('origin mismatch'); - } - - const verificationData = Buffer.concat( - [authenticatorData, this.hash(clientDataJSON)], - 32 + authenticatorData.length, - ); - - return crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(publicKey), signature); - } - - @bindThis - public getProcedures() { - return { - none: { - verify({ publicKey }: { publicKey: Map<number, Buffer> }) { - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyU2F = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - return { - publicKey: publicKeyU2F, - valid: true, - }; - }, - }, - 'android-key': { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map<number, any>; - rpIdHash: Buffer, - credentialId: Buffer, - }) { - if (attStmt.alg !== -7) { - throw new Error('alg mismatch'); - } - - const verificationData = Buffer.concat([ - authenticatorData, - clientDataHash, - ]); - - const attCert: Buffer = attStmt.x5c[0]; - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - if (!attCert.equals(publicKeyData)) { - throw new Error('public key mismatch'); - } - - const isValid = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - // TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON) - - return { - valid: isValid, - publicKey: publicKeyData, - }; - }, - }, - // what a stupid attestation - 'android-safetynet': { - verify: ({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map<number, any>; - rpIdHash: Buffer, - credentialId: Buffer, - }) => { - const verificationData = this.hash( - Buffer.concat([authenticatorData, clientDataHash]), - ); - - const jwsParts = attStmt.response.toString('utf-8').split('.'); - - const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8')); - const response = JSON.parse( - base64URLDecode(jwsParts[1]).toString('utf-8'), - ); - const signature = jwsParts[2]; - - if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { - throw new Error('invalid nonce'); - } - - const certificateChain = header.x5c - .map((key: any) => PEMString(key)) - .concat([GSR2]); - - if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { - throw new Error('invalid common name'); - } - - if (!verifyCertificateChain(certificateChain)) { - throw new Error('Invalid certificate chain!'); - } - - const signatureBase = Buffer.from( - jwsParts[0] + '.' + jwsParts[1], - 'utf-8', - ); - - const valid = crypto - .createVerify('sha256') - .update(signatureBase) - .verify(certificateChain[0], base64URLDecode(signature)); - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - return { - valid, - publicKey: publicKeyData, - }; - }, - }, - packed: { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map<number, any>; - rpIdHash: Buffer, - credentialId: Buffer, - }) { - const verificationData = Buffer.concat([ - authenticatorData, - clientDataHash, - ]); - - if (attStmt.x5c) { - const attCert = attStmt.x5c[0]; - - const validSignature = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - return { - valid: validSignature, - publicKey: publicKeyData, - }; - } else if (attStmt.ecdaaKeyId) { - // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation - throw new Error('ECDAA-Verify is not supported'); - } else { - if (attStmt.alg !== -7) throw new Error('alg mismatch'); - - throw new Error('self attestation is not supported'); - } - }, - }, - - 'fido-u2f': { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map<number, any>, - rpIdHash: Buffer, - credentialId: Buffer - }) { - const x5c: Buffer[] = attStmt.x5c; - if (x5c.length !== 1) { - throw new Error('x5c length does not match expectation'); - } - - const attCert = x5c[0]; - - // TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve - - const negTwo: Buffer = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree: Buffer = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyU2F = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - const verificationData = Buffer.concat([ - NULL_BYTE, - rpIdHash, - clientDataHash, - credentialId, - publicKeyU2F, - ]); - - const validSignature = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - return { - valid: validSignature, - publicKey: publicKeyU2F, - }; - }, - }, - }; - } -} diff --git a/packages/backend/src/core/UserAuthService.ts b/packages/backend/src/core/UserAuthService.ts new file mode 100644 index 0000000000..ccf4dfc6bd --- /dev/null +++ b/packages/backend/src/core/UserAuthService.ts @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { QueryFailedError } from 'typeorm'; +import * as OTPAuth from 'otpauth'; +import { DI } from '@/di-symbols.js'; +import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import { bindThis } from '@/decorators.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import type { MiLocalUser } from '@/models/User.js'; + +@Injectable() +export class UserAuthService { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + ) { + } + + @bindThis + public async twoFactorAuthenticate(profile: MiUserProfile, token: string): Promise<void> { + if (profile.twoFactorBackupSecret?.includes(token)) { + await this.userProfilesRepository.update({ userId: profile.userId }, { + twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token), + }); + } else { + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), + digits: 6, + token, + window: 5, + }); + + if (delta === null) { + throw new Error('authentication failed'); + } + } + } +} diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 3ca22f8bbc..37031e341e 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -1,13 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { IdService } from '@/core/IdService.js'; -import type { User } from '@/models/entities/User.js'; -import type { Blocking } from '@/models/entities/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiBlocking } from '@/models/Blocking.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import type { FollowRequestsRepository, BlockingsRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { FollowRequestsRepository, BlockingsRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/_.js'; import Logger from '@/logger.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @@ -54,7 +58,7 @@ export class UserBlockingService implements OnModuleInit { } @bindThis - public async block(blocker: User, blockee: User, silent = false) { + public async block(blocker: MiUser, blockee: MiUser, silent = false) { await Promise.all([ this.cancelRequest(blocker, blockee, silent), this.cancelRequest(blockee, blocker, silent), @@ -70,7 +74,7 @@ export class UserBlockingService implements OnModuleInit { blockerId: blocker.id, blockee, blockeeId: blockee.id, - } as Blocking; + } as MiBlocking; await this.blockingsRepository.insert(blocking); @@ -89,7 +93,7 @@ export class UserBlockingService implements OnModuleInit { } @bindThis - private async cancelRequest(follower: User, followee: User, silent = false) { + private async cancelRequest(follower: MiUser, followee: MiUser, silent = false) { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, followerId: follower.id, @@ -139,7 +143,7 @@ export class UserBlockingService implements OnModuleInit { } @bindThis - private async removeFromList(listOwner: User, user: User) { + private async removeFromList(listOwner: MiUser, user: MiUser) { const userLists = await this.userListsRepository.findBy({ userId: listOwner.id, }); @@ -153,7 +157,7 @@ export class UserBlockingService implements OnModuleInit { } @bindThis - public async unblock(blocker: User, blockee: User) { + public async unblock(blocker: MiUser, blockee: MiUser) { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, @@ -187,7 +191,7 @@ export class UserBlockingService implements OnModuleInit { } @bindThis - public async checkBlocked(blockerId: User['id'], blockeeId: User['id']): Promise<boolean> { + public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise<boolean> { return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); } } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 44269d3b8a..5b2b0205d9 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { IsNull } from 'typeorm'; -import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; +import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueService } from '@/core/QueueService.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; @@ -14,7 +19,7 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { WebhookService } from '@/core/WebhookService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { bindThis } from '@/decorators.js'; @@ -27,16 +32,16 @@ import Logger from '../logger.js'; const logger = new Logger('following/create'); -type Local = LocalUser | { - id: LocalUser['id']; - host: LocalUser['host']; - uri: LocalUser['uri'] +type Local = MiLocalUser | { + id: MiLocalUser['id']; + host: MiLocalUser['host']; + uri: MiLocalUser['uri'] }; -type Remote = RemoteUser | { - id: RemoteUser['id']; - host: RemoteUser['host']; - uri: RemoteUser['uri']; - inbox: RemoteUser['inbox']; +type Remote = MiRemoteUser | { + id: MiRemoteUser['id']; + host: MiRemoteUser['host']; + uri: MiRemoteUser['uri']; + inbox: MiRemoteUser['inbox']; }; type Both = Local | Remote; @@ -86,11 +91,11 @@ export class UserFollowingService implements OnModuleInit { } @bindThis - public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string, silent = false): Promise<void> { + public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> { const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }), - ]) as [LocalUser | RemoteUser, LocalUser | RemoteUser]; + ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; // check blocking const [blocking, blocked] = await Promise.all([ @@ -175,10 +180,10 @@ export class UserFollowingService implements OnModuleInit { @bindThis private async insertFollowingDoc( followee: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] }, follower: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] }, silent = false, ): Promise<void> { @@ -307,10 +312,10 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async unfollow( follower: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, followee: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, silent = false, ): Promise<void> { @@ -353,21 +358,21 @@ export class UserFollowingService implements OnModuleInit { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser), follower)); this.queueService.deliver(follower, content, followee.inbox, false); } if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { // local user has null host - const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as PartialRemoteUser, followee as PartialLocalUser), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as MiPartialRemoteUser, followee as MiPartialLocalUser), followee)); this.queueService.deliver(followee, content, follower.inbox, false); } } @bindThis private async decrementFollowing( - follower: User, - followee: User, + follower: MiUser, + followee: MiUser, ): Promise<void> { this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id }); @@ -439,10 +444,10 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async createFollowRequest( follower: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, followee: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, requestId?: string, ): Promise<void> { @@ -489,7 +494,7 @@ export class UserFollowingService implements OnModuleInit { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`)); + const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`)); this.queueService.deliver(follower, content, followee.inbox, false); } } @@ -497,14 +502,14 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async cancelFollowRequest( followee: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox'] }, follower: { - id: User['id']; host: User['host']; uri: User['host'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host'] }, ): Promise<void> { if (this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser | PartialRemoteUser, followee as PartialRemoteUser), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser | MiPartialRemoteUser, followee as MiPartialRemoteUser), follower)); if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので this.queueService.deliver(follower, content, followee.inbox, false); @@ -535,9 +540,9 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptFollowRequest( followee: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, - follower: User, + follower: MiUser, ): Promise<void> { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -551,7 +556,7 @@ export class UserFollowingService implements OnModuleInit { await this.insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as PartialLocalUser, request.requestId!), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox, false); } @@ -563,7 +568,7 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptAllFollowRequests( user: { - id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, ): Promise<void> { const requests = await this.followRequestsRepository.findBy({ diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts index d768f08650..425a97f3f1 100644 --- a/packages/backend/src/core/UserKeypairService.ts +++ b/packages/backend/src/core/UserKeypairService.ts @@ -1,15 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { User } from '@/models/entities/User.js'; -import type { UserKeypairsRepository } from '@/models/index.js'; +import type { MiUser } from '@/models/User.js'; +import type { UserKeypairsRepository } from '@/models/_.js'; import { RedisKVCache } from '@/misc/cache.js'; -import type { UserKeypair } from '@/models/entities/UserKeypair.js'; +import type { MiUserKeypair } from '@/models/UserKeypair.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @Injectable() export class UserKeypairService implements OnApplicationShutdown { - private cache: RedisKVCache<UserKeypair>; + private cache: RedisKVCache<MiUserKeypair>; constructor( @Inject(DI.redis) @@ -18,7 +23,7 @@ export class UserKeypairService implements OnApplicationShutdown { @Inject(DI.userKeypairsRepository) private userKeypairsRepository: UserKeypairsRepository, ) { - this.cache = new RedisKVCache<UserKeypair>(this.redisClient, 'userKeypair', { + this.cache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', { lifetime: 1000 * 60 * 60 * 24, // 24h memoryCacheLifetime: Infinity, fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }), @@ -28,7 +33,7 @@ export class UserKeypairService implements OnApplicationShutdown { } @bindThis - public async getUserKeypair(userId: User['id']): Promise<UserKeypair> { + public async getUserKeypair(userId: MiUser['id']): Promise<MiUserKeypair> { return await this.cache.fetch(userId); } diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 08cc907ebf..a71d50bba5 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoiningsRepository, UsersRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserList } from '@/models/entities/UserList.js'; -import type { UserListJoining } from '@/models/entities/UserListJoining.js'; +import type { UserListJoiningsRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiUserList } from '@/models/UserList.js'; +import type { MiUserListJoining } from '@/models/UserListJoining.js'; import { IdService } from '@/core/IdService.js'; -import { UserFollowingService } from '@/core/UserFollowingService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -18,15 +22,11 @@ export class UserListService { public static TooManyUsersError = class extends Error {}; constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: UserListJoiningsRepository, private userEntityService: UserEntityService, private idService: IdService, - private userFollowingService: UserFollowingService, private roleService: RoleService, private globalEventService: GlobalEventService, private proxyAccountService: ProxyAccountService, @@ -35,7 +35,7 @@ export class UserListService { } @bindThis - public async push(target: User, list: UserList, me: User) { + public async push(target: MiUser, list: MiUserList, me: MiUser) { const currentCount = await this.userListJoiningsRepository.countBy({ userListId: list.id, }); @@ -48,7 +48,7 @@ export class UserListService { createdAt: new Date(), userId: target.id, userListId: list.id, - } as UserListJoining); + } as MiUserListJoining); this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index d201ec6c04..2387c9d648 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import type { MutingsRepository, Muting } from '@/models/index.js'; +import type { MutingsRepository, MiMuting } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; @@ -19,7 +24,7 @@ export class UserMutingService { } @bindThis - public async mute(user: User, target: User, expiresAt: Date | null = null): Promise<void> { + public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> { await this.mutingsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -32,7 +37,7 @@ export class UserMutingService { } @bindThis - public async unmute(mutings: Muting[]): Promise<void> { + public async unmute(mutings: MiMuting[]): Promise<void> { if (mutings.length === 0) return; await this.mutingsRepository.delete({ diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 28ae32681d..8940a142d1 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -1,11 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; -import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { FollowingsRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -13,12 +17,6 @@ import { bindThis } from '@/decorators.js'; @Injectable() export class UserSuspendService { constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, @@ -30,7 +28,7 @@ export class UserSuspendService { } @bindThis - public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> { + public async doPostSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise<void> { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); if (this.userEntityService.isLocalUser(user)) { @@ -60,7 +58,7 @@ export class UserSuspendService { } @bindThis - public async doPostUnsuspend(user: User): Promise<void> { + public async doPostUnsuspend(user: MiUser): Promise<void> { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); if (this.userEntityService.isLocalUser(user)) { diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index d00708a442..d2d2776bd2 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { toASCII } from 'punycode'; import { Inject, Injectable } from '@nestjs/common'; diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index 06f832ab09..ffb7573358 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import FFmpeg from 'fluent-ffmpeg'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts new file mode 100644 index 0000000000..5945dc2919 --- /dev/null +++ b/packages/backend/src/core/WebAuthnService.ts @@ -0,0 +1,252 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { + generateAuthenticationOptions, + generateRegistrationOptions, verifyAuthenticationResponse, + verifyRegistrationResponse, +} from '@simplewebauthn/server'; +import { AttestationFormat, isoCBOR } from '@simplewebauthn/server/helpers'; +import { DI } from '@/di-symbols.js'; +import type { UserSecurityKeysRepository } from '@/models/_.js'; +import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; +import { MetaService } from '@/core/MetaService.js'; +import { MiUser } from '@/models/_.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { + AuthenticationResponseJSON, + AuthenticatorTransportFuture, + CredentialDeviceType, + PublicKeyCredentialCreationOptionsJSON, + PublicKeyCredentialDescriptorFuture, + PublicKeyCredentialRequestOptionsJSON, + RegistrationResponseJSON, +} from '@simplewebauthn/typescript-types'; + +@Injectable() +export class WebAuthnService { + constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + + @Inject(DI.config) + private config: Config, + + @Inject(DI.userSecurityKeysRepository) + private userSecurityKeysRepository: UserSecurityKeysRepository, + + private metaService: MetaService, + ) { + } + + @bindThis + public async getRelyingParty(): Promise<{ origin: string; rpId: string; rpName: string; rpIcon?: string; }> { + const instance = await this.metaService.fetch(); + return { + origin: this.config.url, + rpId: this.config.host, + rpName: instance.name ?? this.config.host, + rpIcon: instance.iconUrl ?? undefined, + }; + } + + @bindThis + public async initiateRegistration(userId: MiUser['id'], userName: string, userDisplayName?: string): Promise<PublicKeyCredentialCreationOptionsJSON> { + const relyingParty = await this.getRelyingParty(); + const keys = await this.userSecurityKeysRepository.findBy({ + userId: userId, + }); + + const registrationOptions = await generateRegistrationOptions({ + rpName: relyingParty.rpName, + rpID: relyingParty.rpId, + userID: userId, + userName: userName, + userDisplayName: userDisplayName, + attestationType: 'indirect', + excludeCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{ + id: Buffer.from(key.id, 'base64url'), + type: 'public-key', + transports: key.transports ?? undefined, + })), + authenticatorSelection: { + residentKey: 'required', + userVerification: 'preferred', + }, + }); + + await this.redisClient.setex(`webauthn:challenge:${userId}`, 90, registrationOptions.challenge); + + return registrationOptions; + } + + @bindThis + public async verifyRegistration(userId: MiUser['id'], response: RegistrationResponseJSON): Promise<{ + credentialID: Uint8Array; + credentialPublicKey: Uint8Array; + attestationObject: Uint8Array; + fmt: AttestationFormat; + counter: number; + userVerified: boolean; + credentialDeviceType: CredentialDeviceType; + credentialBackedUp: boolean; + transports?: AuthenticatorTransportFuture[]; + }> { + const challenge = await this.redisClient.get(`webauthn:challenge:${userId}`); + + if (!challenge) { + throw new IdentifiableError('7dbfb66c-9216-4e2b-9c27-cef2ac8efb84', 'challenge not found'); + } + + await this.redisClient.del(`webauthn:challenge:${userId}`); + + const relyingParty = await this.getRelyingParty(); + + let verification; + try { + verification = await verifyRegistrationResponse({ + response: response, + expectedChallenge: challenge, + expectedOrigin: relyingParty.origin, + expectedRPID: relyingParty.rpId, + requireUserVerification: true, + }); + } catch (error) { + console.error(error); + throw new IdentifiableError('5c1446f8-8ca7-4d31-9f39-656afe9c5d87', 'verification failed'); + } + + const { verified } = verification; + + if (!verified || !verification.registrationInfo) { + throw new IdentifiableError('bb333667-3832-4a80-8bb5-c505be7d710d', 'verification failed'); + } + + const { registrationInfo } = verification; + + return { + credentialID: registrationInfo.credentialID, + credentialPublicKey: registrationInfo.credentialPublicKey, + attestationObject: registrationInfo.attestationObject, + fmt: registrationInfo.fmt, + counter: registrationInfo.counter, + userVerified: registrationInfo.userVerified, + credentialDeviceType: registrationInfo.credentialDeviceType, + credentialBackedUp: registrationInfo.credentialBackedUp, + transports: response.response.transports, + }; + } + + @bindThis + public async initiateAuthentication(userId: MiUser['id']): Promise<PublicKeyCredentialRequestOptionsJSON> { + const keys = await this.userSecurityKeysRepository.findBy({ + userId: userId, + }); + + if (keys.length === 0) { + throw new IdentifiableError('f27fd449-9af4-4841-9249-1f989b9fa4a4', 'no keys found'); + } + + const authenticationOptions = await generateAuthenticationOptions({ + allowCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{ + id: Buffer.from(key.id, 'base64url'), + type: 'public-key', + transports: key.transports ?? undefined, + })), + userVerification: 'preferred', + }); + + await this.redisClient.setex(`webauthn:challenge:${userId}`, 90, authenticationOptions.challenge); + + return authenticationOptions; + } + + @bindThis + public async verifyAuthentication(userId: MiUser['id'], response: AuthenticationResponseJSON): Promise<boolean> { + const challenge = await this.redisClient.get(`webauthn:challenge:${userId}`); + + if (!challenge) { + throw new IdentifiableError('2d16e51c-007b-4edd-afd2-f7dd02c947f6', 'challenge not found'); + } + + await this.redisClient.del(`webauthn:challenge:${userId}`); + + const key = await this.userSecurityKeysRepository.findOneBy({ + id: response.id, + userId: userId, + }); + + if (!key) { + throw new IdentifiableError('36b96a7d-b547-412d-aeed-2d611cdc8cdc', 'unknown key'); + } + + // マイグレーション + if (key.counter === 0 && key.publicKey.length === 87) { + const cert = new Uint8Array(Buffer.from(key.publicKey, 'base64url')); + if (cert[0] === 0x04) { // 前の実装ではいつも 0x04 で始まっていた + const halfLength = (cert.length - 1) / 2; + + const cborMap = new Map<number, number | ArrayBufferLike>(); + cborMap.set(1, 2); // kty, EC2 + cborMap.set(3, -7); // alg, ES256 + cborMap.set(-1, 1); // crv, P256 + cborMap.set(-2, cert.slice(1, halfLength + 1)); // x + cborMap.set(-3, cert.slice(halfLength + 1)); // y + + const cborPubKey = Buffer.from(isoCBOR.encode(cborMap)).toString('base64url'); + await this.userSecurityKeysRepository.update({ + id: response.id, + userId: userId, + }, { + publicKey: cborPubKey, + }); + key.publicKey = cborPubKey; + } + } + + const relyingParty = await this.getRelyingParty(); + + let verification; + try { + verification = await verifyAuthenticationResponse({ + response: response, + expectedChallenge: challenge, + expectedOrigin: relyingParty.origin, + expectedRPID: relyingParty.rpId, + authenticator: { + credentialID: Buffer.from(key.id, 'base64url'), + credentialPublicKey: Buffer.from(key.publicKey, 'base64url'), + counter: key.counter, + transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined, + }, + requireUserVerification: true, + }); + } catch (error) { + console.error(error); + throw new IdentifiableError('b18c89a7-5b5e-4cec-bb5b-0419f332d430', 'verification failed'); + } + + const { verified, authenticationInfo } = verification; + + if (!verified) { + return false; + } + + await this.userSecurityKeysRepository.update({ + id: response.id, + userId: userId, + }, { + lastUsed: new Date(), + counter: authenticationInfo.newCounter, + credentialDeviceType: authenticationInfo.credentialDeviceType, + credentialBackedUp: authenticationInfo.credentialBackedUp, + }); + + return verified; + } +} diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts index 6b2428cdf0..3d5747aebd 100644 --- a/packages/backend/src/core/WebfingerService.ts +++ b/packages/backend/src/core/WebfingerService.ts @@ -1,7 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Injectable } from '@nestjs/common'; import { query as urlQuery } from '@/misc/prelude/url.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; @@ -22,9 +25,6 @@ const mRegex = /^([^@]+)@(.*)/; @Injectable() export class WebfingerService { constructor( - @Inject(DI.config) - private config: Config, - private httpRequestService: HttpRequestService, ) { } diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index b6f5263901..1344f0ac97 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { WebhooksRepository } from '@/models/index.js'; -import type { Webhook } from '@/models/entities/Webhook.js'; +import type { WebhooksRepository } from '@/models/_.js'; +import type { MiWebhook } from '@/models/Webhook.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { StreamMessages } from '@/server/api/stream/types.js'; @@ -10,7 +15,7 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class WebhookService implements OnApplicationShutdown { private webhooksFetched = false; - private webhooks: Webhook[] = []; + private webhooks: MiWebhook[] = []; constructor( @Inject(DI.redisForSub) diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index f2d84341f4..440852bdf3 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; -import type { RemoteUser, User } from '@/models/entities/User.js'; +import type { MiRemoteUser, MiUser } from '@/models/User.js'; import { concat, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; import { getApIds } from './type.js'; @@ -12,8 +17,8 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified'; type AudienceInfo = { visibility: Visibility, - mentionedUsers: User[], - visibleUsers: User[], + mentionedUsers: MiUser[], + visibleUsers: MiUser[], }; type GroupedAudience = Record<'public' | 'followers' | 'other', string[]>; @@ -26,16 +31,16 @@ export class ApAudienceService { } @bindThis - public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { + public async parseAudience(actor: MiRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { const toGroups = this.groupingAudience(getApIds(to), actor); const ccGroups = this.groupingAudience(getApIds(cc), actor); const others = unique(concat([toGroups.other, ccGroups.other])); - const limit = promiseLimit<User | null>(2); + const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), - )).filter((x): x is User => x != null); + )).filter((x): x is MiUser => x != null); if (toGroups.public.length > 0) { return { @@ -69,7 +74,7 @@ export class ApAudienceService { } @bindThis - private groupingAudience(ids: string[], actor: RemoteUser): GroupedAudience { + private groupingAudience(ids: string[], actor: MiRemoteUser): GroupedAudience { const groups: GroupedAudience = { public: [], followers: [], @@ -101,7 +106,7 @@ export class ApAudienceService { } @bindThis - private isFollowers(id: string, actor: RemoteUser): boolean { + private isFollowers(id: string, actor: MiRemoteUser): boolean { return id === (actor.followersUri ?? `${actor.uri}/followers`); } } diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index d5a530c903..995c5dcd5f 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -1,13 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import { MemoryKVCache } from '@/misc/cache.js'; -import type { UserPublickey } from '@/models/entities/UserPublickey.js'; +import type { MiUserPublickey } from '@/models/UserPublickey.js'; import { CacheService } from '@/core/CacheService.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; -import { LocalUser, RemoteUser } from '@/models/entities/User.js'; +import { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import { getApId } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { IObject } from './type.js'; @@ -30,8 +35,8 @@ export type UriParseResult = { @Injectable() export class ApDbResolverService implements OnApplicationShutdown { - private publicKeyCache: MemoryKVCache<UserPublickey | null>; - private publicKeyByUserIdCache: MemoryKVCache<UserPublickey | null>; + private publicKeyCache: MemoryKVCache<MiUserPublickey | null>; + private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey | null>; constructor( @Inject(DI.config) @@ -49,8 +54,8 @@ export class ApDbResolverService implements OnApplicationShutdown { private cacheService: CacheService, private apPersonService: ApPersonService, ) { - this.publicKeyCache = new MemoryKVCache<UserPublickey | null>(Infinity); - this.publicKeyByUserIdCache = new MemoryKVCache<UserPublickey | null>(Infinity); + this.publicKeyCache = new MemoryKVCache<MiUserPublickey | null>(Infinity); + this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(Infinity); } @bindThis @@ -73,7 +78,7 @@ export class ApDbResolverService implements OnApplicationShutdown { * AP Note => Misskey Note in DB */ @bindThis - public async getNoteFromApId(value: string | IObject): Promise<Note | null> { + public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> { const parsed = this.parseUri(value); if (parsed.local) { @@ -93,7 +98,7 @@ export class ApDbResolverService implements OnApplicationShutdown { * AP Person => Misskey User in DB */ @bindThis - public async getUserFromApId(value: string | IObject): Promise<LocalUser | RemoteUser | null> { + public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> { const parsed = this.parseUri(value); if (parsed.local) { @@ -102,12 +107,12 @@ export class ApDbResolverService implements OnApplicationShutdown { return await this.cacheService.userByIdCache.fetchMaybe( parsed.id, () => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined), - ) as LocalUser | undefined ?? null; + ) as MiLocalUser | undefined ?? null; } else { return await this.cacheService.uriPersonCache.fetch( parsed.uri, () => this.usersRepository.findOneBy({ uri: parsed.uri }), - ) as RemoteUser | null; + ) as MiRemoteUser | null; } } @@ -116,8 +121,8 @@ export class ApDbResolverService implements OnApplicationShutdown { */ @bindThis public async getAuthUserFromKeyId(keyId: string): Promise<{ - user: RemoteUser; - key: UserPublickey; + user: MiRemoteUser; + key: MiUserPublickey; } | null> { const key = await this.publicKeyCache.fetch(keyId, async () => { const key = await this.userPublickeysRepository.findOneBy({ @@ -132,7 +137,7 @@ export class ApDbResolverService implements OnApplicationShutdown { if (key == null) return null; return { - user: await this.cacheService.findUserById(key.userId) as RemoteUser, + user: await this.cacheService.findUserById(key.userId) as MiRemoteUser, key, }; } @@ -142,10 +147,10 @@ export class ApDbResolverService implements OnApplicationShutdown { */ @bindThis public async getAuthUserFromApId(uri: string): Promise<{ - user: RemoteUser; - key: UserPublickey | null; + user: MiRemoteUser; + key: MiUserPublickey | null; } | null> { - const user = await this.apPersonService.resolvePerson(uri) as RemoteUser; + const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser; const key = await this.publicKeyByUserIdCache.fetch( user.id, diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 09461973d9..81003bcf1c 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; +import type { FollowingsRepository } from '@/models/_.js'; +import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; import { QueueService } from '@/core/QueueService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -20,7 +24,7 @@ interface IFollowersRecipe extends IRecipe { interface IDirectRecipe extends IRecipe { type: 'Direct'; - to: RemoteUser; + to: MiRemoteUser; } const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe => @@ -47,7 +51,7 @@ class DeliverManager { private followingsRepository: FollowingsRepository, private queueService: QueueService, - actor: { id: User['id']; host: null; }, + actor: { id: MiUser['id']; host: null; }, activity: IActivity | null, ) { // 型で弾いてはいるが一応ローカルユーザーかチェック @@ -78,7 +82,7 @@ class DeliverManager { * @param to To */ @bindThis - public addDirectRecipe(to: RemoteUser): void { + public addDirectRecipe(to: MiRemoteUser): void { const recipe: IDirectRecipe = { type: 'Direct', to, @@ -147,12 +151,6 @@ class DeliverManager { @Injectable() export class ApDeliverManagerService { constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, @@ -167,7 +165,7 @@ export class ApDeliverManagerService { * @param activity Activity */ @bindThis - public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: IActivity): Promise<void> { + public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> { const manager = new DeliverManager( this.userEntityService, this.followingsRepository, @@ -186,7 +184,7 @@ export class ApDeliverManagerService { * @param to Target user */ @bindThis - public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: IActivity, to: RemoteUser): Promise<void> { + public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> { const manager = new DeliverManager( this.userEntityService, this.followingsRepository, @@ -199,7 +197,7 @@ export class ApDeliverManagerService { } @bindThis - public createDeliverManager(actor: { id: User['id']; host: null; }, activity: IActivity | null): DeliverManager { + public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager { return new DeliverManager( this.userEntityService, this.followingsRepository, diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 8d5f4883e4..b921ee7454 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; @@ -13,17 +18,15 @@ import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; import { AppLockService } from '@/core/AppLockService.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; -import { AccountMoveService } from '@/core/AccountMoveService.js'; import { IdService } from '@/core/IdService.js'; import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { CacheService } from '@/core/CacheService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { QueueService } from '@/core/QueueService.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; -import type { RemoteUser } from '@/models/entities/User.js'; +import type { MiRemoteUser } from '@/models/User.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; @@ -78,15 +81,13 @@ export class ApInboxService { private apNoteService: ApNoteService, private apPersonService: ApPersonService, private apQuestionService: ApQuestionService, - private accountMoveService: AccountMoveService, - private cacheService: CacheService, private queueService: QueueService, ) { this.logger = this.apLoggerService.logger; } @bindThis - public async performActivity(actor: RemoteUser, activity: IObject): Promise<void> { + public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<void> { if (isCollectionOrOrderedCollection(activity)) { const resolver = this.apResolverService.createResolver(); for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { @@ -114,7 +115,7 @@ export class ApInboxService { } @bindThis - public async performOneActivity(actor: RemoteUser, activity: IObject): Promise<void> { + public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<void> { if (actor.isSuspended) return; if (isCreate(activity)) { @@ -151,7 +152,7 @@ export class ApInboxService { } @bindThis - private async follow(actor: RemoteUser, activity: IFollow): Promise<string> { + private async follow(actor: MiRemoteUser, activity: IFollow): Promise<string> { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { @@ -168,7 +169,7 @@ export class ApInboxService { } @bindThis - private async like(actor: RemoteUser, activity: ILike): Promise<string> { + private async like(actor: MiRemoteUser, activity: ILike): Promise<string> { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -186,7 +187,7 @@ export class ApInboxService { } @bindThis - private async accept(actor: RemoteUser, activity: IAccept): Promise<string> { + private async accept(actor: MiRemoteUser, activity: IAccept): Promise<string> { const uri = activity.id ?? activity; this.logger.info(`Accept: ${uri}`); @@ -204,7 +205,7 @@ export class ApInboxService { } @bindThis - private async acceptFollow(actor: RemoteUser, activity: IFollow): Promise<string> { + private async acceptFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -228,7 +229,7 @@ export class ApInboxService { } @bindThis - private async add(actor: RemoteUser, activity: IAdd): Promise<void> { + private async add(actor: MiRemoteUser, activity: IAdd): Promise<void> { if (actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -248,7 +249,7 @@ export class ApInboxService { } @bindThis - private async announce(actor: RemoteUser, activity: IAnnounce): Promise<void> { + private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<void> { const uri = getApId(activity); this.logger.info(`Announce: ${uri}`); @@ -259,7 +260,7 @@ export class ApInboxService { } @bindThis - private async announceNote(actor: RemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { + private async announceNote(actor: MiRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { const uri = getApId(activity); if (actor.isSuspended) { @@ -319,7 +320,7 @@ export class ApInboxService { } @bindThis - private async block(actor: RemoteUser, activity: IBlock): Promise<string> { + private async block(actor: MiRemoteUser, activity: IBlock): Promise<string> { // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず const blockee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -337,7 +338,7 @@ export class ApInboxService { } @bindThis - private async create(actor: RemoteUser, activity: ICreate): Promise<void> { + private async create(actor: MiRemoteUser, activity: ICreate): Promise<void> { const uri = getApId(activity); this.logger.info(`Create: ${uri}`); @@ -373,7 +374,7 @@ export class ApInboxService { } @bindThis - private async createNote(resolver: Resolver, actor: RemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { + private async createNote(resolver: Resolver, actor: MiRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { const uri = getApId(note); if (typeof note === 'object') { @@ -408,7 +409,7 @@ export class ApInboxService { } @bindThis - private async delete(actor: RemoteUser, activity: IDelete): Promise<string> { + private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> { if (actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -450,7 +451,7 @@ export class ApInboxService { } @bindThis - private async deleteActor(actor: RemoteUser, uri: string): Promise<string> { + private async deleteActor(actor: MiRemoteUser, uri: string): Promise<string> { this.logger.info(`Deleting the Actor: ${uri}`); if (actor.uri !== uri) { @@ -474,7 +475,7 @@ export class ApInboxService { } @bindThis - private async deleteNote(actor: RemoteUser, uri: string): Promise<string> { + private async deleteNote(actor: MiRemoteUser, uri: string): Promise<string> { this.logger.info(`Deleting the Note: ${uri}`); const unlock = await this.appLockService.getApLock(uri); @@ -498,7 +499,7 @@ export class ApInboxService { } @bindThis - private async flag(actor: RemoteUser, activity: IFlag): Promise<string> { + private async flag(actor: MiRemoteUser, activity: IFlag): Promise<string> { // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する const uris = getApIds(activity.object); @@ -526,7 +527,7 @@ export class ApInboxService { } @bindThis - private async reject(actor: RemoteUser, activity: IReject): Promise<string> { + private async reject(actor: MiRemoteUser, activity: IReject): Promise<string> { const uri = activity.id ?? activity; this.logger.info(`Reject: ${uri}`); @@ -544,7 +545,7 @@ export class ApInboxService { } @bindThis - private async rejectFollow(actor: RemoteUser, activity: IFollow): Promise<string> { + private async rejectFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -568,7 +569,7 @@ export class ApInboxService { } @bindThis - private async remove(actor: RemoteUser, activity: IRemove): Promise<void> { + private async remove(actor: MiRemoteUser, activity: IRemove): Promise<void> { if (actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -588,7 +589,7 @@ export class ApInboxService { } @bindThis - private async undo(actor: RemoteUser, activity: IUndo): Promise<string> { + private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> { if (actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -615,7 +616,7 @@ export class ApInboxService { } @bindThis - private async undoAccept(actor: RemoteUser, activity: IAccept): Promise<string> { + private async undoAccept(actor: MiRemoteUser, activity: IAccept): Promise<string> { const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { return 'skip: follower not found'; @@ -637,7 +638,7 @@ export class ApInboxService { } @bindThis - private async undoAnnounce(actor: RemoteUser, activity: IAnnounce): Promise<string> { + private async undoAnnounce(actor: MiRemoteUser, activity: IAnnounce): Promise<string> { const uri = getApId(activity); const note = await this.notesRepository.findOneBy({ @@ -652,7 +653,7 @@ export class ApInboxService { } @bindThis - private async undoBlock(actor: RemoteUser, activity: IBlock): Promise<string> { + private async undoBlock(actor: MiRemoteUser, activity: IBlock): Promise<string> { const blockee = await this.apDbResolverService.getUserFromApId(activity.object); if (blockee == null) { @@ -668,7 +669,7 @@ export class ApInboxService { } @bindThis - private async undoFollow(actor: RemoteUser, activity: IFollow): Promise<string> { + private async undoFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { return 'skip: followee not found'; @@ -706,7 +707,7 @@ export class ApInboxService { } @bindThis - private async undoLike(actor: RemoteUser, activity: ILike): Promise<string> { + private async undoLike(actor: MiRemoteUser, activity: ILike): Promise<string> { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -721,7 +722,7 @@ export class ApInboxService { } @bindThis - private async update(actor: RemoteUser, activity: IUpdate): Promise<string> { + private async update(actor: MiRemoteUser, activity: IUpdate): Promise<string> { if (actor.uri !== activity.actor) { return 'skip: invalid actor'; } @@ -747,7 +748,7 @@ export class ApInboxService { } @bindThis - private async move(actor: RemoteUser, activity: IMove): Promise<string> { + private async move(actor: MiRemoteUser, activity: IMove): Promise<string> { // fetch the new and old accounts const targetUri = getApHrefNullable(activity.target); if (!targetUri) return 'skip: invalid activity target'; diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts index eeffab1b6d..cd9597e423 100644 --- a/packages/backend/src/core/activitypub/ApLoggerService.ts +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index d7269eca92..60868627a2 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -1,9 +1,12 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import * as mfm from 'mfm-js'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; import { MfmService } from '@/core/MfmService.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { extractApHashtagObjects } from './models/tag.js'; import type { IObject } from './type.js'; @@ -11,9 +14,6 @@ import type { IObject } from './type.js'; @Injectable() export class ApMfmService { constructor( - @Inject(DI.config) - private config: Config, - private mfmService: MfmService, ) { } @@ -25,7 +25,7 @@ export class ApMfmService { } @bindThis - public getNoteHtml(note: Note): string | null { + public getNoteHtml(note: MiNote): string | null { if (!note.text) return ''; return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); } diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 797c6267b1..7a9d2e21d8 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -1,24 +1,29 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { createPublicKey, randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import type { PartialLocalUser, LocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; -import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; -import type { Blocking } from '@/models/entities/Blocking.js'; -import type { Relay } from '@/models/entities/Relay.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; -import type { Poll } from '@/models/entities/Poll.js'; -import type { PollVote } from '@/models/entities/PollVote.js'; +import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; +import type { IMentionedRemoteUsers, MiNote } from '@/models/Note.js'; +import type { MiBlocking } from '@/models/Blocking.js'; +import type { MiRelay } from '@/models/Relay.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNoteReaction } from '@/models/NoteReaction.js'; +import type { MiEmoji } from '@/models/Emoji.js'; +import type { MiPoll } from '@/models/Poll.js'; +import type { MiPollVote } from '@/models/PollVote.js'; import { UserKeypairService } from '@/core/UserKeypairService.js'; import { MfmService } from '@/core/MfmService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; -import type { UserKeypair } from '@/models/entities/UserKeypair.js'; -import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, EmojisRepository, PollsRepository } from '@/models/index.js'; +import type { MiUserKeypair } from '@/models/UserKeypair.js'; +import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { isNotNull } from '@/misc/is-not-null.js'; @@ -44,9 +49,6 @@ export class ApRendererService { @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, - @Inject(DI.pollsRepository) private pollsRepository: PollsRepository, @@ -61,7 +63,7 @@ export class ApRendererService { } @bindThis - public renderAccept(object: string | IObject, user: { id: User['id']; host: null }): IAccept { + public renderAccept(object: string | IObject, user: { id: MiUser['id']; host: null }): IAccept { return { type: 'Accept', actor: this.userEntityService.genLocalUserUri(user.id), @@ -70,7 +72,7 @@ export class ApRendererService { } @bindThis - public renderAdd(user: LocalUser, target: string | IObject | undefined, object: string | IObject): IAdd { + public renderAdd(user: MiLocalUser, target: string | IObject | undefined, object: string | IObject): IAdd { return { type: 'Add', actor: this.userEntityService.genLocalUserUri(user.id), @@ -80,7 +82,7 @@ export class ApRendererService { } @bindThis - public renderAnnounce(object: string | IObject, note: Note): IAnnounce { + public renderAnnounce(object: string | IObject, note: MiNote): IAnnounce { const attributedTo = this.userEntityService.genLocalUserUri(note.userId); let to: string[] = []; @@ -116,7 +118,7 @@ export class ApRendererService { * @param block The block to be rendered. The blockee relation must be loaded. */ @bindThis - public renderBlock(block: Blocking): IBlock { + public renderBlock(block: MiBlocking): IBlock { if (block.blockee?.uri == null) { throw new Error('renderBlock: missing blockee uri'); } @@ -130,7 +132,7 @@ export class ApRendererService { } @bindThis - public renderCreate(object: IObject, note: Note): ICreate { + public renderCreate(object: IObject, note: MiNote): ICreate { const activity: ICreate = { id: `${this.config.url}/notes/${note.id}/activity`, actor: this.userEntityService.genLocalUserUri(note.userId), @@ -146,7 +148,7 @@ export class ApRendererService { } @bindThis - public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete { + public renderDelete(object: IObject | string, user: { id: MiUser['id']; host: null }): IDelete { return { type: 'Delete', actor: this.userEntityService.genLocalUserUri(user.id), @@ -156,7 +158,7 @@ export class ApRendererService { } @bindThis - public renderDocument(file: DriveFile): IApDocument { + public renderDocument(file: MiDriveFile): IApDocument { return { type: 'Document', mediaType: file.webpublicType ?? file.type, @@ -166,7 +168,7 @@ export class ApRendererService { } @bindThis - public renderEmoji(emoji: Emoji): IApEmoji { + public renderEmoji(emoji: MiEmoji): IApEmoji { return { id: `${this.config.url}/emojis/${emoji.name}`, type: 'Emoji', @@ -183,7 +185,7 @@ export class ApRendererService { // to anonymise reporters, the reporting actor must be a system user @bindThis - public renderFlag(user: LocalUser, object: IObject | string, content: string): IFlag { + public renderFlag(user: MiLocalUser, object: IObject | string, content: string): IFlag { return { type: 'Flag', actor: this.userEntityService.genLocalUserUri(user.id), @@ -193,7 +195,7 @@ export class ApRendererService { } @bindThis - public renderFollowRelay(relay: Relay, relayActor: LocalUser): IFollow { + public renderFollowRelay(relay: MiRelay, relayActor: MiLocalUser): IFollow { return { id: `${this.config.url}/activities/follow-relay/${relay.id}`, type: 'Follow', @@ -207,15 +209,15 @@ export class ApRendererService { * @param id Follower|Followee ID */ @bindThis - public async renderFollowUser(id: User['id']): Promise<string> { - const user = await this.usersRepository.findOneByOrFail({ id: id }) as PartialLocalUser | PartialRemoteUser; + public async renderFollowUser(id: MiUser['id']): Promise<string> { + const user = await this.usersRepository.findOneByOrFail({ id: id }) as MiPartialLocalUser | MiPartialRemoteUser; return this.userEntityService.getUserUri(user); } @bindThis public renderFollow( - follower: PartialLocalUser | PartialRemoteUser, - followee: PartialLocalUser | PartialRemoteUser, + follower: MiPartialLocalUser | MiPartialRemoteUser, + followee: MiPartialLocalUser | MiPartialRemoteUser, requestId?: string, ): IFollow { return { @@ -236,7 +238,7 @@ export class ApRendererService { } @bindThis - public renderImage(file: DriveFile): IApImage { + public renderImage(file: MiDriveFile): IApImage { return { type: 'Image', url: this.driveFileEntityService.getPublicUrl(file), @@ -246,7 +248,7 @@ export class ApRendererService { } @bindThis - public renderKey(user: LocalUser, key: UserKeypair, postfix?: string): IKey { + public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey { return { id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, type: 'Key', @@ -259,7 +261,7 @@ export class ApRendererService { } @bindThis - public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }): Promise<ILike> { + public async renderLike(noteReaction: MiNoteReaction, note: { uri: string | null }): Promise<ILike> { const reaction = noteReaction.reaction; const object: ILike = { @@ -282,18 +284,18 @@ export class ApRendererService { } @bindThis - public renderMention(mention: PartialLocalUser | PartialRemoteUser): IApMention { + public renderMention(mention: MiPartialLocalUser | MiPartialRemoteUser): IApMention { return { type: 'Mention', href: this.userEntityService.getUserUri(mention), - name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`, + name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as MiLocalUser).username}`, }; } @bindThis public renderMove( - src: PartialLocalUser | PartialRemoteUser, - dst: PartialLocalUser | PartialRemoteUser, + src: MiPartialLocalUser | MiPartialRemoteUser, + dst: MiPartialLocalUser | MiPartialRemoteUser, ): IMove { const actor = this.userEntityService.getUserUri(src); const target = this.userEntityService.getUserUri(dst); @@ -307,15 +309,15 @@ export class ApRendererService { } @bindThis - public async renderNote(note: Note, dive = true): Promise<IPost> { - const getPromisedFiles = async (ids: string[]): Promise<DriveFile[]> => { + public async renderNote(note: MiNote, dive = true): Promise<IPost> { + const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => { if (ids.length === 0) return []; const items = await this.driveFilesRepository.findBy({ id: In(ids) }); - return ids.map(id => items.find(item => item.id === id)).filter((item): item is DriveFile => item != null); + return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null); }; let inReplyTo; - let inReplyToNote: Note | null; + let inReplyToNote: MiNote | null; if (note.replyId) { inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); @@ -374,12 +376,12 @@ export class ApRendererService { }) : []; const hashtagTags = note.tags.map(tag => this.renderHashtag(tag)); - const mentionTags = mentionedUsers.map(u => this.renderMention(u as LocalUser | RemoteUser)); + const mentionTags = mentionedUsers.map(u => this.renderMention(u as MiLocalUser | MiRemoteUser)); const files = await getPromisedFiles(note.fileIds); const text = note.text ?? ''; - let poll: Poll | null = null; + let poll: MiPoll | null = null; if (note.hasPoll) { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); @@ -447,7 +449,7 @@ export class ApRendererService { } @bindThis - public async renderPerson(user: LocalUser) { + public async renderPerson(user: MiLocalUser) { const id = this.userEntityService.genLocalUserUri(user.id); const isSystem = user.username.includes('.'); @@ -521,7 +523,7 @@ export class ApRendererService { } @bindThis - public renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll): IQuestion { + public renderQuestion(user: { id: MiUser['id'] }, note: MiNote, poll: MiPoll): IQuestion { return { type: 'Question', id: `${this.config.url}/questions/${note.id}`, @@ -539,7 +541,7 @@ export class ApRendererService { } @bindThis - public renderReject(object: string | IObject, user: { id: User['id'] }): IReject { + public renderReject(object: string | IObject, user: { id: MiUser['id'] }): IReject { return { type: 'Reject', actor: this.userEntityService.genLocalUserUri(user.id), @@ -548,7 +550,7 @@ export class ApRendererService { } @bindThis - public renderRemove(user: { id: User['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove { + public renderRemove(user: { id: MiUser['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove { return { type: 'Remove', actor: this.userEntityService.genLocalUserUri(user.id), @@ -566,7 +568,7 @@ export class ApRendererService { } @bindThis - public renderUndo(object: string | IObject, user: { id: User['id'] }): IUndo { + public renderUndo(object: string | IObject, user: { id: MiUser['id'] }): IUndo { const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; return { @@ -579,7 +581,7 @@ export class ApRendererService { } @bindThis - public renderUpdate(object: string | IObject, user: { id: User['id'] }): IUpdate { + public renderUpdate(object: string | IObject, user: { id: MiUser['id'] }): IUpdate { return { id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, actor: this.userEntityService.genLocalUserUri(user.id), @@ -591,7 +593,7 @@ export class ApRendererService { } @bindThis - public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): ICreate { + public renderVote(user: { id: MiUser['id'] }, vote: MiPollVote, note: MiNote, poll: MiPoll, pollOwner: MiRemoteUser): ICreate { return { id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, actor: this.userEntityService.genLocalUserUri(user.id), @@ -649,7 +651,7 @@ export class ApRendererService { } @bindThis - public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> { + public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> { const keypair = await this.userKeypairService.getUserKeypair(user.id); const ldSignature = this.ldSignatureService.use(); @@ -708,7 +710,7 @@ export class ApRendererService { } @bindThis - private async getEmojis(names: string[]): Promise<Emoji[]> { + private async getEmojis(names: string[]): Promise<MiEmoji[]> { if (names.length === 0) return []; const allEmojis = await this.customEmojiService.localEmojisCache.fetch(); diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index 44676cac20..b59ce5241f 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as crypto from 'node:crypto'; import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { UserKeypairService } from '@/core/UserKeypairService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { LoggerService } from '@/core/LoggerService.js'; @@ -140,7 +145,7 @@ export class ApRequestService { } @bindThis - public async signedPost(user: { id: User['id'] }, url: string, object: unknown): Promise<void> { + public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown): Promise<void> { const body = JSON.stringify(object); const keypair = await this.userKeypairService.getUserKeypair(user.id); @@ -169,7 +174,7 @@ export class ApRequestService { * @param url URL to fetch */ @bindThis - public async signedGet(url: string, user: { id: User['id'] }): Promise<unknown> { + public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> { const keypair = await this.userKeypairService.getUserKeypair(user.id); const req = ApRequestCreator.createSignedGet({ diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index aa4720c56e..9ca63c9ec5 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -1,7 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; +import { IsNull, Not } from 'typeorm'; +import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; -import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; @@ -18,7 +24,7 @@ import type { IObject, ICollection, IOrderedCollection } from './type.js'; export class Resolver { private history: Set<string>; - private user?: LocalUser; + private user?: MiLocalUser; private logger: Logger; constructor( @@ -27,6 +33,7 @@ export class Resolver { private notesRepository: NotesRepository, private pollsRepository: PollsRepository, private noteReactionsRepository: NoteReactionsRepository, + private followRequestsRepository: FollowRequestsRepository, private utilityService: UtilityService, private instanceActorService: InstanceActorService, private metaService: MetaService, @@ -129,7 +136,7 @@ export class Resolver { }); case 'users': return this.usersRepository.findOneByOrFail({ id: parsed.id }) - .then(user => this.apRendererService.renderPerson(user as LocalUser)); + .then(user => this.apRendererService.renderPerson(user as MiLocalUser)); case 'questions': // Polls are indexed by the note they are attached to. return Promise.all([ @@ -141,13 +148,24 @@ export class Resolver { return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction => this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null }))); case 'follows': - // rest should be <followee id> - if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI'); - - return Promise.all( - [parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })), - ) - .then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as LocalUser | RemoteUser, followee as LocalUser | RemoteUser, url))); + return this.followRequestsRepository.findOneBy({ id: parsed.id }) + .then(async followRequest => { + if (followRequest == null) throw new Error('resolveLocal: invalid follow request ID'); + const [follower, followee] = await Promise.all([ + this.usersRepository.findOneBy({ + id: followRequest.followerId, + host: IsNull(), + }), + this.usersRepository.findOneBy({ + id: followRequest.followeeId, + host: Not(IsNull()), + }), + ]); + if (follower == null || followee == null) { + throw new Error('resolveLocal: follower or followee does not exist'); + } + return this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url)); + }); default: throw new Error(`resolveLocal: type ${parsed.type} unhandled`); } @@ -172,6 +190,9 @@ export class ApResolverService { @Inject(DI.noteReactionsRepository) private noteReactionsRepository: NoteReactionsRepository, + @Inject(DI.followRequestsRepository) + private followRequestsRepository: FollowRequestsRepository, + private utilityService: UtilityService, private instanceActorService: InstanceActorService, private metaService: MetaService, @@ -191,6 +212,7 @@ export class ApResolverService { this.notesRepository, this.pollsRepository, this.noteReactionsRepository, + this.followRequestsRepository, this.utilityService, this.instanceActorService, this.metaService, diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index af7e243229..39b5ff8abc 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as crypto from 'node:crypto'; import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 2bcd3811b2..71c440e5cc 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { JsonLd } from 'jsonld/jsonld-spec.js'; /* eslint:disable:quotemark indent */ diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 1f2984894c..a4cd533892 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository } from '@/models/index.js'; -import type { RemoteUser } from '@/models/entities/User.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { DriveFilesRepository } from '@/models/_.js'; +import type { MiRemoteUser } from '@/models/User.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { MetaService } from '@/core/MetaService.js'; import { truncate } from '@/misc/truncate.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js'; @@ -34,7 +39,7 @@ export class ApImageService { * Imageを作成します。 */ @bindThis - public async createImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> { + public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> { // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { throw new Error('actor has been suspended'); @@ -85,7 +90,7 @@ export class ApImageService { * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ @bindThis - public async resolveImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> { + public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> { // TODO // リモートサーバーからフェッチしてきて登録 diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 62ae3cf93d..9aa8ba5ede 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -1,34 +1,33 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; -import { DI } from '@/di-symbols.js'; -import type { User } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { MiUser } from '@/models/_.js'; import { toArray, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; import { isMention } from '../type.js'; -import { ApResolverService, Resolver } from '../ApResolverService.js'; +import { Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; import type { IObject, IApMention } from '../type.js'; @Injectable() export class ApMentionService { constructor( - @Inject(DI.config) - private config: Config, - - private apResolverService: ApResolverService, private apPersonService: ApPersonService, ) { } @bindThis - public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<User[]> { + public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<MiUser[]> { const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href)); - const limit = promiseLimit<User | null>(2); + const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is User => x != null); + )).filter((x): x is MiUser => x != null); return mentionedUsers; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 46ed976a6b..573dff5b91 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -1,16 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { PollsRepository, EmojisRepository } from '@/models/index.js'; +import type { PollsRepository, EmojisRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; -import type { RemoteUser } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiRemoteUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; +import type { MiEmoji } from '@/models/Emoji.js'; import { MetaService } from '@/core/MetaService.js'; import { AppLockService } from '@/core/AppLockService.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import type Logger from '@/logger.js'; import { IdService } from '@/core/IdService.js'; @@ -96,7 +101,7 @@ export class ApNoteService { * Misskeyに対象のNoteが登録されていればそれを返します。 */ @bindThis - public async fetchNote(object: string | IObject): Promise<Note | null> { + public async fetchNote(object: string | IObject): Promise<MiNote | null> { return await this.apDbResolverService.getNoteFromApId(object); } @@ -104,7 +109,7 @@ export class ApNoteService { * Noteを作成します。 */ @bindThis - public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { + public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> { // eslint-disable-next-line no-param-reassign if (resolver == null) resolver = this.apResolverService.createResolver(); @@ -126,13 +131,13 @@ export class ApNoteService { this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); if (note.id && !checkHttps(note.id)) { - throw new Error('unexpected shcema of note.id: ' + note.id); + throw new Error('unexpected schema of note.id: ' + note.id); } const url = getOneApHrefNullable(note.url); if (url && !checkHttps(url)) { - throw new Error('unexpected shcema of note url: ' + url); + throw new Error('unexpected schema of note url: ' + url); } this.logger.info(`Creating the Note: ${note.id}`); @@ -142,7 +147,7 @@ export class ApNoteService { throw new Error('invalid note.attributedTo: ' + note.attributedTo); } - const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser; + const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { @@ -167,7 +172,7 @@ export class ApNoteService { // 添付ファイル // TODO: attachmentは必ずしもImageではない // TODO: attachmentは必ずしも配列ではない - const limit = promiseLimit<DriveFile>(2); + const limit = promiseLimit<MiDriveFile>(2); const files = (await Promise.all(toArray(note.attachment).map(attach => ( limit(() => this.apImageService.resolveImage(actor, { ...attach, @@ -176,7 +181,7 @@ export class ApNoteService { )))); // リプライ - const reply: Note | null = note.inReplyTo + const reply: MiNote | null = note.inReplyTo ? await this.resolveNote(note.inReplyTo, { resolver }) .then(x => { if (x == null) { @@ -193,11 +198,11 @@ export class ApNoteService { : null; // 引用 - let quote: Note | undefined | null = null; + let quote: MiNote | undefined | null = null; - if (note._misskey_quote || note.quoteUrl) { + if (note._misskey_quote ?? note.quoteUrl) { const tryResolveNote = async (uri: string): Promise< - | { status: 'ok'; res: Note } + | { status: 'ok'; res: MiNote } | { status: 'permerror' | 'temperror' } > => { if (!/^https?:/.test(uri)) return { status: 'permerror' }; @@ -215,7 +220,7 @@ export class ApNoteService { const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); const results = await Promise.all(uris.map(tryResolveNote)); - quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0); + quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0); if (!quote) { if (results.some(x => x.status === 'temperror')) { throw new Error('quote resolve failed'); @@ -266,24 +271,36 @@ export class ApNoteService { const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - return await this.noteCreateService.create(actor, { - createdAt: note.published ? new Date(note.published) : null, - files, - reply, - renote: quote, - name: note.name, - cw, - text, - localOnly: false, - visibility, - visibleUsers, - apMentions, - apHashtags, - apEmojis, - poll, - uri: note.id, - url: url, - }, silent); + try { + return await this.noteCreateService.create(actor, { + createdAt: note.published ? new Date(note.published) : null, + files, + reply, + renote: quote, + name: note.name, + cw, + text, + localOnly: false, + visibility, + visibleUsers, + apMentions, + apHashtags, + apEmojis, + poll, + uri: note.id, + url: url, + }, silent); + } catch (err: any) { + if (err.name !== 'duplicated') { + throw err; + } + this.logger.info('The note is already inserted while creating itself, reading again'); + const duplicate = await this.fetchNote(value); + if (!duplicate) { + throw new Error('The note creation failed with duplication error even when there is no duplication'); + } + return duplicate; + } } /** @@ -293,7 +310,7 @@ export class ApNoteService { * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ @bindThis - public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<Note | null> { + public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<MiNote | null> { const uri = getApId(value); // ブロックしていたら中断 @@ -325,7 +342,7 @@ export class ApNoteService { } @bindThis - public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> { + public async extractEmojis(tags: IObject | IObject[], host: string): Promise<MiEmoji[]> { // eslint-disable-next-line no-param-reassign host = this.utilityService.toPuny(host); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 8fc083719d..ea64883395 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -1,30 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; -import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; -import { User } from '@/models/entities/User.js'; +import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; +import { MiUser } from '@/models/User.js'; import { truncate } from '@/misc/truncate.js'; import type { CacheService } from '@/core/CacheService.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type Logger from '@/logger.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNote } from '@/models/Note.js'; import type { IdService } from '@/core/IdService.js'; import type { MfmService } from '@/core/MfmService.js'; import { toArray } from '@/misc/prelude/array.js'; import type { GlobalEventService } from '@/core/GlobalEventService.js'; import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; -import { UserPublickey } from '@/models/entities/UserPublickey.js'; +import { MiUserProfile } from '@/models/UserProfile.js'; +import { MiUserPublickey } from '@/models/UserPublickey.js'; import type UsersChart from '@/core/chart/charts/users.js'; import type InstanceChart from '@/core/chart/charts/instance.js'; import type { HashtagService } from '@/core/HashtagService.js'; -import { UserNotePining } from '@/models/entities/UserNotePining.js'; +import { MiUserNotePining } from '@/models/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; import type { UtilityService } from '@/core/UtilityService.js'; import type { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -196,20 +201,20 @@ export class ApPersonService implements OnModuleInit { * Misskeyに対象のPersonが登録されていればそれを返し、登録がなければnullを返します。 */ @bindThis - public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> { - const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null | undefined; + public async fetchPerson(uri: string): Promise<MiLocalUser | MiRemoteUser | null> { + const cached = this.cacheService.uriPersonCache.get(uri) as MiLocalUser | MiRemoteUser | null | undefined; if (cached) return cached; // URIがこのサーバーを指しているならデータベースからフェッチ if (uri.startsWith(`${this.config.url}/`)) { const id = uri.split('/').pop(); - const u = await this.usersRepository.findOneBy({ id }) as LocalUser | null; + const u = await this.usersRepository.findOneBy({ id }) as MiLocalUser | null; if (u) this.cacheService.uriPersonCache.set(uri, u); return u; } //#region このサーバーに既に登録されていたらそれを返す - const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser | null; + const exist = await this.usersRepository.findOneBy({ uri }) as MiLocalUser | MiRemoteUser | null; if (exist) { this.cacheService.uriPersonCache.set(uri, exist); @@ -220,7 +225,7 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise<Pick<RemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { + private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { const [avatar, banner] = await Promise.all([icon, image].map(img => { if (img == null) return null; if (user == null) throw new Error('failed to create user: user is null'); @@ -241,7 +246,7 @@ export class ApPersonService implements OnModuleInit { * Personを作成します。 */ @bindThis - public async createPerson(uri: string, resolver?: Resolver): Promise<RemoteUser> { + public async createPerson(uri: string, resolver?: Resolver): Promise<MiRemoteUser> { if (typeof uri !== 'string') throw new Error('uri is not string'); if (uri.startsWith(this.config.url)) { @@ -275,13 +280,13 @@ export class ApPersonService implements OnModuleInit { } // Create user - let user: RemoteUser | null = null; + let user: MiRemoteUser | null = null; //#region カスタム絵文字取得 const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) .then(_emojis => _emojis.map(emoji => emoji.name)) .catch(err => { - this.logger.error(`error occured while fetching user emojis`, { stack: err }); + this.logger.error('error occurred while fetching user emojis', { stack: err }); return []; }); //#endregion @@ -289,7 +294,7 @@ export class ApPersonService implements OnModuleInit { try { // Start transaction await this.db.transaction(async transactionalEntityManager => { - user = await transactionalEntityManager.save(new User({ + user = await transactionalEntityManager.save(new MiUser({ id: this.idService.genId(), avatarId: null, bannerId: null, @@ -313,9 +318,9 @@ export class ApPersonService implements OnModuleInit { isBot, isCat: (person as any).isCat === true, emojis, - })) as RemoteUser; + })) as MiRemoteUser; - await transactionalEntityManager.save(new UserProfile({ + await transactionalEntityManager.save(new MiUserProfile({ userId: user.id, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, url, @@ -326,7 +331,7 @@ export class ApPersonService implements OnModuleInit { })); if (person.publicKey) { - await transactionalEntityManager.save(new UserPublickey({ + await transactionalEntityManager.save(new MiUserPublickey({ userId: user.id, keyId: person.publicKey.id, keyPem: person.publicKey.publicKeyPem, @@ -340,7 +345,7 @@ export class ApPersonService implements OnModuleInit { const u = await this.usersRepository.findOneBy({ uri: person.id }); if (u == null) throw new Error('already registered'); - user = u as RemoteUser; + user = u as MiRemoteUser; } else { this.logger.error(e instanceof Error ? e : new Error(e as string)); throw e; @@ -375,7 +380,7 @@ export class ApPersonService implements OnModuleInit { // Register to the cache this.cacheService.uriPersonCache.set(user.uri, user); } catch (err) { - this.logger.error('error occured while fetching user avatar/banner', { stack: err }); + this.logger.error('error occurred while fetching user avatar/banner', { stack: err }); } //#endregion @@ -402,7 +407,7 @@ export class ApPersonService implements OnModuleInit { if (uri.startsWith(`${this.config.url}/`)) return; //#region このサーバーに既に登録されているか - const exist = await this.fetchPerson(uri) as RemoteUser | null; + const exist = await this.fetchPerson(uri) as MiRemoteUser | null; if (exist === null) return; //#endregion @@ -451,7 +456,7 @@ export class ApPersonService implements OnModuleInit { alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), - } as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>; + } as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>; const moving = ((): boolean => { // 移行先がない→ある @@ -537,7 +542,7 @@ export class ApPersonService implements OnModuleInit { * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ @bindThis - public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> { + public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> { //#region このサーバーに既に登録されていたらそれを返す const exist = await this.fetchPerson(uri); if (exist) return exist; @@ -567,7 +572,7 @@ export class ApPersonService implements OnModuleInit { } @bindThis - public async updateFeatured(userId: User['id'], resolver?: Resolver): Promise<void> { + public async updateFeatured(userId: MiUser['id'], resolver?: Resolver): Promise<void> { const user = await this.usersRepository.findOneByOrFail({ id: userId }); if (!this.userEntityService.isRemoteUser(user)) return; if (!user.featured) return; @@ -585,7 +590,7 @@ export class ApPersonService implements OnModuleInit { const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x))); // Resolve and regist Notes - const limit = promiseLimit<Note | null>(2); + const limit = promiseLimit<MiNote | null>(2); const featuredNotes = await Promise.all(items .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも .slice(0, 5) @@ -595,13 +600,13 @@ export class ApPersonService implements OnModuleInit { })))); await this.db.transaction(async transactionalEntityManager => { - await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); + await transactionalEntityManager.delete(MiUserNotePining, { userId: user.id }); // とりあえずidを別の時間で生成して順番を維持 let td = 0; - for (const note of featuredNotes.filter((note): note is Note => note != null)) { + for (const note of featuredNotes.filter((note): note is MiNote => note != null)) { td -= 1000; - transactionalEntityManager.insert(UserNotePining, { + transactionalEntityManager.insert(MiUserNotePining, { id: this.idService.genId(new Date(Date.now() + td)), createdAt: new Date(), userId: user.id, @@ -617,7 +622,7 @@ export class ApPersonService implements OnModuleInit { * @param movePreventUris ここに列挙されたURIにsrc.movedToUriが含まれる場合、移行処理はしない(無限ループ防止) */ @bindThis - private async processRemoteMove(src: RemoteUser, movePreventUris: string[] = []): Promise<string> { + private async processRemoteMove(src: MiRemoteUser, movePreventUris: string[] = []): Promise<string> { if (!src.movedToUri) return 'skip: no movedToUri'; if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; // ??? if (movePreventUris.length > 10) return 'skip: too many moves'; @@ -627,7 +632,7 @@ export class ApPersonService implements OnModuleInit { if (dst && this.userEntityService.isLocalUser(dst)) { // targetがローカルユーザーだった場合データベースから引っ張ってくる - dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as LocalUser; + dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as MiLocalUser; } else if (dst) { if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move'; diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index 229a44f90f..27bd62268b 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, PollsRepository } from '@/models/index.js'; +import type { NotesRepository, PollsRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; -import type { IPoll } from '@/models/entities/Poll.js'; +import type { IPoll } from '@/models/Poll.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { isQuestion } from '../type.js'; diff --git a/packages/backend/src/core/activitypub/models/icon.ts b/packages/backend/src/core/activitypub/models/icon.ts index 50794a937d..9fed78020d 100644 --- a/packages/backend/src/core/activitypub/models/icon.ts +++ b/packages/backend/src/core/activitypub/models/icon.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type IIcon = { type: string; mediaType?: string; diff --git a/packages/backend/src/core/activitypub/models/identifier.ts b/packages/backend/src/core/activitypub/models/identifier.ts index f6c3bb8c88..22a7b0a76e 100644 --- a/packages/backend/src/core/activitypub/models/identifier.ts +++ b/packages/backend/src/core/activitypub/models/identifier.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type IIdentifier = { type: string; name: string; diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts index 9aeb843562..772ea11864 100644 --- a/packages/backend/src/core/activitypub/models/tag.ts +++ b/packages/backend/src/core/activitypub/models/tag.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { toArray } from '@/misc/prelude/array.js'; import { isHashtag } from '../type.js'; import type { IObject, IApHashtag } from '../type.js'; diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 4bb0fa61ec..16ff86e894 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type Obj = { [x: string]: any }; export type ApObject = IObject | string | (IObject | string)[]; diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts index afd3bab5a2..bd90efec64 100644 --- a/packages/backend/src/core/chart/ChartLoggerService.ts +++ b/packages/backend/src/core/chart/ChartLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index b0e9e534df..f751a68cb4 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; @@ -18,7 +23,7 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class ChartManagementService implements OnApplicationShutdown { private charts; - private saveIntervalId: NodeJS.Timer; + private saveIntervalId: NodeJS.Timeout; constructor( private federationChart: FederationChart, diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index bc0ba25cbb..55da1469e5 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; @@ -16,9 +21,8 @@ const year = 1000 * 60 * 60 * 24 * 365; /** * アクティブユーザーに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class ActiveUsersChart extends Chart<typeof schema> { +export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -38,7 +42,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> { } @bindThis - public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { + public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> { await this.commit({ 'read': [user.id], 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], @@ -51,7 +55,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> { } @bindThis - public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { + public async write(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> { await this.commit({ 'write': [user.id], }); diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts index ce377460c8..03c9b42be1 100644 --- a/packages/backend/src/core/chart/charts/ap-request.ts +++ b/packages/backend/src/core/chart/charts/ap-request.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; @@ -11,9 +16,8 @@ import type { KVs } from '../core.js'; /** * Chart about ActivityPub requests */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class ApRequestChart extends Chart<typeof schema> { +export default class ApRequestChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index b63db591fb..bbcbf1a955 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -12,9 +17,8 @@ import type { KVs } from '../core.js'; /** * ドライブに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class DriveChart extends Chart<typeof schema> { +export default class DriveChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -34,7 +38,7 @@ export default class DriveChart extends Chart<typeof schema> { } @bindThis - public async update(file: DriveFile, isAdditional: boolean): Promise<void> { + public async update(file: MiDriveFile, isAdditional: boolean): Promise<void> { const fileSizeKb = file.size / 1000; await this.commit(file.userHost === null ? { 'local.incCount': isAdditional ? 1 : 0, diff --git a/packages/backend/src/core/chart/charts/entities/active-users.ts b/packages/backend/src/core/chart/charts/entities/active-users.ts index e291e37c1b..e68022ef29 100644 --- a/packages/backend/src/core/chart/charts/entities/active-users.ts +++ b/packages/backend/src/core/chart/charts/entities/active-users.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'activeUsers'; diff --git a/packages/backend/src/core/chart/charts/entities/ap-request.ts b/packages/backend/src/core/chart/charts/entities/ap-request.ts index 3a9f3dacfd..a824515255 100644 --- a/packages/backend/src/core/chart/charts/entities/ap-request.ts +++ b/packages/backend/src/core/chart/charts/entities/ap-request.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'apRequest'; diff --git a/packages/backend/src/core/chart/charts/entities/drive.ts b/packages/backend/src/core/chart/charts/entities/drive.ts index 4bf5bb729e..4a56bd45c5 100644 --- a/packages/backend/src/core/chart/charts/entities/drive.ts +++ b/packages/backend/src/core/chart/charts/entities/drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'drive'; diff --git a/packages/backend/src/core/chart/charts/entities/federation.ts b/packages/backend/src/core/chart/charts/entities/federation.ts index a8466b0b4c..e067c71a7f 100644 --- a/packages/backend/src/core/chart/charts/entities/federation.ts +++ b/packages/backend/src/core/chart/charts/entities/federation.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'federation'; diff --git a/packages/backend/src/core/chart/charts/entities/instance.ts b/packages/backend/src/core/chart/charts/entities/instance.ts index 06962120e2..4ea10d56d1 100644 --- a/packages/backend/src/core/chart/charts/entities/instance.ts +++ b/packages/backend/src/core/chart/charts/entities/instance.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'instance'; diff --git a/packages/backend/src/core/chart/charts/entities/notes.ts b/packages/backend/src/core/chart/charts/entities/notes.ts index 9387dbfb2c..26e2529b17 100644 --- a/packages/backend/src/core/chart/charts/entities/notes.ts +++ b/packages/backend/src/core/chart/charts/entities/notes.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'notes'; diff --git a/packages/backend/src/core/chart/charts/entities/per-user-drive.ts b/packages/backend/src/core/chart/charts/entities/per-user-drive.ts index 6111640ea0..aec3dd5140 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'perUserDrive'; diff --git a/packages/backend/src/core/chart/charts/entities/per-user-following.ts b/packages/backend/src/core/chart/charts/entities/per-user-following.ts index 4118daa474..afb5813058 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-following.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'perUserFollowing'; diff --git a/packages/backend/src/core/chart/charts/entities/per-user-notes.ts b/packages/backend/src/core/chart/charts/entities/per-user-notes.ts index c1fa174452..60a0b01c8e 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-notes.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'perUserNotes'; diff --git a/packages/backend/src/core/chart/charts/entities/per-user-pv.ts b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts index 64c8ed1fb1..78d4464d7e 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-pv.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'perUserPv'; diff --git a/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts b/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts index 5e1a6c7b30..761101d479 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'perUserReaction'; diff --git a/packages/backend/src/core/chart/charts/entities/test-grouped.ts b/packages/backend/src/core/chart/charts/entities/test-grouped.ts index 66b6e8e864..15eb1fd1f8 100644 --- a/packages/backend/src/core/chart/charts/entities/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/entities/test-grouped.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'testGrouped'; diff --git a/packages/backend/src/core/chart/charts/entities/test-intersection.ts b/packages/backend/src/core/chart/charts/entities/test-intersection.ts index a3bdcb367f..2ef63977a5 100644 --- a/packages/backend/src/core/chart/charts/entities/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/entities/test-intersection.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'testIntersection'; diff --git a/packages/backend/src/core/chart/charts/entities/test-unique.ts b/packages/backend/src/core/chart/charts/entities/test-unique.ts index b2cfb71b05..56233585db 100644 --- a/packages/backend/src/core/chart/charts/entities/test-unique.ts +++ b/packages/backend/src/core/chart/charts/entities/test-unique.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'testUnique'; diff --git a/packages/backend/src/core/chart/charts/entities/test.ts b/packages/backend/src/core/chart/charts/entities/test.ts index 7cba21e16a..163db4e79f 100644 --- a/packages/backend/src/core/chart/charts/entities/test.ts +++ b/packages/backend/src/core/chart/charts/entities/test.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'test'; diff --git a/packages/backend/src/core/chart/charts/entities/users.ts b/packages/backend/src/core/chart/charts/entities/users.ts index c0b83094ae..c7bffd3fd4 100644 --- a/packages/backend/src/core/chart/charts/entities/users.ts +++ b/packages/backend/src/core/chart/charts/entities/users.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Chart from '../../core.js'; export const name = 'users'; diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index ae4eb6e48d..fc474b002b 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { FollowingsRepository, InstancesRepository } from '@/models/index.js'; +import type { FollowingsRepository, InstancesRepository } from '@/models/_.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; @@ -13,9 +18,8 @@ import type { KVs } from '../core.js'; /** * フェデレーションに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class FederationChart extends Chart<typeof schema> { +export default class FederationChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index 8ca88d80e3..9df0afb02e 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNote } from '@/models/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -15,9 +20,8 @@ import type { KVs } from '../core.js'; /** * インスタンスごとのチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class InstanceChart extends Chart<typeof schema> { +export default class InstanceChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -93,7 +97,7 @@ export default class InstanceChart extends Chart<typeof schema> { } @bindThis - public async updateNote(host: string, note: Note, isAdditional: boolean): Promise<void> { + public async updateNote(host: string, note: MiNote, isAdditional: boolean): Promise<void> { await this.commit({ 'notes.total': isAdditional ? 1 : -1, 'notes.inc': isAdditional ? 1 : 0, @@ -124,7 +128,7 @@ export default class InstanceChart extends Chart<typeof schema> { } @bindThis - public async updateDrive(file: DriveFile, isAdditional: boolean): Promise<void> { + public async updateDrive(file: MiDriveFile, isAdditional: boolean): Promise<void> { const fileSizeKb = file.size / 1000; await this.commit({ 'drive.totalFiles': isAdditional ? 1 : -1, diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index 23dc248fec..df3295dbac 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull, DataSource } from 'typeorm'; -import type { NotesRepository } from '@/models/index.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { NotesRepository } from '@/models/_.js'; +import type { MiNote } from '@/models/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -13,9 +18,8 @@ import type { KVs } from '../core.js'; /** * ノートに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class NotesChart extends Chart<typeof schema> { +export default class NotesChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -46,7 +50,7 @@ export default class NotesChart extends Chart<typeof schema> { } @bindThis - public async update(note: Note, isAdditional: boolean): Promise<void> { + public async update(note: MiNote, isAdditional: boolean): Promise<void> { const prefix = note.userHost === null ? 'local' : 'remote'; await this.commit({ diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index ffba04b041..18354359c8 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { DriveFilesRepository } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { DriveFilesRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; @@ -14,9 +19,8 @@ import type { KVs } from '../core.js'; /** * ユーザーごとのドライブに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class PerUserDriveChart extends Chart<typeof schema> { +export default class PerUserDriveChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -48,7 +52,7 @@ export default class PerUserDriveChart extends Chart<typeof schema> { } @bindThis - public async update(file: DriveFile, isAdditional: boolean): Promise<void> { + public async update(file: MiDriveFile, isAdditional: boolean): Promise<void> { const fileSizeKb = file.size / 1000; await this.commit({ 'totalCount': isAdditional ? 1 : -1, diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index aea6d44a9a..79bff2cb66 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull, DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; @@ -14,9 +19,8 @@ import type { KVs } from '../core.js'; /** * ユーザーごとのフォローに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class PerUserFollowingChart extends Chart<typeof schema> { +export default class PerUserFollowingChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -57,7 +61,7 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { } @bindThis - public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> { + public async update(follower: { id: MiUser['id']; host: MiUser['host']; }, followee: { id: MiUser['id']; host: MiUser['host']; }, isFollow: boolean): Promise<void> { const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index d8966f34c1..0db0e6f07f 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; @@ -14,9 +19,8 @@ import type { KVs } from '../core.js'; /** * ユーザーごとのノートに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class PerUserNotesChart extends Chart<typeof schema> { +export default class PerUserNotesChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -45,7 +49,7 @@ export default class PerUserNotesChart extends Chart<typeof schema> { } @bindThis - public update(user: { id: User['id'] }, note: Note, isAdditional: boolean): void { + public update(user: { id: MiUser['id'] }, note: MiNote, isAdditional: boolean): void { this.commit({ 'total': isAdditional ? 1 : -1, 'inc': isAdditional ? 1 : 0, diff --git a/packages/backend/src/core/chart/charts/per-user-pv.ts b/packages/backend/src/core/chart/charts/per-user-pv.ts index 53c89d8a9a..cf1b4c71f6 100644 --- a/packages/backend/src/core/chart/charts/per-user-pv.ts +++ b/packages/backend/src/core/chart/charts/per-user-pv.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -12,9 +17,8 @@ import type { KVs } from '../core.js'; /** * ユーザーごとのプロフィール被閲覧数に関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class PerUserPvChart extends Chart<typeof schema> { +export default class PerUserPvChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -34,7 +38,7 @@ export default class PerUserPvChart extends Chart<typeof schema> { } @bindThis - public async commitByUser(user: { id: User['id'] }, key: string): Promise<void> { + public async commitByUser(user: { id: MiUser['id'] }, key: string): Promise<void> { await this.commit({ 'upv.user': [key], 'pv.user': 1, @@ -42,7 +46,7 @@ export default class PerUserPvChart extends Chart<typeof schema> { } @bindThis - public async commitByVisitor(user: { id: User['id'] }, key: string): Promise<void> { + public async commitByVisitor(user: { id: MiUser['id'] }, key: string): Promise<void> { await this.commit({ 'upv.visitor': [key], 'pv.visitor': 1, diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts index 7bc6d4b521..9f4f6e9651 100644 --- a/packages/backend/src/core/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -14,9 +19,8 @@ import type { KVs } from '../core.js'; /** * ユーザーごとのリアクションに関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class PerUserReactionsChart extends Chart<typeof schema> { +export default class PerUserReactionsChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -37,7 +41,7 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { } @bindThis - public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise<void> { + public async update(user: { id: MiUser['id'], host: MiUser['host'] }, note: MiNote): Promise<void> { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; this.commit({ [`${prefix}.count`]: 1, diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts index 128967bc65..00fb872237 100644 --- a/packages/backend/src/core/chart/charts/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/test-grouped.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; @@ -11,9 +16,8 @@ import type { KVs } from '../core.js'; /** * For testing */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class TestGroupedChart extends Chart<typeof schema> { +export default class TestGroupedChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export private total = {} as Record<string, number>; constructor( diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts index 6b4eed9062..45a7e805c5 100644 --- a/packages/backend/src/core/chart/charts/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/test-intersection.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; @@ -11,9 +16,8 @@ import type { KVs } from '../core.js'; /** * For testing */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class TestIntersectionChart extends Chart<typeof schema> { +export default class TestIntersectionChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts index 5d2b3f8ab1..e9d38eaf13 100644 --- a/packages/backend/src/core/chart/charts/test-unique.ts +++ b/packages/backend/src/core/chart/charts/test-unique.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; @@ -11,9 +16,8 @@ import type { KVs } from '../core.js'; /** * For testing */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class TestUniqueChart extends Chart<typeof schema> { +export default class TestUniqueChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts index 238351d8b3..4dd6063b5b 100644 --- a/packages/backend/src/core/chart/charts/test.ts +++ b/packages/backend/src/core/chart/charts/test.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; @@ -11,9 +16,8 @@ import type { KVs } from '../core.js'; /** * For testing */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class TestChart extends Chart<typeof schema> { +export default class TestChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export public total = 0; // publicにするのはテストのため constructor( diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index 7bc3602439..c2026c2aea 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull, DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; @@ -14,9 +19,8 @@ import type { KVs } from '../core.js'; /** * ユーザー数に関するチャート */ -// eslint-disable-next-line import/no-default-export @Injectable() -export default class UsersChart extends Chart<typeof schema> { +export default class UsersChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, @@ -48,7 +52,7 @@ export default class UsersChart extends Chart<typeof schema> { } @bindThis - public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> { + public async update(user: { id: MiUser['id'], host: MiUser['host'] }, isAdditional: boolean): Promise<void> { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; await this.commit({ diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 7a89233eda..8d0a89f2d6 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + /** * チャートエンジン * diff --git a/packages/backend/src/core/chart/entities.ts b/packages/backend/src/core/chart/entities.ts index b44e2e38b7..b6a1299a2f 100644 --- a/packages/backend/src/core/chart/entities.ts +++ b/packages/backend/src/core/chart/entities.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { entity as FederationChart } from './charts/entities/federation.js'; import { entity as NotesChart } from './charts/entities/notes.js'; import { entity as UsersChart } from './charts/entities/users.js'; diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 94ae1856b9..0e65a10d26 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AbuseUserReportsRepository } from '@/models/index.js'; +import type { AbuseUserReportsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; +import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -18,7 +23,7 @@ export class AbuseUserReportEntityService { @bindThis public async pack( - src: AbuseUserReport['id'] | AbuseUserReport, + src: MiAbuseUserReport['id'] | MiAbuseUserReport, ) { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index 328511f5df..ed108f2ce5 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { Antenna } from '@/models/entities/Antenna.js'; +import type { MiAntenna } from '@/models/Antenna.js'; import { bindThis } from '@/decorators.js'; @Injectable() @@ -15,7 +20,7 @@ export class AntennaEntityService { @bindThis public async pack( - src: Antenna['id'] | Antenna, + src: MiAntenna['id'] | MiAntenna, ): Promise<Packed<'Antenna'>> { const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 0b4c3935c7..14a93cda5b 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, AppsRepository } from '@/models/index.js'; +import type { AccessTokensRepository, AppsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { App } from '@/models/entities/App.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiApp } from '@/models/App.js'; +import type { MiUser } from '@/models/User.js'; import { bindThis } from '@/decorators.js'; @Injectable() @@ -19,8 +24,8 @@ export class AppEntityService { @bindThis public async pack( - src: App['id'] | App, - me?: { id: User['id'] } | null | undefined, + src: MiApp['id'] | MiApp, + me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: boolean, includeSecret?: boolean, diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index a1874c63ab..fd356cc89d 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AuthSessionsRepository } from '@/models/index.js'; +import type { AuthSessionsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { AuthSession } from '@/models/entities/AuthSession.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiAuthSession } from '@/models/AuthSession.js'; +import type { MiUser } from '@/models/User.js'; import { bindThis } from '@/decorators.js'; import { AppEntityService } from './AppEntityService.js'; @@ -19,8 +24,8 @@ export class AuthSessionEntityService { @bindThis public async pack( - src: AuthSession['id'] | AuthSession, - me?: { id: User['id'] } | null | undefined, + src: MiAuthSession['id'] | MiAuthSession, + me?: { id: MiUser['id'] } | null | undefined, ) { const session = typeof src === 'object' ? src : await this.authSessionsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index e169c7e90a..44466e24e8 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { BlockingsRepository } from '@/models/index.js'; +import type { BlockingsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { Blocking } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiBlocking } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -20,8 +25,8 @@ export class BlockingEntityService { @bindThis public async pack( - src: Blocking['id'] | Blocking, - me?: { id: User['id'] } | null | undefined, + src: MiBlocking['id'] | MiBlocking, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'Blocking'>> { const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src }); @@ -38,7 +43,7 @@ export class BlockingEntityService { @bindThis public packMany( blockings: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(blockings.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index de99ce72c4..094de4d2d5 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { ChannelFavoritesRepository, ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository, NotesRepository } from '@/models/index.js'; +import type { ChannelFavoritesRepository, ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository, NotesRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { Channel } from '@/models/entities/Channel.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiChannel } from '@/models/Channel.js'; import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; @@ -38,8 +43,8 @@ export class ChannelEntityService { @bindThis public async pack( - src: Channel['id'] | Channel, - me?: { id: User['id'] } | null | undefined, + src: MiChannel['id'] | MiChannel, + me?: { id: MiUser['id'] } | null | undefined, detailed?: boolean, ): Promise<Packed<'Channel'>> { const channel = typeof src === 'object' ? src : await this.channelsRepository.findOneByOrFail({ id: src }); @@ -87,6 +92,7 @@ export class ChannelEntityService { isArchived: channel.isArchived, usersCount: channel.usersCount, notesCount: channel.notesCount, + isSensitive: channel.isSensitive, ...(me ? { isFollowing, diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index f558cbc33d..e141db03f1 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { ClipFavoritesRepository, ClipsRepository, User } from '@/models/index.js'; +import type { ClipFavoritesRepository, ClipsRepository, MiUser } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { Clip } from '@/models/entities/Clip.js'; +import type { } from '@/models/Blocking.js'; +import type { MiClip } from '@/models/Clip.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -23,8 +28,8 @@ export class ClipEntityService { @bindThis public async pack( - src: Clip['id'] | Clip, - me?: { id: User['id'] } | null | undefined, + src: MiClip['id'] | MiClip, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'Clip'>> { const meId = me ? me.id : null; const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src }); @@ -45,8 +50,8 @@ export class ClipEntityService { @bindThis public packMany( - clips: Clip[], - me?: { id: User['id'] } | null | undefined, + clips: MiClip[], + me?: { id: MiUser['id'] } | null | undefined, ) { return Promise.all(clips.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 80442af09b..23273b0413 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -1,14 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { DataSource, In } from 'typeorm'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import type { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { User } from '@/models/entities/User.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { appendQuery, query } from '@/misc/prelude/url.js'; import { deepClone } from '@/misc/clone.js'; +import { bindThis } from '@/decorators.js'; +import { isMimeImage } from '@/misc/is-mime-image.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { UtilityService } from '../UtilityService.js'; import { VideoProcessingService } from '../VideoProcessingService.js'; import { UserEntityService } from './UserEntityService.js'; @@ -19,9 +27,6 @@ type PackOptions = { self?: boolean, withUser?: boolean, }; -import { bindThis } from '@/decorators.js'; -import { isMimeImage } from '@/misc/is-mime-image.js'; -import { isNotNull } from '@/misc/is-not-null.js'; @Injectable() export class DriveFileEntityService { @@ -29,12 +34,6 @@ export class DriveFileEntityService { @Inject(DI.config) private config: Config, - @Inject(DI.db) - private db: DataSource, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -60,7 +59,7 @@ export class DriveFileEntityService { } @bindThis - public getPublicProperties(file: DriveFile): DriveFile['properties'] { + public getPublicProperties(file: MiDriveFile): MiDriveFile['properties'] { if (file.properties.orientation != null) { const properties = deepClone(file.properties); if (file.properties.orientation >= 5) { @@ -85,7 +84,7 @@ export class DriveFileEntityService { } @bindThis - public getThumbnailUrl(file: DriveFile): string | null { + public getThumbnailUrl(file: MiDriveFile): string | null { if (file.type.startsWith('video')) { if (file.thumbnailUrl) return file.thumbnailUrl; @@ -108,7 +107,7 @@ export class DriveFileEntityService { } @bindThis - public getPublicUrl(file: DriveFile, mode?: 'avatar'): string { // static = thumbnail + public getPublicUrl(file: MiDriveFile, mode?: 'avatar'): string { // static = thumbnail // リモートかつメディアプロキシ if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) { return this.getProxiedUrl(file.uri, mode); @@ -134,7 +133,7 @@ export class DriveFileEntityService { } @bindThis - public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> { + public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise<number> { const id = typeof user === 'object' ? user.id : user; const { sum } = await this.driveFilesRepository @@ -185,7 +184,7 @@ export class DriveFileEntityService { @bindThis public async pack( - src: DriveFile['id'] | DriveFile, + src: MiDriveFile['id'] | MiDriveFile, options?: PackOptions, ): Promise<Packed<'DriveFile'>> { const opts = Object.assign({ @@ -219,7 +218,7 @@ export class DriveFileEntityService { @bindThis public async packNullable( - src: DriveFile['id'] | DriveFile, + src: MiDriveFile['id'] | MiDriveFile, options?: PackOptions, ): Promise<Packed<'DriveFile'> | null> { const opts = Object.assign({ @@ -254,7 +253,7 @@ export class DriveFileEntityService { @bindThis public async packMany( - files: DriveFile[], + files: MiDriveFile[], options?: PackOptions, ): Promise<Packed<'DriveFile'>[]> { const items = await Promise.all(files.map(f => this.packNullable(f, options))); @@ -263,7 +262,7 @@ export class DriveFileEntityService { @bindThis public async packManyByIdsMap( - fileIds: DriveFile['id'][], + fileIds: MiDriveFile['id'][], options?: PackOptions, ): Promise<Map<Packed<'DriveFile'>['id'], Packed<'DriveFile'> | null>> { if (fileIds.length === 0) return new Map(); @@ -278,7 +277,7 @@ export class DriveFileEntityService { @bindThis public async packManyByIds( - fileIds: DriveFile['id'][], + fileIds: MiDriveFile['id'][], options?: PackOptions, ): Promise<Packed<'DriveFile'>[]> { if (fileIds.length === 0) return []; diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 13929b145f..55014284bd 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { DriveFolder } from '@/models/entities/DriveFolder.js'; +import type { } from '@/models/Blocking.js'; +import type { MiDriveFolder } from '@/models/DriveFolder.js'; import { bindThis } from '@/decorators.js'; @Injectable() @@ -20,7 +25,7 @@ export class DriveFolderEntityService { @bindThis public async pack( - src: DriveFolder['id'] | DriveFolder, + src: MiDriveFolder['id'] | MiDriveFolder, options?: { detail: boolean }, diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 4a18cd1b3b..5b97cfad5e 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; +import type { } from '@/models/Blocking.js'; +import type { MiEmoji } from '@/models/Emoji.js'; import { bindThis } from '@/decorators.js'; @Injectable() @@ -16,7 +21,7 @@ export class EmojiEntityService { @bindThis public async packSimple( - src: Emoji['id'] | Emoji, + src: MiEmoji['id'] | MiEmoji, ): Promise<Packed<'EmojiSimple'>> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); @@ -40,7 +45,7 @@ export class EmojiEntityService { @bindThis public async packDetailed( - src: Emoji['id'] | Emoji, + src: MiEmoji['id'] | MiEmoji, ): Promise<Packed<'EmojiDetailed'>> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts index 92345457c9..4701cddcba 100644 --- a/packages/backend/src/core/entities/FlashEntityService.ts +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { Flash } from '@/models/entities/Flash.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiFlash } from '@/models/Flash.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -24,8 +29,8 @@ export class FlashEntityService { @bindThis public async pack( - src: Flash['id'] | Flash, - me?: { id: User['id'] } | null | undefined, + src: MiFlash['id'] | MiFlash, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'Flash'>> { const meId = me ? me.id : null; const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src }); @@ -46,8 +51,8 @@ export class FlashEntityService { @bindThis public packMany( - flashs: Flash[], - me?: { id: User['id'] } | null | undefined, + flashs: MiFlash[], + me?: { id: MiUser['id'] } | null | undefined, ) { return Promise.all(flashs.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts index 0351ec3014..2eff86217a 100644 --- a/packages/backend/src/core/entities/FlashLikeEntityService.ts +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { FlashLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { FlashLike } from '@/models/entities/FlashLike.js'; +import type { FlashLikesRepository } from '@/models/_.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiFlashLike } from '@/models/FlashLike.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from './FlashEntityService.js'; @@ -19,8 +24,8 @@ export class FlashLikeEntityService { @bindThis public async pack( - src: FlashLike['id'] | FlashLike, - me?: { id: User['id'] } | null | undefined, + src: MiFlashLike['id'] | MiFlashLike, + me?: { id: MiUser['id'] } | null | undefined, ) { const like = typeof src === 'object' ? src : await this.flashLikesRepository.findOneByOrFail({ id: src }); @@ -33,7 +38,7 @@ export class FlashLikeEntityService { @bindThis public packMany( likes: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(likes.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 6f6f4be412..0e0fec9f46 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { FollowRequestsRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { FollowRequest } from '@/models/entities/FollowRequest.js'; +import type { FollowRequestsRepository } from '@/models/_.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiFollowRequest } from '@/models/FollowRequest.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -19,8 +24,8 @@ export class FollowRequestEntityService { @bindThis public async pack( - src: FollowRequest['id'] | FollowRequest, - me?: { id: User['id'] } | null | undefined, + src: MiFollowRequest['id'] | MiFollowRequest, + me?: { id: MiUser['id'] } | null | undefined, ) { const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index 55ba4e67ad..9f6eb51e8c 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -1,33 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { Following } from '@/models/entities/Following.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiFollowing } from '@/models/Following.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; -type LocalFollowerFollowing = Following & { +type LocalFollowerFollowing = MiFollowing & { followerHost: null; followerInbox: null; followerSharedInbox: null; }; -type RemoteFollowerFollowing = Following & { +type RemoteFollowerFollowing = MiFollowing & { followerHost: string; followerInbox: string; followerSharedInbox: string; }; -type LocalFolloweeFollowing = Following & { +type LocalFolloweeFollowing = MiFollowing & { followeeHost: null; followeeInbox: null; followeeSharedInbox: null; }; -type RemoteFolloweeFollowing = Following & { +type RemoteFolloweeFollowing = MiFollowing & { followeeHost: string; followeeInbox: string; followeeSharedInbox: string; @@ -44,29 +49,29 @@ export class FollowingEntityService { } @bindThis - public isLocalFollower(following: Following): following is LocalFollowerFollowing { + public isLocalFollower(following: MiFollowing): following is LocalFollowerFollowing { return following.followerHost == null; } @bindThis - public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { + public isRemoteFollower(following: MiFollowing): following is RemoteFollowerFollowing { return following.followerHost != null; } @bindThis - public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { + public isLocalFollowee(following: MiFollowing): following is LocalFolloweeFollowing { return following.followeeHost == null; } @bindThis - public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { + public isRemoteFollowee(following: MiFollowing): following is RemoteFolloweeFollowing { return following.followeeHost != null; } @bindThis public async pack( - src: Following['id'] | Following, - me?: { id: User['id'] } | null | undefined, + src: MiFollowing['id'] | MiFollowing, + me?: { id: MiUser['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; @@ -93,7 +98,7 @@ export class FollowingEntityService { @bindThis public packMany( followings: any[], - me?: { id: User['id'] } | null | undefined, + me?: { id: MiUser['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 73c264da94..e740701888 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { GalleryLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { GalleryLike } from '@/models/entities/GalleryLike.js'; +import type { GalleryLikesRepository } from '@/models/_.js'; +import type { } from '@/models/Blocking.js'; +import type { MiGalleryLike } from '@/models/GalleryLike.js'; import { bindThis } from '@/decorators.js'; import { GalleryPostEntityService } from './GalleryPostEntityService.js'; @@ -18,7 +23,7 @@ export class GalleryLikeEntityService { @bindThis public async pack( - src: GalleryLike['id'] | GalleryLike, + src: MiGalleryLike['id'] | MiGalleryLike, me?: any, ) { const like = typeof src === 'object' ? src : await this.galleryLikesRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index c44a5df118..bbaf70f0fd 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { GalleryPost } from '@/models/entities/GalleryPost.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiGalleryPost } from '@/models/GalleryPost.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -26,8 +31,8 @@ export class GalleryPostEntityService { @bindThis public async pack( - src: GalleryPost['id'] | GalleryPost, - me?: { id: User['id'] } | null | undefined, + src: MiGalleryPost['id'] | MiGalleryPost, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'GalleryPost'>> { const meId = me ? me.id : null; const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src }); @@ -52,8 +57,8 @@ export class GalleryPostEntityService { @bindThis public packMany( - posts: GalleryPost[], - me?: { id: User['id'] } | null | undefined, + posts: MiGalleryPost[], + me?: { id: MiUser['id'] } | null | undefined, ) { return Promise.all(posts.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 2cd79b8f8c..006e267b12 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -1,25 +1,23 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { HashtagsRepository } from '@/models/index.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { Hashtag } from '@/models/entities/Hashtag.js'; +import type { } from '@/models/Blocking.js'; +import type { MiHashtag } from '@/models/Hashtag.js'; import { bindThis } from '@/decorators.js'; -import { UserEntityService } from './UserEntityService.js'; @Injectable() export class HashtagEntityService { constructor( - @Inject(DI.hashtagsRepository) - private hashtagsRepository: HashtagsRepository, - - private userEntityService: UserEntityService, ) { } @bindThis public async pack( - src: Hashtag, + src: MiHashtag, ): Promise<Packed<'Hashtag'>> { return { tag: src.name, @@ -34,7 +32,7 @@ export class HashtagEntityService { @bindThis public packMany( - hashtags: Hashtag[], + hashtags: MiHashtag[], ) { return Promise.all(hashtags.map(x => this.pack(x))); } diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 3bf84ed375..0e27e9df7f 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -1,9 +1,12 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { InstancesRepository } from '@/models/index.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { Instance } from '@/models/entities/Instance.js'; +import type { } from '@/models/Blocking.js'; +import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; import { UtilityService } from '../UtilityService.js'; @@ -11,9 +14,6 @@ import { UtilityService } from '../UtilityService.js'; @Injectable() export class InstanceEntityService { constructor( - @Inject(DI.instancesRepository) - private instancesRepository: InstancesRepository, - private metaService: MetaService, private utilityService: UtilityService, @@ -22,7 +22,7 @@ export class InstanceEntityService { @bindThis public async pack( - instance: Instance, + instance: MiInstance, ): Promise<Packed<'FederationInstance'>> { const meta = await this.metaService.fetch(); return { @@ -52,7 +52,7 @@ export class InstanceEntityService { @bindThis public packMany( - instances: Instance[], + instances: MiInstance[], ) { return Promise.all(instances.map(x => this.pack(x))); } diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts index 2d8e7a4681..914eaafe68 100644 --- a/packages/backend/src/core/entities/InviteCodeEntityService.ts +++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { User } from '@/models/entities/User.js'; -import type { RegistrationTicket } from '@/models/entities/RegistrationTicket.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -20,8 +25,8 @@ export class InviteCodeEntityService { @bindThis public async pack( - src: RegistrationTicket['id'] | RegistrationTicket, - me?: { id: User['id'] } | null | undefined, + src: MiRegistrationTicket['id'] | MiRegistrationTicket, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'InviteCode'>> { const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({ where: { @@ -45,7 +50,7 @@ export class InviteCodeEntityService { @bindThis public packMany( targets: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(targets.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 59815d2639..83b024d83b 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { ModerationLogsRepository } from '@/models/index.js'; +import type { ModerationLogsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { ModerationLog } from '@/models/entities/ModerationLog.js'; +import type { } from '@/models/Blocking.js'; +import type { MiModerationLog } from '@/models/ModerationLog.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -19,7 +24,7 @@ export class ModerationLogEntityService { @bindThis public async pack( - src: ModerationLog['id'] | ModerationLog, + src: MiModerationLog['id'] | MiModerationLog, ) { const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index 561d53292e..e3d5d2e211 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { Muting } from '@/models/entities/Muting.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiMuting } from '@/models/Muting.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -21,8 +26,8 @@ export class MutingEntityService { @bindThis public async pack( - src: Muting['id'] | Muting, - me?: { id: User['id'] } | null | undefined, + src: MiMuting['id'] | MiMuting, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'Muting'>> { const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src }); @@ -40,7 +45,7 @@ export class MutingEntityService { @bindThis public packMany( mutings: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(mutings.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 546e5f56d2..bf42e98ce0 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -1,15 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import { DataSource, In } from 'typeorm'; +import { In } from 'typeorm'; import * as mfm from 'mfm-js'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { Packed } from '@/misc/json-schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiNoteReaction } from '@/models/NoteReaction.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -28,9 +33,6 @@ export class NoteEntityService implements OnModuleInit { constructor( private moduleRef: ModuleRef, - @Inject(DI.db) - private db: DataSource, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -52,9 +54,6 @@ export class NoteEntityService implements OnModuleInit { @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - //private userEntityService: UserEntityService, //private driveFileEntityService: DriveFileEntityService, //private customEmojiService: CustomEmojiService, @@ -70,7 +69,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { + private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null) { // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) let hide = false; @@ -129,7 +128,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - private async populatePoll(note: Note, meId: User['id'] | null) { + private async populatePoll(note: MiNote, meId: MiUser['id'] | null) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); const choices = poll.choices.map(c => ({ text: c, @@ -168,8 +167,8 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - private async populateMyReaction(note: Note, meId: User['id'], _hint_?: { - myReactions: Map<Note['id'], NoteReaction | null>; + private async populateMyReaction(note: MiNote, meId: MiUser['id'], _hint_?: { + myReactions: Map<MiNote['id'], MiNoteReaction | null>; }) { if (_hint_?.myReactions) { const reaction = _hint_.myReactions.get(note.id); @@ -199,7 +198,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> { + public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null): Promise<boolean> { // This code must always be synchronized with the checks in generateVisibilityQuery. // visibility が specified かつ自分が指定されていなかったら非表示 if (note.visibility === 'specified') { @@ -253,7 +252,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public async packAttachedFiles(fileIds: Note['fileIds'], packedFiles: Map<Note['fileIds'][number], Packed<'DriveFile'> | null>): Promise<Packed<'DriveFile'>[]> { + public async packAttachedFiles(fileIds: MiNote['fileIds'], packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>): Promise<Packed<'DriveFile'>[]> { const missingIds = []; for (const id of fileIds) { if (!packedFiles.has(id)) missingIds.push(id); @@ -269,14 +268,14 @@ export class NoteEntityService implements OnModuleInit { @bindThis public async pack( - src: Note['id'] | Note, - me?: { id: User['id'] } | null | undefined, + src: MiNote['id'] | MiNote, + me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: boolean; skipHide?: boolean; _hint_?: { - myReactions: Map<Note['id'], NoteReaction | null>; - packedFiles: Map<Note['fileIds'][number], Packed<'DriveFile'> | null>; + myReactions: Map<MiNote['id'], MiNoteReaction | null>; + packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>; }; }, ): Promise<Packed<'Note'>> { @@ -334,12 +333,15 @@ export class NoteEntityService implements OnModuleInit { id: channel.id, name: channel.name, color: channel.color, + isSensitive: channel.isSensitive, } : undefined, mentions: note.mentions.length > 0 ? note.mentions : undefined, uri: note.uri ?? undefined, url: note.url ?? undefined, ...(opts.detail ? { + clippedCount: note.clippedCount, + reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { detail: false, _hint_: options?._hint_, @@ -386,8 +388,8 @@ export class NoteEntityService implements OnModuleInit { @bindThis public async packMany( - notes: Note[], - me?: { id: User['id'] } | null | undefined, + notes: MiNote[], + me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: boolean; skipHide?: boolean; @@ -396,7 +398,7 @@ export class NoteEntityService implements OnModuleInit { if (notes.length === 0) return []; const meId = me ? me.id : null; - const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); + const myReactionsMap = new Map<MiNote['id'], MiNoteReaction | null>(); if (meId) { const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); // パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない @@ -426,7 +428,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public aggregateNoteEmojis(notes: Note[]) { + public aggregateNoteEmojis(notes: MiNote[]) { let emojis: { name: string | null; host: string | null; }[] = []; for (const note of notes) { emojis = emojis.concat(note.emojis diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index badb8fb816..808c8c9f69 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NoteFavoritesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { NoteFavorite } from '@/models/entities/NoteFavorite.js'; +import type { NoteFavoritesRepository } from '@/models/_.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNoteFavorite } from '@/models/NoteFavorite.js'; import { bindThis } from '@/decorators.js'; import { NoteEntityService } from './NoteEntityService.js'; @@ -19,8 +24,8 @@ export class NoteFavoriteEntityService { @bindThis public async pack( - src: NoteFavorite['id'] | NoteFavorite, - me?: { id: User['id'] } | null | undefined, + src: MiNoteFavorite['id'] | MiNoteFavorite, + me?: { id: MiUser['id'] } | null | undefined, ) { const favorite = typeof src === 'object' ? src : await this.noteFavoritesRepository.findOneByOrFail({ id: src }); @@ -35,7 +40,7 @@ export class NoteFavoriteEntityService { @bindThis public packMany( favorites: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(favorites.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index d454ddb70a..9701f37fdb 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -1,12 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NoteReactionsRepository } from '@/models/index.js'; +import type { NoteReactionsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import type { OnModuleInit } from '@nestjs/common'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNoteReaction } from '@/models/NoteReaction.js'; import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; @@ -38,8 +43,8 @@ export class NoteReactionEntityService implements OnModuleInit { @bindThis public async pack( - src: NoteReaction['id'] | NoteReaction, - me?: { id: User['id'] } | null | undefined, + src: MiNoteReaction['id'] | MiNoteReaction, + me?: { id: MiUser['id'] } | null | undefined, options?: { withNote: boolean; }, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 02c6982847..3ee7c91f3a 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, FollowRequestsRepository, NoteReactionsRepository, NotesRepository, User, UsersRepository } from '@/models/index.js'; +import type { AccessTokensRepository, FollowRequestsRepository, NotesRepository, MiUser, UsersRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Notification } from '@/models/entities/Notification.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNotification } from '@/models/Notification.js'; +import type { MiNote } from '@/models/Note.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; @@ -15,7 +20,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); +const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); @Injectable() export class NotificationEntityService implements OnModuleInit { @@ -32,9 +37,6 @@ export class NotificationEntityService implements OnModuleInit { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.noteReactionsRepository) - private noteReactionsRepository: NoteReactionsRepository, - @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, @@ -55,15 +57,15 @@ export class NotificationEntityService implements OnModuleInit { @bindThis public async pack( - src: Notification, - meId: User['id'], + src: MiNotification, + meId: MiUser['id'], // eslint-disable-next-line @typescript-eslint/ban-types options: { }, hint?: { - packedNotes: Map<Note['id'], Packed<'Note'>>; - packedUsers: Map<User['id'], Packed<'User'>>; + packedNotes: Map<MiNote['id'], Packed<'Note'>>; + packedUsers: Map<MiUser['id'], Packed<'User'>>; }, ): Promise<Packed<'Notification'>> { const notification = src; @@ -106,8 +108,8 @@ export class NotificationEntityService implements OnModuleInit { @bindThis public async packMany( - notifications: Notification[], - meId: User['id'], + notifications: MiNotification[], + meId: MiUser['id'], ) { if (notifications.length === 0) return []; diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index 94b26a5017..e3a1e19ddd 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -1,12 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { Page } from '@/models/entities/Page.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiPage } from '@/models/Page.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -30,13 +35,13 @@ export class PageEntityService { @bindThis public async pack( - src: Page['id'] | Page, - me?: { id: User['id'] } | null | undefined, + src: MiPage['id'] | MiPage, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'Page'>> { const meId = me ? me.id : null; const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src }); - const attachedFiles: Promise<DriveFile | null>[] = []; + const attachedFiles: Promise<MiDriveFile | null>[] = []; const collectFile = (xs: any[]) => { for (const x of xs) { if (x.type === 'image') { @@ -95,7 +100,7 @@ export class PageEntityService { script: page.script, eyeCatchingImageId: page.eyeCatchingImageId, eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, - attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), + attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)), likedCount: page.likedCount, isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined, }); @@ -103,8 +108,8 @@ export class PageEntityService { @bindThis public packMany( - pages: Page[], - me?: { id: User['id'] } | null | undefined, + pages: MiPage[], + me?: { id: MiUser['id'] } | null | undefined, ) { return Promise.all(pages.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index 99465d0ea3..4dc691ab93 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { PageLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { PageLike } from '@/models/entities/PageLike.js'; +import type { PageLikesRepository } from '@/models/_.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiPageLike } from '@/models/PageLike.js'; import { bindThis } from '@/decorators.js'; import { PageEntityService } from './PageEntityService.js'; @@ -19,8 +24,8 @@ export class PageLikeEntityService { @bindThis public async pack( - src: PageLike['id'] | PageLike, - me?: { id: User['id'] } | null | undefined, + src: MiPageLike['id'] | MiPageLike, + me?: { id: MiUser['id'] } | null | undefined, ) { const like = typeof src === 'object' ? src : await this.pageLikesRepository.findOneByOrFail({ id: src }); @@ -33,7 +38,7 @@ export class PageLikeEntityService { @bindThis public packMany( likes: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(likes.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts index f8871e0495..7111fab08a 100644 --- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts +++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { RenoteMutingsRepository } from '@/models/index.js'; +import type { RenoteMutingsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { RenoteMuting } from '@/models/entities/RenoteMuting.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -21,8 +26,8 @@ export class RenoteMutingEntityService { @bindThis public async pack( - src: RenoteMuting['id'] | RenoteMuting, - me?: { id: User['id'] } | null | undefined, + src: MiRenoteMuting['id'] | MiRenoteMuting, + me?: { id: MiUser['id'] } | null | undefined, ): Promise<Packed<'RenoteMuting'>> { const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src }); @@ -39,7 +44,7 @@ export class RenoteMutingEntityService { @bindThis public packMany( mutings: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(mutings.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 54818782dd..23e82561d6 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -1,13 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { User } from '@/models/entities/User.js'; -import type { Role } from '@/models/entities/Role.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiRole } from '@/models/Role.js'; import { bindThis } from '@/decorators.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import { UserEntityService } from './UserEntityService.js'; @Injectable() export class RoleEntityService { @@ -17,15 +21,13 @@ export class RoleEntityService { @Inject(DI.roleAssignmentsRepository) private roleAssignmentsRepository: RoleAssignmentsRepository, - - private userEntityService: UserEntityService, ) { } @bindThis public async pack( - src: Role['id'] | Role, - me?: { id: User['id'] } | null | undefined, + src: MiRole['id'] | MiRole, + me?: { id: MiUser['id'] } | null | undefined, ) { const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src }); @@ -71,7 +73,7 @@ export class RoleEntityService { @bindThis public packMany( roles: any[], - me: { id: User['id'] }, + me: { id: MiUser['id'] }, ) { return Promise.all(roles.map(x => this.pack(x, me))); } diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index a6c4407ce8..8c88e8560a 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -1,24 +1,22 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { SigninsRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { Signin } from '@/models/entities/Signin.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import type { } from '@/models/Blocking.js'; +import type { MiSignin } from '@/models/Signin.js'; import { bindThis } from '@/decorators.js'; -import { UserEntityService } from './UserEntityService.js'; @Injectable() export class SigninEntityService { constructor( - @Inject(DI.signinsRepository) - private signinsRepository: SigninsRepository, - - private userEntityService: UserEntityService, ) { } @bindThis public async pack( - src: Signin, + src: MiSignin, ) { return src; } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 7d248f8524..3dd64ce625 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -1,5 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import { In, Not } from 'typeorm'; import * as Redis from 'ioredis'; import _Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; @@ -9,15 +13,15 @@ import type { Packed } from '@/misc/json-schema.js'; import type { Promiseable } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; -import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; -import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; -import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, PagesRepository, UserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/index.js'; +import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; +import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/User.js'; +import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, UserNotePiningsRepository, UserProfilesRepository, AnnouncementReadsRepository, AnnouncementsRepository, MiUserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import type { OnModuleInit } from '@nestjs/common'; -import type { AntennaService } from '../AntennaService.js'; +import type { AnnouncementService } from '../AnnouncementService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -34,15 +38,15 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo const Ajv = _Ajv.default; const ajv = new Ajv(); -function isLocalUser(user: User): user is LocalUser; -function isLocalUser<T extends { host: User['host'] }>(user: T): user is (T & { host: null; }); -function isLocalUser(user: User | { host: User['host'] }): boolean { +function isLocalUser(user: MiUser): user is MiLocalUser; +function isLocalUser<T extends { host: MiUser['host'] }>(user: T): user is (T & { host: null; }); +function isLocalUser(user: MiUser | { host: MiUser['host'] }): boolean { return user.host == null; } -function isRemoteUser(user: User): user is RemoteUser; -function isRemoteUser<T extends { host: User['host'] }>(user: T): user is (T & { host: string; }); -function isRemoteUser(user: User | { host: User['host'] }): boolean { +function isRemoteUser(user: MiUser): user is MiRemoteUser; +function isRemoteUser<T extends { host: MiUser['host'] }>(user: T): user is (T & { host: string; }); +function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean { return !isLocalUser(user); } @@ -53,7 +57,7 @@ export class UserEntityService implements OnModuleInit { private driveFileEntityService: DriveFileEntityService; private pageEntityService: PageEntityService; private customEmojiService: CustomEmojiService; - private antennaService: AntennaService; + private announcementService: AnnouncementService; private roleService: RoleService; private federatedInstanceService: FederatedInstanceService; @@ -93,27 +97,18 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: NoteUnreadsRepository, - @Inject(DI.channelFollowingsRepository) - private channelFollowingsRepository: ChannelFollowingsRepository, - @Inject(DI.userNotePiningsRepository) private userNotePiningsRepository: UserNotePiningsRepository, @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - @Inject(DI.instancesRepository) - private instancesRepository: InstancesRepository, - @Inject(DI.announcementReadsRepository) private announcementReadsRepository: AnnouncementReadsRepository, @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, - @Inject(DI.pagesRepository) - private pagesRepository: PagesRepository, - @Inject(DI.userMemosRepository) private userMemosRepository: UserMemoRepository, @@ -132,7 +127,7 @@ export class UserEntityService implements OnModuleInit { this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.pageEntityService = this.moduleRef.get('PageEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); - this.antennaService = this.moduleRef.get('AntennaService'); + this.announcementService = this.moduleRef.get('AnnouncementService'); this.roleService = this.moduleRef.get('RoleService'); this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService'); } @@ -150,16 +145,15 @@ export class UserEntityService implements OnModuleInit { public isRemoteUser = isRemoteUser; @bindThis - public async getRelation(me: User['id'], target: User['id']) { + public async getRelation(me: MiUser['id'], target: MiUser['id']) { + const following = await this.followingsRepository.findOneBy({ + followerId: me, + followeeId: target, + }); return awaitAll({ id: target, - isFollowing: this.followingsRepository.count({ - where: { - followerId: me, - followeeId: target, - }, - take: 1, - }).then(n => n > 0), + following, + isFollowing: following != null, isFollowed: this.followingsRepository.count({ where: { followerId: target, @@ -213,20 +207,7 @@ export class UserEntityService implements OnModuleInit { } @bindThis - public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> { - const reads = await this.announcementReadsRepository.findBy({ - userId: userId, - }); - - const count = await this.announcementsRepository.countBy(reads.length > 0 ? { - id: Not(In(reads.map(read => read.announcementId))), - } : {}); - - return count > 0; - } - - @bindThis - public async getHasUnreadAntenna(userId: User['id']): Promise<boolean> { + public async getHasUnreadAntenna(userId: MiUser['id']): Promise<boolean> { /* const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); @@ -243,7 +224,7 @@ export class UserEntityService implements OnModuleInit { } @bindThis - public async getHasUnreadNotification(userId: User['id']): Promise<boolean> { + public async getHasUnreadNotification(userId: MiUser['id']): Promise<boolean> { const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`); const latestNotificationIdsRes = await this.redisClient.xrevrange( @@ -257,7 +238,7 @@ export class UserEntityService implements OnModuleInit { } @bindThis - public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> { + public async getHasPendingReceivedFollowRequest(userId: MiUser['id']): Promise<boolean> { const count = await this.followRequestsRepository.countBy({ followeeId: userId, }); @@ -266,7 +247,7 @@ export class UserEntityService implements OnModuleInit { } @bindThis - public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { + public getOnlineStatus(user: MiUser): 'unknown' | 'online' | 'active' | 'offline' { if (user.hideOnlineStatus) return 'unknown'; if (user.lastActiveDate == null) return 'unknown'; const elapsed = Date.now() - user.lastActiveDate.getTime(); @@ -278,12 +259,12 @@ export class UserEntityService implements OnModuleInit { } @bindThis - public getIdenticonUrl(user: User): string { + public getIdenticonUrl(user: MiUser): string { return `${this.config.url}/identicon/${user.username.toLowerCase()}@${user.host ?? this.config.host}`; } @bindThis - public getUserUri(user: LocalUser | PartialLocalUser | RemoteUser | PartialRemoteUser): string { + public getUserUri(user: MiLocalUser | MiPartialLocalUser | MiRemoteUser | MiPartialRemoteUser): string { return this.isRemoteUser(user) ? user.uri : this.genLocalUserUri(user.id); } @@ -294,12 +275,12 @@ export class UserEntityService implements OnModuleInit { } public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>( - src: User['id'] | User, - me?: { id: User['id']; } | null | undefined, + src: MiUser['id'] | MiUser, + me?: { id: MiUser['id']; } | null | undefined, options?: { detail?: D, includeSecrets?: boolean, - userProfile?: UserProfile, + userProfile?: MiUserProfile, }, ): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> { const opts = Object.assign({ @@ -329,7 +310,7 @@ export class UserEntityService implements OnModuleInit { const meId = me ? me.id : null; const isMe = meId === user.id; - const iAmModerator = me ? await this.roleService.isModerator(me as User) : false; + const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null; const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin') @@ -351,6 +332,7 @@ export class UserEntityService implements OnModuleInit { const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null; const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null; + const unreadAnnouncements = isMe && opts.detail ? await this.announcementService.getUnreadAnnouncements(user) : null; const falsy = opts.detail ? false : undefined; @@ -401,6 +383,7 @@ export class UserEntityService implements OnModuleInit { birthday: profile!.birthday, lang: profile!.lang, fields: profile!.fields, + verifiedLinks: profile!.verifiedLinks, followersCount: followersCount ?? 0, followingCount: followingCount ?? 0, notesCount: user.notesCount, @@ -451,6 +434,7 @@ export class UserEntityService implements OnModuleInit { preventAiLearning: profile!.preventAiLearning, isExplorable: user.isExplorable, isDeleted: user.isDeleted, + twoFactorBackupCodesStock: profile?.twoFactorBackupSecret?.length === 5 ? 'full' : (profile?.twoFactorBackupSecret?.length ?? 0) > 0 ? 'partial' : 'none', hideOnlineStatus: user.hideOnlineStatus, hasUnreadSpecifiedNotes: this.noteUnreadsRepository.count({ where: { userId: user.id, isSpecified: true }, @@ -460,7 +444,8 @@ export class UserEntityService implements OnModuleInit { where: { userId: user.id, isMentioned: true }, take: 1, }).then(count => count > 0), - hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id), + hasUnreadAnnouncement: unreadAnnouncements!.length > 0, + unreadAnnouncements, hasUnreadAntenna: this.getHasUnreadAntenna(user.id), hasUnreadChannel: false, // 後方互換性のため hasUnreadNotification: this.getHasUnreadNotification(user.id), @@ -500,6 +485,7 @@ export class UserEntityService implements OnModuleInit { isBlocked: relation.isBlocked, isMuted: relation.isMuted, isRenoteMuted: relation.isRenoteMuted, + notify: relation.following?.notify ?? 'none', } : {}), } as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>; @@ -507,8 +493,8 @@ export class UserEntityService implements OnModuleInit { } public packMany<D extends boolean = false>( - users: (User['id'] | User)[], - me?: { id: User['id'] } | null | undefined, + users: (MiUser['id'] | MiUser)[], + me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: D, includeSecrets?: boolean, diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index 8628819278..a7f2885194 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -1,11 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { UserList } from '@/models/entities/UserList.js'; +import type { } from '@/models/Blocking.js'; +import type { MiUserList } from '@/models/UserList.js'; import { bindThis } from '@/decorators.js'; -import { UserEntityService } from './UserEntityService.js'; @Injectable() export class UserListEntityService { @@ -15,14 +19,12 @@ export class UserListEntityService { @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: UserListJoiningsRepository, - - private userEntityService: UserEntityService, ) { } @bindThis public async pack( - src: UserList['id'] | UserList, + src: MiUserList['id'] | MiUserList, ): Promise<Packed<'UserList'>> { const userList = typeof src === 'object' ? src : await this.userListsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/daemons/DaemonModule.ts b/packages/backend/src/daemons/DaemonModule.ts index 683f9cbfe3..236985076c 100644 --- a/packages/backend/src/daemons/DaemonModule.ts +++ b/packages/backend/src/daemons/DaemonModule.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; import { GlobalModule } from '@/GlobalModule.js'; -import { JanitorService } from './JanitorService.js'; import { QueueStatsService } from './QueueStatsService.js'; import { ServerStatsService } from './ServerStatsService.js'; @@ -11,12 +15,10 @@ import { ServerStatsService } from './ServerStatsService.js'; CoreModule, ], providers: [ - JanitorService, QueueStatsService, ServerStatsService, ], exports: [ - JanitorService, QueueStatsService, ServerStatsService, ], diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts deleted file mode 100644 index f826d50625..0000000000 --- a/packages/backend/src/daemons/JanitorService.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { LessThan } from 'typeorm'; -import { DI } from '@/di-symbols.js'; -import type { AttestationChallengesRepository } from '@/models/index.js'; -import { bindThis } from '@/decorators.js'; -import type { OnApplicationShutdown } from '@nestjs/common'; - -const interval = 30 * 60 * 1000; - -@Injectable() -export class JanitorService implements OnApplicationShutdown { - private intervalId: NodeJS.Timer; - - constructor( - @Inject(DI.attestationChallengesRepository) - private attestationChallengesRepository: AttestationChallengesRepository, - ) { - } - - /** - * Clean up database occasionally - */ - @bindThis - public start(): void { - const tick = async () => { - await this.attestationChallengesRepository.delete({ - createdAt: LessThan(new Date(new Date().getTime() - 5 * 60 * 1000)), - }); - }; - - tick(); - - this.intervalId = setInterval(tick, interval); - } - - @bindThis - public dispose(): void { - clearInterval(this.intervalId); - } - - @bindThis - public onApplicationShutdown(signal?: string | undefined): void { - this.dispose(); - } -} diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 4d0cb96a2f..5edc0f45ab 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import Xev from 'xev'; import * as Bull from 'bullmq'; @@ -14,7 +19,7 @@ const interval = 10000; @Injectable() export class QueueStatsService implements OnApplicationShutdown { - private intervalId: NodeJS.Timer; + private intervalId: NodeJS.Timeout; constructor( @Inject(DI.config) diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index 375fd5e516..d294628740 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import si from 'systeminformation'; import Xev from 'xev'; @@ -15,7 +20,7 @@ const round = (num: number) => Math.round(num * 10) / 10; @Injectable() export class ServerStatsService implements OnApplicationShutdown { - private intervalId: NodeJS.Timer | null = null; + private intervalId: NodeJS.Timeout | null = null; constructor( private metaService: MetaService, diff --git a/packages/backend/src/decorators.ts b/packages/backend/src/decorators.ts index db23317eef..6b439978db 100644 --- a/packages/backend/src/decorators.ts +++ b/packages/backend/src/decorators.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // https://github.com/andreypopp/autobind-decorator /** diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 4a073f102f..72ec98cebe 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const DI = { config: Symbol('config'), db: Symbol('db'), @@ -21,7 +26,6 @@ export const DI = { userProfilesRepository: Symbol('userProfilesRepository'), userKeypairsRepository: Symbol('userKeypairsRepository'), userPendingsRepository: Symbol('userPendingsRepository'), - attestationChallengesRepository: Symbol('attestationChallengesRepository'), userSecurityKeysRepository: Symbol('userSecurityKeysRepository'), userPublickeysRepository: Symbol('userPublickeysRepository'), userListsRepository: Symbol('userListsRepository'), diff --git a/packages/backend/src/env.ts b/packages/backend/src/env.ts index d7c8304b47..af1c3bdd3c 100644 --- a/packages/backend/src/env.ts +++ b/packages/backend/src/env.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const envOption = { onlyQueue: false, onlyServer: false, diff --git a/packages/backend/src/global.d.ts b/packages/backend/src/global.d.ts index 7343aa1994..a9e6243cc4 100644 --- a/packages/backend/src/global.d.ts +++ b/packages/backend/src/global.d.ts @@ -1 +1,6 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + type FIXME = any; diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 465b557ce4..5c10559ec6 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import cluster from 'node:cluster'; import chalk from 'chalk'; import { default as convertColor } from 'color-convert'; @@ -13,6 +18,7 @@ type Context = { type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; +// eslint-disable-next-line import/no-default-export export default class Logger { private context: Context; private parentLogger: Logger | null = null; diff --git a/packages/backend/src/misc/acct.ts b/packages/backend/src/misc/acct.ts index fb3b657cf7..5db72746c0 100644 --- a/packages/backend/src/misc/acct.ts +++ b/packages/backend/src/misc/acct.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type Acct = { username: string; host: string | null; diff --git a/packages/backend/src/misc/api-permissions.ts b/packages/backend/src/misc/api-permissions.ts index 160cdf9fd6..57c9308844 100644 --- a/packages/backend/src/misc/api-permissions.ts +++ b/packages/backend/src/misc/api-permissions.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const kinds = [ 'read:account', 'write:account', diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index e825d51371..c235871931 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as Redis from 'ioredis'; import { bindThis } from '@/decorators.js'; @@ -188,7 +193,7 @@ function nothingToDo<T, V = T>(value: T): V { export class MemoryKVCache<T, V = T> { public cache: Map<string, { date: number; value: V; }>; private lifetime: number; - private gcIntervalHandle: NodeJS.Timer; + private gcIntervalHandle: NodeJS.Timeout; private toMapConverter: (value: T) => V; private fromMapConverter: (cached: V) => T | undefined; diff --git a/packages/backend/src/misc/check-https.ts b/packages/backend/src/misc/check-https.ts index 612032fe97..0b13ccabdd 100644 --- a/packages/backend/src/misc/check-https.ts +++ b/packages/backend/src/misc/check-https.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function checkHttps(url: string): boolean { return url.startsWith('https://') || (url.startsWith('http://') && process.env.NODE_ENV !== 'production'); diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index 910bebfcfe..cef5595451 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -1,16 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { AhoCorasick } from 'slacc'; import RE2 from 're2'; -import type { Note } from '@/models/entities/Note.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiUser } from '@/models/User.js'; type NoteLike = { - userId: Note['userId']; - text: Note['text']; - cw?: Note['cw']; + userId: MiNote['userId']; + text: MiNote['text']; + cw?: MiNote['cw']; }; type UserLike = { - id: User['id']; + id: MiUser['id']; }; const acCache = new Map<string, AhoCorasick>(); diff --git a/packages/backend/src/misc/clone.ts b/packages/backend/src/misc/clone.ts index 16fad24129..9d20deac3b 100644 --- a/packages/backend/src/misc/clone.ts +++ b/packages/backend/src/misc/clone.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // structredCloneが遅いため // SEE: http://var.blog.jp/archives/86038606.html diff --git a/packages/backend/src/misc/content-disposition.ts b/packages/backend/src/misc/content-disposition.ts index b2aec471d5..1ac8c88d21 100644 --- a/packages/backend/src/misc/content-disposition.ts +++ b/packages/backend/src/misc/content-disposition.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import cd from 'content-disposition'; export function contentDisposition(type: 'inline' | 'attachment', filename: string): string { diff --git a/packages/backend/src/misc/correct-filename.ts b/packages/backend/src/misc/correct-filename.ts index 23a0699f39..9130af44c3 100644 --- a/packages/backend/src/misc/correct-filename.ts +++ b/packages/backend/src/misc/correct-filename.ts @@ -1,15 +1,58 @@ -// 与えられた拡張子とファイル名が一致しているかどうかを確認し、 -// 一致していない場合は拡張子を付与して返す +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * Array.includes()よりSet.has()の方が高速 + */ +const targetExtsToSkip = new Set([ + '.gz', + '.tar', + '.tgz', + '.bz2', + '.xz', + '.zip', + '.7z', +]); + +const extRegExp = /\.[0-9a-zA-Z]+$/i; + +/** + * 与えられた拡張子とファイル名が一致しているかどうかを確認し、 + * 一致していない場合は拡張子を付与して返す + * + * extはfile-typeのextを想定 + */ export function correctFilename(filename: string, ext: string | null) { - const dotExt = ext ? ext.startsWith('.') ? ext : `.${ext}` : '.unknown'; - if (filename.endsWith(dotExt)) { - return filename; - } - if (ext === 'jpg' && filename.endsWith('.jpeg')) { - return filename; + const dotExt = ext ? ext[0] === '.' ? ext : `.${ext}` : '.unknown'; + + const match = extRegExp.exec(filename); + if (!match || !match[0]) { + // filenameが拡張子を持っていない場合は拡張子をつける + return `${filename}${dotExt}`; } - if (ext === 'tif' && filename.endsWith('.tiff')) { + + const filenameExt = match[0].toLowerCase(); + if ( + // 未知のファイル形式かつ拡張子がある場合は何もしない + ext === null || + // 拡張子が一致している場合は何もしない + filenameExt === dotExt || + + // jpeg, tiffを同一視 + dotExt === '.jpg' && filenameExt === '.jpeg' || + dotExt === '.tif' && filenameExt === '.tiff' || + // dllもexeもportable executableなので判定が正しく行われない + dotExt === '.exe' && filenameExt === '.dll' || + + // 圧縮形式っぽければ下手に拡張子を変えない + // https://github.com/misskey-dev/misskey/issues/11482 + targetExtsToSkip.has(dotExt) + ) { return filename; } + + // 拡張子があるが一致していないなどの場合は拡張子を付け足す return `${filename}${dotExt}`; } diff --git a/packages/backend/src/misc/create-temp.ts b/packages/backend/src/misc/create-temp.ts index 7b8942e308..2bb0e88489 100644 --- a/packages/backend/src/misc/create-temp.ts +++ b/packages/backend/src/misc/create-temp.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as tmp from 'tmp'; export function createTemp(): Promise<[string, () => void]> { diff --git a/packages/backend/src/misc/dev-null.ts b/packages/backend/src/misc/dev-null.ts index 6706af5e52..f510177c0b 100644 --- a/packages/backend/src/misc/dev-null.ts +++ b/packages/backend/src/misc/dev-null.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Writable, WritableOptions } from 'node:stream'; export class DevNull extends Writable implements NodeJS.WritableStream { diff --git a/packages/backend/src/misc/emoji-regex.ts b/packages/backend/src/misc/emoji-regex.ts index 1c6f5776db..24e4092aeb 100644 --- a/packages/backend/src/misc/emoji-regex.ts +++ b/packages/backend/src/misc/emoji-regex.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // taken from twemoji-parser/dist/lib/regex.js const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef0-\udef6]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedd-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude74\ude78-\ude7c\ude80-\ude86\ude90-\udeac\udeb0-\udeba\udec0-\udec2\uded0-\uded9\udee0-\udee7]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g; diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts index 14c25922ad..0b898d47e8 100644 --- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts +++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as mfm from 'mfm-js'; import { unique } from '@/misc/prelude/array.js'; diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts index d293fd7f52..3bd56e98eb 100644 --- a/packages/backend/src/misc/extract-hashtags.ts +++ b/packages/backend/src/misc/extract-hashtags.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as mfm from 'mfm-js'; import { unique } from '@/misc/prelude/array.js'; diff --git a/packages/backend/src/misc/extract-mentions.ts b/packages/backend/src/misc/extract-mentions.ts index c8762e797b..272eb92192 100644 --- a/packages/backend/src/misc/extract-mentions.ts +++ b/packages/backend/src/misc/extract-mentions.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // test is located in test/extract-mentions import * as mfm from 'mfm-js'; diff --git a/packages/backend/src/misc/fastify-reply-error.ts b/packages/backend/src/misc/fastify-reply-error.ts index 4e987175e2..7c889bab7a 100644 --- a/packages/backend/src/misc/fastify-reply-error.ts +++ b/packages/backend/src/misc/fastify-reply-error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // https://www.fastify.io/docs/latest/Reference/Reply/#async-await-and-promises export class FastifyReplyError extends Error { public message: string; diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index b40745973e..c36b00af63 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + /** * Identicon generator * https://en.wikipedia.org/wiki/Identicon diff --git a/packages/backend/src/misc/gen-key-pair.ts b/packages/backend/src/misc/gen-key-pair.ts index e2ad598501..c0815613e7 100644 --- a/packages/backend/src/misc/gen-key-pair.ts +++ b/packages/backend/src/misc/gen-key-pair.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as crypto from 'node:crypto'; import * as util from 'node:util'; diff --git a/packages/backend/src/misc/generate-invite-code.ts b/packages/backend/src/misc/generate-invite-code.ts index 617b27361d..7c88561179 100644 --- a/packages/backend/src/misc/generate-invite-code.ts +++ b/packages/backend/src/misc/generate-invite-code.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { secureRndstr } from './secure-rndstr.js'; const CHARS = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // [0-9A-Z] w/o [01IO] (32 patterns) diff --git a/packages/backend/src/misc/generate-native-user-token.ts b/packages/backend/src/misc/generate-native-user-token.ts index 7292d765a8..094c625120 100644 --- a/packages/backend/src/misc/generate-native-user-token.ts +++ b/packages/backend/src/misc/generate-native-user-token.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { secureRndstr } from '@/misc/secure-rndstr.js'; +// eslint-disable-next-line import/no-default-export export default () => secureRndstr(16); diff --git a/packages/backend/src/misc/get-ip-hash.ts b/packages/backend/src/misc/get-ip-hash.ts index 1a86fb8814..3a01e4f578 100644 --- a/packages/backend/src/misc/get-ip-hash.ts +++ b/packages/backend/src/misc/get-ip-hash.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import IPCIDR from 'ip-cidr'; export function getIpHash(ip: string): string { diff --git a/packages/backend/src/misc/get-note-summary.ts b/packages/backend/src/misc/get-note-summary.ts index 964f20b25b..1bda5cdcf7 100644 --- a/packages/backend/src/misc/get-note-summary.ts +++ b/packages/backend/src/misc/get-note-summary.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Packed } from './json-schema.js'; /** diff --git a/packages/backend/src/misc/get-reaction-emoji.ts b/packages/backend/src/misc/get-reaction-emoji.ts index c2e0b98582..80ef7ff7bc 100644 --- a/packages/backend/src/misc/get-reaction-emoji.ts +++ b/packages/backend/src/misc/get-reaction-emoji.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// eslint-disable-next-line import/no-default-export export default function(reaction: string): string { switch (reaction) { case 'like': return '👍'; diff --git a/packages/backend/src/misc/i18n.ts b/packages/backend/src/misc/i18n.ts index b1c727827d..4c9d1a08e3 100644 --- a/packages/backend/src/misc/i18n.ts +++ b/packages/backend/src/misc/i18n.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export class I18n<T extends Record<string, any>> { public locale: T; diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts index f0cbc9900d..ec8aa849c9 100644 --- a/packages/backend/src/misc/id/aid.ts +++ b/packages/backend/src/misc/id/aid.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // AID // 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列] diff --git a/packages/backend/src/misc/id/aidx.ts b/packages/backend/src/misc/id/aidx.ts new file mode 100644 index 0000000000..5b031ea4c0 --- /dev/null +++ b/packages/backend/src/misc/id/aidx.ts @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// AIDX +// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ4の[個体ID] + 長さ4の[カウンタ] +// (c) mei23 +// https://misskey.m544.net/notes/71899acdcc9859ec5708ac24 + +import { customAlphabet } from 'nanoid'; + +export const aidxRegExp = /^[0-9a-z]{16}$/; + +const TIME2000 = 946684800000; +const TIME_LENGTH = 8; +const NODE_LENGTH = 4; +const NOISE_LENGTH = 4; + +const nodeId = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', NODE_LENGTH)(); +let counter = 0; + +function getTime(time: number): string { + time = time - TIME2000; + if (time < 0) time = 0; + + return time.toString(36).padStart(TIME_LENGTH, '0').slice(-TIME_LENGTH); +} + +function getNoise(): string { + return counter.toString(36).padStart(NOISE_LENGTH, '0').slice(-NOISE_LENGTH); +} + +export function genAidx(date: Date): string { + const t = date.getTime(); + if (isNaN(t)) throw new Error('Failed to create AIDX: Invalid Date'); + counter++; + return getTime(t) + nodeId + getNoise(); +} + +export function parseAidx(id: string): { date: Date; } { + const time = parseInt(id.slice(0, TIME_LENGTH), 36) + TIME2000; + return { date: new Date(time) }; +} diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts index 337416b059..82cda37237 100644 --- a/packages/backend/src/misc/id/meid.ts +++ b/packages/backend/src/misc/id/meid.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const CHARS = '0123456789abcdef'; // same as object-id diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts index 19d0bc1fd2..fba7156718 100644 --- a/packages/backend/src/misc/id/meidg.ts +++ b/packages/backend/src/misc/id/meidg.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const CHARS = '0123456789abcdef'; // 4bit Fixed hex value 'g' diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts index aec3447bd7..e3b6e8e433 100644 --- a/packages/backend/src/misc/id/object-id.ts +++ b/packages/backend/src/misc/id/object-id.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const CHARS = '0123456789abcdef'; // same as meid diff --git a/packages/backend/src/misc/id/ulid.ts b/packages/backend/src/misc/id/ulid.ts index bfbc363cf5..00dd67dafe 100644 --- a/packages/backend/src/misc/id/ulid.ts +++ b/packages/backend/src/misc/id/ulid.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // Crockford's Base32 // https://github.com/ulid/spec#encoding const CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; diff --git a/packages/backend/src/misc/identifiable-error.ts b/packages/backend/src/misc/identifiable-error.ts index e394123f1b..71a4773fac 100644 --- a/packages/backend/src/misc/identifiable-error.ts +++ b/packages/backend/src/misc/identifiable-error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + /** * ID付きエラー */ diff --git a/packages/backend/src/misc/is-duplicate-key-value-error.ts b/packages/backend/src/misc/is-duplicate-key-value-error.ts index f5343d187c..91e0a6b93d 100644 --- a/packages/backend/src/misc/is-duplicate-key-value-error.ts +++ b/packages/backend/src/misc/is-duplicate-key-value-error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { QueryFailedError } from 'typeorm'; export function isDuplicateKeyValueError(e: unknown | Error): boolean { diff --git a/packages/backend/src/misc/is-instance-muted.ts b/packages/backend/src/misc/is-instance-muted.ts index 73ad0b3b82..b231058a95 100644 --- a/packages/backend/src/misc/is-instance-muted.ts +++ b/packages/backend/src/misc/is-instance-muted.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Packed } from './json-schema.js'; export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean { diff --git a/packages/backend/src/misc/is-mime-image.ts b/packages/backend/src/misc/is-mime-image.ts index 46a66efc0f..1a5a8cf0f4 100644 --- a/packages/backend/src/misc/is-mime-image.ts +++ b/packages/backend/src/misc/is-mime-image.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; const dictionary = { diff --git a/packages/backend/src/misc/is-native-token.ts b/packages/backend/src/misc/is-native-token.ts index 2833c570c8..618e60b7d8 100644 --- a/packages/backend/src/misc/is-native-token.ts +++ b/packages/backend/src/misc/is-native-token.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// eslint-disable-next-line import/no-default-export export default (token: string) => token.length === 16; diff --git a/packages/backend/src/misc/is-not-null.ts b/packages/backend/src/misc/is-not-null.ts index d89a1957be..153a9e51ef 100644 --- a/packages/backend/src/misc/is-not-null.ts +++ b/packages/backend/src/misc/is-not-null.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // we are using {} as "any non-nullish value" as expected // eslint-disable-next-line @typescript-eslint/ban-types export function isNotNull<T extends {}>(input: T | undefined | null): input is T { diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts index 248b25a0bf..059f6a4b5f 100644 --- a/packages/backend/src/misc/is-quote.ts +++ b/packages/backend/src/misc/is-quote.ts @@ -1,5 +1,11 @@ -import type { Note } from '@/models/entities/Note.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ -export default function(note: Note): boolean { +import type { MiNote } from '@/models/Note.js'; + +// eslint-disable-next-line import/no-default-export +export default function(note: MiNote): boolean { return note.renoteId != null && (note.text != null || note.hasPoll || (note.fileIds != null && note.fileIds.length > 0)); } diff --git a/packages/backend/src/misc/is-user-related.ts b/packages/backend/src/misc/is-user-related.ts index e6bbdb5d35..edd65a3c1c 100644 --- a/packages/backend/src/misc/is-user-related.ts +++ b/packages/backend/src/misc/is-user-related.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function isUserRelated(note: any, userIds: Set<string>): boolean { if (userIds.has(note.userId)) { return true; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index ec6bc4a5fb..80c1041c62 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { packedUserLiteSchema, packedUserDetailedNotMeOnlySchema, @@ -30,6 +35,7 @@ import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js'; import { packedFlashSchema } from '@/models/json-schema/flash.js'; +import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -41,6 +47,7 @@ export const refs = { User: packedUserSchema, UserList: packedUserListSchema, + Announcement: packedAnnouncementSchema, App: packedAppSchema, Note: packedNoteSchema, NoteReaction: packedNoteReactionSchema, diff --git a/packages/backend/src/misc/langmap.ts b/packages/backend/src/misc/langmap.ts index 5ee85e6c09..9e287677df 100644 --- a/packages/backend/src/misc/langmap.ts +++ b/packages/backend/src/misc/langmap.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // TODO: sharedに置いてフロントエンドのと統合したい export const langmap = { 'ach': { diff --git a/packages/backend/src/misc/normalize-for-search.ts b/packages/backend/src/misc/normalize-for-search.ts index 200540566e..9d96f4169d 100644 --- a/packages/backend/src/misc/normalize-for-search.ts +++ b/packages/backend/src/misc/normalize-for-search.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function normalizeForSearch(tag: string): string { // ref. // - https://analytics-note.xyz/programming/unicode-normalization-forms/ diff --git a/packages/backend/src/misc/nyaize.ts b/packages/backend/src/misc/nyaize.ts index 350f8d2172..0ac77e1006 100644 --- a/packages/backend/src/misc/nyaize.ts +++ b/packages/backend/src/misc/nyaize.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function nyaize(text: string): string { return text // ja-JP diff --git a/packages/backend/src/misc/prelude/array.ts b/packages/backend/src/misc/prelude/array.ts index 2524eacfb3..b2f29bcecf 100644 --- a/packages/backend/src/misc/prelude/array.ts +++ b/packages/backend/src/misc/prelude/array.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { EndoRelation, Predicate } from './relation.js'; /** diff --git a/packages/backend/src/misc/prelude/await-all.ts b/packages/backend/src/misc/prelude/await-all.ts index fd9832d6f8..6b8a91f8a5 100644 --- a/packages/backend/src/misc/prelude/await-all.ts +++ b/packages/backend/src/misc/prelude/await-all.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type Promiseable<T> = { [K in keyof T]: Promise<T[K]> | T[K]; }; diff --git a/packages/backend/src/misc/prelude/math.ts b/packages/backend/src/misc/prelude/math.ts index 07b94bec30..87b5017d09 100644 --- a/packages/backend/src/misc/prelude/math.ts +++ b/packages/backend/src/misc/prelude/math.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function gcd(a: number, b: number): number { return b === 0 ? a : gcd(b, a % b); } diff --git a/packages/backend/src/misc/prelude/maybe.ts b/packages/backend/src/misc/prelude/maybe.ts index df7c4ed52a..17c100b80d 100644 --- a/packages/backend/src/misc/prelude/maybe.ts +++ b/packages/backend/src/misc/prelude/maybe.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export interface IMaybe<T> { isJust(): this is IJust<T>; } diff --git a/packages/backend/src/misc/prelude/relation.ts b/packages/backend/src/misc/prelude/relation.ts index 1f4703f52f..3456c1a0bc 100644 --- a/packages/backend/src/misc/prelude/relation.ts +++ b/packages/backend/src/misc/prelude/relation.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type Predicate<T> = (a: T) => boolean; export type Relation<T, U> = (a: T, b: U) => boolean; diff --git a/packages/backend/src/misc/prelude/string.ts b/packages/backend/src/misc/prelude/string.ts index b907e0a2e1..a727ab7f1d 100644 --- a/packages/backend/src/misc/prelude/string.ts +++ b/packages/backend/src/misc/prelude/string.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function concat(xs: string[]): string { return xs.join(''); } diff --git a/packages/backend/src/misc/prelude/symbol.ts b/packages/backend/src/misc/prelude/symbol.ts index 51e12f7450..91c058a845 100644 --- a/packages/backend/src/misc/prelude/symbol.ts +++ b/packages/backend/src/misc/prelude/symbol.ts @@ -1 +1,6 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const fallback = Symbol('fallback'); diff --git a/packages/backend/src/misc/prelude/time.ts b/packages/backend/src/misc/prelude/time.ts index b21978b186..4479db1081 100644 --- a/packages/backend/src/misc/prelude/time.ts +++ b/packages/backend/src/misc/prelude/time.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const dateTimeIntervals = { 'day': 86400000, 'hour': 3600000, diff --git a/packages/backend/src/misc/prelude/url.ts b/packages/backend/src/misc/prelude/url.ts index 5239678280..633eb98218 100644 --- a/packages/backend/src/misc/prelude/url.ts +++ b/packages/backend/src/misc/prelude/url.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + /* objを検査して * 1. 配列に何も入っていない時はクエリを付けない * 2. プロパティがundefinedの時はクエリを付けない diff --git a/packages/backend/src/misc/prelude/xml.ts b/packages/backend/src/misc/prelude/xml.ts index b4469a1d8d..bca116a7ec 100644 --- a/packages/backend/src/misc/prelude/xml.ts +++ b/packages/backend/src/misc/prelude/xml.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + const map: Record<string, string> = { '&': '&', '<': '<', diff --git a/packages/backend/src/misc/reset-db.ts b/packages/backend/src/misc/reset-db.ts index 835cd2ba28..a571460a59 100644 --- a/packages/backend/src/misc/reset-db.ts +++ b/packages/backend/src/misc/reset-db.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { DataSource } from 'typeorm'; export async function resetDb(db: DataSource) { diff --git a/packages/backend/src/misc/safe-for-sql.ts b/packages/backend/src/misc/safe-for-sql.ts index 02eb7f0a26..d7bdd0a81c 100644 --- a/packages/backend/src/misc/safe-for-sql.ts +++ b/packages/backend/src/misc/safe-for-sql.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function safeForSql(text: string): boolean { return !/[\0\x08\x09\x1a\n\r"'\\\%]/g.test(text); } diff --git a/packages/backend/src/misc/secure-rndstr.ts b/packages/backend/src/misc/secure-rndstr.ts index cde64c8142..01368d808a 100644 --- a/packages/backend/src/misc/secure-rndstr.ts +++ b/packages/backend/src/misc/secure-rndstr.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as crypto from 'node:crypto'; export const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'; diff --git a/packages/backend/src/misc/show-machine-info.ts b/packages/backend/src/misc/show-machine-info.ts index fa5a53e313..ed0fa651f1 100644 --- a/packages/backend/src/misc/show-machine-info.ts +++ b/packages/backend/src/misc/show-machine-info.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as os from 'node:os'; import sysUtils from 'systeminformation'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/misc/sql-like-escape.ts b/packages/backend/src/misc/sql-like-escape.ts index 8470dca3de..85cc7405e1 100644 --- a/packages/backend/src/misc/sql-like-escape.ts +++ b/packages/backend/src/misc/sql-like-escape.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function sqlLikeEscape(s: string) { return s.replace(/([%_])/g, '\\$1'); } diff --git a/packages/backend/src/misc/status-error.ts b/packages/backend/src/misc/status-error.ts index 0a33f8acaf..4285685d24 100644 --- a/packages/backend/src/misc/status-error.ts +++ b/packages/backend/src/misc/status-error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export class StatusError extends Error { public statusCode: number; public statusMessage?: string; diff --git a/packages/backend/src/misc/truncate.ts b/packages/backend/src/misc/truncate.ts index cb120331a1..b65202fbd4 100644 --- a/packages/backend/src/misc/truncate.ts +++ b/packages/backend/src/misc/truncate.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { substring } from 'stringz'; export function truncate(input: string, size: number): string; diff --git a/packages/backend/src/models/entities/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 07305cf23a..2551af7cb6 100644 --- a/packages/backend/src/models/entities/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class AbuseUserReport { +@Entity('abuse_user_report') +export class MiAbuseUserReport { @PrimaryColumn(id()) public id: string; @@ -15,35 +20,35 @@ export class AbuseUserReport { @Index() @Column(id()) - public targetUserId: User['id']; + public targetUserId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public targetUser: User | null; + public targetUser: MiUser | null; @Index() @Column(id()) - public reporterId: User['id']; + public reporterId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public reporter: User | null; + public reporter: MiUser | null; @Column({ ...id(), nullable: true, }) - public assigneeId: User['id'] | null; + public assigneeId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'SET NULL', }) @JoinColumn() - public assignee: User | null; + public assignee: MiUser | null; @Index() @Column('boolean', { diff --git a/packages/backend/src/models/entities/AccessToken.ts b/packages/backend/src/models/AccessToken.ts index 8e987ffeef..5a6269a729 100644 --- a/packages/backend/src/models/entities/AccessToken.ts +++ b/packages/backend/src/models/AccessToken.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { App } from './App.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiApp } from './App.js'; -@Entity() -export class AccessToken { +@Entity('access_token') +export class MiAccessToken { @PrimaryColumn(id()) public id: string; @@ -39,25 +44,25 @@ export class AccessToken { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column({ ...id(), nullable: true, }) - public appId: App['id'] | null; + public appId: MiApp['id'] | null; - @ManyToOne(type => App, { + @ManyToOne(type => MiApp, { onDelete: 'CASCADE', }) @JoinColumn() - public app: App | null; + public app: MiApp | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/Ad.ts index a496a6d276..6dfc9cb30e 100644 --- a/packages/backend/src/models/entities/Ad.ts +++ b/packages/backend/src/models/Ad.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; -@Entity() -export class Ad { +@Entity('ad') +export class MiAd { @PrimaryColumn(id()) public id: string; @@ -59,7 +64,7 @@ export class Ad { default: 0, nullable: false, }) public dayOfWeek: number; - constructor(data: Partial<Ad>) { + constructor(data: Partial<MiAd>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts new file mode 100644 index 0000000000..34b092a8d4 --- /dev/null +++ b/packages/backend/src/models/Announcement.ts @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Entity, Index, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; + +@Entity('announcement') +export class MiAnnouncement { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Announcement.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + comment: 'The updated date of the Announcement.', + nullable: true, + }) + public updatedAt: Date | null; + + @Column('varchar', { + length: 8192, nullable: false, + }) + public text: string; + + @Column('varchar', { + length: 256, nullable: false, + }) + public title: string; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public imageUrl: string | null; + + // info, warning, error, success + @Column('varchar', { + length: 256, nullable: false, + default: 'info', + }) + public icon: string; + + // normal ... お知らせページ掲載 + // banner ... お知らせページ掲載 + バナー表示 + // dialog ... お知らせページ掲載 + ダイアログ表示 + @Column('varchar', { + length: 256, nullable: false, + default: 'normal', + }) + public display: string; + + @Column('boolean', { + default: false, + }) + public needConfirmationToRead: boolean; + + @Index() + @Column('boolean', { + default: true, + }) + public isActive: boolean; + + @Index() + @Column('boolean', { + default: false, + }) + public forExistingUsers: boolean; + + @Index() + @Column({ + ...id(), + nullable: true, + }) + public userId: MiUser['id'] | null; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + constructor(data: Partial<MiAnnouncement>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/AnnouncementRead.ts b/packages/backend/src/models/AnnouncementRead.ts new file mode 100644 index 0000000000..3d6ec5652c --- /dev/null +++ b/packages/backend/src/models/AnnouncementRead.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiAnnouncement } from './Announcement.js'; + +@Entity('announcement_read') +@Index(['userId', 'announcementId'], { unique: true }) +export class MiAnnouncementRead { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AnnouncementRead.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Index() + @Column(id()) + public announcementId: MiAnnouncement['id']; + + @ManyToOne(type => MiAnnouncement, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public announcement: MiAnnouncement | null; +} diff --git a/packages/backend/src/models/entities/Antenna.ts b/packages/backend/src/models/Antenna.ts index e63e7f2c72..dc398b6dd2 100644 --- a/packages/backend/src/models/entities/Antenna.ts +++ b/packages/backend/src/models/Antenna.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { UserList } from './UserList.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiUserList } from './UserList.js'; -@Entity() -export class Antenna { +@Entity('antenna') +export class MiAntenna { @PrimaryColumn(id()) public id: string; @@ -22,13 +27,13 @@ export class Antenna { ...id(), comment: 'The owner ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, @@ -36,20 +41,20 @@ export class Antenna { }) public name: string; - @Column('enum', { enum: ['home', 'all', 'users', 'list'] }) - public src: 'home' | 'all' | 'users' | 'list'; + @Column('enum', { enum: ['home', 'all', 'users', 'list', 'users_blacklist'] }) + public src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist'; @Column({ ...id(), nullable: true, }) - public userListId: UserList['id'] | null; + public userListId: MiUserList['id'] | null; - @ManyToOne(type => UserList, { + @ManyToOne(type => MiUserList, { onDelete: 'CASCADE', }) @JoinColumn() - public userList: UserList | null; + public userList: MiUserList | null; @Column('varchar', { length: 1024, array: true, diff --git a/packages/backend/src/models/entities/App.ts b/packages/backend/src/models/App.ts index 3a1ea7732e..c599ef8be0 100644 --- a/packages/backend/src/models/entities/App.ts +++ b/packages/backend/src/models/App.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class App { +@Entity('app') +export class MiApp { @PrimaryColumn(id()) public id: string; @@ -19,13 +24,13 @@ export class App { nullable: true, comment: 'The owner ID.', }) - public userId: User['id'] | null; + public userId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'SET NULL', nullable: true, }) - public user: User | null; + public user: MiUser | null; @Index() @Column('varchar', { diff --git a/packages/backend/src/models/entities/AuthSession.ts b/packages/backend/src/models/AuthSession.ts index 6b2f50e8d6..d9de6b6979 100644 --- a/packages/backend/src/models/entities/AuthSession.ts +++ b/packages/backend/src/models/AuthSession.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { App } from './App.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiApp } from './App.js'; -@Entity() -export class AuthSession { +@Entity('auth_session') +export class MiAuthSession { @PrimaryColumn(id()) public id: string; @@ -23,21 +28,21 @@ export class AuthSession { ...id(), nullable: true, }) - public userId: User['id'] | null; + public userId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', nullable: true, }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column(id()) - public appId: App['id']; + public appId: MiApp['id']; - @ManyToOne(type => App, { + @ManyToOne(type => MiApp, { onDelete: 'CASCADE', }) @JoinColumn() - public app: App | null; + public app: MiApp | null; } diff --git a/packages/backend/src/models/entities/Blocking.ts b/packages/backend/src/models/Blocking.ts index 9892ff308e..1e3dd3a644 100644 --- a/packages/backend/src/models/entities/Blocking.ts +++ b/packages/backend/src/models/Blocking.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('blocking') @Index(['blockerId', 'blockeeId'], { unique: true }) -export class Blocking { +export class MiBlocking { @PrimaryColumn(id()) public id: string; @@ -19,24 +24,24 @@ export class Blocking { ...id(), comment: 'The blockee user ID.', }) - public blockeeId: User['id']; + public blockeeId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public blockee: User | null; + public blockee: MiUser | null; @Index() @Column({ ...id(), comment: 'The blocker user ID.', }) - public blockerId: User['id']; + public blockerId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public blocker: User | null; + public blocker: MiUser | null; } diff --git a/packages/backend/src/models/entities/Channel.ts b/packages/backend/src/models/Channel.ts index d7c4583da3..ae3886a657 100644 --- a/packages/backend/src/models/entities/Channel.ts +++ b/packages/backend/src/models/Channel.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { DriveFile } from './DriveFile.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiDriveFile } from './DriveFile.js'; -@Entity() -export class Channel { +@Entity('channel') +export class MiChannel { @PrimaryColumn(id()) public id: string; @@ -26,13 +31,13 @@ export class Channel { nullable: true, comment: 'The owner ID.', }) - public userId: User['id'] | null; + public userId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'SET NULL', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, @@ -51,13 +56,13 @@ export class Channel { nullable: true, comment: 'The ID of banner Channel.', }) - public bannerId: DriveFile['id'] | null; + public bannerId: MiDriveFile['id'] | null; - @ManyToOne(type => DriveFile, { + @ManyToOne(type => MiDriveFile, { onDelete: 'SET NULL', }) @JoinColumn() - public banner: DriveFile | null; + public banner: MiDriveFile | null; @Column('varchar', { array: true, length: 128, default: '{}', @@ -89,4 +94,9 @@ export class Channel { comment: 'The count of users.', }) public usersCount: number; + + @Column('boolean', { + default: false, + }) + public isSensitive: boolean; } diff --git a/packages/backend/src/models/ChannelFavorite.ts b/packages/backend/src/models/ChannelFavorite.ts new file mode 100644 index 0000000000..ab74aa5530 --- /dev/null +++ b/packages/backend/src/models/ChannelFavorite.ts @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiChannel } from './Channel.js'; + +@Entity('channel_favorite') +@Index(['userId', 'channelId'], { unique: true }) +export class MiChannelFavorite { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the ChannelFavorite.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + }) + public channelId: MiChannel['id']; + + @ManyToOne(type => MiChannel, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public channel: MiChannel | null; + + @Index() + @Column({ + ...id(), + }) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; +} diff --git a/packages/backend/src/models/entities/ChannelFollowing.ts b/packages/backend/src/models/ChannelFollowing.ts index c65c38b67d..c62a95332a 100644 --- a/packages/backend/src/models/entities/ChannelFollowing.ts +++ b/packages/backend/src/models/ChannelFollowing.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Channel } from './Channel.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiChannel } from './Channel.js'; -@Entity() +@Entity('channel_following') @Index(['followerId', 'followeeId'], { unique: true }) -export class ChannelFollowing { +export class MiChannelFollowing { @PrimaryColumn(id()) public id: string; @@ -20,24 +25,24 @@ export class ChannelFollowing { ...id(), comment: 'The followee channel ID.', }) - public followeeId: Channel['id']; + public followeeId: MiChannel['id']; - @ManyToOne(type => Channel, { + @ManyToOne(type => MiChannel, { onDelete: 'CASCADE', }) @JoinColumn() - public followee: Channel | null; + public followee: MiChannel | null; @Index() @Column({ ...id(), comment: 'The follower user ID.', }) - public followerId: User['id']; + public followerId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public follower: User | null; + public follower: MiUser | null; } diff --git a/packages/backend/src/models/entities/Clip.ts b/packages/backend/src/models/Clip.ts index 825a32c981..c60b2964e0 100644 --- a/packages/backend/src/models/entities/Clip.ts +++ b/packages/backend/src/models/Clip.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class Clip { +@Entity('clip') +export class MiClip { @PrimaryColumn(id()) public id: string; @@ -23,13 +28,13 @@ export class Clip { ...id(), comment: 'The owner ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/ClipFavorite.ts b/packages/backend/src/models/ClipFavorite.ts new file mode 100644 index 0000000000..054764389b --- /dev/null +++ b/packages/backend/src/models/ClipFavorite.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiClip } from './Clip.js'; + +@Entity('clip_favorite') +@Index(['userId', 'clipId'], { unique: true }) +export class MiClipFavorite { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public clipId: MiClip['id']; + + @ManyToOne(type => MiClip, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public clip: MiClip | null; +} diff --git a/packages/backend/src/models/ClipNote.ts b/packages/backend/src/models/ClipNote.ts new file mode 100644 index 0000000000..b7cc5ee39b --- /dev/null +++ b/packages/backend/src/models/ClipNote.ts @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import { MiClip } from './Clip.js'; + +@Entity('clip_note') +@Index(['noteId', 'clipId'], { unique: true }) +export class MiClipNote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: 'The note ID.', + }) + public noteId: MiNote['id']; + + @ManyToOne(type => MiNote, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: MiNote | null; + + @Index() + @Column({ + ...id(), + comment: 'The clip ID.', + }) + public clipId: MiClip['id']; + + @ManyToOne(type => MiClip, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public clip: MiClip | null; +} diff --git a/packages/backend/src/models/entities/DriveFile.ts b/packages/backend/src/models/DriveFile.ts index 7b9670fb92..c12f0e0f02 100644 --- a/packages/backend/src/models/entities/DriveFile.ts +++ b/packages/backend/src/models/DriveFile.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { DriveFolder } from './DriveFolder.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiDriveFolder } from './DriveFolder.js'; -@Entity() +@Entity('drive_file') @Index(['userId', 'folderId', 'id']) -export class DriveFile { +export class MiDriveFile { @PrimaryColumn(id()) public id: string; @@ -21,13 +26,13 @@ export class DriveFile { nullable: true, comment: 'The owner ID.', }) - public userId: User['id'] | null; + public userId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'SET NULL', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column('varchar', { @@ -141,13 +146,13 @@ export class DriveFile { nullable: true, comment: 'The parent folder ID. If null, it means the DriveFile is located in root.', }) - public folderId: DriveFolder['id'] | null; + public folderId: MiDriveFolder['id'] | null; - @ManyToOne(type => DriveFolder, { + @ManyToOne(type => MiDriveFolder, { onDelete: 'SET NULL', }) @JoinColumn() - public folder: DriveFolder | null; + public folder: MiDriveFolder | null; @Index() @Column('boolean', { diff --git a/packages/backend/src/models/entities/DriveFolder.ts b/packages/backend/src/models/DriveFolder.ts index 2a73a0875d..3e049136bd 100644 --- a/packages/backend/src/models/entities/DriveFolder.ts +++ b/packages/backend/src/models/DriveFolder.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class DriveFolder { +@Entity('drive_folder') +export class MiDriveFolder { @PrimaryColumn(id()) public id: string; @@ -25,13 +30,13 @@ export class DriveFolder { nullable: true, comment: 'The owner ID.', }) - public userId: User['id'] | null; + public userId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column({ @@ -39,11 +44,11 @@ export class DriveFolder { nullable: true, comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.', }) - public parentId: DriveFolder['id'] | null; + public parentId: MiDriveFolder['id'] | null; - @ManyToOne(type => DriveFolder, { + @ManyToOne(type => MiDriveFolder, { onDelete: 'SET NULL', }) @JoinColumn() - public parent: DriveFolder | null; + public parent: MiDriveFolder | null; } diff --git a/packages/backend/src/models/entities/Emoji.ts b/packages/backend/src/models/Emoji.ts index 8fd3e65f5e..563ac1d9d3 100644 --- a/packages/backend/src/models/entities/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; -@Entity() +@Entity('emoji') @Index(['name', 'host'], { unique: true }) -export class Emoji { +export class MiEmoji { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/entities/Flash.ts b/packages/backend/src/models/Flash.ts index 4ccc908a6a..185063029d 100644 --- a/packages/backend/src/models/entities/Flash.ts +++ b/packages/backend/src/models/Flash.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class Flash { +@Entity('flash') +export class MiFlash { @PrimaryColumn(id()) public id: string; @@ -34,13 +39,13 @@ export class Flash { ...id(), comment: 'The ID of author.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 65536, @@ -56,4 +61,13 @@ export class Flash { default: 0, }) public likedCount: number; + + /** + * public ... 公開 + * private ... プロフィールには表示しない + */ + @Column('varchar', { + length: 512, default: 'public', + }) + public visibility: 'public' | 'private'; } diff --git a/packages/backend/src/models/FlashLike.ts b/packages/backend/src/models/FlashLike.ts new file mode 100644 index 0000000000..7c66010ae6 --- /dev/null +++ b/packages/backend/src/models/FlashLike.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiFlash } from './Flash.js'; + +@Entity('flash_like') +@Index(['userId', 'flashId'], { unique: true }) +export class MiFlashLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public flashId: MiFlash['id']; + + @ManyToOne(type => MiFlash, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public flash: MiFlash | null; +} diff --git a/packages/backend/src/models/entities/FollowRequest.ts b/packages/backend/src/models/FollowRequest.ts index 0988e7e504..769b9a6cb5 100644 --- a/packages/backend/src/models/entities/FollowRequest.ts +++ b/packages/backend/src/models/FollowRequest.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('follow_request') @Index(['followerId', 'followeeId'], { unique: true }) -export class FollowRequest { +export class MiFollowRequest { @PrimaryColumn(id()) public id: string; @@ -18,26 +23,26 @@ export class FollowRequest { ...id(), comment: 'The followee user ID.', }) - public followeeId: User['id']; + public followeeId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public followee: User | null; + public followee: MiUser | null; @Index() @Column({ ...id(), comment: 'The follower user ID.', }) - public followerId: User['id']; + public followerId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public follower: User | null; + public follower: MiUser | null; @Column('varchar', { length: 128, nullable: true, diff --git a/packages/backend/src/models/entities/Following.ts b/packages/backend/src/models/Following.ts index 112afd7e6e..8c9f965fad 100644 --- a/packages/backend/src/models/entities/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('following') @Index(['followerId', 'followeeId'], { unique: true }) -export class Following { +export class MiFollowing { @PrimaryColumn(id()) public id: string; @@ -19,26 +24,33 @@ export class Following { ...id(), comment: 'The followee user ID.', }) - public followeeId: User['id']; + public followeeId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public followee: User | null; + public followee: MiUser | null; @Index() @Column({ ...id(), comment: 'The follower user ID.', }) - public followerId: User['id']; + public followerId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public follower: User | null; + public follower: MiUser | null; + + @Index() + @Column('varchar', { + length: 32, + nullable: true, + }) + public notify: 'normal' | null; //#region Denormalized fields @Index() diff --git a/packages/backend/src/models/GalleryLike.ts b/packages/backend/src/models/GalleryLike.ts new file mode 100644 index 0000000000..b5f71764aa --- /dev/null +++ b/packages/backend/src/models/GalleryLike.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiGalleryPost } from './GalleryPost.js'; + +@Entity('gallery_like') +@Index(['userId', 'postId'], { unique: true }) +export class MiGalleryLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public postId: MiGalleryPost['id']; + + @ManyToOne(type => MiGalleryPost, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public post: MiGalleryPost | null; +} diff --git a/packages/backend/src/models/entities/GalleryPost.ts b/packages/backend/src/models/GalleryPost.ts index 36e879afa7..4c6063f32b 100644 --- a/packages/backend/src/models/entities/GalleryPost.ts +++ b/packages/backend/src/models/GalleryPost.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import type { DriveFile } from './DriveFile.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import type { MiDriveFile } from './DriveFile.js'; -@Entity() -export class GalleryPost { +@Entity('gallery_post') +export class MiGalleryPost { @PrimaryColumn(id()) public id: string; @@ -35,20 +40,20 @@ export class GalleryPost { ...id(), comment: 'The ID of author.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column({ ...id(), array: true, default: '{}', }) - public fileIds: DriveFile['id'][]; + public fileIds: MiDriveFile['id'][]; @Index() @Column('boolean', { @@ -69,7 +74,7 @@ export class GalleryPost { }) public tags: string[]; - constructor(data: Partial<GalleryPost>) { + constructor(data: Partial<MiGalleryPost>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/entities/Hashtag.ts b/packages/backend/src/models/Hashtag.ts index 2d6bfaa045..1493774752 100644 --- a/packages/backend/src/models/entities/Hashtag.ts +++ b/packages/backend/src/models/Hashtag.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { id } from '../id.js'; -import type { User } from './User.js'; +import { id } from './util/id.js'; +import type { MiUser } from './User.js'; -@Entity() -export class Hashtag { +@Entity('hashtag') +export class MiHashtag { @PrimaryColumn(id()) public id: string; @@ -17,7 +22,7 @@ export class Hashtag { ...id(), array: true, }) - public mentionedUserIds: User['id'][]; + public mentionedUserIds: MiUser['id'][]; @Index() @Column('integer', { @@ -29,7 +34,7 @@ export class Hashtag { ...id(), array: true, }) - public mentionedLocalUserIds: User['id'][]; + public mentionedLocalUserIds: MiUser['id'][]; @Index() @Column('integer', { @@ -41,7 +46,7 @@ export class Hashtag { ...id(), array: true, }) - public mentionedRemoteUserIds: User['id'][]; + public mentionedRemoteUserIds: MiUser['id'][]; @Index() @Column('integer', { @@ -53,7 +58,7 @@ export class Hashtag { ...id(), array: true, }) - public attachedUserIds: User['id'][]; + public attachedUserIds: MiUser['id'][]; @Index() @Column('integer', { @@ -65,7 +70,7 @@ export class Hashtag { ...id(), array: true, }) - public attachedLocalUserIds: User['id'][]; + public attachedLocalUserIds: MiUser['id'][]; @Index() @Column('integer', { @@ -77,7 +82,7 @@ export class Hashtag { ...id(), array: true, }) - public attachedRemoteUserIds: User['id'][]; + public attachedRemoteUserIds: MiUser['id'][]; @Index() @Column('integer', { diff --git a/packages/backend/src/models/entities/Instance.ts b/packages/backend/src/models/Instance.ts index 09328b57f8..b225d918d6 100644 --- a/packages/backend/src/models/entities/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; -@Entity() -export class Instance { +@Entity('instance') +export class MiInstance { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/Meta.ts index 7bb1b67712..e69bef8e98 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class Meta { +@Entity('meta') +export class MiMeta { @PrimaryColumn({ type: 'varchar', length: 32, @@ -16,6 +21,11 @@ export class Meta { public name: string | null; @Column('varchar', { + length: 64, nullable: true, + }) + public shortName: string | null; + + @Column('varchar', { length: 1024, nullable: true, }) public description: string | null; @@ -106,6 +116,18 @@ export class Meta { length: 1024, nullable: true, }) + public app192IconUrl: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public app512IconUrl: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) public serverErrorImageUrl: string | null; @Column('varchar', { @@ -121,7 +143,7 @@ export class Meta { public infoImageUrl: string | null; @Column('boolean', { - default: true, + default: false, }) public cacheRemoteFiles: boolean; @@ -134,13 +156,13 @@ export class Meta { ...id(), nullable: true, }) - public proxyAccountId: User['id'] | null; + public proxyAccountId: MiUser['id'] | null; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'SET NULL', }) @JoinColumn() - public proxyAccount: User | null; + public proxyAccount: MiUser | null; @Column('boolean', { default: false, @@ -440,6 +462,12 @@ export class Meta { public serverRules: string[]; @Column('varchar', { + length: 8192, + default: '{}', + }) + public manifestJsonOverride: string; + + @Column('varchar', { length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }', }) public preservedUsernames: string[]; diff --git a/packages/backend/src/models/entities/ModerationLog.ts b/packages/backend/src/models/ModerationLog.ts index ab6a226cf7..a12b6ab614 100644 --- a/packages/backend/src/models/entities/ModerationLog.ts +++ b/packages/backend/src/models/ModerationLog.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class ModerationLog { +@Entity('moderation_log') +export class MiModerationLog { @PrimaryColumn(id()) public id: string; @@ -14,13 +19,13 @@ export class ModerationLog { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/entities/MutedNote.ts b/packages/backend/src/models/MutedNote.ts index 78347d8917..89a678a2a7 100644 --- a/packages/backend/src/models/entities/MutedNote.ts +++ b/packages/backend/src/models/MutedNote.ts @@ -1,12 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; -import { mutedNoteReasons } from '../../types.js'; -import { Note } from './Note.js'; -import { User } from './User.js'; +import { mutedNoteReasons } from '@/types.js'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('muted_note') @Index(['noteId', 'userId'], { unique: true }) -export class MutedNote { +export class MiMutedNote { @PrimaryColumn(id()) public id: string; @@ -15,26 +20,26 @@ export class MutedNote { ...id(), comment: 'The note ID.', }) - public noteId: Note['id']; + public noteId: MiNote['id']; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public note: Note | null; + public note: MiNote | null; @Index() @Column({ ...id(), comment: 'The user ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; /** * ミュートされた理由。 diff --git a/packages/backend/src/models/entities/Muting.ts b/packages/backend/src/models/Muting.ts index bf5498b96a..2f06ca8e5e 100644 --- a/packages/backend/src/models/entities/Muting.ts +++ b/packages/backend/src/models/Muting.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('muting') @Index(['muterId', 'muteeId'], { unique: true }) -export class Muting { +export class MiMuting { @PrimaryColumn(id()) public id: string; @@ -25,24 +30,24 @@ export class Muting { ...id(), comment: 'The mutee user ID.', }) - public muteeId: User['id']; + public muteeId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public mutee: User | null; + public mutee: MiUser | null; @Index() @Column({ ...id(), comment: 'The muter user ID.', }) - public muterId: User['id']; + public muterId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public muter: User | null; + public muter: MiUser | null; } diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/Note.ts index 4f49a05950..ed86d4549e 100644 --- a/packages/backend/src/models/entities/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -1,15 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { noteVisibilities } from '../../types.js'; -import { User } from './User.js'; -import { Channel } from './Channel.js'; -import type { DriveFile } from './DriveFile.js'; +import { noteVisibilities } from '@/types.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiChannel } from './Channel.js'; +import type { MiDriveFile } from './DriveFile.js'; -@Entity() +@Entity('note') @Index('IDX_NOTE_TAGS', { synchronize: false }) @Index('IDX_NOTE_MENTIONS', { synchronize: false }) @Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) -export class Note { +export class MiNote { @PrimaryColumn(id()) public id: string; @@ -25,13 +30,13 @@ export class Note { nullable: true, comment: 'The ID of reply target.', }) - public replyId: Note['id'] | null; + public replyId: MiNote['id'] | null; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public reply: Note | null; + public reply: MiNote | null; @Index() @Column({ @@ -39,13 +44,13 @@ export class Note { nullable: true, comment: 'The ID of renote target.', }) - public renoteId: Note['id'] | null; + public renoteId: MiNote['id'] | null; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public renote: Note | null; + public renote: MiNote | null; @Index() @Column('varchar', { @@ -74,13 +79,13 @@ export class Note { ...id(), comment: 'The ID of author.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('boolean', { default: false, @@ -102,6 +107,11 @@ export class Note { }) public repliesCount: number; + @Column('smallint', { + default: 0, + }) + public clippedCount: number; + @Column('jsonb', { default: {}, }) @@ -139,7 +149,7 @@ export class Note { ...id(), array: true, default: '{}', }) - public fileIds: DriveFile['id'][]; + public fileIds: MiDriveFile['id'][]; @Index() @Column('varchar', { @@ -152,14 +162,14 @@ export class Note { ...id(), array: true, default: '{}', }) - public visibleUserIds: User['id'][]; + public visibleUserIds: MiUser['id'][]; @Index() @Column({ ...id(), array: true, default: '{}', }) - public mentions: User['id'][]; + public mentions: MiUser['id'][]; @Column('text', { default: '[]', @@ -188,13 +198,13 @@ export class Note { nullable: true, comment: 'The ID of source channel.', }) - public channelId: Channel['id'] | null; + public channelId: MiChannel['id'] | null; - @ManyToOne(type => Channel, { + @ManyToOne(type => MiChannel, { onDelete: 'CASCADE', }) @JoinColumn() - public channel: Channel | null; + public channel: MiChannel | null; //#region Denormalized fields @Index() @@ -209,7 +219,7 @@ export class Note { nullable: true, comment: '[Denormalized]', }) - public replyUserId: User['id'] | null; + public replyUserId: MiUser['id'] | null; @Column('varchar', { length: 128, nullable: true, @@ -222,7 +232,7 @@ export class Note { nullable: true, comment: '[Denormalized]', }) - public renoteUserId: User['id'] | null; + public renoteUserId: MiUser['id'] | null; @Column('varchar', { length: 128, nullable: true, @@ -231,7 +241,7 @@ export class Note { public renoteUserHost: string | null; //#endregion - constructor(data: Partial<Note>) { + constructor(data: Partial<MiNote>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/NoteFavorite.ts b/packages/backend/src/models/NoteFavorite.ts new file mode 100644 index 0000000000..1171684bcf --- /dev/null +++ b/packages/backend/src/models/NoteFavorite.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import { MiUser } from './User.js'; + +@Entity('note_favorite') +@Index(['userId', 'noteId'], { unique: true }) +export class MiNoteFavorite { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the NoteFavorite.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public noteId: MiNote['id']; + + @ManyToOne(type => MiNote, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: MiNote | null; +} diff --git a/packages/backend/src/models/entities/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts index c3c381af56..7c08d31c6d 100644 --- a/packages/backend/src/models/entities/NoteReaction.ts +++ b/packages/backend/src/models/NoteReaction.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Note } from './Note.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiNote } from './Note.js'; -@Entity() +@Entity('note_reaction') @Index(['userId', 'noteId'], { unique: true }) -export class NoteReaction { +export class MiNoteReaction { @PrimaryColumn(id()) public id: string; @@ -17,23 +22,23 @@ export class NoteReaction { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user?: User | null; + public user?: MiUser | null; @Index() @Column(id()) - public noteId: Note['id']; + public noteId: MiNote['id']; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public note?: Note | null; + public note?: MiNote | null; // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) diff --git a/packages/backend/src/models/entities/NoteThreadMuting.ts b/packages/backend/src/models/NoteThreadMuting.ts index 3c884fe615..2d120e4c25 100644 --- a/packages/backend/src/models/entities/NoteThreadMuting.ts +++ b/packages/backend/src/models/NoteThreadMuting.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('note_thread_muting') @Index(['userId', 'threadId'], { unique: true }) -export class NoteThreadMuting { +export class MiNoteThreadMuting { @PrimaryColumn(id()) public id: string; @@ -16,13 +21,13 @@ export class NoteThreadMuting { @Column({ ...id(), }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column('varchar', { diff --git a/packages/backend/src/models/entities/NoteUnread.ts b/packages/backend/src/models/NoteUnread.ts index af91234d0f..d86a474553 100644 --- a/packages/backend/src/models/entities/NoteUnread.ts +++ b/packages/backend/src/models/NoteUnread.ts @@ -1,34 +1,39 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Note } from './Note.js'; -import type { Channel } from './Channel.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiNote } from './Note.js'; +import type { MiChannel } from './Channel.js'; -@Entity() +@Entity('note_unread') @Index(['userId', 'noteId'], { unique: true }) -export class NoteUnread { +export class MiNoteUnread { @PrimaryColumn(id()) public id: string; @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column(id()) - public noteId: Note['id']; + public noteId: MiNote['id']; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public note: Note | null; + public note: MiNote | null; /** * メンションか否か @@ -50,7 +55,7 @@ export class NoteUnread { ...id(), comment: '[Denormalized]', }) - public noteUserId: User['id']; + public noteUserId: MiUser['id']; @Index() @Column({ @@ -58,6 +63,6 @@ export class NoteUnread { nullable: true, comment: '[Denormalized]', }) - public noteChannelId: Channel['id'] | null; + public noteChannelId: MiChannel['id'] | null; //#endregion } diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts new file mode 100644 index 0000000000..c0a9df2e23 --- /dev/null +++ b/packages/backend/src/models/Notification.ts @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { notificationTypes } from '@/types.js'; +import { MiUser } from './User.js'; +import { MiNote } from './Note.js'; +import { MiFollowRequest } from './FollowRequest.js'; +import { MiAccessToken } from './AccessToken.js'; + +export type MiNotification = { + id: string; + + // RedisのためDateではなくstring + createdAt: string; + + /** + * 通知の送信者(initiator) + */ + notifierId: MiUser['id'] | null; + + /** + * 通知の種類。 + */ + type: typeof notificationTypes[number]; + + noteId: MiNote['id'] | null; + + followRequestId: MiFollowRequest['id'] | null; + + reaction: string | null; + + choice: number | null; + + achievement: string | null; + + /** + * アプリ通知のbody + */ + customBody: string | null; + + /** + * アプリ通知のheader + * (省略時はアプリ名で表示されることを期待) + */ + customHeader: string | null; + + /** + * アプリ通知のicon(URL) + * (省略時はアプリアイコンで表示されることを期待) + */ + customIcon: string | null; + + /** + * アプリ通知のアプリ(のトークン) + */ + appAccessTokenId: MiAccessToken['id'] | null; +} diff --git a/packages/backend/src/models/entities/Page.ts b/packages/backend/src/models/Page.ts index 6078bc1bc7..3cb986f4ee 100644 --- a/packages/backend/src/models/entities/Page.ts +++ b/packages/backend/src/models/Page.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { DriveFile } from './DriveFile.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiDriveFile } from './DriveFile.js'; -@Entity() +@Entity('page') @Index(['userId', 'name'], { unique: true }) -export class Page { +export class MiPage { @PrimaryColumn(id()) public id: string; @@ -55,25 +60,25 @@ export class Page { ...id(), comment: 'The ID of author.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column({ ...id(), nullable: true, }) - public eyeCatchingImageId: DriveFile['id'] | null; + public eyeCatchingImageId: MiDriveFile['id'] | null; - @ManyToOne(type => DriveFile, { + @ManyToOne(type => MiDriveFile, { onDelete: 'CASCADE', }) @JoinColumn() - public eyeCatchingImage: DriveFile | null; + public eyeCatchingImage: MiDriveFile | null; @Column('jsonb', { default: [], @@ -104,14 +109,14 @@ export class Page { ...id(), array: true, default: '{}', }) - public visibleUserIds: User['id'][]; + public visibleUserIds: MiUser['id'][]; @Column('integer', { default: 0, }) public likedCount: number; - constructor(data: Partial<Page>) { + constructor(data: Partial<MiPage>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/PageLike.ts b/packages/backend/src/models/PageLike.ts new file mode 100644 index 0000000000..92adf9bcc2 --- /dev/null +++ b/packages/backend/src/models/PageLike.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiPage } from './Page.js'; + +@Entity('page_like') +@Index(['userId', 'pageId'], { unique: true }) +export class MiPageLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public pageId: MiPage['id']; + + @ManyToOne(type => MiPage, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public page: MiPage | null; +} diff --git a/packages/backend/src/models/entities/PasswordResetRequest.ts b/packages/backend/src/models/PasswordResetRequest.ts index 939fcc460f..79f2e984b8 100644 --- a/packages/backend/src/models/entities/PasswordResetRequest.ts +++ b/packages/backend/src/models/PasswordResetRequest.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class PasswordResetRequest { +@Entity('password_reset_request') +export class MiPasswordResetRequest { @PrimaryColumn(id()) public id: string; @@ -20,11 +25,11 @@ export class PasswordResetRequest { @Column({ ...id(), }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; } diff --git a/packages/backend/src/models/entities/Poll.ts b/packages/backend/src/models/Poll.ts index ee1d646020..5ce0b9a2fc 100644 --- a/packages/backend/src/models/entities/Poll.ts +++ b/packages/backend/src/models/Poll.ts @@ -1,19 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { noteVisibilities } from '../../types.js'; -import { Note } from './Note.js'; -import type { User } from './User.js'; +import { noteVisibilities } from '@/types.js'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import type { MiUser } from './User.js'; -@Entity() -export class Poll { +@Entity('poll') +export class MiPoll { @PrimaryColumn(id()) - public noteId: Note['id']; + public noteId: MiNote['id']; - @OneToOne(type => Note, { + @OneToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public note: Note | null; + public note: MiNote | null; @Column('timestamp with time zone', { nullable: true, @@ -45,7 +50,7 @@ export class Poll { ...id(), comment: '[Denormalized]', }) - public userId: User['id']; + public userId: MiUser['id']; @Index() @Column('varchar', { @@ -55,7 +60,7 @@ export class Poll { public userHost: string | null; //#endregion - constructor(data: Partial<Poll>) { + constructor(data: Partial<MiPoll>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/entities/PollVote.ts b/packages/backend/src/models/PollVote.ts index d447a7be8f..37cd55fc18 100644 --- a/packages/backend/src/models/entities/PollVote.ts +++ b/packages/backend/src/models/PollVote.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Note } from './Note.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiNote } from './Note.js'; -@Entity() +@Entity('poll_vote') @Index(['userId', 'noteId', 'choice'], { unique: true }) -export class PollVote { +export class MiPollVote { @PrimaryColumn(id()) public id: string; @@ -17,23 +22,23 @@ export class PollVote { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column(id()) - public noteId: Note['id']; + public noteId: MiNote['id']; - @ManyToOne(type => Note, { + @ManyToOne(type => MiNote, { onDelete: 'CASCADE', }) @JoinColumn() - public note: Note | null; + public note: MiNote | null; @Column('integer') public choice: number; diff --git a/packages/backend/src/models/PromoNote.ts b/packages/backend/src/models/PromoNote.ts new file mode 100644 index 0000000000..f4425fe88b --- /dev/null +++ b/packages/backend/src/models/PromoNote.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import type { MiUser } from './User.js'; + +@Entity('promo_note') +export class MiPromoNote { + @PrimaryColumn(id()) + public noteId: MiNote['id']; + + @OneToOne(type => MiNote, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: MiNote | null; + + @Column('timestamp with time zone') + public expiresAt: Date; + + //#region Denormalized fields + @Index() + @Column({ + ...id(), + comment: '[Denormalized]', + }) + public userId: MiUser['id']; + //#endregion +} diff --git a/packages/backend/src/models/PromoRead.ts b/packages/backend/src/models/PromoRead.ts new file mode 100644 index 0000000000..09ebfc8346 --- /dev/null +++ b/packages/backend/src/models/PromoRead.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import { MiUser } from './User.js'; + +@Entity('promo_read') +@Index(['userId', 'noteId'], { unique: true }) +export class MiPromoRead { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the PromoRead.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public noteId: MiNote['id']; + + @ManyToOne(type => MiNote, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: MiNote | null; +} diff --git a/packages/backend/src/models/entities/RegistrationTicket.ts b/packages/backend/src/models/RegistrationTicket.ts index 4c42b20be8..d94f465916 100644 --- a/packages/backend/src/models/entities/RegistrationTicket.ts +++ b/packages/backend/src/models/RegistrationTicket.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class RegistrationTicket { +@Entity('registration_ticket') +export class MiRegistrationTicket { @PrimaryColumn(id()) public id: string; @@ -21,31 +26,31 @@ export class RegistrationTicket { @Column('timestamp with time zone') public createdAt: Date; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public createdBy: User | null; + public createdBy: MiUser | null; @Index() @Column({ ...id(), nullable: true, }) - public createdById: User['id'] | null; + public createdById: MiUser['id'] | null; - @OneToOne(type => User, { + @OneToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public usedBy: User | null; + public usedBy: MiUser | null; @Index() @Column({ ...id(), nullable: true, }) - public usedById: User['id'] | null; + public usedById: MiUser['id'] | null; @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/entities/RegistryItem.ts b/packages/backend/src/models/RegistryItem.ts index 670a236ea0..fdce57c467 100644 --- a/packages/backend/src/models/entities/RegistryItem.ts +++ b/packages/backend/src/models/RegistryItem.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; // TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい -@Entity() -export class RegistryItem { +@Entity('registry_item') +export class MiRegistryItem { @PrimaryColumn(id()) public id: string; @@ -23,13 +28,13 @@ export class RegistryItem { ...id(), comment: 'The owner ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 1024, diff --git a/packages/backend/src/models/entities/Relay.ts b/packages/backend/src/models/Relay.ts index 94d1929574..293fccecfc 100644 --- a/packages/backend/src/models/entities/Relay.ts +++ b/packages/backend/src/models/Relay.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; -@Entity() -export class Relay { +@Entity('relay') +export class MiRelay { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/entities/RenoteMuting.ts b/packages/backend/src/models/RenoteMuting.ts index 2f803a5fa8..d2a36249dc 100644 --- a/packages/backend/src/models/entities/RenoteMuting.ts +++ b/packages/backend/src/models/RenoteMuting.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('renote_muting') @Index(['muterId', 'muteeId'], { unique: true }) -export class RenoteMuting { +export class MiRenoteMuting { @PrimaryColumn(id()) public id: string; @@ -19,24 +24,24 @@ export class RenoteMuting { ...id(), comment: 'The mutee user ID.', }) - public muteeId: User['id']; + public muteeId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public mutee: User | null; + public mutee: MiUser | null; @Index() @Column({ ...id(), comment: 'The muter user ID.', }) - public muterId: User['id']; + public muterId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public muter: User | null; + public muter: MiUser | null; } diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 4231acc046..766e7ce21c 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,402 +1,401 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, RenoteMuting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelFavorite, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment, ClipFavorite, UserMemo, UserListFavorite } from './index.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMutedNote, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListJoining, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; const $usersRepository: Provider = { provide: DI.usersRepository, - useFactory: (db: DataSource) => db.getRepository(User), + useFactory: (db: DataSource) => db.getRepository(MiUser), inject: [DI.db], }; const $notesRepository: Provider = { provide: DI.notesRepository, - useFactory: (db: DataSource) => db.getRepository(Note), + useFactory: (db: DataSource) => db.getRepository(MiNote), inject: [DI.db], }; const $announcementsRepository: Provider = { provide: DI.announcementsRepository, - useFactory: (db: DataSource) => db.getRepository(Announcement), + useFactory: (db: DataSource) => db.getRepository(MiAnnouncement), inject: [DI.db], }; const $announcementReadsRepository: Provider = { provide: DI.announcementReadsRepository, - useFactory: (db: DataSource) => db.getRepository(AnnouncementRead), + useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead), inject: [DI.db], }; const $appsRepository: Provider = { provide: DI.appsRepository, - useFactory: (db: DataSource) => db.getRepository(App), + useFactory: (db: DataSource) => db.getRepository(MiApp), inject: [DI.db], }; const $noteFavoritesRepository: Provider = { provide: DI.noteFavoritesRepository, - useFactory: (db: DataSource) => db.getRepository(NoteFavorite), + useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite), inject: [DI.db], }; const $noteThreadMutingsRepository: Provider = { provide: DI.noteThreadMutingsRepository, - useFactory: (db: DataSource) => db.getRepository(NoteThreadMuting), + useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting), inject: [DI.db], }; const $noteReactionsRepository: Provider = { provide: DI.noteReactionsRepository, - useFactory: (db: DataSource) => db.getRepository(NoteReaction), + useFactory: (db: DataSource) => db.getRepository(MiNoteReaction), inject: [DI.db], }; const $noteUnreadsRepository: Provider = { provide: DI.noteUnreadsRepository, - useFactory: (db: DataSource) => db.getRepository(NoteUnread), + useFactory: (db: DataSource) => db.getRepository(MiNoteUnread), inject: [DI.db], }; const $pollsRepository: Provider = { provide: DI.pollsRepository, - useFactory: (db: DataSource) => db.getRepository(Poll), + useFactory: (db: DataSource) => db.getRepository(MiPoll), inject: [DI.db], }; const $pollVotesRepository: Provider = { provide: DI.pollVotesRepository, - useFactory: (db: DataSource) => db.getRepository(PollVote), + useFactory: (db: DataSource) => db.getRepository(MiPollVote), inject: [DI.db], }; const $userProfilesRepository: Provider = { provide: DI.userProfilesRepository, - useFactory: (db: DataSource) => db.getRepository(UserProfile), + useFactory: (db: DataSource) => db.getRepository(MiUserProfile), inject: [DI.db], }; const $userKeypairsRepository: Provider = { provide: DI.userKeypairsRepository, - useFactory: (db: DataSource) => db.getRepository(UserKeypair), + useFactory: (db: DataSource) => db.getRepository(MiUserKeypair), inject: [DI.db], }; const $userPendingsRepository: Provider = { provide: DI.userPendingsRepository, - useFactory: (db: DataSource) => db.getRepository(UserPending), - inject: [DI.db], -}; - -const $attestationChallengesRepository: Provider = { - provide: DI.attestationChallengesRepository, - useFactory: (db: DataSource) => db.getRepository(AttestationChallenge), + useFactory: (db: DataSource) => db.getRepository(MiUserPending), inject: [DI.db], }; const $userSecurityKeysRepository: Provider = { provide: DI.userSecurityKeysRepository, - useFactory: (db: DataSource) => db.getRepository(UserSecurityKey), + useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey), inject: [DI.db], }; const $userPublickeysRepository: Provider = { provide: DI.userPublickeysRepository, - useFactory: (db: DataSource) => db.getRepository(UserPublickey), + useFactory: (db: DataSource) => db.getRepository(MiUserPublickey), inject: [DI.db], }; const $userListsRepository: Provider = { provide: DI.userListsRepository, - useFactory: (db: DataSource) => db.getRepository(UserList), + useFactory: (db: DataSource) => db.getRepository(MiUserList), inject: [DI.db], }; const $userListFavoritesRepository: Provider = { provide: DI.userListFavoritesRepository, - useFactory: (db: DataSource) => db.getRepository(UserListFavorite), + useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite), inject: [DI.db], }; const $userListJoiningsRepository: Provider = { provide: DI.userListJoiningsRepository, - useFactory: (db: DataSource) => db.getRepository(UserListJoining), + useFactory: (db: DataSource) => db.getRepository(MiUserListJoining), inject: [DI.db], }; const $userNotePiningsRepository: Provider = { provide: DI.userNotePiningsRepository, - useFactory: (db: DataSource) => db.getRepository(UserNotePining), + useFactory: (db: DataSource) => db.getRepository(MiUserNotePining), inject: [DI.db], }; const $userIpsRepository: Provider = { provide: DI.userIpsRepository, - useFactory: (db: DataSource) => db.getRepository(UserIp), + useFactory: (db: DataSource) => db.getRepository(MiUserIp), inject: [DI.db], }; const $usedUsernamesRepository: Provider = { provide: DI.usedUsernamesRepository, - useFactory: (db: DataSource) => db.getRepository(UsedUsername), + useFactory: (db: DataSource) => db.getRepository(MiUsedUsername), inject: [DI.db], }; const $followingsRepository: Provider = { provide: DI.followingsRepository, - useFactory: (db: DataSource) => db.getRepository(Following), + useFactory: (db: DataSource) => db.getRepository(MiFollowing), inject: [DI.db], }; const $followRequestsRepository: Provider = { provide: DI.followRequestsRepository, - useFactory: (db: DataSource) => db.getRepository(FollowRequest), + useFactory: (db: DataSource) => db.getRepository(MiFollowRequest), inject: [DI.db], }; const $instancesRepository: Provider = { provide: DI.instancesRepository, - useFactory: (db: DataSource) => db.getRepository(Instance), + useFactory: (db: DataSource) => db.getRepository(MiInstance), inject: [DI.db], }; const $emojisRepository: Provider = { provide: DI.emojisRepository, - useFactory: (db: DataSource) => db.getRepository(Emoji), + useFactory: (db: DataSource) => db.getRepository(MiEmoji), inject: [DI.db], }; const $driveFilesRepository: Provider = { provide: DI.driveFilesRepository, - useFactory: (db: DataSource) => db.getRepository(DriveFile), + useFactory: (db: DataSource) => db.getRepository(MiDriveFile), inject: [DI.db], }; const $driveFoldersRepository: Provider = { provide: DI.driveFoldersRepository, - useFactory: (db: DataSource) => db.getRepository(DriveFolder), + useFactory: (db: DataSource) => db.getRepository(MiDriveFolder), inject: [DI.db], }; const $metasRepository: Provider = { provide: DI.metasRepository, - useFactory: (db: DataSource) => db.getRepository(Meta), + useFactory: (db: DataSource) => db.getRepository(MiMeta), inject: [DI.db], }; const $mutingsRepository: Provider = { provide: DI.mutingsRepository, - useFactory: (db: DataSource) => db.getRepository(Muting), + useFactory: (db: DataSource) => db.getRepository(MiMuting), inject: [DI.db], }; const $renoteMutingsRepository: Provider = { provide: DI.renoteMutingsRepository, - useFactory: (db: DataSource) => db.getRepository(RenoteMuting), + useFactory: (db: DataSource) => db.getRepository(MiRenoteMuting), inject: [DI.db], }; const $blockingsRepository: Provider = { provide: DI.blockingsRepository, - useFactory: (db: DataSource) => db.getRepository(Blocking), + useFactory: (db: DataSource) => db.getRepository(MiBlocking), inject: [DI.db], }; const $swSubscriptionsRepository: Provider = { provide: DI.swSubscriptionsRepository, - useFactory: (db: DataSource) => db.getRepository(SwSubscription), + useFactory: (db: DataSource) => db.getRepository(MiSwSubscription), inject: [DI.db], }; const $hashtagsRepository: Provider = { provide: DI.hashtagsRepository, - useFactory: (db: DataSource) => db.getRepository(Hashtag), + useFactory: (db: DataSource) => db.getRepository(MiHashtag), inject: [DI.db], }; const $abuseUserReportsRepository: Provider = { provide: DI.abuseUserReportsRepository, - useFactory: (db: DataSource) => db.getRepository(AbuseUserReport), + useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport), inject: [DI.db], }; const $registrationTicketsRepository: Provider = { provide: DI.registrationTicketsRepository, - useFactory: (db: DataSource) => db.getRepository(RegistrationTicket), + useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket), inject: [DI.db], }; const $authSessionsRepository: Provider = { provide: DI.authSessionsRepository, - useFactory: (db: DataSource) => db.getRepository(AuthSession), + useFactory: (db: DataSource) => db.getRepository(MiAuthSession), inject: [DI.db], }; const $accessTokensRepository: Provider = { provide: DI.accessTokensRepository, - useFactory: (db: DataSource) => db.getRepository(AccessToken), + useFactory: (db: DataSource) => db.getRepository(MiAccessToken), inject: [DI.db], }; const $signinsRepository: Provider = { provide: DI.signinsRepository, - useFactory: (db: DataSource) => db.getRepository(Signin), + useFactory: (db: DataSource) => db.getRepository(MiSignin), inject: [DI.db], }; const $pagesRepository: Provider = { provide: DI.pagesRepository, - useFactory: (db: DataSource) => db.getRepository(Page), + useFactory: (db: DataSource) => db.getRepository(MiPage), inject: [DI.db], }; const $pageLikesRepository: Provider = { provide: DI.pageLikesRepository, - useFactory: (db: DataSource) => db.getRepository(PageLike), + useFactory: (db: DataSource) => db.getRepository(MiPageLike), inject: [DI.db], }; const $galleryPostsRepository: Provider = { provide: DI.galleryPostsRepository, - useFactory: (db: DataSource) => db.getRepository(GalleryPost), + useFactory: (db: DataSource) => db.getRepository(MiGalleryPost), inject: [DI.db], }; const $galleryLikesRepository: Provider = { provide: DI.galleryLikesRepository, - useFactory: (db: DataSource) => db.getRepository(GalleryLike), + useFactory: (db: DataSource) => db.getRepository(MiGalleryLike), inject: [DI.db], }; const $moderationLogsRepository: Provider = { provide: DI.moderationLogsRepository, - useFactory: (db: DataSource) => db.getRepository(ModerationLog), + useFactory: (db: DataSource) => db.getRepository(MiModerationLog), inject: [DI.db], }; const $clipsRepository: Provider = { provide: DI.clipsRepository, - useFactory: (db: DataSource) => db.getRepository(Clip), + useFactory: (db: DataSource) => db.getRepository(MiClip), inject: [DI.db], }; const $clipNotesRepository: Provider = { provide: DI.clipNotesRepository, - useFactory: (db: DataSource) => db.getRepository(ClipNote), + useFactory: (db: DataSource) => db.getRepository(MiClipNote), inject: [DI.db], }; const $clipFavoritesRepository: Provider = { provide: DI.clipFavoritesRepository, - useFactory: (db: DataSource) => db.getRepository(ClipFavorite), + useFactory: (db: DataSource) => db.getRepository(MiClipFavorite), inject: [DI.db], }; const $antennasRepository: Provider = { provide: DI.antennasRepository, - useFactory: (db: DataSource) => db.getRepository(Antenna), + useFactory: (db: DataSource) => db.getRepository(MiAntenna), inject: [DI.db], }; const $promoNotesRepository: Provider = { provide: DI.promoNotesRepository, - useFactory: (db: DataSource) => db.getRepository(PromoNote), + useFactory: (db: DataSource) => db.getRepository(MiPromoNote), inject: [DI.db], }; const $promoReadsRepository: Provider = { provide: DI.promoReadsRepository, - useFactory: (db: DataSource) => db.getRepository(PromoRead), + useFactory: (db: DataSource) => db.getRepository(MiPromoRead), inject: [DI.db], }; const $relaysRepository: Provider = { provide: DI.relaysRepository, - useFactory: (db: DataSource) => db.getRepository(Relay), + useFactory: (db: DataSource) => db.getRepository(MiRelay), inject: [DI.db], }; const $mutedNotesRepository: Provider = { provide: DI.mutedNotesRepository, - useFactory: (db: DataSource) => db.getRepository(MutedNote), + useFactory: (db: DataSource) => db.getRepository(MiMutedNote), inject: [DI.db], }; const $channelsRepository: Provider = { provide: DI.channelsRepository, - useFactory: (db: DataSource) => db.getRepository(Channel), + useFactory: (db: DataSource) => db.getRepository(MiChannel), inject: [DI.db], }; const $channelFollowingsRepository: Provider = { provide: DI.channelFollowingsRepository, - useFactory: (db: DataSource) => db.getRepository(ChannelFollowing), + useFactory: (db: DataSource) => db.getRepository(MiChannelFollowing), inject: [DI.db], }; const $channelFavoritesRepository: Provider = { provide: DI.channelFavoritesRepository, - useFactory: (db: DataSource) => db.getRepository(ChannelFavorite), + useFactory: (db: DataSource) => db.getRepository(MiChannelFavorite), inject: [DI.db], }; const $registryItemsRepository: Provider = { provide: DI.registryItemsRepository, - useFactory: (db: DataSource) => db.getRepository(RegistryItem), + useFactory: (db: DataSource) => db.getRepository(MiRegistryItem), inject: [DI.db], }; const $webhooksRepository: Provider = { provide: DI.webhooksRepository, - useFactory: (db: DataSource) => db.getRepository(Webhook), + useFactory: (db: DataSource) => db.getRepository(MiWebhook), inject: [DI.db], }; const $adsRepository: Provider = { provide: DI.adsRepository, - useFactory: (db: DataSource) => db.getRepository(Ad), + useFactory: (db: DataSource) => db.getRepository(MiAd), inject: [DI.db], }; const $passwordResetRequestsRepository: Provider = { provide: DI.passwordResetRequestsRepository, - useFactory: (db: DataSource) => db.getRepository(PasswordResetRequest), + useFactory: (db: DataSource) => db.getRepository(MiPasswordResetRequest), inject: [DI.db], }; const $retentionAggregationsRepository: Provider = { provide: DI.retentionAggregationsRepository, - useFactory: (db: DataSource) => db.getRepository(RetentionAggregation), + useFactory: (db: DataSource) => db.getRepository(MiRetentionAggregation), inject: [DI.db], }; const $flashsRepository: Provider = { provide: DI.flashsRepository, - useFactory: (db: DataSource) => db.getRepository(Flash), + useFactory: (db: DataSource) => db.getRepository(MiFlash), inject: [DI.db], }; const $flashLikesRepository: Provider = { provide: DI.flashLikesRepository, - useFactory: (db: DataSource) => db.getRepository(FlashLike), + useFactory: (db: DataSource) => db.getRepository(MiFlashLike), inject: [DI.db], }; const $rolesRepository: Provider = { provide: DI.rolesRepository, - useFactory: (db: DataSource) => db.getRepository(Role), + useFactory: (db: DataSource) => db.getRepository(MiRole), inject: [DI.db], }; const $roleAssignmentsRepository: Provider = { provide: DI.roleAssignmentsRepository, - useFactory: (db: DataSource) => db.getRepository(RoleAssignment), + useFactory: (db: DataSource) => db.getRepository(MiRoleAssignment), inject: [DI.db], }; const $userMemosRepository: Provider = { provide: DI.userMemosRepository, - useFactory: (db: DataSource) => db.getRepository(UserMemo), + useFactory: (db: DataSource) => db.getRepository(MiUserMemo), inject: [DI.db], }; @@ -418,7 +417,6 @@ const $userMemosRepository: Provider = { $userProfilesRepository, $userKeypairsRepository, $userPendingsRepository, - $attestationChallengesRepository, $userSecurityKeysRepository, $userPublickeysRepository, $userListsRepository, @@ -486,7 +484,6 @@ const $userMemosRepository: Provider = { $userProfilesRepository, $userKeypairsRepository, $userPendingsRepository, - $attestationChallengesRepository, $userSecurityKeysRepository, $userPublickeysRepository, $userListsRepository, diff --git a/packages/backend/src/models/entities/RetentionAggregation.ts b/packages/backend/src/models/RetentionAggregation.ts index c7bf38b3af..9da401597c 100644 --- a/packages/backend/src/models/entities/RetentionAggregation.ts +++ b/packages/backend/src/models/RetentionAggregation.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { id } from '../id.js'; -import type { User } from './User.js'; +import { id } from './util/id.js'; +import type { MiUser } from './User.js'; -@Entity() -export class RetentionAggregation { +@Entity('retention_aggregation') +export class MiRetentionAggregation { @PrimaryColumn(id()) public id: string; @@ -28,7 +33,7 @@ export class RetentionAggregation { ...id(), array: true, }) - public userIds: User['id'][]; + public userIds: MiUser['id'][]; @Column('integer', { }) diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/Role.ts index 61f40d59da..df7541db3d 100644 --- a/packages/backend/src/models/entities/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Column, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; type CondFormulaValueAnd = { type: 'and'; @@ -79,8 +84,8 @@ export type RoleCondFormulaValue = CondFormulaValueNotesLessThanOrEq | CondFormulaValueNotesMoreThanOrEq; -@Entity() -export class Role { +@Entity('role') +export class MiRole { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/entities/RoleAssignment.ts b/packages/backend/src/models/RoleAssignment.ts index 972810940f..4e5322c60b 100644 --- a/packages/backend/src/models/entities/RoleAssignment.ts +++ b/packages/backend/src/models/RoleAssignment.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Role } from './Role.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiRole } from './Role.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('role_assignment') @Index(['userId', 'roleId'], { unique: true }) -export class RoleAssignment { +export class MiRoleAssignment { @PrimaryColumn(id()) public id: string; @@ -19,26 +24,26 @@ export class RoleAssignment { ...id(), comment: 'The user ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column({ ...id(), comment: 'The role ID.', }) - public roleId: Role['id']; + public roleId: MiRole['id']; - @ManyToOne(type => Role, { + @ManyToOne(type => MiRole, { onDelete: 'CASCADE', }) @JoinColumn() - public role: Role | null; + public role: MiRole | null; @Index() @Column('timestamp with time zone', { diff --git a/packages/backend/src/models/entities/Signin.ts b/packages/backend/src/models/Signin.ts index 380bf028a6..a8b1a45c53 100644 --- a/packages/backend/src/models/entities/Signin.ts +++ b/packages/backend/src/models/Signin.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class Signin { +@Entity('signin') +export class MiSignin { @PrimaryColumn(id()) public id: string; @@ -14,13 +19,13 @@ export class Signin { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/entities/SwSubscription.ts b/packages/backend/src/models/SwSubscription.ts index 0658294983..be1e4e3687 100644 --- a/packages/backend/src/models/entities/SwSubscription.ts +++ b/packages/backend/src/models/SwSubscription.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class SwSubscription { +@Entity('sw_subscription') +export class MiSwSubscription { @PrimaryColumn(id()) public id: string; @@ -12,13 +17,13 @@ export class SwSubscription { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 512, diff --git a/packages/backend/src/models/entities/UsedUsername.ts b/packages/backend/src/models/UsedUsername.ts index eb90bef6ca..c75bf424c1 100644 --- a/packages/backend/src/models/entities/UsedUsername.ts +++ b/packages/backend/src/models/UsedUsername.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Column } from 'typeorm'; -@Entity() -export class UsedUsername { +@Entity('used_username') +export class MiUsedUsername { @PrimaryColumn('varchar', { length: 128, }) @@ -10,7 +15,7 @@ export class UsedUsername { @Column('timestamp with time zone') public createdAt: Date; - constructor(data: Partial<UsedUsername>) { + constructor(data: Partial<MiUsedUsername>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/User.ts index 6669890cf6..b040d302ce 100644 --- a/packages/backend/src/models/entities/User.ts +++ b/packages/backend/src/models/User.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; -import { DriveFile } from './DriveFile.js'; +import { id } from './util/id.js'; +import { MiDriveFile } from './DriveFile.js'; -@Entity() +@Entity('user') @Index(['usernameLower', 'host'], { unique: true }) -export class User { +export class MiUser { @PrimaryColumn(id()) public id: string; @@ -98,26 +103,26 @@ export class User { nullable: true, comment: 'The ID of avatar DriveFile.', }) - public avatarId: DriveFile['id'] | null; + public avatarId: MiDriveFile['id'] | null; - @OneToOne(type => DriveFile, { + @OneToOne(type => MiDriveFile, { onDelete: 'SET NULL', }) @JoinColumn() - public avatar: DriveFile | null; + public avatar: MiDriveFile | null; @Column({ ...id(), nullable: true, comment: 'The ID of banner DriveFile.', }) - public bannerId: DriveFile['id'] | null; + public bannerId: MiDriveFile['id'] | null; - @OneToOne(type => DriveFile, { + @OneToOne(type => MiDriveFile, { onDelete: 'SET NULL', }) @JoinColumn() - public banner: DriveFile | null; + public banner: MiDriveFile | null; @Column('varchar', { length: 512, nullable: true, @@ -239,7 +244,7 @@ export class User { }) public token: string | null; - constructor(data: Partial<User>) { + constructor(data: Partial<MiUser>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { @@ -248,24 +253,24 @@ export class User { } } -export type LocalUser = User & { +export type MiLocalUser = MiUser & { host: null; uri: null; } -export type PartialLocalUser = Partial<User> & { - id: User['id']; +export type MiPartialLocalUser = Partial<MiUser> & { + id: MiUser['id']; host: null; uri: null; } -export type RemoteUser = User & { +export type MiRemoteUser = MiUser & { host: string; uri: string; } -export type PartialRemoteUser = Partial<User> & { - id: User['id']; +export type MiPartialRemoteUser = Partial<MiUser> & { + id: MiUser['id']; host: string; uri: string; } diff --git a/packages/backend/src/models/entities/UserIp.ts b/packages/backend/src/models/UserIp.ts index 628e3d0361..60a7bc8b01 100644 --- a/packages/backend/src/models/entities/UserIp.ts +++ b/packages/backend/src/models/UserIp.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Index, Column, PrimaryGeneratedColumn } from 'typeorm'; -import { id } from '../id.js'; -import type { User } from './User.js'; +import { id } from './util/id.js'; +import type { MiUser } from './User.js'; -@Entity() +@Entity('user_ip') @Index(['userId', 'ip'], { unique: true }) -export class UserIp { +export class MiUserIp { @PrimaryGeneratedColumn() public id: string; @@ -14,7 +19,7 @@ export class UserIp { @Index() @Column(id()) - public userId: User['id']; + public userId: MiUser['id']; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/entities/UserKeypair.ts b/packages/backend/src/models/UserKeypair.ts index 3cd02d3c4f..a316dbaeb4 100644 --- a/packages/backend/src/models/entities/UserKeypair.ts +++ b/packages/backend/src/models/UserKeypair.ts @@ -1,17 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class UserKeypair { +@Entity('user_keypair') +export class MiUserKeypair { @PrimaryColumn(id()) - public userId: User['id']; + public userId: MiUser['id']; - @OneToOne(type => User, { + @OneToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 4096, @@ -23,7 +28,7 @@ export class UserKeypair { }) public privateKey: string; - constructor(data: Partial<UserKeypair>) { + constructor(data: Partial<MiUserKeypair>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/entities/UserList.ts b/packages/backend/src/models/UserList.ts index 94f3dc3cb3..9af85af97e 100644 --- a/packages/backend/src/models/entities/UserList.ts +++ b/packages/backend/src/models/UserList.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class UserList { +@Entity('user_list') +export class MiUserList { @PrimaryColumn(id()) public id: string; @@ -17,7 +22,7 @@ export class UserList { ...id(), comment: 'The owner ID.', }) - public userId: User['id']; + public userId: MiUser['id']; @Index() @Column('boolean', { @@ -25,11 +30,11 @@ export class UserList { }) public isPublic: boolean; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/UserListFavorite.ts b/packages/backend/src/models/UserListFavorite.ts new file mode 100644 index 0000000000..d0b054b932 --- /dev/null +++ b/packages/backend/src/models/UserListFavorite.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiUserList } from './UserList.js'; + +@Entity('user_list_favorite') +@Index(['userId', 'userListId'], { unique: true }) +export class MiUserListFavorite { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public userListId: MiUserList['id']; + + @ManyToOne(type => MiUserList, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userList: MiUserList | null; +} diff --git a/packages/backend/src/models/entities/UserListJoining.ts b/packages/backend/src/models/UserListJoining.ts index a40793a3e8..4918f2f700 100644 --- a/packages/backend/src/models/entities/UserListJoining.ts +++ b/packages/backend/src/models/UserListJoining.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { UserList } from './UserList.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiUserList } from './UserList.js'; -@Entity() +@Entity('user_list_joining') @Index(['userId', 'userListId'], { unique: true }) -export class UserListJoining { +export class MiUserListJoining { @PrimaryColumn(id()) public id: string; @@ -19,24 +24,24 @@ export class UserListJoining { ...id(), comment: 'The user ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column({ ...id(), comment: 'The list ID.', }) - public userListId: UserList['id']; + public userListId: MiUserList['id']; - @ManyToOne(type => UserList, { + @ManyToOne(type => MiUserList, { onDelete: 'CASCADE', }) @JoinColumn() - public userList: UserList | null; + public userList: MiUserList | null; } diff --git a/packages/backend/src/models/entities/UserMemo.ts b/packages/backend/src/models/UserMemo.ts index 7dc34b4346..ab5e812c44 100644 --- a/packages/backend/src/models/entities/UserMemo.ts +++ b/packages/backend/src/models/UserMemo.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() +@Entity('user_memo') @Index(['userId', 'targetUserId'], { unique: true }) -export class UserMemo { +export class MiUserMemo { @PrimaryColumn(id()) public id: string; @@ -13,26 +18,26 @@ export class UserMemo { ...id(), comment: 'The ID of author.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index() @Column({ ...id(), comment: 'The ID of target user.', }) - public targetUserId: User['id']; + public targetUserId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public targetUser: User | null; + public targetUser: MiUser | null; @Column('varchar', { length: 2048, diff --git a/packages/backend/src/models/UserNotePining.ts b/packages/backend/src/models/UserNotePining.ts new file mode 100644 index 0000000000..1d50a5068e --- /dev/null +++ b/packages/backend/src/models/UserNotePining.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiNote } from './Note.js'; +import { MiUser } from './User.js'; + +@Entity('user_note_pining') +@Index(['userId', 'noteId'], { unique: true }) +export class MiUserNotePining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserNotePinings.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column(id()) + public noteId: MiNote['id']; + + @ManyToOne(type => MiNote, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: MiNote | null; +} diff --git a/packages/backend/src/models/entities/UserPending.ts b/packages/backend/src/models/UserPending.ts index 7637948841..b15ededa14 100644 --- a/packages/backend/src/models/entities/UserPending.ts +++ b/packages/backend/src/models/UserPending.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; +import { id } from './util/id.js'; -@Entity() -export class UserPending { +@Entity('user_pending') +export class MiUserPending { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index c4ed9db9bb..e4405c9da7 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -1,21 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/types.js'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Page } from './Page.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiPage } from './Page.js'; // TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも // ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン -@Entity() -export class UserProfile { +@Entity('user_profile') +export class MiUserProfile { @PrimaryColumn(id()) - public userId: User['id']; + public userId: MiUser['id']; - @OneToOne(type => User, { + @OneToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, nullable: true, @@ -44,6 +49,12 @@ export class UserProfile { }[]; @Column('varchar', { + array: true, + default: '{}', + }) + public verifiedLinks: string[]; + + @Column('varchar', { length: 32, nullable: true, }) public lang: string | null; @@ -96,6 +107,11 @@ export class UserProfile { }) public twoFactorSecret: string | null; + @Column('varchar', { + nullable: true, array: true, + }) + public twoFactorBackupSecret: string[] | null; + @Column('boolean', { default: false, }) @@ -181,13 +197,13 @@ export class UserProfile { ...id(), nullable: true, }) - public pinnedPageId: Page['id'] | null; + public pinnedPageId: MiPage['id'] | null; - @OneToOne(type => Page, { + @OneToOne(type => MiPage, { onDelete: 'SET NULL', }) @JoinColumn() - public pinnedPage: Page | null; + public pinnedPage: MiPage | null; @Index() @Column('boolean', { @@ -239,7 +255,7 @@ export class UserProfile { public userHost: string | null; //#endregion - constructor(data: Partial<UserProfile>) { + constructor(data: Partial<MiUserProfile>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/entities/UserPublickey.ts b/packages/backend/src/models/UserPublickey.ts index 7b505e5b4c..33de73c636 100644 --- a/packages/backend/src/models/entities/UserPublickey.ts +++ b/packages/backend/src/models/UserPublickey.ts @@ -1,17 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; -@Entity() -export class UserPublickey { +@Entity('user_publickey') +export class MiUserPublickey { @PrimaryColumn(id()) - public userId: User['id']; + public userId: MiUser['id']; - @OneToOne(type => User, { + @OneToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Index({ unique: true }) @Column('varchar', { @@ -24,7 +29,7 @@ export class UserPublickey { }) public keyPem: string; - constructor(data: Partial<UserPublickey>) { + constructor(data: Partial<MiUserPublickey>) { if (data == null) return; for (const [k, v] of Object.entries(data)) { diff --git a/packages/backend/src/models/UserSecurityKey.ts b/packages/backend/src/models/UserSecurityKey.ts new file mode 100644 index 0000000000..02c29bfbb5 --- /dev/null +++ b/packages/backend/src/models/UserSecurityKey.ts @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; + +@Entity('user_security_key') +export class MiUserSecurityKey { + @PrimaryColumn('varchar', { + comment: 'Variable-length id given to navigator.credentials.get()', + }) + public id: string; + + @Index() + @Column(id()) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Column('varchar', { + comment: 'User-defined name for this key', + length: 30, + }) + public name: string; + + @Index() + @Column('varchar', { + comment: 'The public key of the UserSecurityKey, hex-encoded.', + }) + public publicKey: string; + + @Column('bigint', { + comment: 'The number of times the UserSecurityKey was validated.', + default: 0, + }) + public counter: number; + + @Column('timestamp with time zone', { + comment: 'Timestamp of the last time the UserSecurityKey was used.', + default: () => 'now()', + }) + public lastUsed: Date; + + @Column('varchar', { + comment: 'The type of Backup Eligibility in authenticator data', + length: 32, nullable: true, + }) + public credentialDeviceType: string | null; + + @Column('boolean', { + comment: 'Whether or not the credential has been backed up', + nullable: true, + }) + public credentialBackedUp: boolean | null; + + @Column('varchar', { + comment: 'The type of the credential returned by the browser', + length: 32, array: true, nullable: true, + }) + public transports: string[] | null; + + constructor(data: Partial<MiUserSecurityKey>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/Webhook.ts b/packages/backend/src/models/Webhook.ts index eabb604de9..5b009c18a6 100644 --- a/packages/backend/src/models/entities/Webhook.ts +++ b/packages/backend/src/models/Webhook.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const; -@Entity() -export class Webhook { +@Entity('webhook') +export class MiWebhook { @PrimaryColumn(id()) public id: string; @@ -19,13 +24,13 @@ export class Webhook { ...id(), comment: 'The owner ID.', }) - public userId: User['id']; + public userId: MiUser['id']; - @ManyToOne(type => User, { + @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) @JoinColumn() - public user: User | null; + public user: MiUser | null; @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts new file mode 100644 index 0000000000..6be7bd0df6 --- /dev/null +++ b/packages/backend/src/models/_.ts @@ -0,0 +1,205 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; +import { MiAccessToken } from '@/models/AccessToken.js'; +import { MiAd } from '@/models/Ad.js'; +import { MiAnnouncement } from '@/models/Announcement.js'; +import { MiAnnouncementRead } from '@/models/AnnouncementRead.js'; +import { MiAntenna } from '@/models/Antenna.js'; +import { MiApp } from '@/models/App.js'; +import { MiAuthSession } from '@/models/AuthSession.js'; +import { MiBlocking } from '@/models/Blocking.js'; +import { MiChannelFollowing } from '@/models/ChannelFollowing.js'; +import { MiChannelFavorite } from '@/models/ChannelFavorite.js'; +import { MiClip } from '@/models/Clip.js'; +import { MiClipNote } from '@/models/ClipNote.js'; +import { MiClipFavorite } from '@/models/ClipFavorite.js'; +import { MiDriveFile } from '@/models/DriveFile.js'; +import { MiDriveFolder } from '@/models/DriveFolder.js'; +import { MiEmoji } from '@/models/Emoji.js'; +import { MiFollowing } from '@/models/Following.js'; +import { MiFollowRequest } from '@/models/FollowRequest.js'; +import { MiGalleryLike } from '@/models/GalleryLike.js'; +import { MiGalleryPost } from '@/models/GalleryPost.js'; +import { MiHashtag } from '@/models/Hashtag.js'; +import { MiInstance } from '@/models/Instance.js'; +import { MiMeta } from '@/models/Meta.js'; +import { MiModerationLog } from '@/models/ModerationLog.js'; +import { MiMutedNote } from '@/models/MutedNote.js'; +import { MiMuting } from '@/models/Muting.js'; +import { MiRenoteMuting } from '@/models/RenoteMuting.js'; +import { MiNote } from '@/models/Note.js'; +import { MiNoteFavorite } from '@/models/NoteFavorite.js'; +import { MiNoteReaction } from '@/models/NoteReaction.js'; +import { MiNoteThreadMuting } from '@/models/NoteThreadMuting.js'; +import { MiNoteUnread } from '@/models/NoteUnread.js'; +import { MiPage } from '@/models/Page.js'; +import { MiPageLike } from '@/models/PageLike.js'; +import { MiPasswordResetRequest } from '@/models/PasswordResetRequest.js'; +import { MiPoll } from '@/models/Poll.js'; +import { MiPollVote } from '@/models/PollVote.js'; +import { MiPromoNote } from '@/models/PromoNote.js'; +import { MiPromoRead } from '@/models/PromoRead.js'; +import { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; +import { MiRegistryItem } from '@/models/RegistryItem.js'; +import { MiRelay } from '@/models/Relay.js'; +import { MiSignin } from '@/models/Signin.js'; +import { MiSwSubscription } from '@/models/SwSubscription.js'; +import { MiUsedUsername } from '@/models/UsedUsername.js'; +import { MiUser } from '@/models/User.js'; +import { MiUserIp } from '@/models/UserIp.js'; +import { MiUserKeypair } from '@/models/UserKeypair.js'; +import { MiUserList } from '@/models/UserList.js'; +import { MiUserListJoining } from '@/models/UserListJoining.js'; +import { MiUserNotePining } from '@/models/UserNotePining.js'; +import { MiUserPending } from '@/models/UserPending.js'; +import { MiUserProfile } from '@/models/UserProfile.js'; +import { MiUserPublickey } from '@/models/UserPublickey.js'; +import { MiUserSecurityKey } from '@/models/UserSecurityKey.js'; +import { MiUserMemo } from '@/models/UserMemo.js'; +import { MiWebhook } from '@/models/Webhook.js'; +import { MiChannel } from '@/models/Channel.js'; +import { MiRetentionAggregation } from '@/models/RetentionAggregation.js'; +import { MiRole } from '@/models/Role.js'; +import { MiRoleAssignment } from '@/models/RoleAssignment.js'; +import { MiFlash } from '@/models/Flash.js'; +import { MiFlashLike } from '@/models/FlashLike.js'; +import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import type { Repository } from 'typeorm'; + +export { + MiAbuseUserReport, + MiAccessToken, + MiAd, + MiAnnouncement, + MiAnnouncementRead, + MiAntenna, + MiApp, + MiAuthSession, + MiBlocking, + MiChannelFollowing, + MiChannelFavorite, + MiClip, + MiClipNote, + MiClipFavorite, + MiDriveFile, + MiDriveFolder, + MiEmoji, + MiFollowing, + MiFollowRequest, + MiGalleryLike, + MiGalleryPost, + MiHashtag, + MiInstance, + MiMeta, + MiModerationLog, + MiMutedNote, + MiMuting, + MiRenoteMuting, + MiNote, + MiNoteFavorite, + MiNoteReaction, + MiNoteThreadMuting, + MiNoteUnread, + MiPage, + MiPageLike, + MiPasswordResetRequest, + MiPoll, + MiPollVote, + MiPromoNote, + MiPromoRead, + MiRegistrationTicket, + MiRegistryItem, + MiRelay, + MiSignin, + MiSwSubscription, + MiUsedUsername, + MiUser, + MiUserIp, + MiUserKeypair, + MiUserList, + MiUserListFavorite, + MiUserListJoining, + MiUserNotePining, + MiUserPending, + MiUserProfile, + MiUserPublickey, + MiUserSecurityKey, + MiWebhook, + MiChannel, + MiRetentionAggregation, + MiRole, + MiRoleAssignment, + MiFlash, + MiFlashLike, + MiUserMemo, +}; + +export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>; +export type AccessTokensRepository = Repository<MiAccessToken>; +export type AdsRepository = Repository<MiAd>; +export type AnnouncementsRepository = Repository<MiAnnouncement>; +export type AnnouncementReadsRepository = Repository<MiAnnouncementRead>; +export type AntennasRepository = Repository<MiAntenna>; +export type AppsRepository = Repository<MiApp>; +export type AuthSessionsRepository = Repository<MiAuthSession>; +export type BlockingsRepository = Repository<MiBlocking>; +export type ChannelFollowingsRepository = Repository<MiChannelFollowing>; +export type ChannelFavoritesRepository = Repository<MiChannelFavorite>; +export type ClipsRepository = Repository<MiClip>; +export type ClipNotesRepository = Repository<MiClipNote>; +export type ClipFavoritesRepository = Repository<MiClipFavorite>; +export type DriveFilesRepository = Repository<MiDriveFile>; +export type DriveFoldersRepository = Repository<MiDriveFolder>; +export type EmojisRepository = Repository<MiEmoji>; +export type FollowingsRepository = Repository<MiFollowing>; +export type FollowRequestsRepository = Repository<MiFollowRequest>; +export type GalleryLikesRepository = Repository<MiGalleryLike>; +export type GalleryPostsRepository = Repository<MiGalleryPost>; +export type HashtagsRepository = Repository<MiHashtag>; +export type InstancesRepository = Repository<MiInstance>; +export type MetasRepository = Repository<MiMeta>; +export type ModerationLogsRepository = Repository<MiModerationLog>; +export type MutedNotesRepository = Repository<MiMutedNote>; +export type MutingsRepository = Repository<MiMuting>; +export type RenoteMutingsRepository = Repository<MiRenoteMuting>; +export type NotesRepository = Repository<MiNote>; +export type NoteFavoritesRepository = Repository<MiNoteFavorite>; +export type NoteReactionsRepository = Repository<MiNoteReaction>; +export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting>; +export type NoteUnreadsRepository = Repository<MiNoteUnread>; +export type PagesRepository = Repository<MiPage>; +export type PageLikesRepository = Repository<MiPageLike>; +export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest>; +export type PollsRepository = Repository<MiPoll>; +export type PollVotesRepository = Repository<MiPollVote>; +export type PromoNotesRepository = Repository<MiPromoNote>; +export type PromoReadsRepository = Repository<MiPromoRead>; +export type RegistrationTicketsRepository = Repository<MiRegistrationTicket>; +export type RegistryItemsRepository = Repository<MiRegistryItem>; +export type RelaysRepository = Repository<MiRelay>; +export type SigninsRepository = Repository<MiSignin>; +export type SwSubscriptionsRepository = Repository<MiSwSubscription>; +export type UsedUsernamesRepository = Repository<MiUsedUsername>; +export type UsersRepository = Repository<MiUser>; +export type UserIpsRepository = Repository<MiUserIp>; +export type UserKeypairsRepository = Repository<MiUserKeypair>; +export type UserListsRepository = Repository<MiUserList>; +export type UserListFavoritesRepository = Repository<MiUserListFavorite>; +export type UserListJoiningsRepository = Repository<MiUserListJoining>; +export type UserNotePiningsRepository = Repository<MiUserNotePining>; +export type UserPendingsRepository = Repository<MiUserPending>; +export type UserProfilesRepository = Repository<MiUserProfile>; +export type UserPublickeysRepository = Repository<MiUserPublickey>; +export type UserSecurityKeysRepository = Repository<MiUserSecurityKey>; +export type WebhooksRepository = Repository<MiWebhook>; +export type ChannelsRepository = Repository<MiChannel>; +export type RetentionAggregationsRepository = Repository<MiRetentionAggregation>; +export type RolesRepository = Repository<MiRole>; +export type RoleAssignmentsRepository = Repository<MiRoleAssignment>; +export type FlashsRepository = Repository<MiFlash>; +export type FlashLikesRepository = Repository<MiFlashLike>; +export type UserMemoRepository = Repository<MiUserMemo>; diff --git a/packages/backend/src/models/entities/Announcement.ts b/packages/backend/src/models/entities/Announcement.ts deleted file mode 100644 index beb2f82462..0000000000 --- a/packages/backend/src/models/entities/Announcement.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class Announcement { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Announcement.', - }) - public createdAt: Date; - - @Column('timestamp with time zone', { - comment: 'The updated date of the Announcement.', - nullable: true, - }) - public updatedAt: Date | null; - - @Column('varchar', { - length: 8192, nullable: false, - }) - public text: string; - - @Column('varchar', { - length: 256, nullable: false, - }) - public title: string; - - @Column('varchar', { - length: 1024, nullable: true, - }) - public imageUrl: string | null; - - constructor(data: Partial<Announcement>) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/AnnouncementRead.ts b/packages/backend/src/models/entities/AnnouncementRead.ts deleted file mode 100644 index 72cf688800..0000000000 --- a/packages/backend/src/models/entities/AnnouncementRead.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Announcement } from './Announcement.js'; - -@Entity() -@Index(['userId', 'announcementId'], { unique: true }) -export class AnnouncementRead { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the AnnouncementRead.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column(id()) - public announcementId: Announcement['id']; - - @ManyToOne(type => Announcement, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public announcement: Announcement | null; -} diff --git a/packages/backend/src/models/entities/AttestationChallenge.ts b/packages/backend/src/models/entities/AttestationChallenge.ts deleted file mode 100644 index 4795642657..0000000000 --- a/packages/backend/src/models/entities/AttestationChallenge.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; - -@Entity() -export class AttestationChallenge { - @PrimaryColumn(id()) - public id: string; - - @Index() - @PrimaryColumn(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - length: 64, - comment: 'Hex-encoded sha256 hash of the challenge.', - }) - public challenge: string; - - @Column('timestamp with time zone', { - comment: 'The date challenge was created for expiry purposes.', - }) - public createdAt: Date; - - @Column('boolean', { - comment: - 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.', - default: false, - }) - public registrationChallenge: boolean; - - constructor(data: Partial<AttestationChallenge>) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/ChannelFavorite.ts b/packages/backend/src/models/entities/ChannelFavorite.ts deleted file mode 100644 index cfb2c892cf..0000000000 --- a/packages/backend/src/models/entities/ChannelFavorite.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Channel } from './Channel.js'; - -@Entity() -@Index(['userId', 'channelId'], { unique: true }) -export class ChannelFavorite { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelFavorite.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - }) - public channelId: Channel['id']; - - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public channel: Channel | null; - - @Index() - @Column({ - ...id(), - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; -} diff --git a/packages/backend/src/models/entities/ClipFavorite.ts b/packages/backend/src/models/entities/ClipFavorite.ts deleted file mode 100644 index 623471e671..0000000000 --- a/packages/backend/src/models/entities/ClipFavorite.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Clip } from './Clip.js'; - -@Entity() -@Index(['userId', 'clipId'], { unique: true }) -export class ClipFavorite { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public clipId: Clip['id']; - - @ManyToOne(type => Clip, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public clip: Clip | null; -} diff --git a/packages/backend/src/models/entities/ClipNote.ts b/packages/backend/src/models/entities/ClipNote.ts deleted file mode 100644 index bc9ef4b874..0000000000 --- a/packages/backend/src/models/entities/ClipNote.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './Note.js'; -import { Clip } from './Clip.js'; - -@Entity() -@Index(['noteId', 'clipId'], { unique: true }) -export class ClipNote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column({ - ...id(), - comment: 'The note ID.', - }) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Index() - @Column({ - ...id(), - comment: 'The clip ID.', - }) - public clipId: Clip['id']; - - @ManyToOne(type => Clip, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public clip: Clip | null; -} diff --git a/packages/backend/src/models/entities/FlashLike.ts b/packages/backend/src/models/entities/FlashLike.ts deleted file mode 100644 index 81d39191ca..0000000000 --- a/packages/backend/src/models/entities/FlashLike.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Flash } from './Flash.js'; - -@Entity() -@Index(['userId', 'flashId'], { unique: true }) -export class FlashLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public flashId: Flash['id']; - - @ManyToOne(type => Flash, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public flash: Flash | null; -} diff --git a/packages/backend/src/models/entities/GalleryLike.ts b/packages/backend/src/models/entities/GalleryLike.ts deleted file mode 100644 index cc54b528e9..0000000000 --- a/packages/backend/src/models/entities/GalleryLike.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { GalleryPost } from './GalleryPost.js'; - -@Entity() -@Index(['userId', 'postId'], { unique: true }) -export class GalleryLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public postId: GalleryPost['id']; - - @ManyToOne(type => GalleryPost, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public post: GalleryPost | null; -} diff --git a/packages/backend/src/models/entities/NoteFavorite.ts b/packages/backend/src/models/entities/NoteFavorite.ts deleted file mode 100644 index 80c97cb531..0000000000 --- a/packages/backend/src/models/entities/NoteFavorite.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './Note.js'; -import { User } from './User.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class NoteFavorite { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the NoteFavorite.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts deleted file mode 100644 index aa6f997124..0000000000 --- a/packages/backend/src/models/entities/Notification.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { notificationTypes } from '@/types.js'; -import { User } from './User.js'; -import { Note } from './Note.js'; -import { FollowRequest } from './FollowRequest.js'; -import { AccessToken } from './AccessToken.js'; - -export type Notification = { - id: string; - - // RedisのためDateではなくstring - createdAt: string; - - /** - * 通知の送信者(initiator) - */ - notifierId: User['id'] | null; - - /** - * 通知の種類。 - * follow - フォローされた - * mention - 投稿で自分が言及された - * reply - 投稿に返信された - * renote - 投稿がRenoteされた - * quote - 投稿が引用Renoteされた - * reaction - 投稿にリアクションされた - * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した - * receiveFollowRequest - フォローリクエストされた - * followRequestAccepted - 自分の送ったフォローリクエストが承認された - * achievementEarned - 実績を獲得 - * app - アプリ通知 - */ - type: typeof notificationTypes[number]; - - noteId: Note['id'] | null; - - followRequestId: FollowRequest['id'] | null; - - reaction: string | null; - - choice: number | null; - - achievement: string | null; - - /** - * アプリ通知のbody - */ - customBody: string | null; - - /** - * アプリ通知のheader - * (省略時はアプリ名で表示されることを期待) - */ - customHeader: string | null; - - /** - * アプリ通知のicon(URL) - * (省略時はアプリアイコンで表示されることを期待) - */ - customIcon: string | null; - - /** - * アプリ通知のアプリ(のトークン) - */ - appAccessTokenId: AccessToken['id'] | null; -} diff --git a/packages/backend/src/models/entities/PageLike.ts b/packages/backend/src/models/entities/PageLike.ts deleted file mode 100644 index f8c5943a3e..0000000000 --- a/packages/backend/src/models/entities/PageLike.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { Page } from './Page.js'; - -@Entity() -@Index(['userId', 'pageId'], { unique: true }) -export class PageLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public pageId: Page['id']; - - @ManyToOne(type => Page, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public page: Page | null; -} diff --git a/packages/backend/src/models/entities/PromoNote.ts b/packages/backend/src/models/entities/PromoNote.ts deleted file mode 100644 index 958008338a..0000000000 --- a/packages/backend/src/models/entities/PromoNote.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './Note.js'; -import type { User } from './User.js'; - -@Entity() -export class PromoNote { - @PrimaryColumn(id()) - public noteId: Note['id']; - - @OneToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Column('timestamp with time zone') - public expiresAt: Date; - - //#region Denormalized fields - @Index() - @Column({ - ...id(), - comment: '[Denormalized]', - }) - public userId: User['id']; - //#endregion -} diff --git a/packages/backend/src/models/entities/PromoRead.ts b/packages/backend/src/models/entities/PromoRead.ts deleted file mode 100644 index 27f5d0dc11..0000000000 --- a/packages/backend/src/models/entities/PromoRead.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './Note.js'; -import { User } from './User.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class PromoRead { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the PromoRead.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/UserListFavorite.ts b/packages/backend/src/models/entities/UserListFavorite.ts deleted file mode 100644 index e57abb460a..0000000000 --- a/packages/backend/src/models/entities/UserListFavorite.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { UserList } from './UserList.js'; - -@Entity() -@Index(['userId', 'userListId'], { unique: true }) -export class UserListFavorite { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public userListId: UserList['id']; - - @ManyToOne(type => UserList, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userList: UserList | null; -} diff --git a/packages/backend/src/models/entities/UserNotePining.ts b/packages/backend/src/models/entities/UserNotePining.ts deleted file mode 100644 index fee95d4f7d..0000000000 --- a/packages/backend/src/models/entities/UserNotePining.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './Note.js'; -import { User } from './User.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class UserNotePining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserNotePinings.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/UserSecurityKey.ts b/packages/backend/src/models/entities/UserSecurityKey.ts deleted file mode 100644 index 947692a32b..0000000000 --- a/packages/backend/src/models/entities/UserSecurityKey.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; - -@Entity() -export class UserSecurityKey { - @PrimaryColumn('varchar', { - comment: 'Variable-length id given to navigator.credentials.get()', - }) - public id: string; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - comment: - 'Variable-length public key used to verify attestations (hex-encoded).', - }) - public publicKey: string; - - @Column('timestamp with time zone', { - comment: - 'The date of the last time the UserSecurityKey was successfully validated.', - }) - public lastUsed: Date; - - @Column('varchar', { - comment: 'User-defined name for this key', - length: 30, - }) - public name: string; - - constructor(data: Partial<UserSecurityKey>) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/id.ts b/packages/backend/src/models/id.ts deleted file mode 100644 index d614fc5048..0000000000 --- a/packages/backend/src/models/id.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const id = () => ({ - type: 'varchar' as const, - length: 32, -}); diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts deleted file mode 100644 index 627281df73..0000000000 --- a/packages/backend/src/models/index.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; -import { AccessToken } from '@/models/entities/AccessToken.js'; -import { Ad } from '@/models/entities/Ad.js'; -import { Announcement } from '@/models/entities/Announcement.js'; -import { AnnouncementRead } from '@/models/entities/AnnouncementRead.js'; -import { Antenna } from '@/models/entities/Antenna.js'; -import { App } from '@/models/entities/App.js'; -import { AttestationChallenge } from '@/models/entities/AttestationChallenge.js'; -import { AuthSession } from '@/models/entities/AuthSession.js'; -import { Blocking } from '@/models/entities/Blocking.js'; -import { ChannelFollowing } from '@/models/entities/ChannelFollowing.js'; -import { ChannelFavorite } from '@/models/entities/ChannelFavorite.js'; -import { Clip } from '@/models/entities/Clip.js'; -import { ClipNote } from '@/models/entities/ClipNote.js'; -import { ClipFavorite } from '@/models/entities/ClipFavorite.js'; -import { DriveFile } from '@/models/entities/DriveFile.js'; -import { DriveFolder } from '@/models/entities/DriveFolder.js'; -import { Emoji } from '@/models/entities/Emoji.js'; -import { Following } from '@/models/entities/Following.js'; -import { FollowRequest } from '@/models/entities/FollowRequest.js'; -import { GalleryLike } from '@/models/entities/GalleryLike.js'; -import { GalleryPost } from '@/models/entities/GalleryPost.js'; -import { Hashtag } from '@/models/entities/Hashtag.js'; -import { Instance } from '@/models/entities/Instance.js'; -import { Meta } from '@/models/entities/Meta.js'; -import { ModerationLog } from '@/models/entities/ModerationLog.js'; -import { MutedNote } from '@/models/entities/MutedNote.js'; -import { Muting } from '@/models/entities/Muting.js'; -import { RenoteMuting } from '@/models/entities/RenoteMuting.js'; -import { Note } from '@/models/entities/Note.js'; -import { NoteFavorite } from '@/models/entities/NoteFavorite.js'; -import { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { NoteThreadMuting } from '@/models/entities/NoteThreadMuting.js'; -import { NoteUnread } from '@/models/entities/NoteUnread.js'; -import { Page } from '@/models/entities/Page.js'; -import { PageLike } from '@/models/entities/PageLike.js'; -import { PasswordResetRequest } from '@/models/entities/PasswordResetRequest.js'; -import { Poll } from '@/models/entities/Poll.js'; -import { PollVote } from '@/models/entities/PollVote.js'; -import { PromoNote } from '@/models/entities/PromoNote.js'; -import { PromoRead } from '@/models/entities/PromoRead.js'; -import { RegistrationTicket } from '@/models/entities/RegistrationTicket.js'; -import { RegistryItem } from '@/models/entities/RegistryItem.js'; -import { Relay } from '@/models/entities/Relay.js'; -import { Signin } from '@/models/entities/Signin.js'; -import { SwSubscription } from '@/models/entities/SwSubscription.js'; -import { UsedUsername } from '@/models/entities/UsedUsername.js'; -import { User } from '@/models/entities/User.js'; -import { UserIp } from '@/models/entities/UserIp.js'; -import { UserKeypair } from '@/models/entities/UserKeypair.js'; -import { UserList } from '@/models/entities/UserList.js'; -import { UserListJoining } from '@/models/entities/UserListJoining.js'; -import { UserNotePining } from '@/models/entities/UserNotePining.js'; -import { UserPending } from '@/models/entities/UserPending.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; -import { UserPublickey } from '@/models/entities/UserPublickey.js'; -import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; -import { UserMemo } from '@/models/entities/UserMemo.js'; -import { Webhook } from '@/models/entities/Webhook.js'; -import { Channel } from '@/models/entities/Channel.js'; -import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; -import { Role } from '@/models/entities/Role.js'; -import { RoleAssignment } from '@/models/entities/RoleAssignment.js'; -import { Flash } from '@/models/entities/Flash.js'; -import { FlashLike } from '@/models/entities/FlashLike.js'; -import { UserListFavorite } from './entities/UserListFavorite.js'; -import type { Repository } from 'typeorm'; - -export { - AbuseUserReport, - AccessToken, - Ad, - Announcement, - AnnouncementRead, - Antenna, - App, - AttestationChallenge, - AuthSession, - Blocking, - ChannelFollowing, - ChannelFavorite, - Clip, - ClipNote, - ClipFavorite, - DriveFile, - DriveFolder, - Emoji, - Following, - FollowRequest, - GalleryLike, - GalleryPost, - Hashtag, - Instance, - Meta, - ModerationLog, - MutedNote, - Muting, - RenoteMuting, - Note, - NoteFavorite, - NoteReaction, - NoteThreadMuting, - NoteUnread, - Page, - PageLike, - PasswordResetRequest, - Poll, - PollVote, - PromoNote, - PromoRead, - RegistrationTicket, - RegistryItem, - Relay, - Signin, - SwSubscription, - UsedUsername, - User, - UserIp, - UserKeypair, - UserList, - UserListFavorite, - UserListJoining, - UserNotePining, - UserPending, - UserProfile, - UserPublickey, - UserSecurityKey, - Webhook, - Channel, - RetentionAggregation, - Role, - RoleAssignment, - Flash, - FlashLike, - UserMemo, -}; - -export type AbuseUserReportsRepository = Repository<AbuseUserReport>; -export type AccessTokensRepository = Repository<AccessToken>; -export type AdsRepository = Repository<Ad>; -export type AnnouncementsRepository = Repository<Announcement>; -export type AnnouncementReadsRepository = Repository<AnnouncementRead>; -export type AntennasRepository = Repository<Antenna>; -export type AppsRepository = Repository<App>; -export type AttestationChallengesRepository = Repository<AttestationChallenge>; -export type AuthSessionsRepository = Repository<AuthSession>; -export type BlockingsRepository = Repository<Blocking>; -export type ChannelFollowingsRepository = Repository<ChannelFollowing>; -export type ChannelFavoritesRepository = Repository<ChannelFavorite>; -export type ClipsRepository = Repository<Clip>; -export type ClipNotesRepository = Repository<ClipNote>; -export type ClipFavoritesRepository = Repository<ClipFavorite>; -export type DriveFilesRepository = Repository<DriveFile>; -export type DriveFoldersRepository = Repository<DriveFolder>; -export type EmojisRepository = Repository<Emoji>; -export type FollowingsRepository = Repository<Following>; -export type FollowRequestsRepository = Repository<FollowRequest>; -export type GalleryLikesRepository = Repository<GalleryLike>; -export type GalleryPostsRepository = Repository<GalleryPost>; -export type HashtagsRepository = Repository<Hashtag>; -export type InstancesRepository = Repository<Instance>; -export type MetasRepository = Repository<Meta>; -export type ModerationLogsRepository = Repository<ModerationLog>; -export type MutedNotesRepository = Repository<MutedNote>; -export type MutingsRepository = Repository<Muting>; -export type RenoteMutingsRepository = Repository<RenoteMuting>; -export type NotesRepository = Repository<Note>; -export type NoteFavoritesRepository = Repository<NoteFavorite>; -export type NoteReactionsRepository = Repository<NoteReaction>; -export type NoteThreadMutingsRepository = Repository<NoteThreadMuting>; -export type NoteUnreadsRepository = Repository<NoteUnread>; -export type PagesRepository = Repository<Page>; -export type PageLikesRepository = Repository<PageLike>; -export type PasswordResetRequestsRepository = Repository<PasswordResetRequest>; -export type PollsRepository = Repository<Poll>; -export type PollVotesRepository = Repository<PollVote>; -export type PromoNotesRepository = Repository<PromoNote>; -export type PromoReadsRepository = Repository<PromoRead>; -export type RegistrationTicketsRepository = Repository<RegistrationTicket>; -export type RegistryItemsRepository = Repository<RegistryItem>; -export type RelaysRepository = Repository<Relay>; -export type SigninsRepository = Repository<Signin>; -export type SwSubscriptionsRepository = Repository<SwSubscription>; -export type UsedUsernamesRepository = Repository<UsedUsername>; -export type UsersRepository = Repository<User>; -export type UserIpsRepository = Repository<UserIp>; -export type UserKeypairsRepository = Repository<UserKeypair>; -export type UserListsRepository = Repository<UserList>; -export type UserListFavoritesRepository = Repository<UserListFavorite>; -export type UserListJoiningsRepository = Repository<UserListJoining>; -export type UserNotePiningsRepository = Repository<UserNotePining>; -export type UserPendingsRepository = Repository<UserPending>; -export type UserProfilesRepository = Repository<UserProfile>; -export type UserPublickeysRepository = Repository<UserPublickey>; -export type UserSecurityKeysRepository = Repository<UserSecurityKey>; -export type WebhooksRepository = Repository<Webhook>; -export type ChannelsRepository = Repository<Channel>; -export type RetentionAggregationsRepository = Repository<RetentionAggregation>; -export type RolesRepository = Repository<Role>; -export type RoleAssignmentsRepository = Repository<RoleAssignment>; -export type FlashsRepository = Repository<Flash>; -export type FlashLikesRepository = Repository<FlashLike>; -export type UserMemoRepository = Repository<UserMemo>; diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts new file mode 100644 index 0000000000..c7e24c7f29 --- /dev/null +++ b/packages/backend/src/models/json-schema/announcement.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedAnnouncementSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + updatedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + title: { + type: 'string', + optional: false, nullable: false, + }, + imageUrl: { + type: 'string', + optional: false, nullable: true, + }, + icon: { + type: 'string', + optional: false, nullable: false, + }, + display: { + type: 'string', + optional: false, nullable: false, + }, + forYou: { + type: 'boolean', + optional: false, nullable: false, + }, + needConfirmationToRead: { + type: 'boolean', + optional: false, nullable: false, + }, + isRead: { + type: 'boolean', + optional: true, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts index 4483510610..7b6475919c 100644 --- a/packages/backend/src/models/json-schema/antenna.ts +++ b/packages/backend/src/models/json-schema/antenna.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedAntennaSchema = { type: 'object', properties: { @@ -42,7 +47,7 @@ export const packedAntennaSchema = { src: { type: 'string', optional: false, nullable: false, - enum: ['home', 'all', 'users', 'list'], + enum: ['home', 'all', 'users', 'list', 'users_blacklist'], }, userListId: { type: 'string', diff --git a/packages/backend/src/models/json-schema/app.ts b/packages/backend/src/models/json-schema/app.ts index c80dc81c33..9e0916299c 100644 --- a/packages/backend/src/models/json-schema/app.ts +++ b/packages/backend/src/models/json-schema/app.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedAppSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/blocking.ts b/packages/backend/src/models/json-schema/blocking.ts index 5532322420..0b58f1f8d7 100644 --- a/packages/backend/src/models/json-schema/blocking.ts +++ b/packages/backend/src/models/json-schema/blocking.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedBlockingSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts index fd61a70c0e..f1019d1461 100644 --- a/packages/backend/src/models/json-schema/channel.ts +++ b/packages/backend/src/models/json-schema/channel.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedChannelSchema = { type: 'object', properties: { @@ -67,5 +72,9 @@ export const packedChannelSchema = { type: 'string', optional: false, nullable: false, }, + isSensitive: { + type: 'boolean', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/clip.ts b/packages/backend/src/models/json-schema/clip.ts index 7310e59013..64f7a2ad9c 100644 --- a/packages/backend/src/models/json-schema/clip.ts +++ b/packages/backend/src/models/json-schema/clip.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedClipSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts index 4359076612..87f1340812 100644 --- a/packages/backend/src/models/json-schema/drive-file.ts +++ b/packages/backend/src/models/json-schema/drive-file.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedDriveFileSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/drive-folder.ts b/packages/backend/src/models/json-schema/drive-folder.ts index 88cb8ab4a2..51107d423f 100644 --- a/packages/backend/src/models/json-schema/drive-folder.ts +++ b/packages/backend/src/models/json-schema/drive-folder.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedDriveFolderSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 63f56e77cb..99a58f8773 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedEmojiSimpleSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 42d93dfac9..ac07519f16 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedFederationInstanceSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/flash.ts b/packages/backend/src/models/json-schema/flash.ts index 8471a138ec..9453ba1dce 100644 --- a/packages/backend/src/models/json-schema/flash.ts +++ b/packages/backend/src/models/json-schema/flash.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedFlashSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts index 2bcffbfc4d..3a24ebb619 100644 --- a/packages/backend/src/models/json-schema/following.ts +++ b/packages/backend/src/models/json-schema/following.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedFollowingSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/gallery-post.ts b/packages/backend/src/models/json-schema/gallery-post.ts index fc503d4a64..cf260c0bf5 100644 --- a/packages/backend/src/models/json-schema/gallery-post.ts +++ b/packages/backend/src/models/json-schema/gallery-post.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedGalleryPostSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/hashtag.ts b/packages/backend/src/models/json-schema/hashtag.ts index 98f8827640..a48e972a5d 100644 --- a/packages/backend/src/models/json-schema/hashtag.ts +++ b/packages/backend/src/models/json-schema/hashtag.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedHashtagSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/invite-code.ts b/packages/backend/src/models/json-schema/invite-code.ts index b70a779f29..cd8bf98d90 100644 --- a/packages/backend/src/models/json-schema/invite-code.ts +++ b/packages/backend/src/models/json-schema/invite-code.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedInviteCodeSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/muting.ts b/packages/backend/src/models/json-schema/muting.ts index 3ab99e17e7..dde9dc0288 100644 --- a/packages/backend/src/models/json-schema/muting.ts +++ b/packages/backend/src/models/json-schema/muting.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedMutingSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/note-favorite.ts b/packages/backend/src/models/json-schema/note-favorite.ts index d133f7367d..3f0007d917 100644 --- a/packages/backend/src/models/json-schema/note-favorite.ts +++ b/packages/backend/src/models/json-schema/note-favorite.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedNoteFavoriteSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/note-reaction.ts b/packages/backend/src/models/json-schema/note-reaction.ts index 0d8fc5449b..e3335f426e 100644 --- a/packages/backend/src/models/json-schema/note-reaction.ts +++ b/packages/backend/src/models/json-schema/note-reaction.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedNoteReactionSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 58ef425dcd..eb744aa109 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedNoteSchema = { type: 'object', properties: { @@ -134,6 +139,10 @@ export const packedNoteSchema = { type: 'string', optional: false, nullable: true, }, + isSensitive: { + type: 'boolean', + optional: true, nullable: false, + } }, }, }, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index e88ca61ba0..2c434913da 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { notificationTypes } from '@/types.js'; export const packedNotificationSchema = { diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 55ba3ce7f7..3f20a4b802 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedPageSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/queue.ts b/packages/backend/src/models/json-schema/queue.ts index 7ceeda26af..43da6e605d 100644 --- a/packages/backend/src/models/json-schema/queue.ts +++ b/packages/backend/src/models/json-schema/queue.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedQueueCountSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/renote-muting.ts b/packages/backend/src/models/json-schema/renote-muting.ts index 69ed8510da..feed1ceb09 100644 --- a/packages/backend/src/models/json-schema/renote-muting.ts +++ b/packages/backend/src/models/json-schema/renote-muting.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedRenoteMutingSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/user-list.ts b/packages/backend/src/models/json-schema/user-list.ts index 1e620516e4..e257d9984c 100644 --- a/packages/backend/src/models/json-schema/user-list.ts +++ b/packages/backend/src/models/json-schema/user-list.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedUserListSchema = { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index f9a20ac398..f15b225a30 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export const packedUserLiteSchema = { type: 'object', properties: { @@ -164,6 +169,15 @@ export const packedUserDetailedNotMeOnlySchema = { }, }, }, + verifiedLinks: { + type: 'array', + nullable: false, optional: false, + items: { + type: 'string', + nullable: false, optional: false, + format: 'url', + }, + }, followersCount: { type: 'number', nullable: false, optional: false, @@ -259,6 +273,10 @@ export const packedUserDetailedNotMeOnlySchema = { type: 'string', nullable: false, optional: true, }, + notify: { + type: 'string', + nullable: false, optional: true, + }, //#endregion }, } as const; @@ -316,6 +334,11 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, + twoFactorBackupCodesStock: { + type: 'string', + enum: ['full', 'partial', 'none'], + nullable: false, optional: false, + }, hideOnlineStatus: { type: 'boolean', nullable: false, optional: false, diff --git a/packages/backend/src/models/util/id.ts b/packages/backend/src/models/util/id.ts new file mode 100644 index 0000000000..81e83b8db9 --- /dev/null +++ b/packages/backend/src/models/util/id.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const id = () => ({ + type: 'varchar' as const, + length: 32, +}); diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 488979c409..10126eab2b 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + // https://github.com/typeorm/typeorm/issues/2400 import pg from 'pg'; pg.types.setTypeParser(20, Number); @@ -6,72 +11,71 @@ import { DataSource, Logger } from 'typeorm'; import * as highlight from 'cli-highlight'; import { entities as charts } from '@/core/chart/entities.js'; -import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; -import { AccessToken } from '@/models/entities/AccessToken.js'; -import { Ad } from '@/models/entities/Ad.js'; -import { Announcement } from '@/models/entities/Announcement.js'; -import { AnnouncementRead } from '@/models/entities/AnnouncementRead.js'; -import { Antenna } from '@/models/entities/Antenna.js'; -import { App } from '@/models/entities/App.js'; -import { AttestationChallenge } from '@/models/entities/AttestationChallenge.js'; -import { AuthSession } from '@/models/entities/AuthSession.js'; -import { Blocking } from '@/models/entities/Blocking.js'; -import { ChannelFollowing } from '@/models/entities/ChannelFollowing.js'; -import { ChannelFavorite } from '@/models/entities/ChannelFavorite.js'; -import { Clip } from '@/models/entities/Clip.js'; -import { ClipNote } from '@/models/entities/ClipNote.js'; -import { ClipFavorite } from '@/models/entities/ClipFavorite.js'; -import { DriveFile } from '@/models/entities/DriveFile.js'; -import { DriveFolder } from '@/models/entities/DriveFolder.js'; -import { Emoji } from '@/models/entities/Emoji.js'; -import { Following } from '@/models/entities/Following.js'; -import { FollowRequest } from '@/models/entities/FollowRequest.js'; -import { GalleryLike } from '@/models/entities/GalleryLike.js'; -import { GalleryPost } from '@/models/entities/GalleryPost.js'; -import { Hashtag } from '@/models/entities/Hashtag.js'; -import { Instance } from '@/models/entities/Instance.js'; -import { Meta } from '@/models/entities/Meta.js'; -import { ModerationLog } from '@/models/entities/ModerationLog.js'; -import { MutedNote } from '@/models/entities/MutedNote.js'; -import { Muting } from '@/models/entities/Muting.js'; -import { RenoteMuting } from '@/models/entities/RenoteMuting.js'; -import { Note } from '@/models/entities/Note.js'; -import { NoteFavorite } from '@/models/entities/NoteFavorite.js'; -import { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { NoteThreadMuting } from '@/models/entities/NoteThreadMuting.js'; -import { NoteUnread } from '@/models/entities/NoteUnread.js'; -import { Page } from '@/models/entities/Page.js'; -import { PageLike } from '@/models/entities/PageLike.js'; -import { PasswordResetRequest } from '@/models/entities/PasswordResetRequest.js'; -import { Poll } from '@/models/entities/Poll.js'; -import { PollVote } from '@/models/entities/PollVote.js'; -import { PromoNote } from '@/models/entities/PromoNote.js'; -import { PromoRead } from '@/models/entities/PromoRead.js'; -import { RegistrationTicket } from '@/models/entities/RegistrationTicket.js'; -import { RegistryItem } from '@/models/entities/RegistryItem.js'; -import { Relay } from '@/models/entities/Relay.js'; -import { Signin } from '@/models/entities/Signin.js'; -import { SwSubscription } from '@/models/entities/SwSubscription.js'; -import { UsedUsername } from '@/models/entities/UsedUsername.js'; -import { User } from '@/models/entities/User.js'; -import { UserIp } from '@/models/entities/UserIp.js'; -import { UserKeypair } from '@/models/entities/UserKeypair.js'; -import { UserList } from '@/models/entities/UserList.js'; -import { UserListFavorite } from '@/models/entities/UserListFavorite.js'; -import { UserListJoining } from '@/models/entities/UserListJoining.js'; -import { UserNotePining } from '@/models/entities/UserNotePining.js'; -import { UserPending } from '@/models/entities/UserPending.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; -import { UserPublickey } from '@/models/entities/UserPublickey.js'; -import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; -import { Webhook } from '@/models/entities/Webhook.js'; -import { Channel } from '@/models/entities/Channel.js'; -import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; -import { Role } from '@/models/entities/Role.js'; -import { RoleAssignment } from '@/models/entities/RoleAssignment.js'; -import { Flash } from '@/models/entities/Flash.js'; -import { FlashLike } from '@/models/entities/FlashLike.js'; -import { UserMemo } from '@/models/entities/UserMemo.js'; +import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; +import { MiAccessToken } from '@/models/AccessToken.js'; +import { MiAd } from '@/models/Ad.js'; +import { MiAnnouncement } from '@/models/Announcement.js'; +import { MiAnnouncementRead } from '@/models/AnnouncementRead.js'; +import { MiAntenna } from '@/models/Antenna.js'; +import { MiApp } from '@/models/App.js'; +import { MiAuthSession } from '@/models/AuthSession.js'; +import { MiBlocking } from '@/models/Blocking.js'; +import { MiChannelFollowing } from '@/models/ChannelFollowing.js'; +import { MiChannelFavorite } from '@/models/ChannelFavorite.js'; +import { MiClip } from '@/models/Clip.js'; +import { MiClipNote } from '@/models/ClipNote.js'; +import { MiClipFavorite } from '@/models/ClipFavorite.js'; +import { MiDriveFile } from '@/models/DriveFile.js'; +import { MiDriveFolder } from '@/models/DriveFolder.js'; +import { MiEmoji } from '@/models/Emoji.js'; +import { MiFollowing } from '@/models/Following.js'; +import { MiFollowRequest } from '@/models/FollowRequest.js'; +import { MiGalleryLike } from '@/models/GalleryLike.js'; +import { MiGalleryPost } from '@/models/GalleryPost.js'; +import { MiHashtag } from '@/models/Hashtag.js'; +import { MiInstance } from '@/models/Instance.js'; +import { MiMeta } from '@/models/Meta.js'; +import { MiModerationLog } from '@/models/ModerationLog.js'; +import { MiMutedNote } from '@/models/MutedNote.js'; +import { MiMuting } from '@/models/Muting.js'; +import { MiRenoteMuting } from '@/models/RenoteMuting.js'; +import { MiNote } from '@/models/Note.js'; +import { MiNoteFavorite } from '@/models/NoteFavorite.js'; +import { MiNoteReaction } from '@/models/NoteReaction.js'; +import { MiNoteThreadMuting } from '@/models/NoteThreadMuting.js'; +import { MiNoteUnread } from '@/models/NoteUnread.js'; +import { MiPage } from '@/models/Page.js'; +import { MiPageLike } from '@/models/PageLike.js'; +import { MiPasswordResetRequest } from '@/models/PasswordResetRequest.js'; +import { MiPoll } from '@/models/Poll.js'; +import { MiPollVote } from '@/models/PollVote.js'; +import { MiPromoNote } from '@/models/PromoNote.js'; +import { MiPromoRead } from '@/models/PromoRead.js'; +import { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; +import { MiRegistryItem } from '@/models/RegistryItem.js'; +import { MiRelay } from '@/models/Relay.js'; +import { MiSignin } from '@/models/Signin.js'; +import { MiSwSubscription } from '@/models/SwSubscription.js'; +import { MiUsedUsername } from '@/models/UsedUsername.js'; +import { MiUser } from '@/models/User.js'; +import { MiUserIp } from '@/models/UserIp.js'; +import { MiUserKeypair } from '@/models/UserKeypair.js'; +import { MiUserList } from '@/models/UserList.js'; +import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import { MiUserListJoining } from '@/models/UserListJoining.js'; +import { MiUserNotePining } from '@/models/UserNotePining.js'; +import { MiUserPending } from '@/models/UserPending.js'; +import { MiUserProfile } from '@/models/UserProfile.js'; +import { MiUserPublickey } from '@/models/UserPublickey.js'; +import { MiUserSecurityKey } from '@/models/UserSecurityKey.js'; +import { MiWebhook } from '@/models/Webhook.js'; +import { MiChannel } from '@/models/Channel.js'; +import { MiRetentionAggregation } from '@/models/RetentionAggregation.js'; +import { MiRole } from '@/models/Role.js'; +import { MiRoleAssignment } from '@/models/RoleAssignment.js'; +import { MiFlash } from '@/models/Flash.js'; +import { MiFlashLike } from '@/models/FlashLike.js'; +import { MiUserMemo } from '@/models/UserMemo.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -121,72 +125,71 @@ class MyCustomLogger implements Logger { } export const entities = [ - Announcement, - AnnouncementRead, - Meta, - Instance, - App, - AuthSession, - AccessToken, - User, - UserProfile, - UserKeypair, - UserPublickey, - UserList, - UserListFavorite, - UserListJoining, - UserNotePining, - UserSecurityKey, - UsedUsername, - AttestationChallenge, - Following, - FollowRequest, - Muting, - RenoteMuting, - Blocking, - Note, - NoteFavorite, - NoteReaction, - NoteThreadMuting, - NoteUnread, - Page, - PageLike, - GalleryPost, - GalleryLike, - DriveFile, - DriveFolder, - Poll, - PollVote, - Emoji, - Hashtag, - SwSubscription, - AbuseUserReport, - RegistrationTicket, - Signin, - ModerationLog, - Clip, - ClipNote, - ClipFavorite, - Antenna, - PromoNote, - PromoRead, - Relay, - MutedNote, - Channel, - ChannelFollowing, - ChannelFavorite, - RegistryItem, - Ad, - PasswordResetRequest, - UserPending, - Webhook, - UserIp, - RetentionAggregation, - Role, - RoleAssignment, - Flash, - FlashLike, - UserMemo, + MiAnnouncement, + MiAnnouncementRead, + MiMeta, + MiInstance, + MiApp, + MiAuthSession, + MiAccessToken, + MiUser, + MiUserProfile, + MiUserKeypair, + MiUserPublickey, + MiUserList, + MiUserListFavorite, + MiUserListJoining, + MiUserNotePining, + MiUserSecurityKey, + MiUsedUsername, + MiFollowing, + MiFollowRequest, + MiMuting, + MiRenoteMuting, + MiBlocking, + MiNote, + MiNoteFavorite, + MiNoteReaction, + MiNoteThreadMuting, + MiNoteUnread, + MiPage, + MiPageLike, + MiGalleryPost, + MiGalleryLike, + MiDriveFile, + MiDriveFolder, + MiPoll, + MiPollVote, + MiEmoji, + MiHashtag, + MiSwSubscription, + MiAbuseUserReport, + MiRegistrationTicket, + MiSignin, + MiModerationLog, + MiClip, + MiClipNote, + MiClipFavorite, + MiAntenna, + MiPromoNote, + MiPromoRead, + MiRelay, + MiMutedNote, + MiChannel, + MiChannelFollowing, + MiChannelFavorite, + MiRegistryItem, + MiAd, + MiPasswordResetRequest, + MiUserPending, + MiWebhook, + MiUserIp, + MiRetentionAggregation, + MiRole, + MiRoleAssignment, + MiFlash, + MiFlashLike, + MiUserMemo, ...charts, ]; @@ -227,7 +230,7 @@ export function createPostgresDataSource(config: Config) { options: { host: config.redis.host, port: config.redis.port, - family: config.redis.family == null ? 0 : config.redis.family, + family: config.redis.family ?? 0, password: config.redis.pass, keyPrefix: `${config.redis.prefix}:query:`, db: config.redis.db ?? 0, diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts index 648af893c2..618d1d5c2f 100644 --- a/packages/backend/src/queue/QueueLoggerService.ts +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index e1c6b93d9b..e6327002c5 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; import { GlobalModule } from '@/GlobalModule.js'; diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index f575b1718e..5201bfed8e 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import * as Bull from 'bullmq'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts index d49951a1c3..87d075304d 100644 --- a/packages/backend/src/queue/const.ts +++ b/packages/backend/src/queue/const.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Config } from '@/config.js'; import type * as Bull from 'bullmq'; @@ -16,7 +21,7 @@ export function baseQueueOptions(config: Config, queueName: typeof QUEUE[keyof t return { connection: { ...config.redisForJobQueue, - keyPrefix: undefined + keyPrefix: undefined, }, prefix: config.redisForJobQueue.prefix ? `${config.redisForJobQueue.prefix}:queue:${queueName}` : `queue:${queueName}`, }; diff --git a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts index 600ce0828f..5aac3f19e8 100644 --- a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts +++ b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; -import type { RetentionAggregationsRepository, UsersRepository } from '@/models/index.js'; +import type { RetentionAggregationsRepository, UsersRepository } from '@/models/_.js'; import { deepClone } from '@/misc/clone.js'; import { IdService } from '@/core/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; @@ -16,9 +20,6 @@ export class AggregateRetentionProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index c4ee212bab..9b07389dc3 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { MutingsRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { UserMutingService } from '@/core/UserMutingService.js'; @@ -14,9 +18,6 @@ export class CheckExpiredMutingsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 22d7c1b4fb..55c444eee6 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,6 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; @@ -23,9 +26,6 @@ export class CleanChartsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - private federationChart: FederationChart, private notesChart: NotesChart, private usersChart: UsersChart, diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index cefa6da5e9..f0453f7054 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In, LessThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AntennasRepository, MutedNotesRepository, RoleAssignmentsRepository, UserIpsRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { AntennasRepository, MutedNotesRepository, RoleAssignmentsRepository, UserIpsRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import type { Config } from '@/config.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -53,12 +58,14 @@ export class CleanProcessorService { reason: 'word', }); - // 7日以上使われてないアンテナを停止 - this.antennasRepository.update({ - lastUsedAt: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 7))), - }, { - isActive: false, - }); + // 使われてないアンテナを停止 + if (this.config.deactivateAntennaThreshold > 0) { + this.antennasRepository.update({ + lastUsedAt: LessThan(new Date(Date.now() - this.config.deactivateAntennaThreshold)), + }, { + isActive: false, + }); + } const expiredRoleAssignments = await this.roleAssignmentsRepository.createQueryBuilder('assign') .where('assign.expiresAt IS NOT NULL') diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index 6f887089eb..b62cc8a8fd 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { DriveFile, DriveFilesRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { bindThis } from '@/decorators.js'; @@ -14,9 +18,6 @@ export class CleanRemoteFilesProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -31,7 +32,7 @@ export class CleanRemoteFilesProcessorService { this.logger.info('Deleting cached remote files...'); let deletedCount = 0; - let cursor: DriveFile['id'] | null = null; + let cursor: MiDriveFile['id'] | null = null; while (true) { const files = await this.driveFilesRepository.find({ diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 3b7db5f05c..39967165d4 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -1,12 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNote } from '@/models/Note.js'; import { EmailService } from '@/core/EmailService.js'; import { bindThis } from '@/decorators.js'; import { SearchService } from '@/core/SearchService.js'; @@ -19,9 +23,6 @@ export class DeleteAccountProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -52,7 +53,7 @@ export class DeleteAccountProcessorService { } { // Delete notes - let cursor: Note['id'] | null = null; + let cursor: MiNote['id'] | null = null; while (true) { const notes = await this.notesRepository.find({ @@ -64,7 +65,7 @@ export class DeleteAccountProcessorService { order: { id: 1, }, - }) as Note[]; + }) as MiNote[]; if (notes.length === 0) { break; @@ -83,7 +84,7 @@ export class DeleteAccountProcessorService { } { // Delete files - let cursor: DriveFile['id'] | null = null; + let cursor: MiDriveFile['id'] | null = null; while (true) { const files = await this.driveFilesRepository.find({ @@ -95,7 +96,7 @@ export class DeleteAccountProcessorService { order: { id: 1, }, - }) as DriveFile[]; + }) as MiDriveFile[]; if (files.length === 0) { break; diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 07e3762330..6d0a45bcc0 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, DriveFilesRepository, DriveFile } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, DriveFilesRepository, MiDriveFile } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { bindThis } from '@/decorators.js'; @@ -15,9 +19,6 @@ export class DeleteDriveFilesProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -40,7 +41,7 @@ export class DeleteDriveFilesProcessorService { } let deletedCount = 0; - let cursor: DriveFile['id'] | null = null; + let cursor: MiDriveFile['id'] | null = null; while (true) { const files = await this.driveFilesRepository.find({ diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index edf87bd921..a4638bfaaf 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -1,6 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { bindThis } from '@/decorators.js'; @@ -13,9 +16,6 @@ export class DeleteFileProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 406e9df850..4a1d9f28b4 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,15 +1,19 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Bull from 'bullmq'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, InstancesRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { InstancesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; import { ApRequestService } from '@/core/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import { MemorySingleCache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/Instance.js'; +import type { MiInstance } from '@/models/Instance.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import FederationChart from '@/core/chart/charts/federation.js'; @@ -22,19 +26,13 @@ import type { DeliverJobData } from '../types.js'; @Injectable() export class DeliverProcessorService { private logger: Logger; - private suspendedHostsCache: MemorySingleCache<Instance[]>; + private suspendedHostsCache: MemorySingleCache<MiInstance[]>; private latest: string | null; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - private metaService: MetaService, private utilityService: UtilityService, private federatedInstanceService: FederatedInstanceService, @@ -46,7 +44,7 @@ export class DeliverProcessorService { private queueLoggerService: QueueLoggerService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('deliver'); - this.suspendedHostsCache = new MemorySingleCache<Instance[]>(1000 * 60 * 60); + this.suspendedHostsCache = new MemorySingleCache<MiInstance[]>(1000 * 60 * 60); } @bindThis diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 21501592f2..4a48084436 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { PollVotesRepository, NotesRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { PollVotesRepository, NotesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; @@ -14,9 +18,6 @@ export class EndedPollNotificationProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts index 21c0bfe80e..f941fb6e85 100644 --- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { format as DateFormat } from 'date-fns'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AntennasRepository, UsersRepository, UserListJoiningsRepository, User } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { AntennasRepository, UsersRepository, UserListJoiningsRepository, MiUser } from '@/models/_.js'; import Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { bindThis } from '@/decorators.js'; @@ -19,9 +23,6 @@ export class ExportAntennasProcessorService { private logger: Logger; constructor ( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -62,7 +63,7 @@ export class ExportAntennasProcessorService { const antennas = await this.antennsRepository.findBy({ userId: job.data.user.id }); write('['); for (const [index, antenna] of antennas.entries()) { - let users: User[] | undefined; + let users: MiUser[] | undefined; if (antenna.userListId !== null) { const joinings = await this.userListJoiningsRepository.findBy({ userListId: antenna.userListId }); users = await this.usersRepository.findBy({ diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index d100c6d09f..0a37e3ca1e 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, BlockingsRepository, Blocking } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, BlockingsRepository, MiBlocking } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; @@ -19,9 +23,6 @@ export class ExportBlockingProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -53,7 +54,7 @@ export class ExportBlockingProcessorService { const stream = fs.createWriteStream(path, { flags: 'a' }); let exportedCount = 0; - let cursor: Blocking['id'] | null = null; + let cursor: MiBlocking['id'] | null = null; while (true) { const blockings = await this.blockingsRepository.find({ diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index 3203d9f3e5..d5387fe42e 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; @@ -5,7 +10,7 @@ import { format as dateFormat } from 'date-fns'; import mime from 'mime-types'; import archiver from 'archiver'; import { DI } from '@/di-symbols.js'; -import type { EmojisRepository, UsersRepository } from '@/models/index.js'; +import type { EmojisRepository, UsersRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts index 2be42b1a7a..7248c7a649 100644 --- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -1,15 +1,19 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { NoteFavorite, NoteFavoritesRepository, NotesRepository, PollsRepository, User, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { MiNoteFavorite, NoteFavoritesRepository, PollsRepository, MiUser, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; -import type { Poll } from '@/models/entities/Poll.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiPoll } from '@/models/Poll.js'; +import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -20,18 +24,12 @@ export class ExportFavoritesProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.pollsRepository) private pollsRepository: PollsRepository, - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: NoteFavoritesRepository, @@ -74,7 +72,7 @@ export class ExportFavoritesProcessorService { await write('['); let exportedFavoritesCount = 0; - let cursor: NoteFavorite['id'] | null = null; + let cursor: MiNoteFavorite['id'] | null = null; while (true) { const favorites = await this.noteFavoritesRepository.find({ @@ -87,7 +85,7 @@ export class ExportFavoritesProcessorService { id: 1, }, relations: ['note', 'note.user'], - }) as (NoteFavorite & { note: Note & { user: User } })[]; + }) as (MiNoteFavorite & { note: MiNote & { user: MiUser } })[]; if (favorites.length === 0) { job.updateProgress(100); @@ -97,7 +95,7 @@ export class ExportFavoritesProcessorService { cursor = favorites.at(-1)?.id ?? null; for (const favorite of favorites) { - let poll: Poll | undefined; + let poll: MiPoll | undefined; if (favorite.note.hasPoll) { poll = await this.pollsRepository.findOneByOrFail({ noteId: favorite.note.id }); } @@ -129,7 +127,7 @@ export class ExportFavoritesProcessorService { } } -function serialize(favorite: NoteFavorite & { note: Note & { user: User } }, poll: Poll | null = null): Record<string, unknown> { +function serialize(favorite: MiNoteFavorite & { note: MiNote & { user: MiUser } }, poll: MiPoll | null = null): Record<string, unknown> { return { id: favorite.id, createdAt: favorite.createdAt, diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index d54e5e0b34..c9739eb1cb 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -1,14 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan, Not } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, FollowingsRepository, MutingsRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, FollowingsRepository, MutingsRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; -import type { Following } from '@/models/entities/Following.js'; +import type { MiFollowing } from '@/models/Following.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; @@ -20,9 +24,6 @@ export class ExportFollowingProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -56,7 +57,7 @@ export class ExportFollowingProcessorService { try { const stream = fs.createWriteStream(path, { flags: 'a' }); - let cursor: Following['id'] | null = null; + let cursor: MiFollowing['id'] | null = null; const mutings = job.data.excludeMuting ? await this.mutingsRepository.findBy({ muterId: user.id, @@ -73,7 +74,7 @@ export class ExportFollowingProcessorService { order: { id: 1, }, - }) as Following[]; + }) as MiFollowing[]; if (followings.length === 0) { break; diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 030e38931e..c8425c1f2d 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository, UsersRepository, BlockingsRepository, Muting } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { MutingsRepository, UsersRepository, MiMuting } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; @@ -19,15 +23,9 @@ export class ExportMutingProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.blockingsRepository) - private blockingsRepository: BlockingsRepository, - @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, @@ -56,7 +54,7 @@ export class ExportMutingProcessorService { const stream = fs.createWriteStream(path, { flags: 'a' }); let exportedCount = 0; - let cursor: Muting['id'] | null = null; + let cursor: MiMuting['id'] | null = null; while (true) { const mutes = await this.mutingsRepository.find({ diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 94c81a3cf8..e0bc80e190 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -1,15 +1,19 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { NotesRepository, PollsRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; -import type { Poll } from '@/models/entities/Poll.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiPoll } from '@/models/Poll.js'; +import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { Packed } from '@/misc/json-schema.js'; @@ -22,9 +26,6 @@ export class ExportNotesProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -75,7 +76,7 @@ export class ExportNotesProcessorService { await write('['); let exportedNotesCount = 0; - let cursor: Note['id'] | null = null; + let cursor: MiNote['id'] | null = null; while (true) { const notes = await this.notesRepository.find({ @@ -87,7 +88,7 @@ export class ExportNotesProcessorService { order: { id: 1, }, - }) as Note[]; + }) as MiNote[]; if (notes.length === 0) { job.updateProgress(100); @@ -97,7 +98,7 @@ export class ExportNotesProcessorService { cursor = notes.at(-1)?.id ?? null; for (const note of notes) { - let poll: Poll | undefined; + let poll: MiPoll | undefined; if (note.hasPoll) { poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); } @@ -130,7 +131,7 @@ export class ExportNotesProcessorService { } } -function serialize(note: Note, poll: Poll | null = null, files: Packed<'DriveFile'>[]): Record<string, unknown> { +function serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record<string, unknown> { return { id: note.id, text: note.text, diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index ec63358053..7baaa7081a 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { UserListJoiningsRepository, UserListsRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UserListJoiningsRepository, UserListsRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; @@ -19,9 +23,6 @@ export class ExportUserListsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts index 74ef20fdd8..7c95bccaff 100644 --- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable, Inject } from '@nestjs/common'; import _Ajv from 'ajv'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import Logger from '@/logger.js'; -import type { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 2f1a9e5b03..64520b770b 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; +import type { UsersRepository, DriveFilesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 37b929cb03..a52af54a39 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -1,10 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { ZipReader } from 'slacc'; -import { DataSource } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { EmojisRepository, DriveFilesRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { EmojisRepository, DriveFilesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; @@ -21,15 +24,6 @@ export class ImportCustomEmojisProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.db) - private db: DataSource, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 15bee9672e..2b5e41a12d 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; +import type { UsersRepository, DriveFilesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 723935cd31..9db4e5d8e0 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, DriveFilesRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; @@ -19,9 +23,6 @@ export class ImportMutingProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 824ee8157a..54ca1a86df 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, DriveFilesRepository, UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, DriveFilesRepository, UserListJoiningsRepository, UserListsRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; @@ -20,9 +24,6 @@ export class ImportUserListsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index ce1d7aaa1b..99e823f9fa 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -1,20 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import httpSignature from '@peertube/http-signature'; import * as Bull from 'bullmq'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; -import { ApRequestService } from '@/core/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import FederationChart from '@/core/chart/charts/federation.js'; import { getApId } from '@/core/activitypub/type.js'; -import type { RemoteUser } from '@/models/entities/User.js'; -import type { UserPublickey } from '@/models/entities/UserPublickey.js'; +import type { MiRemoteUser } from '@/models/User.js'; +import type { MiUserPublickey } from '@/models/UserPublickey.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -30,16 +32,12 @@ export class InboxProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - private utilityService: UtilityService, private metaService: MetaService, private apInboxService: ApInboxService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, private ldSignatureService: LdSignatureService, - private apRequestService: ApRequestService, private apPersonService: ApPersonService, private apDbResolverService: ApDbResolverService, private instanceChart: InstanceChart, @@ -76,8 +74,8 @@ export class InboxProcessorService { // HTTP-Signature keyIdを元にDBから取得 let authUser: { - user: RemoteUser; - key: UserPublickey | null; + user: MiRemoteUser; + key: MiUserPublickey | null; } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId); // keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得 diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts index 816c5fc5ec..5b2d2ef313 100644 --- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts +++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { UserFollowingService } from '@/core/UserFollowingService.js'; @@ -5,9 +10,9 @@ import { UserBlockingService } from '@/core/UserBlockingService.js'; import { bindThis } from '@/decorators.js'; import type Logger from '@/logger.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; -import { LocalUser, RemoteUser } from '@/models/entities/User.js'; +import { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import { RelationshipJobData } from '../types.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -40,7 +45,7 @@ export class RelationshipProcessorService { const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: job.data.from.id }), this.usersRepository.findOneByOrFail({ id: job.data.to.id }), - ]) as [LocalUser | RemoteUser, LocalUser | RemoteUser]; + ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; await this.userFollowingService.unfollow(follower, followee, job.data.silent); return 'ok'; } diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index eab8e1e68d..b3b055ef8c 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,18 +1,13 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; -import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; -import ActiveUsersChart from '@/core/chart/charts/active-users.js'; -import InstanceChart from '@/core/chart/charts/instance.js'; -import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import DriveChart from '@/core/chart/charts/drive.js'; -import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; -import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; -import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; -import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -22,21 +17,9 @@ export class ResyncChartsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - - private federationChart: FederationChart, private notesChart: NotesChart, private usersChart: UsersChart, - private activeUsersChart: ActiveUsersChart, - private instanceChart: InstanceChart, - private perUserNotesChart: PerUserNotesChart, private driveChart: DriveChart, - private perUserReactionsChart: PerUserReactionsChart, - private perUserFollowingChart: PerUserFollowingChart, - private perUserDriveChart: PerUserDriveChart, - private apRequestChart: ApRequestChart, - private queueLoggerService: QueueLoggerService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index f1696bf567..7b1efb71e0 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,6 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; @@ -23,9 +26,6 @@ export class TickChartsProcessorService { private logger: Logger; constructor( - @Inject(DI.config) - private config: Config, - private federationChart: FederationChart, private notesChart: NotesChart, private usersChart: UsersChart, diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 25e91761ef..a41f5565c8 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Bull from 'bullmq'; import { DI } from '@/di-symbols.js'; -import type { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; @@ -42,6 +47,7 @@ export class WebhookDeliverProcessorService { 'Content-Type': 'application/json', }, body: JSON.stringify({ + server: this.config.url, hookId: job.data.webhookId, userId: job.data.userId, eventId: job.data.eventId, diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 776dd3aa12..c9122f5ca2 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { User } from '@/models/entities/User.js'; -import type { Webhook } from '@/models/entities/Webhook.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiWebhook } from '@/models/Webhook.js'; import type { IActivity } from '@/core/activitypub/type.js'; import type httpSignature from '@peertube/http-signature'; @@ -73,7 +78,7 @@ export type DbUserDeleteJobData = { export type DbUserImportJobData = { user: ThinUser; - fileId: DriveFile['id']; + fileId: MiDriveFile['id']; }; export type DBAntennaImportJobData = { @@ -93,14 +98,14 @@ export type ObjectStorageFileJobData = { }; export type EndedPollNotificationJobData = { - noteId: Note['id']; + noteId: MiNote['id']; }; export type WebhookDeliverJobData = { type: string; content: unknown; - webhookId: Webhook['id']; - userId: User['id']; + webhookId: MiWebhook['id']; + userId: MiUser['id']; to: string; secret: string; createdAt: number; @@ -108,5 +113,5 @@ export type WebhookDeliverJobData = { }; export type ThinUser = { - id: User['id']; + id: MiUser['id']; }; diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 634f5f0a4e..2428fa2792 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IncomingMessage } from 'node:http'; import { Inject, Injectable } from '@nestjs/common'; import fastifyAccepts from '@fastify/accepts'; @@ -6,16 +11,16 @@ import { Brackets, In, IsNull, LessThan, Not } from 'typeorm'; import accepts from 'accepts'; import vary from 'vary'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/index.js'; +import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js'; import * as url from '@/misc/prelude/url.js'; import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { QueueService } from '@/core/QueueService.js'; -import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; +import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; import { UserKeypairService } from '@/core/UserKeypairService.js'; -import type { Following } from '@/models/entities/Following.js'; +import type { MiFollowing } from '@/models/Following.js'; import { countIf } from '@/misc/prelude/array.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiNote } from '@/models/Note.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -82,7 +87,7 @@ export class ActivityPubServerService { * @param note Note */ @bindThis - private async packActivity(note: Note): Promise<any> { + private async packActivity(note: MiNote): Promise<any> { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { const renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId }); return this.apRendererService.renderAnnounce(renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`, note); @@ -153,7 +158,7 @@ export class ActivityPubServerService { if (page) { const query = { followeeId: user.id, - } as FindOptionsWhere<Following>; + } as FindOptionsWhere<MiFollowing>; // カーソルが指定されている場合 if (cursor) { @@ -245,7 +250,7 @@ export class ActivityPubServerService { if (page) { const query = { followerId: user.id, - } as FindOptionsWhere<Following>; + } as FindOptionsWhere<MiFollowing>; // カーソルが指定されている場合 if (cursor) { @@ -419,7 +424,7 @@ export class ActivityPubServerService { } @bindThis - private async userInfo(request: FastifyRequest, reply: FastifyReply, user: User | null) { + private async userInfo(request: FastifyRequest, reply: FastifyReply, user: MiUser | null) { if (user == null) { reply.code(404); return; @@ -427,7 +432,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as LocalUser))); + return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as MiLocalUser))); } @bindThis @@ -643,7 +648,7 @@ export class ActivityPubServerService { id: request.params.followee, host: Not(IsNull()), }), - ]) as [LocalUser | RemoteUser | null, LocalUser | RemoteUser | null]; + ]) as [MiLocalUser | MiRemoteUser | null, MiLocalUser | MiRemoteUser | null]; if (follower == null || followee == null) { reply.code(404); @@ -678,7 +683,7 @@ export class ActivityPubServerService { id: followRequest.followeeId, host: Not(IsNull()), }), - ]) as [LocalUser | RemoteUser | null, LocalUser | RemoteUser | null]; + ]) as [MiLocalUser | MiRemoteUser | null, MiLocalUser | MiRemoteUser | null]; if (follower == null || followee == null) { reply.code(404); diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 2547d73365..11721263d3 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; @@ -6,7 +11,7 @@ import rename from 'rename'; import sharp from 'sharp'; import { sharpBmp } from 'sharp-read-bmp'; import type { Config } from '@/config.js'; -import type { DriveFile, DriveFilesRepository } from '@/models/index.js'; +import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { createTemp } from '@/misc/create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; @@ -367,8 +372,8 @@ export class FileServerService { @bindThis private async getStreamAndTypeFromUrl(url: string): Promise< - { state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: DriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; } - | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; filename: string; mime: string; ext: string | null; path: string; } + { state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: MiDriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; } + | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; } | '404' | '204' > { @@ -406,8 +411,8 @@ export class FileServerService { @bindThis private async getFileFromKey(key: string): Promise< - { state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; filename: string; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; } - | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; filename: string; mime: string; ext: string | null; path: string; } + { state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; } + | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; } | '404' | '204' > { diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 666a91fcee..79f130dabe 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -1,6 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; @@ -14,6 +18,7 @@ import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_0path = '/nodeinfo/2.0'; +const nodeinfo_homepage = 'https://misskey-hub.net'; @Injectable() export class NodeinfoServerService { @@ -21,12 +26,6 @@ export class NodeinfoServerService { @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - private userEntityService: UserEntityService, private metaService: MetaService, private notesChart: NotesChart, @@ -37,10 +36,10 @@ export class NodeinfoServerService { @bindThis public getLinks() { - return [/* (awaiting release) { - rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', - href: config.url + nodeinfo2_1path - }, */{ + return [{ + rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', + href: this.config.url + nodeinfo2_1path + }, { rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', href: this.config.url + nodeinfo2_0path, }]; @@ -48,7 +47,7 @@ export class NodeinfoServerService { @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - const nodeinfo2 = async () => { + const nodeinfo2 = async (version: number) => { const now = Date.now(); const notesChart = await this.notesChart.getChart('hour', 1, null); @@ -75,10 +74,12 @@ export class NodeinfoServerService { const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; - return { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const document: any = { software: { name: 'misskey', version: this.config.version, + homepage: nodeinfo_homepage, repository: meta.repositoryUrl, }, protocols: ['activitypub'], @@ -116,23 +117,36 @@ export class NodeinfoServerService { themeColor: meta.themeColor ?? '#86b300', }, }; + if (version >= 21) { + document.software.repository = meta.repositoryUrl; + document.software.homepage = meta.repositoryUrl; + } + return document; }; const cache = new MemorySingleCache<Awaited<ReturnType<typeof nodeinfo2>>>(1000 * 60 * 10); fastify.get(nodeinfo2_1path, async (request, reply) => { - const base = await cache.fetch(() => nodeinfo2()); + const base = await cache.fetch(() => nodeinfo2(21)); - reply.header('Cache-Control', 'public, max-age=600'); + reply + .type( + 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"', + ) + .header('Cache-Control', 'public, max-age=600'); return { version: '2.1', ...base }; }); fastify.get(nodeinfo2_0path, async (request, reply) => { - const base = await cache.fetch(() => nodeinfo2()); + const base = await cache.fetch(() => nodeinfo2(20)); delete (base as any).software.repository; - reply.header('Cache-Control', 'public, max-age=600'); + reply + .type( + 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"', + ) + .header('Cache-Control', 'public, max-age=600'); return { version: '2.0', ...base }; }); diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index da86b2c1d3..fa81380f01 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { EndpointsModule } from '@/server/api/EndpointsModule.js'; import { CoreModule } from '@/core/CoreModule.js'; @@ -36,6 +41,7 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { ClientLoggerService } from './web/ClientLoggerService.js'; import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; @Module({ imports: [ @@ -78,6 +84,7 @@ import { RoleTimelineChannelService } from './api/stream/channels/role-timeline. ServerStatsChannelService, UserListChannelService, OpenApiServerService, + OAuth2ProviderService, ], exports: [ ServerService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 051920958e..0e4a5ece3e 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import cluster from 'node:cluster'; import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; @@ -7,7 +12,7 @@ import fastifyStatic from '@fastify/static'; import { IsNull } from 'typeorm'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { Config } from '@/config.js'; -import type { EmojisRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { EmojisRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; @@ -25,6 +30,7 @@ import { WellKnownServerService } from './WellKnownServerService.js'; import { FileServerService } from './FileServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; const _dirname = fileURLToPath(new URL('.', import.meta.url)); @@ -58,12 +64,13 @@ export class ServerService implements OnApplicationShutdown { private clientServerService: ClientServerService, private globalEventService: GlobalEventService, private loggerService: LoggerService, + private oauth2ProviderService: OAuth2ProviderService, ) { this.logger = this.loggerService.getLogger('server', 'gray', false); } @bindThis - public async launch() { + public async launch(): Promise<void> { const fastify = Fastify({ trustProxy: true, logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), @@ -92,6 +99,7 @@ export class ServerService implements OnApplicationShutdown { fastify.register(this.activityPubServerService.createServer); fastify.register(this.nodeinfoServerService.createServer); fastify.register(this.wellKnownServerService.createServer); + fastify.register(this.oauth2ProviderService.createServer); fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => { const path = request.params.path; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index aabe631fb2..8fc3c96de6 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -1,12 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import vary from 'vary'; import fastifyAccepts from '@fastify/accepts'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import * as Acct from '@/misc/acct.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -68,7 +73,7 @@ export class WellKnownServerService { }); fastify.get('/.well-known/host-meta.json', async (request, reply) => { - reply.header('Content-Type', jrd); + reply.header('Content-Type', 'application/json'); return { links: [{ rel: 'lrdd', @@ -88,13 +93,13 @@ fastify.get('/.well-known/change-password', async (request, reply) => { */ fastify.get<{ Querystring: { resource: string } }>(webFingerPath, async (request, reply) => { - const fromId = (id: User['id']): FindOptionsWhere<User> => ({ + const fromId = (id: MiUser['id']): FindOptionsWhere<MiUser> => ({ id, host: IsNull(), isSuspended: false, }); - const generateQuery = (resource: string): FindOptionsWhere<User> | number => + const generateQuery = (resource: string): FindOptionsWhere<MiUser> | number => resource.startsWith(`${this.config.url.toLowerCase()}/users/`) ? fromId(resource.split('/').pop()!) : fromAcct(Acct.parse( @@ -102,7 +107,7 @@ fastify.get('/.well-known/change-password', async (request, reply) => { resource.startsWith('acct:') ? resource.slice('acct:'.length) : resource)); - const fromAcct = (acct: Acct.Acct): FindOptionsWhere<User> | number => + const fromAcct = (acct: Acct.Acct): FindOptionsWhere<MiUser> | number => !acct.host || acct.host === this.config.host.toLowerCase() ? { usernameLower: acct.username, host: IsNull(), diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 3e8b9fb727..085a0fd58a 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -1,13 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import * as fs from 'node:fs'; import * as stream from 'node:stream/promises'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { LocalUser, User } from '@/models/entities/User.js'; -import type { AccessToken } from '@/models/entities/AccessToken.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; +import type { MiAccessToken } from '@/models/AccessToken.js'; import type Logger from '@/logger.js'; -import type { UserIpsRepository } from '@/models/index.js'; +import type { UserIpsRepository } from '@/models/_.js'; import { MetaService } from '@/core/MetaService.js'; import { createTemp } from '@/misc/create-temp.js'; import { bindThis } from '@/decorators.js'; @@ -29,8 +34,8 @@ const accessDenied = { @Injectable() export class ApiCallService implements OnApplicationShutdown { private logger: Logger; - private userIpHistories: Map<User['id'], Set<string>>; - private userIpHistoriesClearIntervalId: NodeJS.Timer; + private userIpHistories: Map<MiUser['id'], Set<string>>; + private userIpHistoriesClearIntervalId: NodeJS.Timeout; constructor( @Inject(DI.userIpsRepository) @@ -43,7 +48,7 @@ export class ApiCallService implements OnApplicationShutdown { private apiLoggerService: ApiLoggerService, ) { this.logger = this.apiLoggerService.logger; - this.userIpHistories = new Map<User['id'], Set<string>>(); + this.userIpHistories = new Map<MiUser['id'], Set<string>>(); this.userIpHistoriesClearIntervalId = setInterval(() => { this.userIpHistories.clear(); @@ -191,7 +196,7 @@ export class ApiCallService implements OnApplicationShutdown { } @bindThis - private async logIp(request: FastifyRequest, user: LocalUser) { + private async logIp(request: FastifyRequest, user: MiLocalUser) { const meta = await this.metaService.fetch(); if (!meta.enableIpLogging) return; const ip = request.ip; @@ -217,8 +222,8 @@ export class ApiCallService implements OnApplicationShutdown { @bindThis private async call( ep: IEndpoint & { exec: any }, - user: LocalUser | null | undefined, - token: AccessToken | null | undefined, + user: MiLocalUser | null | undefined, + token: MiAccessToken | null | undefined, data: any, file: { name: string; diff --git a/packages/backend/src/server/api/ApiLoggerService.ts b/packages/backend/src/server/api/ApiLoggerService.ts index 7f534b1efd..2339366a5d 100644 --- a/packages/backend/src/server/api/ApiLoggerService.ts +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index d3b1c7786d..1758c03aca 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import cors from '@fastify/cors'; import multipart from '@fastify/multipart'; import fastifyCookie from '@fastify/cookie'; import { ModuleRef } from '@nestjs/core'; import type { Config } from '@/config.js'; -import type { UsersRepository, InstancesRepository, AccessTokensRepository } from '@/models/index.js'; +import type { InstancesRepository, AccessTokensRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -22,9 +27,6 @@ export class ApiServerService { @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 8b0fff80d9..f075688194 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/index.js'; -import type { LocalUser } from '@/models/entities/User.js'; -import type { AccessToken } from '@/models/entities/AccessToken.js'; +import type { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/_.js'; +import type { MiLocalUser } from '@/models/User.js'; +import type { MiAccessToken } from '@/models/AccessToken.js'; import { MemoryKVCache } from '@/misc/cache.js'; -import type { App } from '@/models/entities/App.js'; +import type { MiApp } from '@/models/App.js'; import { CacheService } from '@/core/CacheService.js'; import isNativeToken from '@/misc/is-native-token.js'; import { bindThis } from '@/decorators.js'; @@ -18,7 +23,7 @@ export class AuthenticationError extends Error { @Injectable() export class AuthenticateService implements OnApplicationShutdown { - private appCache: MemoryKVCache<App>; + private appCache: MemoryKVCache<MiApp>; constructor( @Inject(DI.usersRepository) @@ -32,18 +37,18 @@ export class AuthenticateService implements OnApplicationShutdown { private cacheService: CacheService, ) { - this.appCache = new MemoryKVCache<App>(Infinity); + this.appCache = new MemoryKVCache<MiApp>(Infinity); } @bindThis - public async authenticate(token: string | null | undefined): Promise<[LocalUser | null, AccessToken | null]> { + public async authenticate(token: string | null | undefined): Promise<[MiLocalUser | null, MiAccessToken | null]> { if (token == null) { return [null, null]; } if (isNativeToken(token)) { const user = await this.cacheService.localUserByNativeTokenCache.fetch(token, - () => this.usersRepository.findOneBy({ token }) as Promise<LocalUser | null>); + () => this.usersRepository.findOneBy({ token }) as Promise<MiLocalUser | null>); if (user == null) { throw new AuthenticationError('user not found'); @@ -70,7 +75,7 @@ export class AuthenticateService implements OnApplicationShutdown { const user = await this.cacheService.localUserByIdCache.fetch(accessToken.userId, () => this.usersRepository.findOneBy({ id: accessToken.userId, - }) as Promise<LocalUser>); + }) as Promise<MiLocalUser>); if (accessToken.appId) { const app = await this.appCache.fetch(accessToken.appId, @@ -79,7 +84,7 @@ export class AuthenticateService implements OnApplicationShutdown { return [user, { id: accessToken.id, permission: app.permission, - } as AccessToken]; + } as MiAccessToken]; } else { return [user, accessToken]; } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 4e6bc46e67..41a11bfb19 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; @@ -155,6 +160,7 @@ import * as ep___federation_users from './endpoints/federation/users.js'; import * as ep___federation_stats from './endpoints/federation/stats.js'; import * as ep___following_create from './endpoints/following/create.js'; import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_update from './endpoints/following/update.js'; import * as ep___following_invalidate from './endpoints/following/invalidate.js'; import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; @@ -278,6 +284,7 @@ import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; +import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; import * as ep___pages_create from './endpoints/pages/create.js'; import * as ep___pages_delete from './endpoints/pages/delete.js'; @@ -331,6 +338,7 @@ import * as ep___users_lists_unfavorite from './endpoints/users/lists/unfavorite import * as ep___users_lists_create_from_public from './endpoints/users/lists/create-from-public.js'; import * as ep___users_notes from './endpoints/users/notes.js'; import * as ep___users_pages from './endpoints/users/pages.js'; +import * as ep___users_flashs from './endpoints/users/flashs.js'; import * as ep___users_reactions from './endpoints/users/reactions.js'; import * as ep___users_recommendation from './endpoints/users/recommendation.js'; import * as ep___users_relation from './endpoints/users/relation.js'; @@ -500,6 +508,7 @@ const $federation_users: Provider = { provide: 'ep:federation/users', useClass: const $federation_stats: Provider = { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }; const $following_create: Provider = { provide: 'ep:following/create', useClass: ep___following_create.default }; const $following_delete: Provider = { provide: 'ep:following/delete', useClass: ep___following_delete.default }; +const $following_update: Provider = { provide: 'ep:following/update', useClass: ep___following_update.default }; const $following_invalidate: Provider = { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }; const $following_requests_accept: Provider = { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }; const $following_requests_cancel: Provider = { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }; @@ -623,6 +632,7 @@ const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep__ const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }; const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; +const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default }; const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; const $pages_create: Provider = { provide: 'ep:pages/create', useClass: ep___pages_create.default }; const $pages_delete: Provider = { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }; @@ -676,6 +686,7 @@ const $users_lists_unfavorite: Provider = { provide: 'ep:users/lists/unfavorite' const $users_lists_create_from_public: Provider = { provide: 'ep:users/lists/create-from-public', useClass: ep___users_lists_create_from_public.default }; const $users_notes: Provider = { provide: 'ep:users/notes', useClass: ep___users_notes.default }; const $users_pages: Provider = { provide: 'ep:users/pages', useClass: ep___users_pages.default }; +const $users_flashs: Provider = { provide: 'ep:users/flashs', useClass: ep___users_flashs.default }; const $users_reactions: Provider = { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }; const $users_recommendation: Provider = { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }; const $users_relation: Provider = { provide: 'ep:users/relation', useClass: ep___users_relation.default }; @@ -849,6 +860,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $federation_stats, $following_create, $following_delete, + $following_update, $following_invalidate, $following_requests_accept, $following_requests_cancel, @@ -972,6 +984,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_userListTimeline, $notifications_create, $notifications_markAllAsRead, + $notifications_testNotification, $pagePush, $pages_create, $pages_delete, @@ -1025,6 +1038,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_lists_create_from_public, $users_notes, $users_pages, + $users_flashs, $users_reactions, $users_recommendation, $users_relation, @@ -1192,6 +1206,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $federation_stats, $following_create, $following_delete, + $following_update, $following_invalidate, $following_requests_accept, $following_requests_cancel, @@ -1366,6 +1381,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_lists_create_from_public, $users_notes, $users_pages, + $users_flashs, $users_reactions, $users_recommendation, $users_relation, diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index c94884a78c..e2b98c34e7 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UsersRepository } from '@/models/_.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -24,7 +29,7 @@ export class GetterService { * Get note for API processing */ @bindThis - public async getNote(noteId: Note['id']) { + public async getNote(noteId: MiNote['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) { @@ -38,21 +43,21 @@ export class GetterService { * Get user for API processing */ @bindThis - public async getUser(userId: User['id']) { + public async getUser(userId: MiUser['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) { throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); } - return user as LocalUser | RemoteUser; + return user as MiLocalUser | MiRemoteUser; } /** * Get remote user for API processing */ @bindThis - public async getRemoteUser(userId: User['id']) { + public async getRemoteUser(userId: MiUser['id']) { const user = await this.getUser(userId); if (!this.userEntityService.isRemoteUser(user)) { @@ -66,7 +71,7 @@ export class GetterService { * Get local user for API processing */ @bindThis - public async getLocalUser(userId: User['id']) { + public async getLocalUser(userId: MiUser['id']) { const user = await this.getUser(userId); if (!this.userEntityService.isLocalUser(user)) { diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index f6ffbfab50..0e644aa091 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import Limiter from 'ratelimiter'; import * as Redis from 'ioredis'; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index bd3d8a28da..150f3f24d4 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -1,19 +1,29 @@ -import { randomBytes } from 'node:crypto'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import * as OTPAuth from 'otpauth'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; +import type { + SigninsRepository, + UserProfilesRepository, + UsersRepository, +} from '@/models/_.js'; import type { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { LocalUser } from '@/models/entities/User.js'; +import type { MiLocalUser } from '@/models/User.js'; import { IdService } from '@/core/IdService.js'; -import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { bindThis } from '@/decorators.js'; +import { WebAuthnService } from '@/core/WebAuthnService.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; -import type { FastifyRequest, FastifyReply } from 'fastify'; +import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types'; +import type { FastifyReply, FastifyRequest } from 'fastify'; @Injectable() export class SigninApiService { @@ -24,22 +34,17 @@ export class SigninApiService { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userSecurityKeysRepository) - private userSecurityKeysRepository: UserSecurityKeysRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - @Inject(DI.attestationChallengesRepository) - private attestationChallengesRepository: AttestationChallengesRepository, - @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, private idService: IdService, private rateLimiterService: RateLimiterService, private signinService: SigninService, - private twoFactorAuthenticationService: TwoFactorAuthenticationService, + private userAuthService: UserAuthService, + private webAuthnService: WebAuthnService, ) { } @@ -50,11 +55,7 @@ export class SigninApiService { username: string; password: string; token?: string; - signature?: string; - authenticatorData?: string; - clientDataJSON?: string; - credentialId?: string; - challengeId?: string; + credential?: AuthenticationResponseJSON; }; }>, reply: FastifyReply, @@ -105,7 +106,7 @@ export class SigninApiService { const user = await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull(), - }) as LocalUser; + }) as MiLocalUser; if (user == null) { return error(404, { @@ -125,7 +126,7 @@ export class SigninApiService { const same = await bcrypt.compare(password, profile.password!); const fail = async (status?: number, failure?: { id: string }) => { - // Append signin history + // Append signin history await this.signinsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -155,78 +156,25 @@ export class SigninApiService { }); } - const delta = OTPAuth.TOTP.validate({ - secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), - digits: 6, - token, - window: 1, - }); - - if (delta === null) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { return await fail(403, { id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', }); - } else { - return this.signinService.signin(request, reply, user); } - } else if (body.credentialId && body.clientDataJSON && body.authenticatorData && body.signature) { + + return this.signinService.signin(request, reply, user); + } else if (body.credential) { if (!same && !profile.usePasswordLessLogin) { return await fail(403, { id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', }); } - const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex'); - const clientData = JSON.parse(clientDataJSON.toString('utf-8')); - const challenge = await this.attestationChallengesRepository.findOneBy({ - userId: user.id, - id: body.challengeId, - registrationChallenge: false, - challenge: this.twoFactorAuthenticationService.hash(clientData.challenge).toString('hex'), - }); - - if (!challenge) { - return await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - } - - await this.attestationChallengesRepository.delete({ - userId: user.id, - id: body.challengeId, - }); - - if (new Date().getTime() - challenge.createdAt.getTime() >= 5 * 60 * 1000) { - return await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - } - - const securityKey = await this.userSecurityKeysRepository.findOneBy({ - id: Buffer.from( - body.credentialId - .replace(/-/g, '+') - .replace(/_/g, '/'), - 'base64', - ).toString('hex'), - }); - - if (!securityKey) { - return await fail(403, { - id: '66269679-aeaf-4474-862b-eb761197e046', - }); - } + const authorized = await this.webAuthnService.verifyAuthentication(user.id, body.credential); - const isValid = this.twoFactorAuthenticationService.verifySignin({ - publicKey: Buffer.from(securityKey.publicKey, 'hex'), - authenticatorData: Buffer.from(body.authenticatorData, 'hex'), - clientDataJSON, - clientData, - signature: Buffer.from(body.signature, 'hex'), - challenge: challenge.challenge, - }); - - if (isValid) { + if (authorized) { return this.signinService.signin(request, reply, user); } else { return await fail(403, { @@ -240,42 +188,11 @@ export class SigninApiService { }); } - const keys = await this.userSecurityKeysRepository.findBy({ - userId: user.id, - }); - - if (keys.length === 0) { - return await fail(403, { - id: 'f27fd449-9af4-4841-9249-1f989b9fa4a4', - }); - } - - // 32 byte challenge - const challenge = randomBytes(32).toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); - - const challengeId = this.idService.genId(); - - await this.attestationChallengesRepository.insert({ - userId: user.id, - id: challengeId, - challenge: this.twoFactorAuthenticationService.hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: false, - }); + const authRequest = await this.webAuthnService.initiateAuthentication(user.id); reply.code(200); - return { - challenge, - challengeId, - securityKeys: keys.map(key => ({ - id: key.id, - })), - }; + return authRequest; } - // never get here + // never get here } } - diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 96666f1f49..cebba8c8ee 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { SigninsRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { SigninsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import type { LocalUser } from '@/models/entities/User.js'; +import type { MiLocalUser } from '@/models/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -12,9 +16,6 @@ import type { FastifyRequest, FastifyReply } from 'fastify'; @Injectable() export class SigninService { constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, @@ -25,7 +26,7 @@ export class SigninService { } @bindThis - public signin(request: FastifyRequest, reply: FastifyReply, user: LocalUser) { + public signin(request: FastifyRequest, reply: FastifyReply, user: MiLocalUser) { setImmediate(async () => { // Append signin history const record = await this.signinsRepository.insert({ diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 7b215cea79..431df581b5 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, RegistrationTicket } from '@/models/index.js'; +import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket } from '@/models/_.js'; import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { CaptchaService } from '@/core/CaptchaService.js'; @@ -10,7 +15,7 @@ import { IdService } from '@/core/IdService.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { EmailService } from '@/core/EmailService.js'; -import { LocalUser } from '@/models/entities/User.js'; +import { MiLocalUser } from '@/models/User.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { bindThis } from '@/decorators.js'; import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js'; @@ -109,7 +114,7 @@ export class SignupApiService { } } - let ticket: RegistrationTicket | null = null; + let ticket: MiRegistrationTicket | null = null; if (instance.disableRegistration) { if (invitationCode == null || typeof invitationCode !== 'string') { @@ -246,7 +251,7 @@ export class SignupApiService { }); } - return this.signinService.signin(request, reply, account as LocalUser); + return this.signinService.signin(request, reply, account as MiLocalUser); } catch (err) { throw new FastifyReplyError(400, typeof err === 'string' ? err : (err as Error).toString()); } diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index e4291becf0..9acaa688c5 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -1,18 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { EventEmitter } from 'events'; import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import * as WebSocket from 'ws'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, AccessToken } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { UsersRepository, MiAccessToken } from '@/models/_.js'; import { NoteReadService } from '@/core/NoteReadService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; -import { LocalUser } from '@/models/entities/User.js'; +import { MiLocalUser } from '@/models/User.js'; import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; -import MainStreamConnection from './stream/index.js'; +import MainStreamConnection from './stream/Connection.js'; import { ChannelsService } from './stream/ChannelsService.js'; import type * as http from 'node:http'; @@ -23,9 +26,6 @@ export class StreamingApiServerService { #cleanConnectionsIntervalId: NodeJS.Timeout | null = null; constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.redisForSub) private redisForSub: Redis.Redis, @@ -55,8 +55,8 @@ export class StreamingApiServerService { const q = new URL(request.url, `http://${request.headers.host}`).searchParams; - let user: LocalUser | null = null; - let app: AccessToken | null = null; + let user: MiLocalUser | null = null; + let app: MiAccessToken | null = null; // https://datatracker.ietf.org/doc/html/rfc6750.html#section-2.1 // Note that the standard WHATWG WebSocket API does not support setting any headers, @@ -112,8 +112,8 @@ export class StreamingApiServerService { this.#wss.on('connection', async (connection: WebSocket.WebSocket, request: http.IncomingMessage, ctx: { stream: MainStreamConnection, - user: LocalUser | null; - app: AccessToken | null + user: MiLocalUser | null; + app: MiAccessToken | null }) => { const { stream, user, app } = ctx; diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index 364fa7a19b..d5279faa1c 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs'; import _Ajv from 'ajv'; import type { Schema, SchemaType } from '@/misc/json-schema.js'; -import type { LocalUser } from '@/models/entities/User.js'; -import type { AccessToken } from '@/models/entities/AccessToken.js'; +import type { MiLocalUser } from '@/models/User.js'; +import type { MiAccessToken } from '@/models/AccessToken.js'; import { ApiError } from './error.js'; import type { IEndpointMeta } from './endpoints.js'; @@ -23,16 +28,16 @@ type File = { // TODO: paramsの型をT['params']のスキーマ定義から推論する type Executor<T extends IEndpointMeta, Ps extends Schema> = - (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => + (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { - public exec: (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; + public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; constructor(meta: T, paramDef: Ps, cb: Executor<T, Ps>) { const validate = ajv.compile(paramDef); - this.exec = (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { + this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { let cleanup: undefined | (() => void) = undefined; if (meta.requireFile) { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 41c3a29eec..ab20a708ef 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Schema } from '@/misc/json-schema.js'; import { RolePolicies } from '@/core/RoleService.js'; @@ -155,6 +160,7 @@ import * as ep___federation_users from './endpoints/federation/users.js'; import * as ep___federation_stats from './endpoints/federation/stats.js'; import * as ep___following_create from './endpoints/following/create.js'; import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_update from './endpoints/following/update.js'; import * as ep___following_invalidate from './endpoints/following/invalidate.js'; import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; @@ -278,6 +284,7 @@ import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; +import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; import * as ep___pages_create from './endpoints/pages/create.js'; import * as ep___pages_delete from './endpoints/pages/delete.js'; @@ -331,6 +338,7 @@ import * as ep___users_lists_create_from_public from './endpoints/users/lists/cr import * as ep___users_lists_update from './endpoints/users/lists/update.js'; import * as ep___users_notes from './endpoints/users/notes.js'; import * as ep___users_pages from './endpoints/users/pages.js'; +import * as ep___users_flashs from './endpoints/users/flashs.js'; import * as ep___users_reactions from './endpoints/users/reactions.js'; import * as ep___users_recommendation from './endpoints/users/recommendation.js'; import * as ep___users_relation from './endpoints/users/relation.js'; @@ -498,6 +506,7 @@ const eps = [ ['federation/stats', ep___federation_stats], ['following/create', ep___following_create], ['following/delete', ep___following_delete], + ['following/update', ep___following_update], ['following/invalidate', ep___following_invalidate], ['following/requests/accept', ep___following_requests_accept], ['following/requests/cancel', ep___following_requests_cancel], @@ -621,6 +630,7 @@ const eps = [ ['notes/user-list-timeline', ep___notes_userListTimeline], ['notifications/create', ep___notifications_create], ['notifications/mark-all-as-read', ep___notifications_markAllAsRead], + ['notifications/test-notification', ep___notifications_testNotification], ['page-push', ep___pagePush], ['pages/create', ep___pages_create], ['pages/delete', ep___pages_delete], @@ -674,6 +684,7 @@ const eps = [ ['users/lists/create-from-public', ep___users_lists_create_from_public], ['users/notes', ep___users_notes], ['users/pages', ep___users_pages], + ['users/flashs', ep___users_flashs], ['users/reactions', ep___users_reactions], ['users/recommendation', ep___users_recommendation], ['users/relation', ep___users_relation], @@ -800,4 +811,5 @@ const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => { }; }); +// eslint-disable-next-line import/no-default-export export default endpoints; diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index b8ea74b7c5..be4fc82f0c 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AbuseUserReportsRepository } from '@/models/index.js'; +import type { AbuseUserReportsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { AbuseUserReportEntityService } from '@/core/entities/AbuseUserReportEntityService.js'; @@ -87,9 +92,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index 8a3541dffe..070e88f6f3 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js'; +import { localUsernameSchema, passwordSchema } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -32,9 +37,8 @@ export const paramDef = { required: ['username', 'password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 16232813a8..60e928ccbe 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { QueueService } from '@/core/QueueService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -22,16 +26,14 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, private userEntityService: UserEntityService, private queueService: QueueService, - private globalEventService: GlobalEventService, private userSuspendService: UserSuspendService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 757030839e..a13d08fd3a 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; @@ -27,9 +32,8 @@ export const paramDef = { required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'imageUrl', 'dayOfWeek'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index f4c9885408..d3c53d4f67 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -27,9 +32,8 @@ export const paramDef = { required: ['id'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 725ddb58be..adff3ed0ae 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 70082290ba..5b77f67e10 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'dayOfWeek'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 751b6be7f4..262b36b9a4 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,8 +1,11 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AnnouncementsRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import { DI } from '@/di-symbols.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; export const meta = { tags: ['admin'], @@ -52,30 +55,35 @@ export const paramDef = { title: { type: 'string', minLength: 1 }, text: { type: 'string', minLength: 1 }, imageUrl: { type: 'string', nullable: true, minLength: 1 }, + icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' }, + display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' }, + forExistingUsers: { type: 'boolean', default: false }, + needConfirmationToRead: { type: 'boolean', default: false }, + userId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, }, required: ['title', 'text', 'imageUrl'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.announcementsRepository) - private announcementsRepository: AnnouncementsRepository, - - private idService: IdService, + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await this.announcementsRepository.insert({ - id: this.idService.genId(), + const { raw, packed } = await this.announcementService.create({ createdAt: new Date(), updatedAt: null, title: ps.title, text: ps.text, imageUrl: ps.imageUrl, - }).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0])); + icon: ps.icon, + display: ps.display, + forExistingUsers: ps.forExistingUsers, + needConfirmationToRead: ps.needConfirmationToRead, + userId: ps.userId, + }, me); - return Object.assign({}, announcement, { createdAt: announcement.createdAt.toISOString(), updatedAt: null }); + return packed; }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 18d50b8b2a..80ec281253 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,7 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -27,19 +33,20 @@ export const paramDef = { required: ['id'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, + + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await this.announcementsRepository.delete(announcement.id); + await this.announcementService.delete(announcement, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 11231f6e04..c82e702eef 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/index.js'; -import type { Announcement } from '@/models/entities/Announcement.js'; +import type { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/_.js'; +import type { MiAnnouncement } from '@/models/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -61,13 +66,13 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id', nullable: true }, }, required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, @@ -79,10 +84,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + if (ps.userId) { + query.andWhere('announcement.userId = :userId', { userId: ps.userId }); + } else { + query.andWhere('announcement.userId IS NULL'); + } const announcements = await query.limit(ps.limit).getMany(); - const reads = new Map<Announcement, number>(); + const reads = new Map<MiAnnouncement, number>(); for (const announcement of announcements) { reads.set(announcement, await this.announcementReadsRepository.countBy({ @@ -97,6 +107,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { title: announcement.title, text: announcement.text, imageUrl: announcement.imageUrl, + icon: announcement.icon, + display: announcement.display, + isActive: announcement.isActive, + forExistingUsers: announcement.forExistingUsers, + needConfirmationToRead: announcement.needConfirmationToRead, + userId: announcement.userId, reads: reads.get(announcement)!, })); }); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 8cf9341a71..d36590c264 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,7 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -26,29 +32,40 @@ export const paramDef = { title: { type: 'string', minLength: 1 }, text: { type: 'string', minLength: 1 }, imageUrl: { type: 'string', nullable: true, minLength: 0 }, + icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'] }, + display: { type: 'string', enum: ['normal', 'banner', 'dialog'] }, + forExistingUsers: { type: 'boolean' }, + needConfirmationToRead: { type: 'boolean' }, + isActive: { type: 'boolean' }, }, - required: ['id', 'title', 'text', 'imageUrl'], + required: ['id'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, + + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await this.announcementsRepository.update(announcement.id, { + await this.announcementService.update(announcement, { updatedAt: new Date(), title: ps.title, text: ps.text, /* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */ imageUrl: ps.imageUrl || null, - }); + display: ps.display, + icon: ps.icon, + forExistingUsers: ps.forExistingUsers, + needConfirmationToRead: ps.needConfirmationToRead, + isActive: ps.isActive, + }, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index d0485fddd8..9ef09b172e 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; @@ -22,9 +27,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index c193ed3fb3..e47ecd81cf 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index a8964af449..8af44029c5 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; @@ -15,9 +20,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index 4f7e02fe92..75d689966f 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 2901fdb774..ac8a70e3da 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 1d27ac2137..7fb5342f8d 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository, UsersRepository } from '@/models/index.js'; +import type { DriveFilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -148,9 +153,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 6e604ed885..66ee4cab3b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -22,9 +27,8 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 200ede0b06..24d3a8a943 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { ApiError } from '../../../error.js'; @@ -47,9 +51,8 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -57,7 +60,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private customEmojiService: CustomEmojiService, private emojiEntityService: EmojiEntityService, - private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); @@ -73,11 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], - }); - - this.moderationLogService.insertModerationLog(me, 'addEmoji', { - emojiId: emoji.id, - }); + }, me); return this.emojiEntityService.packDetailed(emoji); }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 82dca9cc70..c5f986ff02 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { DI } from '@/di-symbols.js'; import { DriveService } from '@/core/DriveService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -47,13 +51,9 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.db) - private db: DataSource, - @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, @@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchEmoji); } - let driveFile: DriveFile; + let driveFile: MiDriveFile; try { // Create file diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 9f8263629b..e6c1bf317f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -19,14 +24,13 @@ export const paramDef = { required: ['ids'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { - await this.customEmojiService.deleteBulk(ps.ids); + await this.customEmojiService.deleteBulk(ps.ids, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 429c819fe0..58aa0b9950 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -25,14 +30,13 @@ export const paramDef = { required: ['id'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { - await this.customEmojiService.delete(ps.id); + await this.customEmojiService.delete(ps.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index e26f0506ce..208616c0ac 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; @@ -16,9 +21,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 8d50413e95..855ab8cd24 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; @@ -72,9 +77,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 29b20fab86..ab16d86a3d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { EmojisRepository } from '@/models/index.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; +import type { EmojisRepository } from '@/models/_.js'; +import type { MiEmoji } from '@/models/Emoji.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; @@ -66,9 +71,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, @@ -80,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere('emoji.host IS NULL'); - let emojis: Emoji[]; + let emojis: MiEmoji[]; if (ps.query) { //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 83f882cac5..a5dd6d5e3a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -22,9 +27,8 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 1d3a432bb7..515053f57b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -22,9 +27,8 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 453968c7a9..8e834ad1dd 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['ids'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts index b90b9757be..2dc9595a7e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['ids'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private customEmojiService: CustomEmojiService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index edc1af5a53..2d69857408 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -54,9 +59,8 @@ export const paramDef = { required: ['id', 'name', 'aliases'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -80,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, - }); + }, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index 38fe99b222..b63f01bec3 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index b7f2858a77..6dbfe3c4f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/_.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; @@ -20,9 +25,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 83f729953a..36ea390e45 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { QueueService } from '@/core/QueueService.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 4fd74e591d..357bf83e87 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/_.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -21,15 +27,15 @@ export const paramDef = { required: ['host', 'isSuspended'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, private utilityService: UtilityService, private federatedInstanceService: FederatedInstanceService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) }); @@ -38,9 +44,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('instance not found'); } - this.federatedInstanceService.update(instance.id, { + await this.federatedInstanceService.update(instance.id, { isSuspended: ps.isSuspended, }); + + if (instance.isSuspended !== ps.isSuspended) { + if (ps.isSuspended) { + this.moderationLogService.log(me, 'suspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); + } else { + this.moderationLogService.log(me, 'unsuspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); + } + } }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 8ffd2b01e7..4bd9e7de7f 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -16,9 +21,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 09d61bd741..f953b889a3 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -27,9 +32,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index bfcc8a700b..cf94c998fa 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserIpsRepository } from '@/models/index.js'; +import type { UserIpsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userIpsRepository) private userIpsRepository: UserIpsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 664b4d819f..7112e06bdc 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; @@ -47,9 +52,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts index d8bf6e286f..a20a51121a 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 084bdb598b..c3ba07cdd0 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; @@ -80,6 +85,14 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + app192IconUrl: { + type: 'string', + optional: false, nullable: true, + }, + app512IconUrl: { + type: 'string', + optional: false, nullable: true, + }, enableEmail: { type: 'boolean', optional: false, nullable: false, @@ -273,6 +286,10 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + manifestJsonOverride: { + type: 'string', + optional: true, nullable: false, + }, policies: { type: 'object', optional: false, nullable: false, @@ -288,9 +305,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, @@ -305,6 +321,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { maintainerEmail: instance.maintainerEmail, version: this.config.version, name: instance.name, + shortName: instance.shortName, uri: this.config.url, description: instance.description, langs: instance.langs, @@ -327,6 +344,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { notFoundImageUrl: instance.notFoundImageUrl, infoImageUrl: instance.infoImageUrl, iconUrl: instance.iconUrl, + app192IconUrl: instance.app192IconUrl, + app512IconUrl: instance.app512IconUrl, backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, defaultLightTheme: instance.defaultLightTheme, @@ -379,6 +398,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { enableServerMachineStats: instance.enableServerMachineStats, enableIdenticonGeneration: instance.enableIdenticonGeneration, policies: { ...DEFAULT_POLICIES, ...instance.policies }, + manifestJsonOverride: instance.manifestJsonOverride, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 8401cf51d9..4061e1b5df 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { PromoNotesRepository } from '@/models/index.js'; +import type { PromoNotesRepository } from '@/models/_.js'; import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['noteId', 'expiresAt'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.promoNotesRepository) private promoNotesRepository: PromoNotesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 099e2ff220..c9142e9885 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -16,9 +21,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private moderationLogService: ModerationLogService, private queueService: QueueService, @@ -26,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { super(meta, paramDef, async (ps, me) => { this.queueService.destroy(); - this.moderationLogService.insertModerationLog(me, 'clearQueue'); + this.moderationLogService.log(me, 'clearQueue'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 9442bda5eb..1515ae4c74 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject('queue:deliver') public deliverQueue: DeliverQueue, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 55a3410d49..febe0d07c6 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject('queue:inbox') public inboxQueue: InboxQueue, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts index 8330d6c82f..0cba5b4e25 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: ['type'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private moderationLogService: ModerationLogService, private queueService: QueueService, @@ -66,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { break; } - this.moderationLogService.insertModerationLog(me, 'promoteQueue'); + this.moderationLogService.log(me, 'promoteQueue'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 7f3732c970..901195e9a5 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js'; @@ -38,9 +43,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index f2d4aa8996..b675db2b89 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URL } from 'node:url'; import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -54,9 +59,8 @@ export const paramDef = { required: ['inbox'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private relayService: RelayService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 910c90e78e..0633c57ed5 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; @@ -46,9 +51,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private relayService: RelayService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 5e26f61fa7..661b4243c4 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; @@ -17,9 +22,8 @@ export const paramDef = { required: ['inbox'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private relayService: RelayService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index e9c3b0e69f..6ce7583276 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -33,17 +39,18 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private moderationLogService: ModerationLogService, ) { - super(meta, paramDef, async (ps) => { + super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { @@ -65,6 +72,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { password: hash, }); + this.moderationLogService.log(me, 'resetPassword', { + targetId: user.id, + }); + return { password: passwd, }; diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index aead894611..8667640a67 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; +import type { UsersRepository, AbuseUserReportsRepository } from '@/models/_.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; import { QueueService } from '@/core/QueueService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @@ -24,9 +29,8 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts index b80aaba122..a0f3edd867 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository, UsersRepository } from '@/models/index.js'; +import type { RolesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; import { RoleService } from '@/core/RoleService.js'; @@ -48,9 +53,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -79,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { return; } - await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null); + await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index 916172f54a..f567b0d387 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; @@ -50,9 +55,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts index b56ebdb3ee..7b989050eb 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { RolesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['admin', 'role'], @@ -30,24 +35,20 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, - private globalEventService: GlobalEventService, + private roleService: RoleService, ) { - super(meta, paramDef, async (ps) => { + super(meta, paramDef, async (ps, me) => { const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); if (role == null) { throw new ApiError(meta.errors.noSuchRole); } - await this.rolesRepository.delete({ - id: ps.roleId, - }); - this.globalEventService.publishInternalEvent('roleDeleted', role); + await this.roleService.delete(role, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts index edaf638ea9..3ed4b324dc 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; @@ -19,9 +24,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts index 01028a086f..5f0accab6f 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; @@ -30,9 +35,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts index 45c4f76943..4c27583111 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository, UsersRepository } from '@/models/index.js'; +import type { RolesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; import { RoleService } from '@/core/RoleService.js'; @@ -50,9 +55,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -77,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchUser); } - await this.roleService.unassign(user.id, role.id); + await this.roleService.unassign(user.id, role.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts index 5a34eee96c..b4e7e29e90 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -22,9 +27,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private metaService: MetaService, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 1fedab4540..e4e59e487c 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['admin', 'role'], @@ -59,23 +65,22 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, - private globalEventService: GlobalEventService, + private roleService: RoleService, ) { - super(meta, paramDef, async (ps) => { - const roleExist = await this.rolesRepository.exist({ where: { id: ps.roleId } }); - if (!roleExist) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { throw new ApiError(meta.errors.noSuchRole); } const date = new Date(); - await this.rolesRepository.update(ps.roleId, { + await this.roleService.update(role, { updatedAt: date, name: ps.name, description: ps.description, @@ -91,9 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { canEditMembersByModerator: ps.canEditMembersByModerator, displayOrder: ps.displayOrder, policies: ps.policies, - }); - const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); - this.globalEventService.publishInternalEvent('roleUpdated', updated); + }, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 63650bb2bf..b1772be777 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; -import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['roleId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index 5ddc62f476..b9f2c6a6f1 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmailService } from '@/core/EmailService.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: ['to', 'subject', 'text'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private emailService: EmailService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 4ef4fdc665..3169373b0e 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as os from 'node:os'; import si from 'systeminformation'; import { Inject, Injectable } from '@nestjs/common'; @@ -95,9 +100,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 69c95ef19c..f87a5a3574 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ModerationLogsRepository } from '@/models/index.js'; +import type { ModerationLogsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { ModerationLogEntityService } from '@/core/entities/ModerationLogEntityService.js'; @@ -57,13 +62,14 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + type: { type: 'string', nullable: true }, + userId: { type: 'string', format: 'misskey:id', nullable: true }, }, required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.moderationLogsRepository) private moderationLogsRepository: ModerationLogsRepository, @@ -74,6 +80,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); + if (ps.type != null) { + query.andWhere('report.type = :type', { type: ps.type }); + } + + if (ps.userId != null) { + query.andWhere('report.userId = :userId', { userId: ps.userId }); + } + const reports = await query.limit(ps.limit).getMany(); return await this.moderationLogEntityService.packMany(reports); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 6f805b6b4e..e065b99e93 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, SigninsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, SigninsRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -25,9 +30,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 2ae5bc3de3..e89e1a1490 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index eabbceac0e..89199f8bff 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull, Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import type { RelationshipJobData } from '@/queue/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -56,7 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isSuspended: true, }); - this.moderationLogService.insertModerationLog(me, 'suspend', { + this.moderationLogService.log(me, 'suspend', { targetId: user.id, }); @@ -68,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } @bindThis - private async unFollowAll(follower: User) { + private async unFollowAll(follower: MiUser) { const followings = await this.followingsRepository.find({ where: { followerId: follower.id, diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index 2805c21a74..a2779148ed 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { DI } from '@/di-symbols.js'; @@ -20,9 +25,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -41,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isSuspended: false, }); - this.moderationLogService.insertModerationLog(me, 'unsuspend', { + this.moderationLogService.log(me, 'unsuspend', { targetId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 144360a921..ea6ebdd1fe 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,9 +1,12 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import type { Meta } from '@/models/entities/Meta.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import type { MiMeta } from '@/models/Meta.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; export const meta = { @@ -36,9 +39,12 @@ export const paramDef = { infoImageUrl: { type: 'string', nullable: true }, notFoundImageUrl: { type: 'string', nullable: true }, iconUrl: { type: 'string', nullable: true }, + app192IconUrl: { type: 'string', nullable: true }, + app512IconUrl: { type: 'string', nullable: true }, backgroundImageUrl: { type: 'string', nullable: true }, logoImageUrl: { type: 'string', nullable: true }, name: { type: 'string', nullable: true }, + shortName: { type: 'string', nullable: true }, description: { type: 'string', nullable: true }, defaultLightTheme: { type: 'string', nullable: true }, defaultDarkTheme: { type: 'string', nullable: true }, @@ -101,22 +107,19 @@ export const paramDef = { enableIdenticonGeneration: { type: 'boolean' }, serverRules: { type: 'array', items: { type: 'string' } }, preservedUsernames: { type: 'array', items: { type: 'string' } }, + manifestJsonOverride: { type: 'string' }, }, required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.db) - private db: DataSource, - private metaService: MetaService, private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { - const set = {} as Partial<Meta>; + const set = {} as Partial<MiMeta>; if (typeof ps.disableRegistration === 'boolean') { set.disableRegistration = ps.disableRegistration; @@ -154,6 +157,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { set.iconUrl = ps.iconUrl; } + if (ps.app192IconUrl !== undefined) { + set.app192IconUrl = ps.app192IconUrl; + } + + if (ps.app512IconUrl !== undefined) { + set.app512IconUrl = ps.app512IconUrl; + } + if (ps.serverErrorImageUrl !== undefined) { set.serverErrorImageUrl = ps.serverErrorImageUrl; } @@ -178,6 +189,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { set.name = ps.name; } + if (ps.shortName !== undefined) { + set.shortName = ps.shortName; + } + if (ps.description !== undefined) { set.description = ps.description; } @@ -422,8 +437,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { set.preservedUsernames = ps.preservedUsernames; } + if (ps.manifestJsonOverride !== undefined) { + set.manifestJsonOverride = ps.manifestJsonOverride; + } + + const before = await this.metaService.fetch(true); + await this.metaService.update(set); - this.moderationLogService.insertModerationLog(me, 'updateMeta'); + + const after = await this.metaService.fetch(true); + + this.moderationLogService.log(me, 'updateServerSettings', { + before, + after, + }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index 33808ee70f..2e9fd5ad29 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,7 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -19,15 +25,16 @@ export const paramDef = { required: ['userId', 'text'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -36,9 +43,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('user not found'); } + const currentProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + await this.userProfilesRepository.update({ userId: user.id }, { moderationNote: ps.text, }); + + this.moderationLogService.log(me, 'updateUserNote', { + userId: user.id, + before: currentProfile.moderationNote, + after: ps.text, + }); }); } } diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 735af51ee2..498afe3448 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,8 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; import { DI } from '@/di-symbols.js'; -import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/_.js'; export const meta = { tags: ['meta'], @@ -15,40 +22,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - createdAt: { - type: 'string', - optional: false, nullable: false, - format: 'date-time', - }, - updatedAt: { - type: 'string', - optional: false, nullable: true, - format: 'date-time', - }, - text: { - type: 'string', - optional: false, nullable: false, - }, - title: { - type: 'string', - optional: false, nullable: false, - }, - imageUrl: { - type: 'string', - optional: false, nullable: true, - }, - isRead: { - type: 'boolean', - optional: true, nullable: false, - }, - }, + ref: 'Announcement', }, }, } as const; @@ -57,16 +31,15 @@ export const paramDef = { type: 'object', properties: { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - withUnreads: { type: 'boolean', default: false }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + isActive: { type: 'boolean', default: true }, }, required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, @@ -75,27 +48,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private announcementReadsRepository: AnnouncementReadsRepository, private queryService: QueryService, + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId) + .where('announcement.isActive = :isActive', { isActive: ps.isActive }) + .andWhere(new Brackets(qb => { + if (me) qb.orWhere('announcement.userId = :meId', { meId: me.id }); + qb.orWhere('announcement.userId IS NULL'); + })); const announcements = await query.limit(ps.limit).getMany(); - if (me) { - const reads = (await this.announcementReadsRepository.findBy({ - userId: me.id, - })).map(x => x.announcementId); - - for (const announcement of announcements) { - (announcement as any).isRead = reads.includes(announcement.id); - } - } - - return (ps.withUnreads ? announcements.filter((a: any) => !a.isRead) : announcements).map((a) => ({ - ...a, - createdAt: a.createdAt.toISOString(), - updatedAt: a.updatedAt?.toISOString() ?? null, - })); + return this.announcementService.packMany(announcements, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 5754a9f12a..15fca4904d 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import type { UserListsRepository, AntennasRepository } from '@/models/index.js'; +import type { UserListsRepository, AntennasRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -42,7 +47,7 @@ export const paramDef = { type: 'object', properties: { name: { type: 'string', minLength: 1, maxLength: 100 }, - src: { type: 'string', enum: ['home', 'all', 'users', 'list'] }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] }, userListId: { type: 'string', format: 'misskey:id', nullable: true }, keywords: { type: 'array', items: { type: 'array', items: { @@ -65,9 +70,8 @@ export const paramDef = { required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, @@ -81,8 +85,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - if ((ps.keywords.length === 0) || ps.keywords[0].every(x => x === '')) { - throw new Error('invalid param'); + if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) { + throw new Error('either keywords or excludeKeywords is required.'); } const currentAntennasCount = await this.antennasRepository.countBy({ diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index 5da7a2cb66..e6240aec65 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['antennaId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index a0f8979574..3a9f969d24 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/_.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 2c4247cb70..eaae7bff62 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository, AntennasRepository } from '@/models/index.js'; +import type { NotesRepository, AntennasRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; @@ -48,9 +53,8 @@ export const paramDef = { required: ['antennaId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.redis) private redisClient: Redis.Redis, diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index ef7ed5b72c..77c9b31763 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/_.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['antennaId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 55218b644b..0e98746881 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AntennasRepository, UserListsRepository } from '@/models/index.js'; +import type { AntennasRepository, UserListsRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -41,7 +46,7 @@ export const paramDef = { properties: { antennaId: { type: 'string', format: 'misskey:id' }, name: { type: 'string', minLength: 1, maxLength: 100 }, - src: { type: 'string', enum: ['home', 'all', 'users', 'list'] }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] }, userListId: { type: 'string', format: 'misskey:id', nullable: true }, keywords: { type: 'array', items: { type: 'array', items: { @@ -64,9 +69,8 @@ export const paramDef = { required: ['antennaId', 'name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, @@ -78,6 +82,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) { + throw new Error('either keywords or excludeKeywords is required.'); + } // Fetch the antenna const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index c45a86761c..a4a7fd2037 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -30,9 +35,8 @@ export const paramDef = { required: ['uri'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private apResolverService: ApResolverService, ) { diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index a103d4196a..f442fbdd2f 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,9 +1,13 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, NotesRepository } from '@/models/index.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { LocalUser, User } from '@/models/entities/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; import { isActor, isPost, getApId } from '@/core/activitypub/type.js'; import type { SchemaType } from '@/misc/json-schema.js'; import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; @@ -14,7 +18,6 @@ import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '../../error.js'; @@ -81,16 +84,9 @@ export const paramDef = { required: ['uri'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - private utilityService: UtilityService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, @@ -114,7 +110,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { * URIからUserかNoteを解決する */ @bindThis - private async fetchAny(uri: string, me: LocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { + private async fetchAny(uri: string, me: MiLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { // ブロックしてたら中断 const fetchedMeta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(fetchedMeta.blockedHosts, this.utilityService.extractDbHost(uri))) return null; @@ -147,7 +143,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } @bindThis - private async mergePack(me: LocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise<SchemaType<typeof meta.res> | null> { + private async mergePack(me: MiLocalUser | null | undefined, user: MiUser | null | undefined, note: MiNote | null | undefined): Promise<SchemaType<typeof meta.res> | null> { if (user != null) { return { type: 'User', diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index aaef02d03f..cb00221506 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { unique } from '@/misc/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['name', 'description', 'permission'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.appsRepository) private appsRepository: AppsRepository, diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index eaafa8dc1b..cb968a1c65 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/_.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: ['appId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.appsRepository) private appsRepository: AppsRepository, diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index aa199ab730..1b1893fd94 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AuthSessionsRepository, AppsRepository, AccessTokensRepository } from '@/models/index.js'; +import type { AuthSessionsRepository, AppsRepository, AccessTokensRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { DI } from '@/di-symbols.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: ['token'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.appsRepository) private appsRepository: AppsRepository, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 631fb4f024..8b6a2c213d 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AppsRepository, AuthSessionsRepository } from '@/models/index.js'; +import type { AppsRepository, AuthSessionsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; @@ -45,9 +50,8 @@ export const paramDef = { required: ['appSecret'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index db3bf7aa63..0f5da0f252 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AuthSessionsRepository } from '@/models/index.js'; +import type { AuthSessionsRepository } from '@/models/_.js'; import { AuthSessionEntityService } from '@/core/entities/AuthSessionEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -48,9 +53,8 @@ export const paramDef = { required: ['token'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.authSessionsRepository) private authSessionsRepository: AuthSessionsRepository, diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index b1e7bbfded..ffddda090b 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, AppsRepository, AccessTokensRepository, AuthSessionsRepository } from '@/models/index.js'; +import type { AppsRepository, AccessTokensRepository, AuthSessionsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -57,13 +62,9 @@ export const paramDef = { required: ['appSecret', 'token'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.appsRepository) private appsRepository: AppsRepository, diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 4ad40c8f1c..3c7d7ac8cd 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; @@ -55,9 +60,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index ad3d9f22b3..0ce334d559 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; @@ -55,9 +60,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index d61bb0d214..58d24540d1 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { BlockingsRepository } from '@/models/index.js'; +import type { BlockingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { BlockingEntityService } from '@/core/entities/BlockingEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.blockingsRepository) private blockingsRepository: BlockingsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 69e2f2504c..e72120e156 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; -import type { Channel } from '@/models/entities/Channel.js'; +import type { ChannelsRepository, DriveFilesRepository } from '@/models/_.js'; +import type { MiChannel } from '@/models/Channel.js'; import { IdService } from '@/core/IdService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -44,13 +49,13 @@ export const paramDef = { description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 }, bannerId: { type: 'string', format: 'misskey:id', nullable: true }, color: { type: 'string', minLength: 1, maxLength: 16 }, + isSensitive: { type: 'boolean', nullable: true }, }, required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -81,8 +86,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { name: ps.name, description: ps.description ?? null, bannerId: banner ? banner.id : null, + isSensitive: ps.isSensitive ?? false, ...(ps.color !== undefined ? { color: ps.color } : {}), - } as Channel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0])); + } as MiChannel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0])); return await this.channelEntityService.pack(channel, me); }); diff --git a/packages/backend/src/server/api/endpoints/channels/favorite.ts b/packages/backend/src/server/api/endpoints/channels/favorite.ts index c8544273a1..1f78a86dd4 100644 --- a/packages/backend/src/server/api/endpoints/channels/favorite.ts +++ b/packages/backend/src/server/api/endpoints/channels/favorite.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFavoritesRepository, ChannelsRepository } from '@/models/index.js'; +import type { ChannelFavoritesRepository, ChannelsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 953f027aa2..412ea1bb16 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/_.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index f3ca66cfd2..5a43e8be1b 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -32,9 +36,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index a1656903aa..6514f1ea3c 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFollowingsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: ChannelFollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/my-favorites.ts b/packages/backend/src/server/api/endpoints/channels/my-favorites.ts index 60525ed060..057a438ac9 100644 --- a/packages/backend/src/server/api/endpoints/channels/my-favorites.ts +++ b/packages/backend/src/server/api/endpoints/channels/my-favorites.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFavoritesRepository } from '@/models/index.js'; -import { QueryService } from '@/core/QueryService.js'; +import type { ChannelFavoritesRepository } from '@/models/_.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -30,15 +34,13 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelFavoritesRepository) private channelFavoritesRepository: ChannelFavoritesRepository, private channelEntityService: ChannelEntityService, - private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const query = this.channelFavoritesRepository.createQueryBuilder('favorite') diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 4561bb2e94..b1dd693537 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts index dfb6937964..65df45706b 100644 --- a/packages/backend/src/server/api/endpoints/channels/search.ts +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; -import type { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/_.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['query'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 070d14631e..3eaa83c7e8 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/_.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index e3119cc40f..026b649537 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelsRepository, Note, NotesRepository } from '@/models/index.js'; +import type { ChannelsRepository, MiNote, NotesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; @@ -46,9 +51,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.redis) private redisClient: Redis.Redis, @@ -73,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchChannel); } - let timeline: Note[] = []; + let timeline: MiNote[] = []; const limit = ps.limit + (ps.untilId ? 1 : 0); // untilIdに指定したものも含まれるため+1 let noteIdsRes: [string, string[]][] = []; diff --git a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts index 67fb1ea03e..b4c7af8154 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFavoritesRepository, ChannelsRepository } from '@/models/index.js'; +import type { ChannelFavoritesRepository, ChannelsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -30,9 +35,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index f46ff9f286..46883dd548 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -31,9 +35,8 @@ export const paramDef = { required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 30d7f8b244..ab69f62a7b 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository, ChannelsRepository } from '@/models/index.js'; +import type { DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -55,13 +60,13 @@ export const paramDef = { }, }, color: { type: 'string', minLength: 1, maxLength: 16 }, + isSensitive: { type: 'boolean', nullable: true }, }, required: ['channelId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, @@ -109,6 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ...(ps.color !== undefined ? { color: ps.color } : {}), ...(typeof ps.isArchived === 'boolean' ? { isArchived: ps.isArchived } : {}), ...(banner ? { bannerId: banner.id } : {}), + ...(typeof ps.isSensitive === 'boolean' ? { isSensitive: ps.isSensitive } : {}), }); return await this.channelEntityService.pack(channel.id, me); diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 2ab58e4309..e768923ce1 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private activeUsersChart: ActiveUsersChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index e40a53d82e..f518ae41ca 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private apRequestChart: ApRequestChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 9a5aff4af9..94afab113e 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private driveChart: DriveChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index ed3a968681..bc33930ca4 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private federationChart: FederationChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index c992d525c9..a432845b38 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private instanceChart: InstanceChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 5750cd5b78..e1e9d06311 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private notesChart: NotesChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 5e372294b7..b4a58c9872 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private perUserDriveChart: PerUserDriveChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index 3f50918fa7..c609c5a7fe 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getJsonSchema } from '@/core/chart/core.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private perUserFollowingChart: PerUserFollowingChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 0517b3283f..ad6a342fb7 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private perUserNotesChart: PerUserNotesChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/pv.ts b/packages/backend/src/server/api/endpoints/charts/user/pv.ts index 8d1a9aee10..635a403d12 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/pv.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/pv.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private perUserPvChart: PerUserPvChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index f2ff413195..92bc7028ad 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: ['span', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private perUserReactionsChart: PerUserReactionsChart, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 1374f02046..3be3721e3a 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['span'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private usersChart: UsersChart, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 2837f2cf81..749593aa65 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,11 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { IdService } from '@/core/IdService.js'; -import { DI } from '@/di-symbols.js'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { RoleService } from '@/core/RoleService.js'; +import { ClipService } from '@/core/ClipService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -58,62 +59,27 @@ export const paramDef = { required: ['clipId', 'noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, - - @Inject(DI.clipNotesRepository) - private clipNotesRepository: ClipNotesRepository, - - private idService: IdService, - private roleService: RoleService, - private getterService: GetterService, + private clipService: ClipService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await this.clipsRepository.findOneBy({ - id: ps.clipId, - userId: me.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } - - const note = await this.getterService.getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - const exist = await this.clipNotesRepository.exist({ - where: { - noteId: note.id, - clipId: clip.id, - }, - }); - - if (exist) { - throw new ApiError(meta.errors.alreadyClipped); - } - - const currentCount = await this.clipNotesRepository.countBy({ - clipId: clip.id, - }); - if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) { - throw new ApiError(meta.errors.tooManyClipNotes); + try { + await this.clipService.addNote(me, ps.clipId, ps.noteId); + } catch (e) { + if (e instanceof ClipService.NoSuchClipError) { + throw new ApiError(meta.errors.noSuchClip); + } else if (e instanceof ClipService.NoSuchNoteError) { + throw new ApiError(meta.errors.noSuchNote); + } else if (e instanceof ClipService.AlreadyAddedError) { + throw new ApiError(meta.errors.alreadyClipped); + } else if (e instanceof ClipService.TooManyClipNotesError) { + throw new ApiError(meta.errors.tooManyClipNotes); + } else { + throw e; + } } - - await this.clipNotesRepository.insert({ - id: this.idService.genId(), - noteId: note.id, - clipId: clip.id, - }); - - await this.clipsRepository.update(clip.id, { - lastClippedAt: new Date(), - }); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 5395a5c373..b4c7b52e72 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,11 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { IdService } from '@/core/IdService.js'; -import type { ClipsRepository } from '@/models/index.js'; +import type { MiClip } from '@/models/_.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '@/server/api/error.js'; +import { ClipService } from '@/core/ClipService.js'; export const meta = { tags: ['clips'], @@ -41,34 +44,22 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, - private clipEntityService: ClipEntityService, - private roleService: RoleService, - private idService: IdService, + private clipService: ClipService, ) { super(meta, paramDef, async (ps, me) => { - const currentCount = await this.clipsRepository.countBy({ - userId: me.id, - }); - if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) { - throw new ApiError(meta.errors.tooManyClips); + let clip: MiClip; + try { + clip = await this.clipService.create(me, ps.name, ps.isPublic, ps.description ?? null); + } catch (e) { + if (e instanceof ClipService.TooManyClipsError) { + throw new ApiError(meta.errors.tooManyClips); + } + throw e; } - - const clip = await this.clipsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: me.id, - name: ps.name, - isPublic: ps.isPublic, - description: ps.description, - }).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0])); - return await this.clipEntityService.pack(clip, me); }); } diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index 077a9ec40f..239945e8a4 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipsRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; +import { ClipService } from '@/core/ClipService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -28,24 +32,20 @@ export const paramDef = { required: ['clipId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, + private clipService: ClipService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await this.clipsRepository.findOneBy({ - id: ps.clipId, - userId: me.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); + try { + await this.clipService.delete(me, ps.clipId); + } catch (e) { + if (e instanceof ClipService.NoSuchClipError) { + throw new ApiError(meta.errors.noSuchClip); + } + throw e; } - - await this.clipsRepository.delete(clip.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts index ce09855531..6cd34f0a54 100644 --- a/packages/backend/src/server/api/endpoints/clips/favorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js'; +import type { ClipsRepository, ClipFavoritesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -37,9 +42,8 @@ export const paramDef = { required: ['clipId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index 3b8deab709..c124762e33 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/_.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts index fc727e93bd..c58c16e25f 100644 --- a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts +++ b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipFavoritesRepository } from '@/models/index.js'; +import type { ClipFavoritesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipFavoritesRepository) private clipFavoritesRepository: ClipFavoritesRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 49607babee..1427d8d0a7 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository, ClipsRepository, ClipNotesRepository } from '@/models/index.js'; +import type { NotesRepository, ClipsRepository, ClipNotesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: ['clipId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index d0ef795819..7b153cb555 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,8 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { GetterService } from '@/server/api/GetterService.js'; +import { ClipService } from '@/core/ClipService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -38,37 +41,22 @@ export const paramDef = { required: ['clipId', 'noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, - - @Inject(DI.clipNotesRepository) - private clipNotesRepository: ClipNotesRepository, - - private getterService: GetterService, + private clipService: ClipService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await this.clipsRepository.findOneBy({ - id: ps.clipId, - userId: me.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); + try { + await this.clipService.removeNote(me, ps.clipId, ps.noteId); + } catch (e) { + if (e instanceof ClipService.NoSuchClipError) { + throw new ApiError(meta.errors.noSuchClip); + } else if (e instanceof ClipService.NoSuchNoteError) { + throw new ApiError(meta.errors.noSuchNote); + } + throw e; } - - const note = await this.getterService.getNote(ps.noteId).catch(err => { - if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw err; - }); - - await this.clipNotesRepository.delete({ - noteId: note.id, - clipId: clip.id, - }); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 99d630a9b5..03b1e09dfb 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/_.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['clipId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts index 3da252a226..d1007f7a19 100644 --- a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js'; +import type { ClipsRepository, ClipFavoritesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: ['clipId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 70f1959353..0b9878578c 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,8 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipsRepository } from '@/models/index.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; -import { DI } from '@/di-symbols.js'; +import { ClipService } from '@/core/ClipService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -40,33 +44,24 @@ export const paramDef = { required: ['clipId', 'name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, + private clipService: ClipService, private clipEntityService: ClipEntityService, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the clip - const clip = await this.clipsRepository.findOneBy({ - id: ps.clipId, - userId: me.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); + try { + await this.clipService.update(me, ps.clipId, ps.name, ps.isPublic, ps.description); + } catch (e) { + if (e instanceof ClipService.NoSuchClipError) { + throw new ApiError(meta.errors.noSuchClip); + } + throw e; } - await this.clipsRepository.update(clip.id, { - name: ps.name, - description: ps.description, - isPublic: ps.isPublic, - }); - - return await this.clipEntityService.pack(clip.id, me); + return await this.clipEntityService.pack(ps.clipId, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index a6ece0311b..71d3ca5f14 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private metaService: MetaService, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index f4343248b8..6f3a62977f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 328d0e4643..779231a856 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { NotesRepository, DriveFilesRepository } from '@/models/_.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index cdcdde7e8a..85e6312b6a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -26,9 +31,8 @@ export const paramDef = { required: ['md5'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index a1c1f9325e..5e97588c99 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -1,13 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; -import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { DriveService } from '@/core/DriveService.js'; -import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -67,13 +70,9 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - private driveFileEntityService: DriveFileEntityService, private metaService: MetaService, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 2ced97ee02..f46bf49965 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DriveService } from '@/core/DriveService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -61,11 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.accessDenied); } - // Delete - await this.driveService.deleteFile(file); - - // Publish fileDeleted event - this.globalEventService.publishDriveStream(me.id, 'fileDeleted', file.id); + await this.driveService.deleteFile(file, false, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index d6d85f4e77..7b784f253e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['md5'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 858063eb4b..0ceb31e58d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index 271b33ef4b..474c7f02d3 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -49,9 +54,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -60,7 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { - let file: DriveFile | null = null; + let file: MiDriveFile | null = null; if (ps.fileId) { file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index c43f812e2f..c01f3de03c 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { DriveService } from '@/core/DriveService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -66,23 +70,17 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.driveFoldersRepository) - private driveFoldersRepository: DriveFoldersRepository, - - private driveFileEntityService: DriveFileEntityService, + private driveService: DriveService, private roleService: RoleService, - private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); - const alwaysMarkNsfw = (await this.roleService.getUserPolicies(me.id)).alwaysMarkNsfw; if (file == null) { throw new ApiError(meta.errors.noSuchFile); } @@ -91,49 +89,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.accessDenied); } - if (ps.name) file.name = ps.name; - if (!this.driveFileEntityService.validateFileName(file.name)) { - throw new ApiError(meta.errors.invalidFileName); - } - - if (ps.comment !== undefined) file.comment = ps.comment; - - if (ps.isSensitive !== undefined && ps.isSensitive !== file.isSensitive && alwaysMarkNsfw && !ps.isSensitive) { - throw new ApiError(meta.errors.restrictedByRole); - } - - if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive; + let packedFile; - if (ps.folderId !== undefined) { - if (ps.folderId === null) { - file.folderId = null; + try { + packedFile = await this.driveService.updateFile(file, { + folderId: ps.folderId, + name: ps.name, + isSensitive: ps.isSensitive, + comment: ps.comment, + }, me); + } catch (e) { + if (e instanceof DriveService.InvalidFileNameError) { + throw new ApiError(meta.errors.invalidFileName); + } else if (e instanceof DriveService.NoSuchFolderError) { + throw new ApiError(meta.errors.noSuchFolder); + } else if (e instanceof DriveService.CannotUnmarkSensitiveError) { + throw new ApiError(meta.errors.restrictedByRole); } else { - const folder = await this.driveFoldersRepository.findOneBy({ - id: ps.folderId, - userId: me.id, - }); - - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); - } - - file.folderId = folder.id; + throw e; } } - await this.driveFilesRepository.update(file.id, { - name: file.name, - comment: file.comment, - folderId: file.folderId, - isSensitive: file.isSensitive, - }); - - const fileObj = await this.driveFileEntityService.pack(file, { self: true }); - - // Publish fileUpdated event - this.globalEventService.publishDriveStream(me.id, 'fileUpdated', fileObj); - - return fileObj; + return packedFile; }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index c835587c4a..bbe62063cf 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -1,11 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; -import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveService } from '@/core/DriveService.js'; -import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -37,13 +40,9 @@ export const paramDef = { required: ['url'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - private driveFileEntityService: DriveFileEntityService, private driveService: DriveService, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index eb674f3e15..3a09266591 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFoldersRepository) private driveFoldersRepository: DriveFoldersRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index 39c9c6bc58..bc3a9bbe21 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -44,9 +49,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFoldersRepository) private driveFoldersRepository: DriveFoldersRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index d921bc1b17..46a00ca3dc 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository, DriveFilesRepository } from '@/models/index.js'; +import type { DriveFoldersRepository, DriveFilesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['folderId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index ee24db11f2..2f5cdcc648 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/_.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFoldersRepository) private driveFoldersRepository: DriveFoldersRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index c06263b902..dd44fc46c9 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/_.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['folderId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFoldersRepository) private driveFoldersRepository: DriveFoldersRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index ff0a78b929..f8683132b2 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/_.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -50,9 +55,8 @@ export const paramDef = { required: ['folderId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFoldersRepository) private driveFoldersRepository: DriveFoldersRepository, diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index a1c14a8e3f..27e1656f82 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 0f13b14d01..787009f13c 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmailService } from '@/core/EmailService.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: ['emailAddress'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private emailService: EmailService, ) { diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts index 51027f35c0..ead8c9979e 100644 --- a/packages/backend/src/server/api/endpoints/emoji.ts +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; -import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -30,13 +34,9 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 3c2d0ce4a4..2adf0a21b3 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; -import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -37,13 +41,9 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index b38c97f60a..cecaded20a 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; @@ -16,9 +21,8 @@ export const paramDef = { required: ['endpoint'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( ) { super(meta, paramDef, async (ps) => { diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index 9e706db747..86def04aca 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 6b6079ad51..7380c593e3 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 1b2f9446f8..a92cf6a9d8 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index c5aa1ec60b..d72ceeeea2 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index b140321f44..be73e5dbb8 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/_.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 66502748b3..71eec11235 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/_.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 19418e698c..e3ffea7b7e 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull, MoreThan, Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { FollowingsRepository, InstancesRepository } from '@/models/index.js'; +import type { FollowingsRepository, InstancesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 4596e0c0b5..c0aa882088 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; @@ -17,9 +22,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private getterService: GetterService, private apPersonService: ApPersonService, diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 06f252005b..d97171865a 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['host'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 5849d3111f..37859d8330 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -1,8 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Parser from 'rss-parser'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; const rssParser = new Parser(); @@ -23,13 +26,9 @@ export const paramDef = { required: ['url'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - private httpRequestService: HttpRequestService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index 3172bdbfda..b46660d218 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -37,9 +42,8 @@ export const paramDef = { required: ['title', 'summary', 'script', 'permissions'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/delete.ts b/packages/backend/src/server/api/endpoints/flash/delete.ts index e94ede9f68..e5448c816a 100644 --- a/packages/backend/src/server/api/endpoints/flash/delete.ts +++ b/packages/backend/src/server/api/endpoints/flash/delete.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: ['flashId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/featured.ts b/packages/backend/src/server/api/endpoints/flash/featured.ts index 99c8763b11..1fa5612ac4 100644 --- a/packages/backend/src/server/api/endpoints/flash/featured.ts +++ b/packages/backend/src/server/api/endpoints/flash/featured.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts index 57245f9f41..a90e5f653a 100644 --- a/packages/backend/src/server/api/endpoints/flash/like.ts +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: ['flashId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/my-likes.ts b/packages/backend/src/server/api/endpoints/flash/my-likes.ts index 7d1149ada9..e328bdbee5 100644 --- a/packages/backend/src/server/api/endpoints/flash/my-likes.ts +++ b/packages/backend/src/server/api/endpoints/flash/my-likes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FlashLikesRepository } from '@/models/index.js'; +import type { FlashLikesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { FlashLikeEntityService } from '@/core/entities/FlashLikeEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashLikesRepository) private flashLikesRepository: FlashLikesRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts index 45a3b50e08..442d8dcd75 100644 --- a/packages/backend/src/server/api/endpoints/flash/my.ts +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FlashsRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/show.ts b/packages/backend/src/server/api/endpoints/flash/show.ts index 14720a8c8d..c41a27c925 100644 --- a/packages/backend/src/server/api/endpoints/flash/show.ts +++ b/packages/backend/src/server/api/endpoints/flash/show.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FlashsRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,13 +38,9 @@ export const paramDef = { required: ['flashId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/unlike.ts b/packages/backend/src/server/api/endpoints/flash/unlike.ts index 696512b06c..d5c20a1167 100644 --- a/packages/backend/src/server/api/endpoints/flash/unlike.ts +++ b/packages/backend/src/server/api/endpoints/flash/unlike.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: ['flashId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts index 78dfd4a06a..8b5e1f99e9 100644 --- a/packages/backend/src/server/api/endpoints/flash/update.ts +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { FlashsRepository, DriveFilesRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -44,19 +49,16 @@ export const paramDef = { permissions: { type: 'array', items: { type: 'string', } }, + visibility: { type: 'string', enum: ['public', 'private'] }, }, required: ['flashId', 'title', 'summary', 'script', 'permissions'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, - - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, ) { super(meta, paramDef, async (ps, me) => { const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); @@ -73,6 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { summary: ps.summary, script: ps.script, permissions: ps.permissions, + visibility: ps.visibility, }); }); } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 009fc96f64..e0e7fed87a 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; @@ -14,7 +19,7 @@ export const meta = { limit: { duration: ms('1hour'), - max: 50, + max: 100, }, requireCredential: true, @@ -70,13 +75,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 77ef263169..f44692ba6d 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; @@ -55,13 +60,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 0e57f6328f..53ef925b2f 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; @@ -24,7 +29,7 @@ export const meta = { noSuchUser: { message: 'No such user.', code: 'NO_SUCH_USER', - id: '5b12c78d-2b28-4dca-99d2-f56139b42ff8', + id: 'b77e6ae6-a3e5-40da-9cc8-c240115479cc', }, followerIsYourself: { @@ -36,7 +41,7 @@ export const meta = { notFollowing: { message: 'The other use is not following you.', code: 'NOT_FOLLOWING', - id: '5dbf82f5-c92b-40b1-87d1-6c8c0741fd09', + id: '918faac3-074f-41ae-9c43-ed5d2946770d', }, }, @@ -55,13 +60,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index cca3e60614..91fe922200 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private getterService: GetterService, private userFollowingService: UserFollowingService, diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 7325e73cac..d9d5c7041b 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,11 +1,14 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; -import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,13 +47,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - private userEntityService: UserEntityService, private getterService: GetterService, private userFollowingService: UserFollowingService, diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index 29588e8731..c4faa88f65 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; -import type { FollowRequestsRepository } from '@/models/index.js'; +import type { FollowRequestsRepository } from '@/models/_.js'; import { FollowRequestEntityService } from '@/core/entities/FollowRequestEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -49,9 +54,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index a8fdc44876..35f047bcef 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private getterService: GetterService, private userFollowingService: UserFollowingService, diff --git a/packages/backend/src/server/api/endpoints/following/update.ts b/packages/backend/src/server/api/endpoints/following/update.ts new file mode 100644 index 0000000000..25f393e517 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/following/update.ts @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FollowingsRepository } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { UserFollowingService } from '@/core/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['following', 'users'], + + limit: { + duration: ms('1hour'), + max: 100, + }, + + requireCredential: true, + + kind: 'write:following', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '14318698-f67e-492a-99da-5353a5ac52be', + }, + + followeeIsYourself: { + message: 'Followee is yourself.', + code: 'FOLLOWEE_IS_YOURSELF', + id: '4c4cbaf9-962a-463b-8418-a5e365dbf2eb', + }, + + notFollowing: { + message: 'You are not following that user.', + code: 'NOT_FOLLOWING', + id: 'b8dc75cf-1cb5-46c9-b14b-5f1ffbd782c9', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + notify: { type: 'string', enum: ['normal', 'none'] }, + }, + required: ['userId', 'notify'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, + ) { + super(meta, paramDef, async (ps, me) => { + const follower = me; + + // Check if the follower is yourself + if (me.id === ps.userId) { + throw new ApiError(meta.errors.followeeIsYourself); + } + + // Get followee + const followee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; + }); + + // Check not following + const exist = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notFollowing); + } + + await this.followingsRepository.update({ + id: exist.id, + }, { + notify: ps.notify === 'none' ? null : ps.notify, + }); + + return await this.userEntityService.pack(follower.id, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 46347247f0..cbab3a83a4 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 4ee3d68a92..c5d06f67dd 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index b9aac3fb34..3ca5f4989a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index ca6bfa7e0f..94701712de 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; -import { GalleryPost } from '@/models/entities/GalleryPost.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js'; +import { MiGalleryPost } from '@/models/GalleryPost.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { IdService } from '@/core/IdService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -46,9 +51,8 @@ export const paramDef = { required: ['title', 'fileIds'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, @@ -65,13 +69,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { id: fileId, userId: me.id, }), - ))).filter((file): file is DriveFile => file != null); + ))).filter((file): file is MiDriveFile => file != null); if (files.length === 0) { throw new Error(); } - const post = await this.galleryPostsRepository.insert(new GalleryPost({ + const post = await this.galleryPostsRepository.insert(new MiGalleryPost({ id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 6cdcc17b39..deef2912bb 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['postId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index c0bb55f640..c557054066 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: ['postId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index f7e828142b..b3eda1be52 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['postId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index 513089217d..832b62282f 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/index.js'; +import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: ['postId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index a2a10d8400..632214a0c2 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -45,9 +50,8 @@ export const paramDef = { required: ['postId', 'title', 'fileIds'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, @@ -63,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { id: fileId, userId: me.id, }), - ))).filter((file): file is DriveFile => file != null); + ))).filter((file): file is MiDriveFile => file != null); if (files.length === 0) { throw new Error(); diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 810bde03e8..8a61168f25 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 693d938bf0..21d863107d 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/_.js'; import { HashtagEntityService } from '@/core/entities/HashtagEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['sort'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.hashtagsRepository) private hashtagsRepository: HashtagsRepository, diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 81a790316b..acfef16b11 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['query'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.hashtagsRepository) private hashtagsRepository: HashtagsRepository, diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 06b0d6e9b2..3ba16fdc85 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/_.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { HashtagEntityService } from '@/core/entities/HashtagEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: ['tag'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.hashtagsRepository) private hashtagsRepository: HashtagsRepository, diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index ce1cd9f01f..75d4fe3819 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository } from '@/models/index.js'; -import type { Note } from '@/models/entities/Note.js'; +import type { NotesRepository } from '@/models/_.js'; +import type { MiNote } from '@/models/Note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MetaService } from '@/core/MetaService.js'; @@ -63,9 +68,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -96,7 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const tags: { name: string; - users: Note['userId'][]; + users: MiNote['userId'][]; }[] = []; for (const note of tagNotes) { diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index b00b005add..1cef76d3d2 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['tag', 'sort'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 4d593542db..c0530bf392 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,13 +37,9 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 6c31075e05..9f8e2894b8 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as OTPAuth from 'otpauth'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -21,13 +25,9 @@ export const paramDef = { required: ['token'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, @@ -47,15 +47,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret), digits: 6, token, - window: 1, + window: 5, }); if (delta === null) { throw new Error('not verified'); } + const backupCodes = Array.from({ length: 5 }, () => new OTPAuth.Secret().base32); + await this.userProfilesRepository.update(me.id, { twoFactorSecret: profile.twoFactorTempSecret, + twoFactorBackupSecret: backupCodes, twoFactorEnabled: true, }); @@ -64,6 +67,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { detail: true, includeSecrets: true, })); + + return { + backupCodes: backupCodes, + }; }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index a7e39fc028..6d530aba3b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -1,153 +1,102 @@ -import { promisify } from 'node:util'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; -import cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; -import type { AttestationChallengesRepository, UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; - -const cborDecodeFirst = promisify(cbor.decodeFirst) as any; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js'; +import { WebAuthnService } from '@/core/WebAuthnService.js'; +import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + incorrectPassword: { + message: 'Incorrect password.', + code: 'INCORRECT_PASSWORD', + id: '0d7ec6d2-e652-443e-a7bf-9ee9a0cd77b0', + }, + + twoFactorNotEnabled: { + message: '2fa not enabled.', + code: 'TWO_FACTOR_NOT_ENABLED', + id: '798d6847-b1ed-4f9c-b1f9-163c42655995', + }, + }, } as const; export const paramDef = { type: 'object', properties: { - clientDataJSON: { type: 'string' }, - attestationObject: { type: 'string' }, password: { type: 'string' }, - challengeId: { type: 'string' }, + token: { type: 'string', nullable: true }, name: { type: 'string', minLength: 1, maxLength: 30 }, + credential: { type: 'object' }, }, - required: ['clientDataJSON', 'attestationObject', 'password', 'challengeId', 'name'], + required: ['password', 'name', 'credential'], } as const; // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( - @Inject(DI.config) - private config: Config, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: UserSecurityKeysRepository, - @Inject(DI.attestationChallengesRepository) - private attestationChallengesRepository: AttestationChallengesRepository, - + private webAuthnService: WebAuthnService, + private userAuthService: UserAuthService, private userEntityService: UserEntityService, private globalEventService: GlobalEventService, - private twoFactorAuthenticationService: TwoFactorAuthenticationService, ) { super(meta, paramDef, async (ps, me) => { - const rpIdHashReal = this.twoFactorAuthenticationService.hash(Buffer.from(this.config.hostname, 'utf-8')); - + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } - - const clientData = JSON.parse(ps.clientDataJSON); - - if (clientData.type !== 'webauthn.create') { - throw new Error('not a creation attestation'); - } - if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw new Error('origin mismatch'); - } - - const clientDataJSONHash = this.twoFactorAuthenticationService.hash(Buffer.from(ps.clientDataJSON, 'utf-8')); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - const attestation = await cborDecodeFirst(ps.attestationObject); - - const rpIdHash = attestation.authData.slice(0, 32); - if (!rpIdHashReal.equals(rpIdHash)) { - throw new Error('rpIdHash mismatch'); - } - - const flags = attestation.authData[32]; - - // eslint:disable-next-line:no-bitwise - if (!(flags & 1)) { - throw new Error('user not present'); - } - - const authData = Buffer.from(attestation.authData); - const credentialIdLength = authData.readUInt16BE(53); - const credentialId = authData.slice(55, 55 + credentialIdLength); - const publicKeyData = authData.slice(55 + credentialIdLength); - const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData); - if (publicKey.get(3) !== -7) { - throw new Error('alg mismatch'); - } - - const procedures = this.twoFactorAuthenticationService.getProcedures(); - - if (!(procedures as any)[attestation.fmt]) { - throw new Error(`unsupported fmt: ${attestation.fmt}. Supported ones: ${Object.keys(procedures)}`); + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } } - const verificationData = (procedures as any)[attestation.fmt].verify({ - attStmt: attestation.attStmt, - authenticatorData: authData, - clientDataHash: clientDataJSONHash, - credentialId, - publicKey, - rpIdHash, - }); - if (!verificationData.valid) throw new Error('signature invalid'); - - const attestationChallenge = await this.attestationChallengesRepository.findOneBy({ - userId: me.id, - id: ps.challengeId, - registrationChallenge: true, - challenge: this.twoFactorAuthenticationService.hash(clientData.challenge).toString('hex'), - }); - - if (!attestationChallenge) { - throw new Error('non-existent challenge'); + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { + throw new ApiError(meta.errors.incorrectPassword); } - await this.attestationChallengesRepository.delete({ - userId: me.id, - id: ps.challengeId, - }); - - // Expired challenge (> 5min old) - if ( - new Date().getTime() - attestationChallenge.createdAt.getTime() >= - 5 * 60 * 1000 - ) { - throw new Error('expired challenge'); + if (!profile.twoFactorEnabled) { + throw new ApiError(meta.errors.twoFactorNotEnabled); } - const credentialIdString = credentialId.toString('hex'); + const keyInfo = await this.webAuthnService.verifyRegistration(me.id, ps.credential); + const credentialId = Buffer.from(keyInfo.credentialID).toString('base64url'); await this.userSecurityKeysRepository.insert({ + id: credentialId, userId: me.id, - id: credentialIdString, - lastUsed: new Date(), name: ps.name, - publicKey: verificationData.publicKey.toString('hex'), + publicKey: Buffer.from(keyInfo.credentialPublicKey).toString('base64url'), + counter: keyInfo.counter, + credentialDeviceType: keyInfo.credentialDeviceType, + credentialBackedUp: keyInfo.credentialBackedUp, + transports: keyInfo.transports, }); // Publish meUpdated event @@ -157,7 +106,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { })); return { - id: credentialIdString, + id: credentialId, name: ps.name, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 0ee9f556a8..2ed701014d 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['value'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 19c77365c6..c39005f2dd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -1,25 +1,48 @@ -import { promisify } from 'node:util'; -import * as crypto from 'node:crypto'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository, AttestationChallengesRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; - -const randomBytes = promisify(crypto.randomBytes); +import { WebAuthnService } from '@/core/WebAuthnService.js'; +import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + userNotFound: { + message: 'User not found.', + code: 'USER_NOT_FOUND', + id: '652f899f-66d4-490e-993e-6606c8ec04c3', + }, + + incorrectPassword: { + message: 'Incorrect password.', + code: 'INCORRECT_PASSWORD', + id: '38769596-efe2-4faf-9bec-abbb3f2cd9ba', + }, + + twoFactorNotEnabled: { + message: '2fa not enabled.', + code: 'TWO_FACTOR_NOT_ENABLED', + id: 'bf32b864-449b-47b8-974e-f9a5468546f1', + }, + }, } as const; export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -31,47 +54,48 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - @Inject(DI.attestationChallengesRepository) - private attestationChallengesRepository: AttestationChallengesRepository, - - private idService: IdService, - private twoFactorAuthenticationService: TwoFactorAuthenticationService, + private webAuthnService: WebAuthnService, + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const token = ps.token; + const profile = await this.userProfilesRepository.findOne({ + where: { + userId: me.id, + }, + relations: ['user'], + }); - if (!same) { - throw new Error('incorrect password'); + if (profile == null) { + throw new ApiError(meta.errors.userNotFound); } - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - // 32 byte challenge - const entropy = await randomBytes(32); - const challenge = entropy.toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } - const challengeId = this.idService.genId(); + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { + throw new ApiError(meta.errors.incorrectPassword); + } - await this.attestationChallengesRepository.insert({ - userId: me.id, - id: challengeId, - challenge: this.twoFactorAuthenticationService.hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: true, - }); + if (!profile.twoFactorEnabled) { + throw new ApiError(meta.errors.twoFactorNotEnabled); + } - return { - challengeId, - challenge, - }; + return await this.webAuthnService.initiateRegistration( + me.id, + profile.user?.username ?? me.id, + profile.user?.name ?? undefined, + ); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index eb4d7f9c14..b358c812ee 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,44 +1,72 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import * as OTPAuth from 'otpauth'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + incorrectPassword: { + message: 'Incorrect password.', + code: 'INCORRECT_PASSWORD', + id: '78d6c839-20c9-4c66-b90a-fc0542168b48', + }, + }, } as const; export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } - if (!same) { - throw new Error('incorrect password'); + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { + throw new ApiError(meta.errors.incorrectPassword); } // Generate user's secret key diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 4b726aed80..da8ac98556 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,29 +1,44 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + incorrectPassword: { + message: 'Incorrect password.', + code: 'INCORRECT_PASSWORD', + id: '141c598d-a825-44c8-9173-cfb9d92be493', + }, + }, } as const; export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, credentialId: { type: 'string' }, }, required: ['password', 'credentialId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: UserSecurityKeysRepository, @@ -32,16 +47,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userProfilesRepository: UserProfilesRepository, private userEntityService: UserEntityService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } - if (!same) { - throw new Error('incorrect password'); + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { + throw new ApiError(meta.errors.incorrectPassword); } // Make sure we only delete the user's own creds diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index e0e7ba6658..338f12c5cd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,47 +1,75 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + incorrectPassword: { + message: 'Incorrect password.', + code: 'INCORRECT_PASSWORD', + id: '7add0395-9901-4098-82f9-4f67af65f775', + }, + }, } as const; export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, private userEntityService: UserEntityService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } - if (!same) { - throw new Error('incorrect password'); + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { + throw new ApiError(meta.errors.incorrectPassword); } await this.userProfilesRepository.update(me.id, { twoFactorSecret: null, + twoFactorBackupSecret: null, twoFactorEnabled: false, usePasswordLessLogin: false, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts index 2ef5e5a279..1a140c1d05 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import type { UserSecurityKeysRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -20,7 +25,7 @@ export const meta = { }, accessDenied: { - message: 'You do not have edit privilege of the channel.', + message: 'You do not have edit privilege of this key.', code: 'ACCESS_DENIED', id: '1fb7cb09-d46a-4fff-b8df-057708cce513', }, @@ -36,16 +41,12 @@ export const paramDef = { required: ['name', 'credentialId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: UserSecurityKeysRepository, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - private userEntityService: UserEntityService, private globalEventService: GlobalEventService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index 48fb03a8af..daa3e536a4 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -17,9 +22,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index f5a946eb91..32061c2aa4 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/_.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index 873835a36c..a3c37ffdb7 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,8 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -15,24 +21,38 @@ export const paramDef = { properties: { currentPassword: { type: 'string' }, newPassword: { type: 'string', minLength: 1 }, + token: { type: 'string', nullable: true }, }, required: ['currentPassword', 'newPassword'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.currentPassword, profile.password!); - if (!same) { + if (!passwordMatched) { throw new Error('incorrect password'); } diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts index 4eef496385..b24b3438dc 100644 --- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts +++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService.js'; @@ -15,9 +20,8 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private achievementService: AchievementService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 77a03d9811..fbac845fda 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -15,13 +21,13 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -29,19 +35,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + private userAuthService: UserAuthService, private deleteAccountService: DeleteAccountService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); + + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id }); if (userDetailed.isDeleted) { return; } - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { + const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + if (!passwordMatched) { throw new Error('incorrect password'); } diff --git a/packages/backend/src/server/api/endpoints/i/export-antennas.ts b/packages/backend/src/server/api/endpoints/i/export-antennas.ts index 4182c1b247..23b2f6b4ce 100644 --- a/packages/backend/src/server/api/endpoints/i/export-antennas.ts +++ b/packages/backend/src/server/api/endpoints/i/export-antennas.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 4be88cbc2b..8068a3b305 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/export-favorites.ts b/packages/backend/src/server/api/endpoints/i/export-favorites.ts index f522d4c409..c22905bc67 100644 --- a/packages/backend/src/server/api/endpoints/i/export-favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/export-favorites.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index 1741781c0f..880833ab76 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 8e8042b1f9..8eb70a387a 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index ed54c9991c..791f637790 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index 5c2be38b71..f387f6d016 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private queueService: QueueService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index bdfb63974a..d6f13c535a 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteFavoriteEntityService } from '@/core/entities/NoteFavoriteEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: NoteFavoritesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index 915639e5f7..7e37adc4ac 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryLikesRepository } from '@/models/index.js'; +import type { GalleryLikesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryLikeEntityService } from '@/core/entities/GalleryLikeEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -44,9 +49,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryLikesRepository) private galleryLikesRepository: GalleryLikesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index 5ba9afd4a8..148d38aa54 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index 3179457817..d62bfbb3ed 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MutedNotesRepository } from '@/models/index.js'; +import type { MutedNotesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -28,9 +33,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.mutedNotesRepository) private mutedNotesRepository: MutedNotesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts index 8582e98f76..71db8710af 100644 --- a/packages/backend/src/server/api/endpoints/i/import-antennas.ts +++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; -import type { AntennasRepository, DriveFilesRepository, UsersRepository, Antenna as _Antenna } from '@/models/index.js'; +import type { AntennasRepository, DriveFilesRepository, UsersRepository, MiAntenna as _Antenna } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { DownloadService } from '@/core/DownloadService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index 32c16300fb..965ad30547 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -52,9 +57,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index 1926a1f503..38c9283043 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -51,9 +56,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index 34f2627563..926cf13d7f 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -52,9 +57,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 1b3cb5359d..2167996435 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,9 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -51,9 +56,8 @@ export const paramDef = { required: ['fileId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index 261dd527c0..86b726e054 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -1,13 +1,15 @@ -import { Inject, Injectable } from '@nestjs/common'; -import ms from 'ms'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; +import { Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '@/server/api/error.js'; -import { LocalUser, RemoteUser } from '@/models/entities/User.js'; +import { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; @@ -72,13 +74,9 @@ export const paramDef = { required: ['moveToAccount'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - private remoteUserResolveService: RemoteUserResolveService, private apiLoggerService: ApiLoggerService, private accountMoveService: AccountMoveService, @@ -101,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { this.apiLoggerService.logger.warn(`failed to resolve remote user: ${e}`); throw new ApiError(meta.errors.noSuchUser); }); - const destination = await this.getterService.getUser(moveTo.id) as LocalUser | RemoteUser; + const destination = await this.getterService.getUser(moveTo.id) as MiLocalUser | MiRemoteUser; const newUri = this.userEntityService.getUserUri(destination); // update local db diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index f5662f4a0e..91dd72e805 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,16 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets, In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { obsoleteNotificationTypes, notificationTypes } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueryService } from '@/core/QueryService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; -import { Notification } from '@/models/entities/Notification.js'; +import { MiNotification } from '@/models/Notification.js'; export const meta = { tags: ['account', 'notifications'], @@ -53,29 +57,18 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.redis) private redisClient: Redis.Redis, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.mutingsRepository) - private mutingsRepository: MutingsRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, private idService: IdService, private notificationEntityService: NotificationEntityService, private notificationService: NotificationService, - private queryService: QueryService, private noteReadService: NoteReadService, ) { super(meta, paramDef, async (ps, me) => { @@ -102,7 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { return []; } - let notifications = notificationsRes.map(x => JSON.parse(x[1][1])).filter(x => x.id !== ps.untilId && x !== ps.sinceId) as Notification[]; + let notifications = notificationsRes.map(x => JSON.parse(x[1][1])).filter(x => x.id !== ps.untilId && x !== ps.sinceId) as MiNotification[]; if (includeTypes && includeTypes.length > 0) { notifications = notifications.filter(notification => includeTypes.includes(notification.type)); diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 9f073ba596..6bf7e6aa9b 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { PageLikesRepository } from '@/models/index.js'; +import type { PageLikesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { PageLikeEntityService } from '@/core/entities/PageLikeEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pageLikesRepository) private pageLikesRepository: PageLikesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 772486befc..b8082c018f 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 2293500945..c89cdfa3a4 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -47,9 +52,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private userEntityService: UserEntityService, private notePiningService: NotePiningService, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index b92de4b739..e43ab7c15e 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NoteUnreadsRepository } from '@/models/index.js'; +import type { NoteUnreadsRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: NoteUnreadsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 352fe54c5d..ba7859d0d4 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,11 +1,11 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { IdService } from '@/core/IdService.js'; -import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; export const meta = { tags: ['account'], @@ -15,11 +15,6 @@ export const meta = { kind: 'write:account', errors: { - noSuchAnnouncement: { - message: 'No such announcement.', - code: 'NO_SUCH_ANNOUNCEMENT', - id: '184663db-df88-4bc2-8b52-fb85f0681939', - }, }, } as const; @@ -31,51 +26,13 @@ export const paramDef = { required: ['announcementId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.announcementsRepository) - private announcementsRepository: AnnouncementsRepository, - - @Inject(DI.announcementReadsRepository) - private announcementReadsRepository: AnnouncementReadsRepository, - - private userEntityService: UserEntityService, - private idService: IdService, - private globalEventService: GlobalEventService, + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { - // Check if announcement exists - const announcementExist = await this.announcementsRepository.exist({ where: { id: ps.announcementId } }); - - if (!announcementExist) { - throw new ApiError(meta.errors.noSuchAnnouncement); - } - - // Check if already read - const alreadyRead = await this.announcementReadsRepository.exist({ - where: { - announcementId: ps.announcementId, - userId: me.id, - }, - }); - - if (alreadyRead) { - return; - } - - // Create read - await this.announcementReadsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - announcementId: ps.announcementId, - userId: me.id, - }); - - if (!await this.userEntityService.getHasUnreadAnnouncement(me.id)) { - this.globalEventService.publishMainStream(me.id, 'readAllAnnouncements'); - } + await this.announcementService.read(me, ps.announcementId); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 23ff63f5e9..b70dcfbace 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -20,9 +25,8 @@ export const paramDef = { required: ['password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index 17154c1f76..211e6637dc 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -19,9 +24,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 233686dbe1..9c6f2d6781 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['key'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index 99cdf95bad..729e729b8c 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['key'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 362a5e89f4..ffd2860fde 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -19,9 +24,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index 99f69d8bed..7239bb66e1 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -19,9 +24,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 78a641f5e2..ae687fefe9 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['key'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index 0a4ecb9c51..7637cdcf73 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -15,9 +20,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index c8e72203c4..c074b152df 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -23,9 +28,8 @@ export const paramDef = { required: ['key', 'value'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registryItemsRepository) private registryItemsRepository: RegistryItemsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 415a60147b..8e2f271005 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AccessTokensRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { AccessTokensRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -18,14 +22,11 @@ export const paramDef = { required: ['tokenId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, - - private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } }); diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index aa8cb5cf42..139bede7bc 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { SigninsRepository } from '@/models/index.js'; +import type { SigninsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index db239dc284..b59c0e954f 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private userEntityService: UserEntityService, private notePiningService: NotePiningService, diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 58e056bd37..a36b3a732b 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -1,14 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import bcrypt from 'bcryptjs'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { EmailService } from '@/core/EmailService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -41,34 +47,43 @@ export const paramDef = { properties: { password: { type: 'string' }, email: { type: 'string', nullable: true }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, private userEntityService: UserEntityService, private emailService: EmailService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } - if (!same) { + const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 8f5e6177c2..b11e091957 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,13 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + 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 { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; import * as Acct from '@/misc/acct.js'; -import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; -import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js'; -import type { UserProfile } from '@/models/entities/UserProfile.js'; +import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/_.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; +import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/User.js'; +import type { MiUserProfile } from '@/models/UserProfile.js'; import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; @@ -20,9 +27,11 @@ import { HashtagService } from '@/core/HashtagService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { CacheService } from '@/core/CacheService.js'; -import { AccountMoveService } from '@/core/AccountMoveService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import type { Config } from '@/config.js'; +import { safeForSql } from '@/misc/safe-for-sql.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; import { ApiError } from '../../error.js'; @@ -33,6 +42,11 @@ export const meta = { kind: 'write:account', + limit: { + duration: ms('1hour'), + max: 10, + }, + errors: { noSuchAvatar: { message: 'No such avatar file.', @@ -166,10 +180,12 @@ export const paramDef = { }, } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -187,19 +203,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private globalEventService: GlobalEventService, private userFollowingService: UserFollowingService, private accountUpdateService: AccountUpdateService, - private accountMoveService: AccountMoveService, private remoteUserResolveService: RemoteUserResolveService, private apiLoggerService: ApiLoggerService, private hashtagService: HashtagService, private roleService: RoleService, private cacheService: CacheService, + private httpRequestService: HttpRequestService, ) { super(meta, paramDef, async (ps, _user, token) => { - const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); + const user = await this.usersRepository.findOneByOrFail({ id: _user.id }) as MiLocalUser; const isSecure = token == null; - const updates = {} as Partial<User>; - const profileUpdates = {} as Partial<UserProfile>; + const updates = {} as Partial<MiUser>; + const profileUpdates = {} as Partial<MiUserProfile>; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); @@ -294,9 +310,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (ps.fields) { profileUpdates.fields = ps.fields - .filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '') + .filter(x => typeof x.name === 'string' && x.name.trim() !== '' && typeof x.value === 'string' && x.value.trim() !== '') .map(x => { - return { name: x.name, value: x.value }; + return { name: x.name.trim(), value: x.value.trim() }; }); } @@ -362,7 +378,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (Object.keys(updates).includes('alsoKnownAs')) { this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates }); } - if (Object.keys(profileUpdates).length > 0) await this.userProfilesRepository.update(user.id, profileUpdates); + + await this.userProfilesRepository.update(user.id, { + ...profileUpdates, + verifiedLinks: [], + }); const iObj = await this.userEntityService.pack<true, true>(user.id, user, { detail: true, @@ -384,7 +404,34 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // フォロワーにUpdateを配信 this.accountUpdateService.publishToFollowers(user.id); + const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://')); + for (const url of urls) { + this.verifyLink(url.value, user); + } + return iObj; }); } + + private async verifyLink(url: string, user: MiLocalUser) { + if (!safeForSql(url)) return; + + const html = await this.httpRequestService.getHtml(url); + + const { window } = new JSDOM(html); + const doc = window.document; + + const myLink = `${this.config.url}/@${user.username}`; + + const includesMyLink = Array.from(doc.getElementsByTagName('a')).some(a => a.href === myLink); + + if (includesMyLink) { + await this.userProfilesRepository.createQueryBuilder('profile').update() + .where('userId = :userId', { userId: user.id }) + .set({ + verifiedLinks: () => `array_append("verifiedLinks", '${url}')`, // ここでSQLインジェクションされそうなのでとりあえず safeForSql で弾いている + }) + .execute(); + } + } } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 51fcce6cf0..48eaeff406 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import type { WebhooksRepository } from '@/models/index.js'; -import { webhookEventTypes } from '@/models/entities/Webhook.js'; +import type { WebhooksRepository } from '@/models/_.js'; +import { webhookEventTypes } from '@/models/Webhook.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -29,19 +34,18 @@ export const paramDef = { properties: { name: { type: 'string', minLength: 1, maxLength: 100 }, url: { type: 'string', minLength: 1, maxLength: 1024 }, - secret: { type: 'string', minLength: 1, maxLength: 1024 }, + secret: { type: 'string', maxLength: 1024, default: '' }, on: { type: 'array', items: { type: 'string', enum: webhookEventTypes, } }, }, - required: ['name', 'url', 'secret', 'on'], + required: ['name', 'url', 'on'], } as const; // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 7bdad136aa..db7d0db13c 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -31,9 +36,8 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index 58c84938cc..aa8921fe24 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -17,9 +22,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index d15ca0050d..f1294bb5c8 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['webhookId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index 8ec308eda7..b3e000524d 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { WebhooksRepository } from '@/models/index.js'; -import { webhookEventTypes } from '@/models/entities/Webhook.js'; +import type { WebhooksRepository } from '@/models/_.js'; +import { webhookEventTypes } from '@/models/Webhook.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -29,20 +34,19 @@ export const paramDef = { webhookId: { type: 'string', format: 'misskey:id' }, name: { type: 'string', minLength: 1, maxLength: 100 }, url: { type: 'string', minLength: 1, maxLength: 1024 }, - secret: { type: 'string', minLength: 1, maxLength: 1024 }, + secret: { type: 'string', maxLength: 1024, default: '' }, on: { type: 'array', items: { type: 'string', enum: webhookEventTypes, } }, active: { type: 'boolean' }, }, - required: ['webhookId', 'name', 'url', 'secret', 'on', 'active'], + required: ['webhookId', 'name', 'url', 'on', 'active'], } as const; // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts index a64184be10..7361ab616c 100644 --- a/packages/backend/src/server/api/endpoints/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/invite/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js'; import { IdService } from '@/core/IdService.js'; import { RoleService } from '@/core/RoleService.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/invite/delete.ts b/packages/backend/src/server/api/endpoints/invite/delete.ts index afca44954d..3b57775739 100644 --- a/packages/backend/src/server/api/endpoints/invite/delete.ts +++ b/packages/backend/src/server/api/endpoints/invite/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { RoleService } from '@/core/RoleService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -40,9 +45,8 @@ export const paramDef = { required: ['inviteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/invite/limit.ts b/packages/backend/src/server/api/endpoints/invite/limit.ts index 9a213b7b25..43b94e4f06 100644 --- a/packages/backend/src/server/api/endpoints/invite/limit.ts +++ b/packages/backend/src/server/api/endpoints/invite/limit.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { RoleService } from '@/core/RoleService.js'; import { DI } from '@/di-symbols.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/invite/list.ts b/packages/backend/src/server/api/endpoints/invite/list.ts index e047790261..06139b6806 100644 --- a/packages/backend/src/server/api/endpoints/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/invite/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/_.js'; import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: RegistrationTicketsRepository, diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index adfa579558..fa6486ed18 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import JSON5 from 'json5'; -import type { AdsRepository, UsersRepository } from '@/models/index.js'; +import type { AdsRepository, UsersRepository } from '@/models/_.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -35,6 +40,10 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + shortName: { + type: 'string', + optional: false, nullable: true, + }, uri: { type: 'string', optional: false, nullable: false, @@ -248,9 +257,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, @@ -284,6 +292,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { version: this.config.version, name: instance.name, + shortName: instance.shortName, uri: this.config.url, description: instance.description, langs: instance.langs, diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 0ea29f04dc..e40656cb6d 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { DI } from '@/di-symbols.js'; @@ -38,9 +43,8 @@ export const paramDef = { required: ['session', 'permission'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index ef53f9ef41..49c2b5707d 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserMutingService } from '@/core/UserMutingService.js'; @@ -54,9 +59,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 90b74590be..a3fd2dd82f 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserMutingService } from '@/core/UserMutingService.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 4711e86d6b..2a41182ebc 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { MutingEntityService } from '@/core/entities/MutingEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 4b7ed80123..98c317346f 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/_.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.appsRepository) private appsRepository: AppsRepository, diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 9013b300e7..95ba5e8b64 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 5f03fd4b74..1a82a4b5d7 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 0a5542f497..677c0ea307 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; +import type { ClipNotesRepository, ClipsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 10f43b04c0..b94a019da4 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { Note } from '@/models/entities/Note.js'; -import type { NotesRepository } from '@/models/index.js'; +import type { MiNote } from '@/models/Note.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -57,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw err; }); - const conversation: Note[] = []; + const conversation: MiNote[] = []; let i = 0; const get = async (id: any) => { diff --git a/packages/backend/src/server/api/endpoints/notes/create.test.ts b/packages/backend/src/server/api/endpoints/notes/create.test.ts index 6bff7fc0c9..6d34aaccf3 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.test.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.test.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + process.env.NODE_ENV = 'test'; import { readFile } from 'node:fs/promises'; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 739316997a..2e4d316c47 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,11 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { User } from '@/models/entities/User.js'; -import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { Channel } from '@/models/entities/Channel.js'; +import type { MiUser } from '@/models/User.js'; +import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiChannel } from '@/models/Channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -157,9 +162,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -180,15 +184,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private noteCreateService: NoteCreateService, ) { super(meta, paramDef, async (ps, me) => { - let visibleUsers: User[] = []; + let visibleUsers: MiUser[] = []; if (ps.visibleUserIds) { visibleUsers = await this.usersRepository.findBy({ id: In(ps.visibleUserIds), }); } - let files: DriveFile[] = []; - const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? ps.mediaIds ?? null; if (fileIds != null) { files = await this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId AND file.id IN (:...fileIds)', { @@ -204,7 +208,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } } - let renote: Note | null = null; + let renote: MiNote | null = null; if (ps.renoteId != null) { // Fetch renote to note renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); @@ -229,7 +233,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } } - let reply: Note | null = null; + let reply: MiNote | null = null; if (ps.replyId != null) { // Fetch reply reply = await this.notesRepository.findOneBy({ id: ps.replyId }); @@ -264,7 +268,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } } - let channel: Channel | null = null; + let channel: MiChannel | null = null; if (ps.channelId != null) { channel = await this.channelsRepository.findOneBy({ id: ps.channelId, isArchived: false }); diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 16c4c01387..55aaaf4f78 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; @@ -44,9 +49,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -66,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note); + await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, false, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 9299d66039..cc648e22a8 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -import type { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -44,9 +49,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: NoteFavoritesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index bb3a7c501a..8ab9775a2c 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; -import type { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/_.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -35,9 +40,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: NoteFavoritesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 3a3cb0739b..5283b0e0bc 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 4ce2fdaec7..0b3b5c902e 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,9 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -45,16 +49,14 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, private queryService: QueryService, - private metaService: MetaService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index af94cf6087..e9ae5dc755 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; -import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -52,9 +56,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -64,7 +67,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private noteEntityService: NoteEntityService, private queryService: QueryService, - private metaService: MetaService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index fe7407f48a..af1e0398dc 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -51,16 +55,14 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, private queryService: QueryService, - private metaService: MetaService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 6ee9de1e23..65e7bd8cd5 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -59,6 +63,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .where(`'{"${me.id}"}' <@ note.mentions`) .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); })) + // Avoid scanning primary key index + .orderBy('CONCAT(note.id)', 'DESC') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 889f644643..29190af62a 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, MutingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; +import type { NotesRepository, MutingsRepository, PollsRepository, PollVotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -30,9 +35,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index 3a33b037f8..a58bf09b85 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; -import type { RemoteUser } from '@/models/entities/User.js'; +import type { UsersRepository, PollsRepository, PollVotesRepository } from '@/models/_.js'; +import type { MiRemoteUser } from '@/models/User.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -71,9 +76,8 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -159,7 +163,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // リモート投票の場合リプライ送信 if (note.userHost != null) { - const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as RemoteUser; + const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as MiRemoteUser; this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox, false); } diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 4772c4f809..a2c1778199 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,10 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NoteReactionsRepository } from '@/models/index.js'; -import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import { Brackets, type FindOptionsWhere } from 'typeorm'; +import type { NoteReactionsRepository } from '@/models/_.js'; +import type { MiNoteReaction } from '@/models/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; import { DI } from '@/di-symbols.js'; -import type { FindOptionsWhere } from 'typeorm'; +import { QueryService } from '@/core/QueryService.js'; export const meta = { tags: ['notes', 'reactions'], @@ -39,44 +45,36 @@ export const paramDef = { noteId: { type: 'string', format: 'misskey:id' }, type: { type: 'string', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - offset: { type: 'integer', default: 0 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, }, required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteReactionsRepository) private noteReactionsRepository: NoteReactionsRepository, private noteReactionEntityService: NoteReactionEntityService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = { - noteId: ps.noteId, - } as FindOptionsWhere<NoteReaction>; + const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId) + .andWhere('reaction.noteId = :noteId', { noteId: ps.noteId }) + .leftJoinAndSelect('reaction.user', 'user') + .leftJoinAndSelect('reaction.note', 'note'); if (ps.type) { // ローカルリアクションはホスト名が . とされているが // DB 上ではそうではないので、必要に応じて変換 const suffix = '@.:'; const type = ps.type.endsWith(suffix) ? ps.type.slice(0, ps.type.length - suffix.length) + ':' : ps.type; - query.reaction = type; + query.andWhere('reaction.reaction = :type', { type }); } - const reactions = await this.noteReactionsRepository.find({ - where: query, - take: ps.limit, - skip: ps.offset, - order: { - id: -1, - }, - relations: ['user', 'note'], - }); + const reactions = await query.limit(ps.limit).getMany(); return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me))); }); diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 97cb026779..ff22ef1322 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: ['noteId', 'reaction'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private getterService: GetterService, private reactionService: ReactionService, diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index 207f0b4cf2..b43ab044fa 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private getterService: GetterService, private reactionService: ReactionService, diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 4ee12b3353..9f16181a30 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 900c40d32a..70142c9818 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index dc0a5dceee..b00f5207d8 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -58,9 +63,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index cd0e351e45..4425d4593c 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,10 +1,12 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { SearchService } from '@/core/SearchService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; @@ -52,13 +54,9 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - private noteEntityService: NoteEntityService, private searchService: SearchService, private roleService: RoleService, diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 2aec7d64dd..5bb8196543 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,8 +1,11 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; @@ -34,13 +37,9 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - private noteEntityService: NoteEntityService, private getterService: GetterService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 93517ab10c..b5fd47723c 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, NoteThreadMutingsRepository, NoteFavoritesRepository } from '@/models/index.js'; +import type { NotesRepository, NoteThreadMutingsRepository, NoteFavoritesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index abea069da8..449a838604 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -import type { NotesRepository, NoteThreadMutingsRepository } from '@/models/index.js'; +import type { NotesRepository, NoteThreadMutingsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -37,9 +42,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index 30016d48bc..d3f1787ee4 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { NoteThreadMutingsRepository } from '@/models/index.js'; +import type { NoteThreadMutingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: NoteThreadMutingsRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 7e9bf85d88..042115ab84 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; @@ -41,9 +46,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -65,7 +69,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで + // パフォーマンス上の利点が無さそう? + //.andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index b91bc7b5ec..00cb9a0a28 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,9 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { URLSearchParams } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; @@ -38,16 +40,9 @@ export const paramDef = { required: ['noteId', 'targetLang'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - private noteEntityService: NoteEntityService, private getterService: GetterService, private metaService: MetaService, diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index e9581beedc..f67e9365fc 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, NotesRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; @@ -37,9 +42,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 4c19e1a553..6932073791 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { NotesRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -53,9 +58,8 @@ export const paramDef = { required: ['listId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -91,6 +95,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .andWhere('userListJoining.userListId = :userListId', { userListId: list.id }); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 4102a924ad..268628cf76 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NotificationService } from '@/core/NotificationService.js'; @@ -9,6 +14,11 @@ export const meta = { kind: 'write:notifications', + limit: { + duration: 1000 * 60, + max: 10, + }, + errors: { }, } as const; @@ -23,9 +33,8 @@ export const paramDef = { required: ['body'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private notificationService: NotificationService, ) { diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index e601bf9d5b..dc092c1f3a 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,6 +1,10 @@ -import { Inject, Injectable } from '@nestjs/common'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; import { NotificationService } from '@/core/NotificationService.js'; export const meta = { @@ -17,9 +21,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private notificationService: NotificationService, ) { diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts new file mode 100644 index 0000000000..8f5f8485c3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NotificationService } from '@/core/NotificationService.js'; + +export const meta = { + tags: ['notifications'], + + requireCredential: true, + + kind: 'write:notifications', + + limit: { + duration: 1000 * 60, + max: 10, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private notificationService: NotificationService, + ) { + super(meta, paramDef, async (ps, user) => { + this.notificationService.createNotification(user.id, 'test', {}); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 1d6fb567f0..0a68516586 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -29,9 +34,8 @@ export const paramDef = { required: ['pageId', 'event'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index e08ab399f8..c0e8fab16c 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository, PagesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, PagesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import { Page } from '@/models/entities/Page.js'; +import { MiPage } from '@/models/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -63,9 +68,8 @@ export const paramDef = { required: ['title', 'name', 'content', 'variables', 'script'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, @@ -98,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } }); - const page = await this.pagesRepository.insert(new Page({ + const page = await this.pagesRepository.insert(new MiPage({ id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index e64733131c..1291c0d209 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -34,9 +39,8 @@ export const paramDef = { required: ['pageId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index b1c056124e..1f43d6606c 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -26,9 +31,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index bc66488103..6c69cad9d5 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { PagesRepository, PageLikesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -43,9 +48,8 @@ export const paramDef = { required: ['pageId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index bf2b2a431e..efb0bd0677 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, PagesRepository } from '@/models/index.js'; -import type { Page } from '@/models/entities/Page.js'; +import type { UsersRepository, PagesRepository } from '@/models/_.js'; +import type { MiPage } from '@/models/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -40,9 +45,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -53,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private pageEntityService: PageEntityService, ) { super(meta, paramDef, async (ps, me) => { - let page: Page | null = null; + let page: MiPage | null = null; if (ps.pageId) { page = await this.pagesRepository.findOneBy({ id: ps.pageId }); diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index f0c0198460..7a76cd7408 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { PagesRepository, PageLikesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -36,9 +41,8 @@ export const paramDef = { required: ['pageId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 751274067e..aaea1efa87 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { PagesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { PagesRepository, DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -68,9 +73,8 @@ export const paramDef = { required: ['pageId', 'title', 'name', 'content', 'variables', 'script'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, @@ -112,13 +116,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.pagesRepository.update(page.id, { updatedAt: new Date(), title: ps.title, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing name: ps.name === undefined ? page.name : ps.name, summary: ps.summary === undefined ? page.summary : ps.summary, content: ps.content, variables: ps.variables, script: ps.script, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing font: ps.font === undefined ? page.font : ps.font, eyeCatchingImageId: ps.eyeCatchingImageId === null ? null diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 5807bf101e..ee2fe48834 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,9 +29,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index f2c6e798ef..390042c815 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import * as Acct from '@/misc/acct.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -30,9 +35,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -48,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { host: acct.host ?? IsNull(), }))); - return await this.userEntityService.packMany(users.filter(x => x !== null) as User[], me, { detail: true }); + return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index a76866fe14..b197756acc 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { PromoReadsRepository } from '@/models/index.js'; +import type { PromoReadsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -28,9 +33,8 @@ export const paramDef = { required: ['noteId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.promoReadsRepository) private promoReadsRepository: PromoReadsRepository, diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index beb5850d78..3c9d266e21 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -1,10 +1,14 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import type { RenoteMutingsRepository } from '@/models/index.js'; -import type { RenoteMuting } from '@/models/entities/RenoteMuting.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { RenoteMutingsRepository } from '@/models/_.js'; +import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; @@ -51,14 +55,12 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, - private globalEventService: GlobalEventService, private getterService: GetterService, private idService: IdService, ) { @@ -92,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { createdAt: new Date(), muterId: muter.id, muteeId: mutee.id, - } as RenoteMuting); + } as MiRenoteMuting); }); } } diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts index 70901a1406..f4969896d9 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts @@ -1,7 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RenoteMutingsRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { RenoteMutingsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; @@ -42,14 +46,12 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, - private globalEventService: GlobalEventService, private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/renote-mute/list.ts b/packages/backend/src/server/api/endpoints/renote-mute/list.ts index cb4e1feba4..493593ae2d 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/list.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RenoteMutingsRepository } from '@/models/index.js'; +import type { RenoteMutingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { RenoteMutingEntityService } from '@/core/entities/RenoteMutingEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -33,9 +38,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 284ed8410d..adb160c58b 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { PasswordResetRequestsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { PasswordResetRequestsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; import type { Config } from '@/config.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['username', 'email'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 1d4825f812..0eeee81580 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import * as Redis from 'ioredis'; @@ -23,9 +28,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.db) private db: DataSource, diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index e6f1af7b22..1858c922a0 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; +import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -25,9 +30,8 @@ export const paramDef = { required: ['token', 'password'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.passwordResetRequestsRepository) private passwordResetRequestsRepository: PasswordResetRequestsRepository, diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts index e9c0fd4dcd..dac6d65407 100644 --- a/packages/backend/src/server/api/endpoints/retention.ts +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { RetentionAggregationsRepository } from '@/models/index.js'; +import type { RetentionAggregationsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.retentionAggregationsRepository) private retentionAggregationsRepository: RetentionAggregationsRepository, diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts index 5ad29839c2..d1de73ad32 100644 --- a/packages/backend/src/server/api/endpoints/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/roles/list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; @@ -18,9 +23,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index a30c31b727..6dc35907e1 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository, RolesRepository } from '@/models/index.js'; +import type { NotesRepository, RolesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -45,9 +50,8 @@ export const paramDef = { required: ['roleId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.redis) private redisClient: Redis.Redis, diff --git a/packages/backend/src/server/api/endpoints/roles/show.ts b/packages/backend/src/server/api/endpoints/roles/show.ts index cc755dcc76..2afa0e7b7f 100644 --- a/packages/backend/src/server/api/endpoints/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/roles/show.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { RolesRepository } from '@/models/index.js'; +import type { RolesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; @@ -27,9 +32,8 @@ export const paramDef = { required: ['roleId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index cc27201886..37aac908b5 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; -import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['roleId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 552441e430..c8cb63e6b3 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as os from 'node:os'; import si from 'systeminformation'; import { Injectable } from '@nestjs/common'; @@ -18,9 +23,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( private metaService: MetaService, ) { diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 48a85758a0..05468240d3 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { InstancesRepository, NoteReactionsRepository, NotesRepository, UsersRepository } from '@/models/index.js'; +import type { InstancesRepository, NoteReactionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import NotesChart from '@/core/chart/charts/notes.js'; @@ -52,16 +57,9 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index bfd5de7b00..5cfbeab73f 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { IdService } from '@/core/IdService.js'; -import type { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; @@ -52,9 +57,8 @@ export const paramDef = { required: ['endpoint', 'auth', 'publickey'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, diff --git a/packages/backend/src/server/api/endpoints/sw/show-registration.ts b/packages/backend/src/server/api/endpoints/sw/show-registration.ts index bede10be5c..126299e3f7 100644 --- a/packages/backend/src/server/api/endpoints/sw/show-registration.ts +++ b/packages/backend/src/server/api/endpoints/sw/show-registration.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -38,9 +43,8 @@ export const paramDef = { required: ['endpoint'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index f12b98617d..f00fdd6697 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -19,9 +24,8 @@ export const paramDef = { required: ['endpoint'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts index b82c4bf49d..a1a97df0be 100644 --- a/packages/backend/src/server/api/endpoints/sw/update-registration.ts +++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; @@ -47,9 +52,8 @@ export const paramDef = { required: ['endpoint'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index c88f7f2daf..6d6d44f752 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -21,9 +26,8 @@ export const paramDef = { required: ['required'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 6293c5cb50..e37df62c0c 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js'; +import type { UsedUsernamesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { localUsernameSchema } from '@/models/entities/User.js'; +import { localUsernameSchema } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; @@ -31,9 +36,8 @@ export const paramDef = { required: ['username'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 47d0a81552..21c585f1ad 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts index 2a095d83ea..e4845d57bf 100644 --- a/packages/backend/src/server/api/endpoints/users/achievements.ts +++ b/packages/backend/src/server/api/endpoints/users/achievements.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -15,9 +20,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index c2ad420cb5..725e07db39 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.clipsRepository) private clipsRepository: ClipsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/flashs.ts b/packages/backend/src/server/api/endpoints/users/flashs.ts new file mode 100644 index 0000000000..18026dcefb --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/flashs.ts @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import type { FlashsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['users', 'flashs'], + + description: 'Show all flashs this user created.', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId) + .andWhere('flash.userId = :userId', { userId: ps.userId }) + .andWhere('flash.visibility = \'public\''); + + const flashs = await query + .limit(ps.limit) + .getMany(); + + return await this.flashEntityService.packMany(flashs); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 18d66500ab..b22fd2ff7a 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; @@ -61,9 +66,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 6ea7b923d6..03487275a3 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; @@ -61,9 +66,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 3ee01953d4..757af98e00 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -32,9 +37,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index b4c1e2ec87..d6fb65cecb 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Not, In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { maximum } from '@/misc/prelude/array.js'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -53,13 +58,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index beb0ba85ff..fd1bb48a4e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import type { UserList } from '@/models/entities/UserList.js'; +import type { MiUserList } from '@/models/UserList.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; @@ -66,7 +71,7 @@ export const paramDef = { } as const; @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, @@ -103,7 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { createdAt: new Date(), userId: me.id, name: ps.name, - } as UserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); + } as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); const users = (await this.userListJoiningsRepository.findBy({ userListId: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 7510889526..60b2b3f17e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import type { UserList } from '@/models/entities/UserList.js'; +import type { MiUserList } from '@/models/UserList.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: ['name'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, @@ -66,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { createdAt: new Date(), userId: me.id, name: ps.name, - } as UserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); + } as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); return await this.userListEntityService.pack(userList); }); diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 237cb075ab..763f5afd9d 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -30,9 +35,8 @@ export const paramDef = { required: ['listId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts index 2c09a47fef..1707afee60 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserListFavoritesRepository, UserListsRepository } from '@/models/index.js'; +import type { UserListFavoritesRepository, UserListsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { ApiError } from '@/server/api/error.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index eab29944b2..0e86dd3a68 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository, UsersRepository } from '@/models/index.js'; +import type { UserListsRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { ApiError } from '@/server/api/error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index d50b70efc2..0b01061740 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListJoiningsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -42,9 +47,8 @@ export const paramDef = { required: ['listId', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 6e1f6b2c62..9bb1a71f58 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserListService } from '@/core/UserListService.js'; @@ -65,9 +70,8 @@ export const paramDef = { required: ['listId', 'userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 3fd418d04e..df44870b04 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository, UserListFavoritesRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListFavoritesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts index a7c3b58947..23611ab8c4 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserListFavoritesRepository, UserListsRepository } from '@/models/index.js'; +import type { UserListFavoritesRepository, UserListsRepository } from '@/models/_.js'; import { ApiError } from '@/server/api/error.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index b0a95a2f28..eb6cfbaf26 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -39,9 +44,8 @@ export const paramDef = { required: ['listId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index f42f84e6a7..5934baef47 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -52,9 +57,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -76,9 +80,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('note.channel', 'channel') .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); + query.andWhere(new Brackets(qb => { + qb.orWhere('note.channelId IS NULL'); + qb.orWhere('channel.isSensitive = false'); + })); + this.queryService.generateVisibilityQuery(query, me); if (me) { this.queryService.generateMutedUserQuery(query, me, user); diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index e9d13ba00f..cf2f274c70 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; -import type { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -32,9 +37,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 37fc854c33..372ab80c4c 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository, NoteReactionsRepository } from '@/models/index.js'; +import type { UserProfilesRepository, NoteReactionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; @@ -45,9 +50,8 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index eebc5d14d5..1b30e99b15 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: [], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 3267c18846..326042ed3d 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,8 +1,11 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -122,13 +125,9 @@ export const paramDef = { required: ['userId'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index be361e02c4..50aa6fa09e 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; +import type { AbuseUserReportsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -48,13 +53,9 @@ export const paramDef = { required: ['userId', 'comment'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 1d0c7d0c1d..74408cc64a 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,8 +1,13 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -41,9 +46,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.config) private config: Config, @@ -77,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 - let users: User[] = []; + let users: MiUser[] = []; if (me) { const followingQuery = this.followingsRepository.createQueryBuilder('following') diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 836218ccd9..aff5b98779 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -37,9 +42,8 @@ export const paramDef = { required: ['query'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -55,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ps.query = ps.query.trim(); const isUsername = ps.query.startsWith('@'); - let users: User[] = []; + let users: MiUser[] = []; if (isUsername) { const usernameQuery = this.usersRepository.createQueryBuilder('user') diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 8e25af64fe..389497301d 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { UsersRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; @@ -74,9 +79,8 @@ export const paramDef = { ], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -106,7 +110,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // リクエストされた通りに並べ替え - const _users: User[] = []; + const _users: MiUser[] = []; for (const id of ps.userIds) { _users.push(users.find(x => x.id === id)!); } @@ -122,7 +126,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.failedToResolveRemoteUser); }); } else { - const q: FindOptionsWhere<User> = ps.userId != null + const q: FindOptionsWhere<MiUser> = ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: IsNull() }; diff --git a/packages/backend/src/server/api/endpoints/users/update-memo.ts b/packages/backend/src/server/api/endpoints/users/update-memo.ts index ca7756ef75..194d488052 100644 --- a/packages/backend/src/server/api/endpoints/users/update-memo.ts +++ b/packages/backend/src/server/api/endpoints/users/update-memo.ts @@ -1,7 +1,12 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import type { UserMemoRepository } from '@/models/index.js'; +import type { UserMemoRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; @@ -35,9 +40,8 @@ export const paramDef = { required: ['userId', 'memo'], } as const; -// eslint-disable-next-line import/no-default-export @Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @Inject(DI.userMemosRepository) private userMemosRepository: UserMemoRepository, diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts index 34f4521606..6506565a0d 100644 --- a/packages/backend/src/server/api/error.ts +++ b/packages/backend/src/server/api/error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + type E = { message: string, code: string, id: string, kind?: 'client' | 'server' | 'permission', httpStatusCode?: number }; export class ApiError extends Error { diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts index e804ba276c..cb22d0f7c9 100644 --- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts +++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { fileURLToPath } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/server/api/openapi/errors.ts b/packages/backend/src/server/api/openapi/errors.ts index d7f791c6da..84c3c638fa 100644 --- a/packages/backend/src/server/api/openapi/errors.ts +++ b/packages/backend/src/server/api/openapi/errors.ts @@ -1,3 +1,7 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ export const errors = { '400': { diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index fa62480c02..4f972d3f7e 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Config } from '@/config.js'; import endpoints from '../endpoints.js'; import { errors as basicErrors } from './errors.js'; diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts index 0cef361caf..0b9eb4fe24 100644 --- a/packages/backend/src/server/api/openapi/schemas.ts +++ b/packages/backend/src/server/api/openapi/schemas.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import type { Schema } from '@/misc/json-schema.js'; import { refs } from '@/misc/json-schema.js'; diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 4a544fadfe..8fd106c10c 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/Connection.ts index 8b1c2c09c9..fd91681fc1 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -1,12 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as WebSocket from 'ws'; -import type { User } from '@/models/entities/User.js'; -import type { AccessToken } from '@/models/entities/AccessToken.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiAccessToken } from '@/models/AccessToken.js'; import type { Packed } from '@/misc/json-schema.js'; import type { NoteReadService } from '@/core/NoteReadService.js'; import type { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; -import { UserProfile } from '@/models/index.js'; +import { MiUserProfile } from '@/models/_.js'; import type { ChannelsService } from './ChannelsService.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; @@ -15,21 +20,22 @@ import type { StreamEventEmitter, StreamMessages } from './types.js'; /** * Main stream connection */ +// eslint-disable-next-line import/no-default-export export default class Connection { - public user?: User; - public token?: AccessToken; + public user?: MiUser; + public token?: MiAccessToken; private wsConnection: WebSocket.WebSocket; public subscriber: StreamEventEmitter; private channels: Channel[] = []; private subscribingNotes: any = {}; private cachedNotes: Packed<'Note'>[] = []; - public userProfile: UserProfile | null = null; + public userProfile: MiUserProfile | null = null; public following: Set<string> = new Set(); public followingChannels: Set<string> = new Set(); public userIdsWhoMeMuting: Set<string> = new Set(); public userIdsWhoBlockingMe: Set<string> = new Set(); public userIdsWhoMeMutingRenotes: Set<string> = new Set(); - private fetchIntervalId: NodeJS.Timer | null = null; + private fetchIntervalId: NodeJS.Timeout | null = null; constructor( private channelsService: ChannelsService, @@ -37,8 +43,8 @@ export default class Connection { private notificationService: NotificationService, private cacheService: CacheService, - user: User | null | undefined, - token: AccessToken | null | undefined, + user: MiUser | null | undefined, + token: MiAccessToken | null | undefined, ) { if (user) this.user = user; if (token) this.token = token; diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 94b92e02ef..ad32d08fee 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -1,9 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { bindThis } from '@/decorators.js'; -import type Connection from './index.js'; +import type Connection from './Connection.js'; /** * Stream channel */ +// eslint-disable-next-line import/no-default-export export default abstract class Channel { protected connection: Connection; public id: string; diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 157fcd6aa3..bfb36d9cb8 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index d48dea7258..87648a3a77 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 9e5b40997b..a01714e76d 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/json-schema.js'; diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 52bb29fabe..83f53c1836 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index d3339072c1..a33f1a956a 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 94ebf86418..3945b1a1eb 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index fe0cc37b6b..bd8888f679 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 5a33e13cf5..760fb8d19f 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 9ca4db8ced..f32f8c5cec 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 139320ce35..f969d02337 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index 7f48c54999..f0dc472303 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Xev from 'xev'; import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index 6218fada97..76b5875343 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/json-schema.js'; diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index 9eae0cf2d3..cacae275a8 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import Xev from 'xev'; import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index ea4cff0bc0..8bbba0b6db 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,6 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; +import type { UserListJoiningsRepository, UserListsRepository } from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -13,8 +18,8 @@ class UserListChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private listId: string; - public listUsers: User['id'][] = []; - private listUsersClock: NodeJS.Timer; + public listUsers: MiUser['id'][] = []; + private listUsersClock: NodeJS.Timeout; constructor( private userListsRepository: UserListsRepository, diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index f239b06637..90e0a61f26 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -1,48 +1,53 @@ -import type { Channel } from '@/models/entities/Channel.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserProfile } from '@/models/entities/UserProfile.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { Antenna } from '@/models/entities/Antenna.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { DriveFolder } from '@/models/entities/DriveFolder.js'; -import type { UserList } from '@/models/entities/UserList.js'; -import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; -import type { Signin } from '@/models/entities/Signin.js'; -import type { Page } from '@/models/entities/Page.js'; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { MiChannel } from '@/models/Channel.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiUserProfile } from '@/models/UserProfile.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiAntenna } from '@/models/Antenna.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiDriveFolder } from '@/models/DriveFolder.js'; +import type { MiUserList } from '@/models/UserList.js'; +import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; +import type { MiSignin } from '@/models/Signin.js'; +import type { MiPage } from '@/models/Page.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { Webhook } from '@/models/entities/Webhook.js'; -import type { Meta } from '@/models/entities/Meta.js'; -import { Role, RoleAssignment } from '@/models/index.js'; +import type { MiWebhook } from '@/models/Webhook.js'; +import type { MiMeta } from '@/models/Meta.js'; +import { MiRole, MiRoleAssignment } from '@/models/_.js'; import type Emitter from 'strict-event-emitter-types'; import type { EventEmitter } from 'events'; //#region Stream type-body definitions export interface InternalStreamTypes { - userChangeSuspendedState: { id: User['id']; isSuspended: User['isSuspended']; }; - userTokenRegenerated: { id: User['id']; oldToken: string; newToken: string; }; - remoteUserUpdated: { id: User['id']; }; - follow: { followerId: User['id']; followeeId: User['id']; }; - unfollow: { followerId: User['id']; followeeId: User['id']; }; - blockingCreated: { blockerId: User['id']; blockeeId: User['id']; }; - blockingDeleted: { blockerId: User['id']; blockeeId: User['id']; }; - policiesUpdated: Role['policies']; - roleCreated: Role; - roleDeleted: Role; - roleUpdated: Role; - userRoleAssigned: RoleAssignment; - userRoleUnassigned: RoleAssignment; - webhookCreated: Webhook; - webhookDeleted: Webhook; - webhookUpdated: Webhook; - antennaCreated: Antenna; - antennaDeleted: Antenna; - antennaUpdated: Antenna; - metaUpdated: Meta; - followChannel: { userId: User['id']; channelId: Channel['id']; }; - unfollowChannel: { userId: User['id']; channelId: Channel['id']; }; - updateUserProfile: UserProfile; - mute: { muterId: User['id']; muteeId: User['id']; }; - unmute: { muterId: User['id']; muteeId: User['id']; }; + userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; + userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; + remoteUserUpdated: { id: MiUser['id']; }; + follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; + unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; + blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + blockingDeleted: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + policiesUpdated: MiRole['policies']; + roleCreated: MiRole; + roleDeleted: MiRole; + roleUpdated: MiRole; + userRoleAssigned: MiRoleAssignment; + userRoleUnassigned: MiRoleAssignment; + webhookCreated: MiWebhook; + webhookDeleted: MiWebhook; + webhookUpdated: MiWebhook; + antennaCreated: MiAntenna; + antennaDeleted: MiAntenna; + antennaUpdated: MiAntenna; + metaUpdated: MiMeta; + followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; + unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; + updateUserProfile: MiUserProfile; + mute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; + unmute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; } export interface BroadcastTypes { @@ -59,6 +64,9 @@ export interface BroadcastTypes { [other: string]: any; }[]; }; + announcementCreated: { + announcement: Packed<'Announcement'>; + }; } export interface MainStreamTypes { @@ -71,10 +79,10 @@ export interface MainStreamTypes { unfollow: Packed<'User'>; meUpdated: Packed<'User'>; pageEvent: { - pageId: Page['id']; + pageId: MiPage['id']; event: string; var: any; - userId: User['id']; + userId: MiUser['id']; user: Packed<'User'>; }; urlUploadFinished: { @@ -83,38 +91,41 @@ export interface MainStreamTypes { }; readAllNotifications: undefined; unreadNotification: Packed<'Notification'>; - unreadMention: Note['id']; + unreadMention: MiNote['id']; readAllUnreadMentions: undefined; - unreadSpecifiedNote: Note['id']; + unreadSpecifiedNote: MiNote['id']; readAllUnreadSpecifiedNotes: undefined; readAllAntennas: undefined; - unreadAntenna: Antenna; + unreadAntenna: MiAntenna; readAllAnnouncements: undefined; myTokenRegenerated: undefined; - signin: Signin; + signin: MiSignin; registryUpdated: { scope?: string[]; key: string; value: any | null; }; driveFileCreated: Packed<'DriveFile'>; - readAntenna: Antenna; + readAntenna: MiAntenna; receiveFollowRequest: Packed<'User'>; + announcementCreated: { + announcement: Packed<'Announcement'>; + }; } export interface DriveStreamTypes { fileCreated: Packed<'DriveFile'>; - fileDeleted: DriveFile['id']; + fileDeleted: MiDriveFile['id']; fileUpdated: Packed<'DriveFile'>; folderCreated: Packed<'DriveFolder'>; - folderDeleted: DriveFolder['id']; + folderDeleted: MiDriveFolder['id']; folderUpdated: Packed<'DriveFolder'>; } export interface NoteStreamTypes { pollVoted: { choice: number; - userId: User['id']; + userId: MiUser['id']; }; deleted: { deletedAt: Date; @@ -125,16 +136,16 @@ export interface NoteStreamTypes { name: string; url: string; } | null; - userId: User['id']; + userId: MiUser['id']; }; unreacted: { reaction: string; - userId: User['id']; + userId: MiUser['id']; }; } type NoteStreamEventTypes = { [key in keyof NoteStreamTypes]: { - id: Note['id']; + id: MiNote['id']; body: NoteStreamTypes[key]; }; }; @@ -145,7 +156,7 @@ export interface UserListStreamTypes { } export interface AntennaStreamTypes { - note: Note; + note: MiNote; } export interface RoleTimelineStreamTypes { @@ -154,9 +165,9 @@ export interface RoleTimelineStreamTypes { export interface AdminStreamTypes { newAbuseUserReport: { - id: AbuseUserReport['id']; - targetUserId: User['id'], - reporterId: User['id'], + id: MiAbuseUserReport['id']; + targetUserId: MiUser['id'], + reporterId: MiUser['id'], comment: string; }; } @@ -198,31 +209,31 @@ export type StreamMessages = { payload: EventUnionFromDictionary<SerializedAll<BroadcastTypes>>; }; main: { - name: `mainStream:${User['id']}`; + name: `mainStream:${MiUser['id']}`; payload: EventUnionFromDictionary<SerializedAll<MainStreamTypes>>; }; drive: { - name: `driveStream:${User['id']}`; + name: `driveStream:${MiUser['id']}`; payload: EventUnionFromDictionary<SerializedAll<DriveStreamTypes>>; }; note: { - name: `noteStream:${Note['id']}`; + name: `noteStream:${MiNote['id']}`; payload: EventUnionFromDictionary<SerializedAll<NoteStreamEventTypes>>; }; userList: { - name: `userListStream:${UserList['id']}`; + name: `userListStream:${MiUserList['id']}`; payload: EventUnionFromDictionary<SerializedAll<UserListStreamTypes>>; }; roleTimeline: { - name: `roleTimelineStream:${Role['id']}`; + name: `roleTimelineStream:${MiRole['id']}`; payload: EventUnionFromDictionary<SerializedAll<RoleTimelineStreamTypes>>; }; antenna: { - name: `antennaStream:${Antenna['id']}`; + name: `antennaStream:${MiAntenna['id']}`; payload: EventUnionFromDictionary<SerializedAll<AntennaStreamTypes>>; }; admin: { - name: `adminStream:${User['id']}`; + name: `adminStream:${MiUser['id']}`; payload: EventUnionFromDictionary<SerializedAll<AdminStreamTypes>>; }; notes: { diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts new file mode 100644 index 0000000000..c3a78561c2 --- /dev/null +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -0,0 +1,487 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import dns from 'node:dns/promises'; +import { fileURLToPath } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { JSDOM } from 'jsdom'; +import httpLinkHeader from 'http-link-header'; +import ipaddr from 'ipaddr.js'; +import oauth2orize, { type OAuth2, AuthorizationError, ValidateFunctionArity2, OAuth2Req, MiddlewareRequest } from 'oauth2orize'; +import oauth2Pkce from 'oauth2orize-pkce'; +import fastifyView from '@fastify/view'; +import pug from 'pug'; +import bodyParser from 'body-parser'; +import fastifyExpress from '@fastify/express'; +import { verifyChallenge } from 'pkce-challenge'; +import { mf2 } from 'microformats-parser'; +import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { kinds } from '@/misc/api-permissions.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import type { AccessTokensRepository, UsersRepository } from '@/models/_.js'; +import { IdService } from '@/core/IdService.js'; +import { CacheService } from '@/core/CacheService.js'; +import type { MiLocalUser } from '@/models/User.js'; +import { MemoryKVCache } from '@/misc/cache.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import Logger from '@/logger.js'; +import { StatusError } from '@/misc/status-error.js'; +import type { ServerResponse } from 'node:http'; +import type { FastifyInstance } from 'fastify'; + +// TODO: Consider migrating to @node-oauth/oauth2-server once +// https://github.com/node-oauth/node-oauth2-server/issues/180 is figured out. +// Upstream the various validations and RFC9207 implementation in that case. + +// Follows https://indieauth.spec.indieweb.org/#client-identifier +// This is also mostly similar to https://developers.google.com/identity/protocols/oauth2/web-server#uri-validation +// although Google has stricter rule. +function validateClientId(raw: string): URL { + // "Clients are identified by a [URL]." + const url = ((): URL => { + try { + return new URL(raw); + } catch { throw new AuthorizationError('client_id must be a valid URL', 'invalid_request'); } + })(); + + // "Client identifier URLs MUST have either an https or http scheme" + // But then again: + // https://datatracker.ietf.org/doc/html/rfc6749.html#section-3.1.2.1 + // 'The redirection endpoint SHOULD require the use of TLS as described + // in Section 1.6 when the requested response type is "code" or "token"' + const allowedProtocols = process.env.NODE_ENV === 'test' ? ['http:', 'https:'] : ['https:']; + if (!allowedProtocols.includes(url.protocol)) { + throw new AuthorizationError('client_id must be a valid HTTPS URL', 'invalid_request'); + } + + // "MUST contain a path component (new URL() implicitly adds one)" + + // "MUST NOT contain single-dot or double-dot path segments," + const segments = url.pathname.split('/'); + if (segments.includes('.') || segments.includes('..')) { + throw new AuthorizationError('client_id must not contain dot path segments', 'invalid_request'); + } + + // ("MAY contain a query string component") + + // "MUST NOT contain a fragment component" + if (url.hash) { + throw new AuthorizationError('client_id must not contain a fragment component', 'invalid_request'); + } + + // "MUST NOT contain a username or password component" + if (url.username || url.password) { + throw new AuthorizationError('client_id must not contain a username or a password', 'invalid_request'); + } + + // ("MAY contain a port") + + // "host names MUST be domain names or a loopback interface and MUST NOT be + // IPv4 or IPv6 addresses except for IPv4 127.0.0.1 or IPv6 [::1]." + if (!url.hostname.match(/\.\w+$/) && !['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) { + throw new AuthorizationError('client_id must have a domain name as a host name', 'invalid_request'); + } + + return url; +} + +interface ClientInformation { + id: string; + redirectUris: string[]; + name: string; +} + +// https://indieauth.spec.indieweb.org/#client-information-discovery +// "Authorization servers SHOULD support parsing the [h-app] Microformat from the client_id, +// and if there is an [h-app] with a url property matching the client_id URL, +// then it should use the name and icon and display them on the authorization prompt." +// (But we don't display any icon for now) +// https://indieauth.spec.indieweb.org/#redirect-url +// "The client SHOULD publish one or more <link> tags or Link HTTP headers with a rel attribute +// of redirect_uri at the client_id URL. +// Authorization endpoints verifying that a redirect_uri is allowed for use by a client MUST +// look for an exact match of the given redirect_uri in the request against the list of +// redirect_uris discovered after resolving any relative URLs." +async function discoverClientInformation(logger: Logger, httpRequestService: HttpRequestService, id: string): Promise<ClientInformation> { + try { + const res = await httpRequestService.send(id); + const redirectUris: string[] = []; + + const linkHeader = res.headers.get('link'); + if (linkHeader) { + redirectUris.push(...httpLinkHeader.parse(linkHeader).get('rel', 'redirect_uri').map(r => r.uri)); + } + + const text = await res.text(); + const fragment = JSDOM.fragment(text); + + redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href)); + + let name = id; + if (text) { + const microformats = mf2(text, { baseUrl: res.url }); + const nameProperty = microformats.items.find(item => item.type?.includes('h-app') && item.properties.url.includes(id))?.properties.name[0]; + if (typeof nameProperty === 'string') { + name = nameProperty; + } + } + + return { + id, + redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()), + name: typeof name === 'string' ? name : id, + }; + } catch (err) { + console.error(err); + logger.error('Error while fetching client information', { err }); + if (err instanceof StatusError) { + throw new AuthorizationError('Failed to fetch client information', 'invalid_request'); + } else { + throw new AuthorizationError('Failed to parse client information', 'server_error'); + } + } +} + +type OmitFirstElement<T extends unknown[]> = T extends [unknown, ...(infer R)] + ? R + : []; + +interface OAuthParsedRequest extends OAuth2Req { + codeChallenge: string; + codeChallengeMethod: string; +} + +interface OAuthHttpResponse extends ServerResponse { + redirect(location: string): void; +} + +interface OAuth2DecisionRequest extends MiddlewareRequest { + body: { + transaction_id: string; + cancel: boolean; + login_token: string; + } +} + +function getQueryMode(issuerUrl: string): oauth2orize.grant.Options['modes'] { + return { + query: (txn, res, params): void => { + // https://datatracker.ietf.org/doc/html/rfc9207#name-response-parameter-iss + // "In authorization responses to the client, including error responses, + // an authorization server supporting this specification MUST indicate its + // identity by including the iss parameter in the response." + params.iss = issuerUrl; + + const parsed = new URL(txn.redirectURI); + for (const [key, value] of Object.entries(params)) { + parsed.searchParams.append(key, value as string); + } + + return (res as OAuthHttpResponse).redirect(parsed.toString()); + }, + }; +} + +/** + * Maps the transaction ID and the oauth/authorize parameters. + * + * Flow: + * 1. oauth/authorize endpoint will call store() to store the parameters + * and puts the generated transaction ID to the dialog page + * 2. oauth/decision will call load() to retrieve the parameters and then remove() + */ +class OAuth2Store { + #cache = new MemoryKVCache<OAuth2>(1000 * 60 * 5); // expires after 5min + + load(req: OAuth2DecisionRequest, cb: (err: Error | null, txn?: OAuth2) => void): void { + const { transaction_id } = req.body; + if (!transaction_id) { + cb(new AuthorizationError('Missing transaction ID', 'invalid_request')); + return; + } + const loaded = this.#cache.get(transaction_id); + if (!loaded) { + cb(new AuthorizationError('Invalid or expired transaction ID', 'access_denied')); + return; + } + cb(null, loaded); + } + + store(req: OAuth2DecisionRequest, oauth2: OAuth2, cb: (err: Error | null, transactionID?: string) => void): void { + const transactionId = secureRndstr(128); + this.#cache.set(transactionId, oauth2); + cb(null, transactionId); + } + + remove(req: OAuth2DecisionRequest, tid: string, cb: () => void): void { + this.#cache.delete(tid); + cb(); + } +} + +@Injectable() +export class OAuth2ProviderService { + #server = oauth2orize.createServer({ + store: new OAuth2Store(), + }); + #logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + private httpRequestService: HttpRequestService, + @Inject(DI.accessTokensRepository) + accessTokensRepository: AccessTokensRepository, + idService: IdService, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private cacheService: CacheService, + loggerService: LoggerService, + ) { + this.#logger = loggerService.getLogger('oauth'); + + const grantCodeCache = new MemoryKVCache<{ + clientId: string, + userId: string, + redirectUri: string, + codeChallenge: string, + scopes: string[], + + // fields to prevent multiple code use + grantedToken?: string, + revoked?: boolean, + used?: boolean, + }>(1000 * 60 * 5); // expires after 5m + + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics + // "Authorization servers MUST support PKCE [RFC7636]." + this.#server.grant(oauth2Pkce.extensions()); + this.#server.grant(oauth2orize.grant.code({ + modes: getQueryMode(config.url), + }, (client, redirectUri, token, ares, areq, locals, done) => { + (async (): Promise<OmitFirstElement<Parameters<typeof done>>> => { + this.#logger.info(`Checking the user before sending authorization code to ${client.id}`); + + if (!token) { + throw new AuthorizationError('No user', 'invalid_request'); + } + const user = await this.cacheService.localUserByNativeTokenCache.fetch(token, + () => this.usersRepository.findOneBy({ token }) as Promise<MiLocalUser | null>); + if (!user) { + throw new AuthorizationError('No such user', 'invalid_request'); + } + + this.#logger.info(`Sending authorization code on behalf of user ${user.id} to ${client.id} through ${redirectUri}, with scope: [${areq.scope}]`); + + const code = secureRndstr(128); + grantCodeCache.set(code, { + clientId: client.id, + userId: user.id, + redirectUri, + codeChallenge: (areq as OAuthParsedRequest).codeChallenge, + scopes: areq.scope, + }); + return [code]; + })().then(args => done(null, ...args), err => done(err)); + })); + this.#server.exchange(oauth2orize.exchange.authorizationCode((client, code, redirectUri, body, authInfo, done) => { + (async (): Promise<OmitFirstElement<Parameters<typeof done>> | undefined> => { + this.#logger.info('Checking the received authorization code for the exchange'); + const granted = grantCodeCache.get(code); + if (!granted) { + return; + } + + // https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.1.2 + // "If an authorization code is used more than once, the authorization server + // MUST deny the request and SHOULD revoke (when possible) all tokens + // previously issued based on that authorization code." + if (granted.used) { + this.#logger.info(`Detected multiple code use from ${granted.clientId} for user ${granted.userId}. Revoking the code.`); + grantCodeCache.delete(code); + granted.revoked = true; + if (granted.grantedToken) { + await accessTokensRepository.delete({ token: granted.grantedToken }); + } + return; + } + granted.used = true; + + // https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.1.3 + if (body.client_id !== granted.clientId) return; + if (redirectUri !== granted.redirectUri) return; + + // https://datatracker.ietf.org/doc/html/rfc7636.html#section-4.6 + if (!body.code_verifier) return; + if (!(await verifyChallenge(body.code_verifier as string, granted.codeChallenge))) return; + + const accessToken = secureRndstr(128); + const now = new Date(); + + // NOTE: we don't have a setup for automatic token expiration + await accessTokensRepository.insert({ + id: idService.genId(), + createdAt: now, + lastUsedAt: now, + userId: granted.userId, + token: accessToken, + hash: accessToken, + name: granted.clientId, + permission: granted.scopes, + }); + + if (granted.revoked) { + this.#logger.info('Canceling the token as the authorization code was revoked in parallel during the process.'); + await accessTokensRepository.delete({ token: accessToken }); + return; + } + + granted.grantedToken = accessToken; + this.#logger.info(`Generated access token for ${granted.clientId} for user ${granted.userId}, with scope: [${granted.scopes}]`); + + return [accessToken, undefined, { scope: granted.scopes.join(' ') }]; + })().then(args => done(null, ...args ?? []), err => done(err)); + })); + } + + @bindThis + public async createServer(fastify: FastifyInstance): Promise<void> { + // https://datatracker.ietf.org/doc/html/rfc8414.html + // https://indieauth.spec.indieweb.org/#indieauth-server-metadata + fastify.get('/.well-known/oauth-authorization-server', async (_request, reply) => { + reply.send({ + issuer: this.config.url, + authorization_endpoint: new URL('/oauth/authorize', this.config.url), + token_endpoint: new URL('/oauth/token', this.config.url), + scopes_supported: kinds, + response_types_supported: ['code'], + grant_types_supported: ['authorization_code'], + service_documentation: 'https://misskey-hub.net', + code_challenge_methods_supported: ['S256'], + authorization_response_iss_parameter_supported: true, + }); + }); + + fastify.get('/oauth/authorize', async (request, reply) => { + const oauth2 = (request.raw as MiddlewareRequest).oauth2; + if (!oauth2) { + throw new Error('Unexpected lack of authorization information'); + } + + this.#logger.info(`Rendering authorization page for "${oauth2.client.name}"`); + + reply.header('Cache-Control', 'no-store'); + return await reply.view('oauth', { + transactionId: oauth2.transactionID, + clientName: oauth2.client.name, + scope: oauth2.req.scope.join(' '), + }); + }); + fastify.post('/oauth/decision', async () => { }); + fastify.post('/oauth/token', async () => { }); + + fastify.register(fastifyView, { + root: fileURLToPath(new URL('../web/views', import.meta.url)), + engine: { pug }, + defaultContext: { + version: this.config.version, + config: this.config, + }, + }); + + await fastify.register(fastifyExpress); + fastify.use('/oauth/authorize', this.#server.authorize(((areq, done) => { + (async (): Promise<Parameters<typeof done>> => { + // This should return client/redirectURI AND the error, or + // the handler can't send error to the redirection URI + + const { codeChallenge, codeChallengeMethod, clientID, redirectURI, scope } = areq as OAuthParsedRequest; + + this.#logger.info(`Validating authorization parameters, with client_id: ${clientID}, redirect_uri: ${redirectURI}, scope: ${scope}`); + + const clientUrl = validateClientId(clientID); + + // https://indieauth.spec.indieweb.org/#client-information-discovery + // "the server may want to resolve the domain name first and avoid fetching the document + // if the IP address is within the loopback range defined by [RFC5735] + // or any other implementation-specific internal IP address." + if (process.env.NODE_ENV !== 'test' || process.env.MISSKEY_TEST_CHECK_IP_RANGE === '1') { + const lookup = await dns.lookup(clientUrl.hostname); + if (ipaddr.parse(lookup.address).range() !== 'unicast') { + throw new AuthorizationError('client_id resolves to disallowed IP range.', 'invalid_request'); + } + } + + // Find client information from the remote. + const clientInfo = await discoverClientInformation(this.#logger, this.httpRequestService, clientUrl.href); + + // Require the redirect URI to be included in an explicit list, per + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.1.3 + if (!clientInfo.redirectUris.includes(redirectURI)) { + throw new AuthorizationError('Invalid redirect_uri', 'invalid_request'); + } + + try { + const scopes = [...new Set(scope)].filter(s => kinds.includes(s)); + if (!scopes.length) { + throw new AuthorizationError('`scope` parameter has no known scope', 'invalid_scope'); + } + areq.scope = scopes; + + // Require PKCE parameters. + // Recommended by https://indieauth.spec.indieweb.org/#authorization-request, but also prevents downgrade attack: + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#name-pkce-downgrade-attack + if (typeof codeChallenge !== 'string') { + throw new AuthorizationError('`code_challenge` parameter is required', 'invalid_request'); + } + if (codeChallengeMethod !== 'S256') { + throw new AuthorizationError('`code_challenge_method` parameter must be set as S256', 'invalid_request'); + } + } catch (err) { + return [err as Error, clientInfo, redirectURI]; + } + + return [null, clientInfo, redirectURI]; + })().then(args => done(...args), err => done(err)); + }) as ValidateFunctionArity2)); + fastify.use('/oauth/authorize', this.#server.errorHandler({ + mode: 'indirect', + modes: getQueryMode(this.config.url), + })); + fastify.use('/oauth/authorize', this.#server.errorHandler()); + + fastify.use('/oauth/decision', bodyParser.urlencoded({ extended: false })); + fastify.use('/oauth/decision', this.#server.decision((req, done) => { + const { body } = req as OAuth2DecisionRequest; + this.#logger.info(`Received the decision. Cancel: ${!!body.cancel}`); + req.user = body.login_token; + done(null, undefined); + })); + fastify.use('/oauth/decision', this.#server.errorHandler()); + + // Clients may use JSON or urlencoded + fastify.use('/oauth/token', bodyParser.urlencoded({ extended: false })); + fastify.use('/oauth/token', bodyParser.json({ strict: true })); + fastify.use('/oauth/token', this.#server.token()); + fastify.use('/oauth/token', this.#server.errorHandler()); + + // Return 404 for any unknown paths under /oauth so that clients can know + // whether a certain endpoint is supported or not. + fastify.all('/oauth/*', async (_request, reply) => { + reply.code(404); + reply.send({ + error: { + message: 'Unknown OAuth endpoint.', + code: 'UNKNOWN_OAUTH_ENDPOINT', + id: 'aa49e620-26cb-4e28-aad6-8cbcb58db147', + kind: 'client', + }, + }); + }); + } +} diff --git a/packages/backend/src/server/web/ClientLoggerService.ts b/packages/backend/src/server/web/ClientLoggerService.ts index 6a882aa766..213266f59c 100644 --- a/packages/backend/src/server/web/ClientLoggerService.ts +++ b/packages/backend/src/server/web/ClientLoggerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 363cca8feb..1faff24201 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { randomUUID } from 'node:crypto'; import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -26,13 +31,12 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; -import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, Meta, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { RoleService } from '@/core/RoleService.js'; -import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; @@ -105,23 +109,68 @@ export class ClientServerService { @bindThis private async manifestHandler(reply: FastifyReply) { - const res = deepClone(manifest); - const instance = await this.metaService.fetch(true); - res.short_name = instance.name ?? 'Misskey'; - res.name = instance.name ?? 'Misskey'; - if (instance.themeColor) res.theme_color = instance.themeColor; + let manifest = { + // 空文字列の場合右辺を使いたいため + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + 'short_name': instance.shortName || instance.name || this.config.host, + // 空文字列の場合右辺を使いたいため + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + 'name': instance.name || this.config.host, + 'start_url': '/', + 'display': 'standalone', + 'background_color': '#313a42', + // 空文字列の場合右辺を使いたいため + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + 'theme_color': instance.themeColor || '#86b300', + 'icons': [{ + // 空文字列の場合右辺を使いたいため + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + 'src': instance.app192IconUrl || '/static-assets/icons/192.png', + 'sizes': '192x192', + 'type': 'image/png', + 'purpose': 'maskable', + }, { + // 空文字列の場合右辺を使いたいため + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + 'src': instance.app512IconUrl || '/static-assets/icons/512.png', + 'sizes': '512x512', + 'type': 'image/png', + 'purpose': 'maskable', + }, { + 'src': '/static-assets/splash.png', + 'sizes': '300x300', + 'type': 'image/png', + 'purpose': 'any', + }], + 'share_target': { + 'action': '/share/', + 'method': 'GET', + 'enctype': 'application/x-www-form-urlencoded', + 'params': { + 'title': 'title', + 'text': 'text', + 'url': 'url', + }, + }, + }; + + manifest = { + ...manifest, + ...JSON.parse(instance.manifestJsonOverride === '' ? '{}' : instance.manifestJsonOverride), + }; reply.header('Cache-Control', 'max-age=300'); - return (res); + return (manifest); } @bindThis - private generateCommonPugData(meta: Meta) { + private generateCommonPugData(meta: MiMeta) { return { instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, + appleTouchIcon: meta.app512IconUrl, themeColor: meta.themeColor, serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg', infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', @@ -138,21 +187,23 @@ export class ClientServerService { // Authenticate fastify.addHook('onRequest', async (request, reply) => { - if (request.url === bullBoardPath || request.url.startsWith(bullBoardPath + '/')) { + // %71ueueとかでリクエストされたら困るため + const url = decodeURI(request.url); + if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) { const token = request.cookies.token; if (token == null) { - reply.code(401); - throw new Error('login required'); + reply.code(401).send('Login required'); + return; } const user = await this.usersRepository.findOneBy({ token }); if (user == null) { - reply.code(403); - throw new Error('no such user'); + reply.code(403).send('No such user'); + return; } const isAdministrator = await this.roleService.isAdministrator(user); if (!isAdministrator) { - reply.code(403); - throw new Error('access denied'); + reply.code(403).send('Access denied'); + return; } } }); @@ -677,7 +728,7 @@ export class ClientServerService { fastify.setErrorHandler(async (error, request, reply) => { const errId = randomUUID(); - this.clientLoggerService.logger.error(`Internal error occured in ${request.routerPath}: ${error.message}`, { + this.clientLoggerService.logger.error(`Internal error occurred in ${request.routerPath}: ${error.message}`, { path: request.routerPath, params: request.params, query: request.query, diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 0bd0d3c692..78551e800b 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -1,10 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { Feed } from 'feed'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { DriveFilesRepository, NotesRepository, UserProfilesRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/User.js'; +import type { MiUser } from '@/models/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -15,9 +20,6 @@ export class FeedService { @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, @@ -33,7 +35,7 @@ export class FeedService { } @bindThis - public async packFeed(user: User) { + public async packFeed(user: MiUser) { const author = { link: `${this.config.url}/@${user.username}`, name: user.name ?? user.username, diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index e61e92c623..d590244e34 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { summaly } from 'summaly'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/web/bios.css b/packages/backend/src/server/web/bios.css index b0da3ee39b..c934a55fa9 100644 --- a/packages/backend/src/server/web/bios.css +++ b/packages/backend/src/server/web/bios.css @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + * { font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace; } diff --git a/packages/backend/src/server/web/bios.js b/packages/backend/src/server/web/bios.js index 51899dd3a3..029eb92aad 100644 --- a/packages/backend/src/server/web/bios.js +++ b/packages/backend/src/server/web/bios.js @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + 'use strict'; window.onload = async () => { diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 38ae8ad2e5..48939ef7a0 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + /** * BOOT LOADER * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。 diff --git a/packages/backend/src/server/web/cli.css b/packages/backend/src/server/web/cli.css index 07cd27830b..b7737c3f21 100644 --- a/packages/backend/src/server/web/cli.css +++ b/packages/backend/src/server/web/cli.css @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + * { font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace; } diff --git a/packages/backend/src/server/web/cli.js b/packages/backend/src/server/web/cli.js index 5bb576a27b..e63a80327c 100644 --- a/packages/backend/src/server/web/cli.js +++ b/packages/backend/src/server/web/cli.js @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + 'use strict'; window.onload = async () => { diff --git a/packages/backend/src/server/web/error.css b/packages/backend/src/server/web/error.css index ab913f7a9f..ea3056bdaf 100644 --- a/packages/backend/src/server/web/error.css +++ b/packages/backend/src/server/web/error.css @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + * { font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif; } diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index d59f00fe16..952be9bf0b 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + html { background-color: var(--bg); color: var(--fg); diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 2b61c6bc2f..71bcf9462f 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -7,15 +7,15 @@ doctype html // - - _____ _ _ - | |_|___ ___| |_ ___ _ _ + _____ _ _ + | |_|___ ___| |_ ___ _ _ | | | | |_ -|_ -| '_| -_| | | |_|_|_|_|___|___|_,_|___|_ | |___| Thank you for using Misskey! If you are reading this message... how about joining the development? https://github.com/misskey-dev/misskey - + html @@ -28,14 +28,14 @@ html meta(property='og:site_name' content= instanceName || 'Misskey') meta(name='viewport' content='width=device-width, initial-scale=1') link(rel='icon' href= icon || '/favicon.ico') - link(rel='apple-touch-icon' href= icon || '/apple-touch-icon.png') + link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png') link(rel='manifest' href='/manifest.json') link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`) link(rel='prefetch' href=serverErrorImageUrl) link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) //- https://github.com/misskey-dev/misskey/issues/9842 - link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.25.0') + link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.35.0') link(rel='modulepreload' href=`/vite/${clientEntry.file}`) if !config.clientManifestExists diff --git a/packages/backend/src/server/web/views/oauth.pug b/packages/backend/src/server/web/views/oauth.pug new file mode 100644 index 0000000000..1470dbfbdf --- /dev/null +++ b/packages/backend/src/server/web/views/oauth.pug @@ -0,0 +1,9 @@ +extends ./base + +block meta + //- Should be removed by the page when it loads, so that it won't needlessly + //- stay when user navigates away via the navigation bar + //- XXX: Remove navigation bar in auth page? + meta(name='misskey:oauth:transaction-id' content=transactionId) + meta(name='misskey:oauth:client-name' content=clientName) + meta(name='misskey:oauth:scope' content=scope) diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 7c6a1e5199..16654edd88 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,4 +1,24 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * note - 通知オンにしているユーザーが投稿した + * follow - フォローされた + * mention - 投稿で自分が言及された + * reply - 投稿に返信された + * renote - 投稿がRenoteされた + * quote - 投稿が引用Renoteされた + * reaction - 投稿にリアクションされた + * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した + * receiveFollowRequest - フォローリクエストされた + * followRequestAccepted - 自分の送ったフォローリクエストが承認された + * achievementEarned - 実績を獲得 + * app - アプリ通知 + * test - テスト通知(サーバー側) + */ +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test'] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; @@ -6,3 +26,140 @@ export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; export const ffVisibility = ['public', 'followers', 'private'] as const; + +export const moderationLogTypes = [ + 'updateServerSettings', + 'suspend', + 'unsuspend', + 'updateUserNote', + 'addCustomEmoji', + 'updateCustomEmoji', + 'deleteCustomEmoji', + 'assignRole', + 'unassignRole', + 'updateRole', + 'deleteRole', + 'clearQueue', + 'promoteQueue', + 'deleteDriveFile', + 'deleteNote', + 'createGlobalAnnouncement', + 'createUserAnnouncement', + 'updateGlobalAnnouncement', + 'updateUserAnnouncement', + 'deleteGlobalAnnouncement', + 'deleteUserAnnouncement', + 'resetPassword', + 'suspendRemoteInstance', + 'unsuspendRemoteInstance', + 'markSensitiveDriveFile', + 'unmarkSensitiveDriveFile', +] as const; + +export type ModerationLogPayloads = { + updateServerSettings: { + before: any | null; + after: any | null; + }; + suspend: { + targetId: string; + }; + unsuspend: { + targetId: string; + }; + updateUserNote: { + userId: string; + before: string | null; + after: string | null; + }; + addCustomEmoji: { + emojiId: string; + emoji: any; + }; + updateCustomEmoji: { + emojiId: string; + before: any; + after: any; + }; + deleteCustomEmoji: { + emojiId: string; + emoji: any; + }; + assignRole: { + userId: string; + roleId: string; + roleName: string; + expiresAt: string | null; + }; + unassignRole: { + userId: string; + roleId: string; + roleName: string; + }; + updateRole: { + roleId: string; + before: any; + after: any; + }; + deleteRole: { + roleId: string; + role: any; + }; + clearQueue: Record<string, never>; + promoteQueue: Record<string, never>; + deleteDriveFile: { + fileId: string; + fileUserId: string | null; + }; + deleteNote: { + noteId: string; + noteUserId: string; + note: any; + }; + createGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + createUserAnnouncement: { + announcementId: string; + announcement: any; + userId: string; + }; + updateGlobalAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + updateUserAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + deleteGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + deleteUserAnnouncement: { + announcementId: string; + announcement: any; + }; + resetPassword: { + targetId: string; + }; + suspendRemoteInstance: { + id: string; + host: string; + }; + unsuspendRemoteInstance: { + id: string; + host: string; + }; + markSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; + unmarkSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; +}; |