From 191c5f07a4407e5a684e471559ce265e669d9334 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Thu, 10 Aug 2023 17:21:56 +0800 Subject: [PATCH 01/15] feat: Hiding stack traces in production env --- packages/backend/src/core/CaptchaService.ts | 21 +++++---- .../src/core/CreateSystemUserService.ts | 3 +- .../backend/src/core/CustomEmojiService.ts | 3 +- packages/backend/src/core/DriveService.ts | 13 +++-- packages/backend/src/core/IdService.ts | 5 +- .../backend/src/core/NoteCreateService.ts | 10 ++-- packages/backend/src/core/PollService.ts | 15 +++--- packages/backend/src/core/RelayService.ts | 3 +- .../src/core/RemoteUserResolveService.ts | 15 +++--- packages/backend/src/core/SearchService.ts | 5 +- .../core/TwoFactorAuthenticationService.ts | 47 ++++++++++--------- .../backend/src/core/UserFollowingService.ts | 5 +- .../src/core/activitypub/ApRendererService.ts | 5 +- .../src/core/activitypub/ApResolverService.ts | 19 ++++---- .../core/activitypub/models/ApNoteService.ts | 37 ++++++++++----- packages/backend/src/error.ts | 7 +++ .../backend/src/server/api/GetterService.ts | 5 +- .../server/api/endpoints/admin/relays/add.ts | 3 +- .../server/api/endpoints/admin/show-user.ts | 5 +- .../server/api/endpoints/antennas/create.ts | 3 +- .../src/server/api/endpoints/i/2fa/done.ts | 5 +- .../server/api/endpoints/i/2fa/key-done.ts | 23 ++++----- .../server/api/endpoints/i/2fa/register.ts | 3 +- .../server/api/endpoints/i/2fa/remove-key.ts | 3 +- .../server/api/endpoints/i/2fa/unregister.ts | 3 +- .../server/api/endpoints/i/delete-account.ts | 3 +- .../src/server/api/endpoints/reset-db.ts | 3 +- .../server/api/endpoints/reset-password.ts | 1 + .../src/server/api/stream/ChannelsService.ts | 3 +- .../src/server/oauth/OAuth2ProviderService.ts | 3 +- .../src/server/web/ClientServerService.ts | 7 +-- .../src/server/web/UrlPreviewService.ts | 5 +- .../frontend/src/components/MkUrlPreview.vue | 3 +- .../src/components/MkYouTubePlayer.vue | 3 +- .../frontend/src/components/global/MkUrl.vue | 3 +- packages/frontend/src/error.ts | 7 +++ packages/frontend/src/pages/auth.vue | 3 +- packages/frontend/src/pages/follow.vue | 3 +- packages/frontend/src/pages/instance-info.vue | 9 ++-- packages/frontend/src/pages/miauth.vue | 3 +- .../frontend/src/pages/settings/general.vue | 3 +- .../pages/settings/preferences-backups.vue | 20 ++++---- packages/frontend/src/scripts/please-login.ts | 2 +- packages/frontend/src/scripts/time.ts | 4 +- packages/frontend/src/scripts/upload.ts | 3 +- 45 files changed, 217 insertions(+), 140 deletions(-) create mode 100644 packages/backend/src/error.ts create mode 100644 packages/frontend/src/error.ts diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index f64196f4fcae..12eb20466c88 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -6,6 +6,7 @@ import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; type CaptchaResponse = { success: boolean; @@ -35,7 +36,7 @@ export class CaptchaService { }, { throwErrorWhenResponseNotOk: false }); if (!res.ok) { - throw new Error(`${res.status}`); + throw ErrorHandling(`${res.status}`); } return await res.json() as CaptchaResponse; @@ -44,48 +45,48 @@ export class CaptchaService { @bindThis public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw new Error('recaptcha-failed: no response provided'); + throw ErrorHandling('recaptcha-failed: no response provided'); } const result = await this.getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(err => { - throw new Error(`recaptcha-request-failed: ${err}`); + throw ErrorHandling(`recaptcha-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw new Error(`recaptcha-failed: ${errorCodes}`); + throw ErrorHandling(`recaptcha-failed: ${errorCodes}`); } } @bindThis public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw new Error('hcaptcha-failed: no response provided'); + throw ErrorHandling('hcaptcha-failed: no response provided'); } const result = await this.getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(err => { - throw new Error(`hcaptcha-request-failed: ${err}`); + throw ErrorHandling(`hcaptcha-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw new Error(`hcaptcha-failed: ${errorCodes}`); + throw ErrorHandling(`hcaptcha-failed: ${errorCodes}`); } } @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw new Error('turnstile-failed: no response provided'); + throw ErrorHandling('turnstile-failed: no response provided'); } const result = await this.getCaptchaResponse('https://challenges.cloudflare.com/turnstile/v0/siteverify', secret, response).catch(err => { - throw new Error(`turnstile-request-failed: ${err}`); + throw ErrorHandling(`turnstile-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw new Error(`turnstile-failed: ${errorCodes}`); + throw ErrorHandling(`turnstile-failed: ${errorCodes}`); } } } diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 5eece8cd4657..5ba301d2d8c5 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -16,6 +16,7 @@ import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class CreateSystemUserService { @@ -49,7 +50,7 @@ export class CreateSystemUserService { host: IsNull(), }); - if (exist) throw new Error('the user is already exists'); + if (exist) throw ErrorHandling('the user is already exists'); account = await transactionalEntityManager.insert(User, { id: this.idService.genId(), diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index f24a880914df..478d7e416d3a 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -18,6 +18,7 @@ import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/server/api/stream/types.js'; +import { ErrorHandling } from '@/error.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -107,7 +108,7 @@ export class CustomEmojiService implements OnApplicationShutdown { }): Promise { 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'); + if (sameNameEmoji != null && sameNameEmoji.id !== id) throw ErrorHandling('name already exists'); await this.emojisRepository.update(emoji.id, { updatedAt: new Date(), diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 8c5c41ca0122..caaeb95445ff 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -42,6 +42,7 @@ 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 { ErrorHandling } from '@/error.js'; type AddFileArgs = { /** User who wish to add file */ @@ -535,7 +536,7 @@ export class DriveService { userId: user ? user.id : IsNull(), }); - if (driveFolder == null) throw new Error('folder-not-found'); + if (driveFolder == null) throw ErrorHandling('folder-not-found'); return driveFolder; }; @@ -750,9 +751,13 @@ export class DriveService { this.deleteLogger.warn(`The object storage had no such key to delete: ${key}. Skipping this.`, err as Error); return; } else { - throw new Error(`Failed to delete the file from the object storage with the given key: ${key}`, { - cause: err, - }); + const error = new Error(`Failed to delete the file from the object storage with the given key: ${key}`); + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: ''}); + Object.defineProperty(err, 'stack', { value: ''}); + } + error['cause'] = err; + throw error; } } } diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 186fd36b42ef..11da4c74d27b 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -13,6 +13,7 @@ import { genMeidg, parseMeidg } from '@/misc/id/meidg.js'; import { genObjectId, parseObjectId } from '@/misc/id/object-id.js'; import { bindThis } from '@/decorators.js'; import { parseUlid } from '@/misc/id/ulid.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class IdService { @@ -35,7 +36,7 @@ export class IdService { case 'meidg': return genMeidg(date); case 'ulid': return ulid(date.getTime()); case 'objectid': return genObjectId(date); - default: throw new Error('unrecognized id generation method'); + default: throw ErrorHandling('unrecognized id generation method'); } } @@ -47,7 +48,7 @@ export class IdService { case 'meid': return parseMeid(id); case 'meidg': return parseMeidg(id); case 'ulid': return parseUlid(id); - default: throw new Error('unrecognized id generation method'); + default: throw ErrorHandling('unrecognized id generation method'); } } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 001947322d5d..fa1604b1d88a 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -53,6 +53,7 @@ import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; +import { ErrorHandling } from '@/error.js'; const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -251,7 +252,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Renote対象が「ホームまたは全体」以外の公開範囲ならreject if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) { - throw new Error('Renote target is not public or home'); + throw ErrorHandling('Renote target is not public or home'); } // Renote対象がpublicではないならhomeにする @@ -316,7 +317,7 @@ export class NoteCreateService implements OnApplicationShutdown { } if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw new Error('invalid param'); + if (data.visibleUsers == null) throw ErrorHandling('invalid param'); for (const u of data.visibleUsers) { if (!mentionedUsers.some(x => x.id === u.id)) { @@ -436,6 +437,9 @@ export class NoteCreateService implements OnApplicationShutdown { if (isDuplicateKeyValueError(e)) { const err = new Error('Duplicated note'); err.name = 'duplicated'; + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(err, 'stack', { value: ''}); + } throw err; } @@ -525,7 +529,7 @@ export class NoteCreateService implements OnApplicationShutdown { // 未読通知を作成 if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw new Error('invalid param'); + if (data.visibleUsers == null) throw ErrorHandling('invalid param'); for (const u of data.visibleUsers) { // ローカルユーザーのみ diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index f317087b4175..d0327dcdbe81 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { bindThis } from '@/decorators.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class PollService { @@ -45,16 +46,16 @@ export class PollService { public async vote(user: User, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); - if (poll == null) throw new Error('poll not found'); + if (poll == null) throw ErrorHandling('poll not found'); // Check whether is valid choice - if (poll.choices[choice] == null) throw new Error('invalid choice param'); + if (poll.choices[choice] == null) throw ErrorHandling('invalid choice param'); // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); if (blocked) { - throw new Error('blocked'); + throw ErrorHandling('blocked'); } } @@ -66,10 +67,10 @@ export class PollService { if (poll.multiple) { if (exist.some(x => x.choice === choice)) { - throw new Error('already voted'); + throw ErrorHandling('already voted'); } } else if (exist.length !== 0) { - throw new Error('already voted'); + throw ErrorHandling('already voted'); } // Create vote @@ -94,10 +95,10 @@ export class PollService { @bindThis public async deliverQuestionUpdate(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); - if (note == null) throw new Error('note not found'); + if (note == null) throw ErrorHandling('note not found'); const user = await this.usersRepository.findOneBy({ id: note.userId }); - if (user == null) throw new Error('note not found'); + if (user == null) throw ErrorHandling('note not found'); if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 1ac906991bf9..13fc24092d0c 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -16,6 +16,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; const ACTOR_USERNAME = 'relay.actor' as const; @@ -74,7 +75,7 @@ export class RelayService { }); if (relay == null) { - throw new Error('relay not found'); + throw ErrorHandling('relay not found'); } const relayActor = await this.getRelayActor(); diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index 31682ea98d89..d4dd9c4ff9c3 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -18,6 +18,7 @@ import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class RemoteUserResolveService { @@ -47,7 +48,7 @@ export class RemoteUserResolveService { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { - throw new Error('user not found'); + throw ErrorHandling('user not found'); } else { return u; } @@ -60,7 +61,7 @@ export class RemoteUserResolveService { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { - throw new Error('user not found'); + throw ErrorHandling('user not found'); } else { return u; } @@ -82,7 +83,7 @@ export class RemoteUserResolveService { .getUserFromApId(self.href) .then((u) => { if (u == null) { - throw new Error('local user not found'); + throw ErrorHandling('local user not found'); } else { return u; } @@ -112,7 +113,7 @@ export class RemoteUserResolveService { // validate uri const uri = new URL(self.href); if (uri.hostname !== host) { - throw new Error('Invalid uri'); + throw ErrorHandling('Invalid uri'); } await this.usersRepository.update({ @@ -130,7 +131,7 @@ export class RemoteUserResolveService { this.logger.info(`return resynced remote user: ${acctLower}`); return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { if (u == null) { - throw new Error('user not found'); + throw ErrorHandling('user not found'); } else { return u as LocalUser | RemoteUser; } @@ -146,12 +147,12 @@ export class RemoteUserResolveService { this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await this.webfingerService.webfinger(acctLower).catch(err => { this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`); - throw new Error(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`); + throw ErrorHandling(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`); }); const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); if (!self) { this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); - throw new Error('self link not found'); + throw ErrorHandling('self link not found'); } return self; } diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 88b368dd2230..6b745a3152e0 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -15,6 +15,7 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { QueryService } from '@/core/QueryService.js'; import { IdService } from '@/core/IdService.js'; import type { Index, MeiliSearch } from 'meilisearch'; +import { ErrorHandling } from '@/error.js'; type K = string; type V = string | number | boolean; @@ -37,7 +38,7 @@ function compileValue(value: V): string { } else if (typeof value === 'boolean') { return value.toString(); } - throw new Error('unrecognized value'); + throw ErrorHandling('unrecognized value'); } function compileQuery(q: Q): string { @@ -51,7 +52,7 @@ function compileQuery(q: Q): string { 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 'not': return `(NOT ${compileQuery(q.q)})`; - default: throw new Error('unrecognized query operator'); + default: throw ErrorHandling('unrecognized query operator'); } } diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index ecf7676f4b8a..7329697bffee 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -9,6 +9,7 @@ import * as jsrsasign from 'jsrsasign'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); @@ -72,7 +73,7 @@ function verifyCertificateChain(certificates: string[]) { 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'); + if (certStruct == null) throw ErrorHandling('certStruct is null'); const algorithm = certificate.getSignatureAlgorithmField(); const signatureHex = certificate.getSignatureValueHex(); @@ -142,14 +143,14 @@ export class TwoFactorAuthenticationService { challenge: string }) { if (clientData.type !== 'webauthn.get') { - throw new Error('type is not webauthn.get'); + throw ErrorHandling('type is not webauthn.get'); } if (this.hash(clientData.challenge).toString('hex') !== challenge) { - throw new Error('challenge mismatch'); + throw ErrorHandling('challenge mismatch'); } if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw new Error('origin mismatch'); + throw ErrorHandling('origin mismatch'); } const verificationData = Buffer.concat( @@ -171,11 +172,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); + throw ErrorHandling('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'); + throw ErrorHandling('invalid or no -3 key given'); } const publicKeyU2F = Buffer.concat( @@ -206,7 +207,7 @@ export class TwoFactorAuthenticationService { credentialId: Buffer, }) { if (attStmt.alg !== -7) { - throw new Error('alg mismatch'); + throw ErrorHandling('alg mismatch'); } const verificationData = Buffer.concat([ @@ -219,11 +220,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); + throw ErrorHandling('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'); + throw ErrorHandling('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -232,7 +233,7 @@ export class TwoFactorAuthenticationService { ); if (!attCert.equals(publicKeyData)) { - throw new Error('public key mismatch'); + throw ErrorHandling('public key mismatch'); } const isValid = crypto @@ -278,7 +279,7 @@ export class TwoFactorAuthenticationService { const signature = jwsParts[2]; if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { - throw new Error('invalid nonce'); + throw ErrorHandling('invalid nonce'); } const certificateChain = header.x5c @@ -286,11 +287,11 @@ export class TwoFactorAuthenticationService { .concat([GSR2]); if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { - throw new Error('invalid common name'); + throw ErrorHandling('invalid common name'); } if (!verifyCertificateChain(certificateChain)) { - throw new Error('Invalid certificate chain!'); + throw ErrorHandling('Invalid certificate chain!'); } const signatureBase = Buffer.from( @@ -306,11 +307,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); + throw ErrorHandling('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'); + throw ErrorHandling('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -355,11 +356,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); + throw ErrorHandling('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'); + throw ErrorHandling('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -373,11 +374,11 @@ export class TwoFactorAuthenticationService { }; } 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'); + throw ErrorHandling('ECDAA-Verify is not supported'); } else { - if (attStmt.alg !== -7) throw new Error('alg mismatch'); + if (attStmt.alg !== -7) throw ErrorHandling('alg mismatch'); - throw new Error('self attestation is not supported'); + throw ErrorHandling('self attestation is not supported'); } }, }, @@ -400,7 +401,7 @@ export class TwoFactorAuthenticationService { }) { const x5c: Buffer[] = attStmt.x5c; if (x5c.length !== 1) { - throw new Error('x5c length does not match expectation'); + throw ErrorHandling('x5c length does not match expectation'); } const attCert = x5c[0]; @@ -410,11 +411,11 @@ export class TwoFactorAuthenticationService { const negTwo: Buffer = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); + throw ErrorHandling('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'); + throw ErrorHandling('invalid or no -3 key given'); } const publicKeyU2F = Buffer.concat( diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 8e356d19bb78..33aa1e44abd1 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -29,6 +29,7 @@ import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import Logger from '../logger.js'; +import { ErrorHandling } from '@/error.js'; const logger = new Logger('following/create'); @@ -459,8 +460,8 @@ export class UserFollowingService implements OnModuleInit { this.userBlockingService.checkBlocked(followee.id, follower.id), ]); - if (blocking) throw new Error('blocking'); - if (blocked) throw new Error('blocked'); + if (blocking) throw ErrorHandling('blocking'); + if (blocked) throw ErrorHandling('blocked'); const followRequest = await this.followRequestsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index f31d5f84e549..911a0207615d 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -30,6 +30,7 @@ import { isNotNull } from '@/misc/is-not-null.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class ApRendererService { @@ -98,7 +99,7 @@ export class ApRendererService { to = [`${attributedTo}/followers`]; cc = []; } else { - throw new Error('renderAnnounce: cannot render non-public note'); + throw ErrorHandling('renderAnnounce: cannot render non-public note'); } return { @@ -120,7 +121,7 @@ export class ApRendererService { @bindThis public renderBlock(block: Blocking): IBlock { if (block.blockee?.uri == null) { - throw new Error('renderBlock: missing blockee uri'); + throw ErrorHandling('renderBlock: missing blockee uri'); } return { diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 31bea744e2b2..ca85a3111b00 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -20,6 +20,7 @@ import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; +import { ErrorHandling } from '@/error.js'; export class Resolver { private history: Set; @@ -60,7 +61,7 @@ export class Resolver { if (isCollectionOrOrderedCollection(collection)) { return collection; } else { - throw new Error(`unrecognized collection type: ${collection.type}`); + throw ErrorHandling(`unrecognized collection type: ${collection.type}`); } } @@ -74,15 +75,15 @@ export class Resolver { // URLs with fragment parts cannot be resolved correctly because // the fragment part does not get transmitted over HTTP(S). // Avoid strange behaviour by not trying to resolve these at all. - throw new Error(`cannot resolve URL with fragment: ${value}`); + throw ErrorHandling(`cannot resolve URL with fragment: ${value}`); } if (this.history.has(value)) { - throw new Error('cannot resolve already resolved one'); + throw ErrorHandling('cannot resolve already resolved one'); } if (this.history.size > this.recursionLimit) { - throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); + throw ErrorHandling(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); } this.history.add(value); @@ -94,7 +95,7 @@ export class Resolver { const meta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(meta.blockedHosts, host)) { - throw new Error('Instance is blocked'); + throw ErrorHandling('Instance is blocked'); } if (this.config.signToActivityPubGet && !this.user) { @@ -110,7 +111,7 @@ export class Resolver { !(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') : object['@context'] !== 'https://www.w3.org/ns/activitystreams' ) { - throw new Error('invalid response'); + throw ErrorHandling('invalid response'); } return object; @@ -119,7 +120,7 @@ export class Resolver { @bindThis private resolveLocal(url: string): Promise { const parsed = this.apDbResolverService.parseUri(url); - if (!parsed.local) throw new Error('resolveLocal: not local'); + if (!parsed.local) throw ErrorHandling('resolveLocal: not local'); switch (parsed.type) { case 'notes': @@ -147,14 +148,14 @@ export class Resolver { this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null }))); case 'follows': // rest should be - if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI'); + if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw ErrorHandling('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))); default: - throw new Error(`resolveLocal: type ${parsed.type} unhandled`); + throw ErrorHandling(`resolveLocal: type ${parsed.type} unhandled`); } } } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 41d1bc48a7ca..fc06515ac5e3 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -37,6 +37,7 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class ApNoteService { @@ -80,16 +81,28 @@ export class ApNoteService { const expectHost = this.utilityService.extractDbHost(uri); if (!validPost.includes(getApType(object))) { - return new Error(`invalid Note: invalid object type ${getApType(object)}`); + const error = new Error(`invalid Note: invalid object type ${getApType(object)}`); + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: ''}); + } + return error } if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { - return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); + const error = new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: ''}); + } + return error } const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)); if (object.attributedTo && actualHost !== expectHost) { - return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); + const error = new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: ''}); + } + return error } return null; @@ -123,7 +136,7 @@ export class ApNoteService { value, object, }); - throw new Error('invalid note'); + throw ErrorHandling('invalid note'); } const note = object as IPost; @@ -131,27 +144,27 @@ export class ApNoteService { this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); if (note.id && !checkHttps(note.id)) { - throw new Error('unexpected schema of note.id: ' + note.id); + throw ErrorHandling('unexpected schema of note.id: ' + note.id); } const url = getOneApHrefNullable(note.url); if (url && !checkHttps(url)) { - throw new Error('unexpected schema of note url: ' + url); + throw ErrorHandling('unexpected schema of note url: ' + url); } this.logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ if (note.attributedTo == null) { - throw new Error('invalid note.attributedTo: ' + note.attributedTo); + throw ErrorHandling('invalid note.attributedTo: ' + note.attributedTo); } const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { - throw new Error('actor has been suspended'); + throw ErrorHandling('actor has been suspended'); } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); @@ -186,7 +199,7 @@ export class ApNoteService { .then(x => { if (x == null) { this.logger.warn('Specified inReplyTo, but not found'); - throw new Error('inReplyTo not found'); + throw ErrorHandling('inReplyTo not found'); } return x; @@ -223,7 +236,7 @@ export class ApNoteService { quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0); if (!quote) { if (results.some(x => x.status === 'temperror')) { - throw new Error('quote resolve failed'); + throw ErrorHandling('quote resolve failed'); } } } @@ -297,7 +310,7 @@ export class ApNoteService { 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'); + throw ErrorHandling('The note creation failed with duplication error even when there is no duplication'); } return duplicate; } @@ -376,7 +389,7 @@ export class ApNoteService { }); const emoji = await this.emojisRepository.findOneBy({ host, name }); - if (emoji == null) throw new Error('emoji update failed'); + if (emoji == null) throw ErrorHandling('emoji update failed'); return emoji; } diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts new file mode 100644 index 000000000000..5e85978e2698 --- /dev/null +++ b/packages/backend/src/error.ts @@ -0,0 +1,7 @@ +export function ErrorHandling(message: string): Error { + const error = new Error(message); + if (process.env.NODE_ENV === "production") { + error.stack = undefined; + } + return error; +} \ No newline at end of file diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index c16f2f504fb3..a619abc8239a 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -11,6 +11,7 @@ import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class GetterService { @@ -61,7 +62,7 @@ export class GetterService { const user = await this.getUser(userId); if (!this.userEntityService.isRemoteUser(user)) { - throw new Error('user is not a remote user'); + throw ErrorHandling('user is not a remote user'); } return user; @@ -75,7 +76,7 @@ export class GetterService { const user = await this.getUser(userId); if (!this.userEntityService.isLocalUser(user)) { - throw new Error('user is not a local user'); + throw ErrorHandling('user is not a local user'); } return user; 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 e420a42ab345..25ecf9901cea 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -8,6 +8,7 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; import { ApiError } from '../../../error.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { tags: ['admin'], @@ -67,7 +68,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { try { - if (new URL(ps.inbox).protocol !== 'https:') throw new Error('https only'); + if (new URL(ps.inbox).protocol !== 'https:') throw ErrorHandling('https only'); } catch { throw new ApiError(meta.errors.invalidUrl); } 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 1e8c38c96bd4..de060248408e 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { tags: ['admin'], @@ -53,7 +54,7 @@ export default class extends Endpoint { ]); if (user == null || profile == null) { - throw new Error('user not found'); + throw ErrorHandling('user not found'); } const isModerator = await this.roleService.isModerator(user); @@ -61,7 +62,7 @@ export default class extends Endpoint { const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { - throw new Error('cannot show info of admin'); + throw ErrorHandling('cannot show info of admin'); } const signins = await this.signinsRepository.findBy({ userId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 89245684409d..e82e3b78a549 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -12,6 +12,7 @@ import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { tags: ['antennas'], @@ -87,7 +88,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { if ((ps.keywords.length === 0) || ps.keywords[0].every(x => x === '')) { - throw new Error('invalid param'); + throw ErrorHandling('invalid param'); } const currentAntennasCount = await this.antennasRepository.countBy({ 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 d7ba21c25986..0cf0061487d0 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -10,6 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { requireCredential: true, @@ -41,7 +42,7 @@ export default class extends Endpoint { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); if (profile.twoFactorTempSecret == null) { - throw new Error('二段階認証の設定が開始されていません'); + throw ErrorHandling('二段階認証の設定が開始されていません'); } const delta = OTPAuth.TOTP.validate({ @@ -52,7 +53,7 @@ export default class extends Endpoint { }); if (delta === null) { - throw new Error('not verified'); + throw ErrorHandling('not verified'); } await this.userProfilesRepository.update(me.id, { 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 fd6e70a9dea1..07d9aff311f3 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 @@ -14,6 +14,7 @@ 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'; +import { ErrorHandling } from '@/error.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; @@ -64,20 +65,20 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw new Error('incorrect password'); + throw ErrorHandling('incorrect password'); } if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); + throw ErrorHandling('2fa not enabled'); } const clientData = JSON.parse(ps.clientDataJSON); if (clientData.type !== 'webauthn.create') { - throw new Error('not a creation attestation'); + throw ErrorHandling('not a creation attestation'); } if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw new Error('origin mismatch'); + throw ErrorHandling('origin mismatch'); } const clientDataJSONHash = this.twoFactorAuthenticationService.hash(Buffer.from(ps.clientDataJSON, 'utf-8')); @@ -86,14 +87,14 @@ export default class extends Endpoint { const rpIdHash = attestation.authData.slice(0, 32); if (!rpIdHashReal.equals(rpIdHash)) { - throw new Error('rpIdHash mismatch'); + throw ErrorHandling('rpIdHash mismatch'); } const flags = attestation.authData[32]; // eslint:disable-next-line:no-bitwise if (!(flags & 1)) { - throw new Error('user not present'); + throw ErrorHandling('user not present'); } const authData = Buffer.from(attestation.authData); @@ -102,13 +103,13 @@ export default class extends Endpoint { const publicKeyData = authData.slice(55 + credentialIdLength); const publicKey: Map = await cborDecodeFirst(publicKeyData); if (publicKey.get(3) !== -7) { - throw new Error('alg mismatch'); + throw ErrorHandling('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)}`); + throw ErrorHandling(`unsupported fmt: ${attestation.fmt}. Supported ones: ${Object.keys(procedures)}`); } const verificationData = (procedures as any)[attestation.fmt].verify({ @@ -119,7 +120,7 @@ export default class extends Endpoint { publicKey, rpIdHash, }); - if (!verificationData.valid) throw new Error('signature invalid'); + if (!verificationData.valid) throw ErrorHandling('signature invalid'); const attestationChallenge = await this.attestationChallengesRepository.findOneBy({ userId: me.id, @@ -129,7 +130,7 @@ export default class extends Endpoint { }); if (!attestationChallenge) { - throw new Error('non-existent challenge'); + throw ErrorHandling('non-existent challenge'); } await this.attestationChallengesRepository.delete({ @@ -142,7 +143,7 @@ export default class extends Endpoint { new Date().getTime() - attestationChallenge.createdAt.getTime() >= 5 * 60 * 1000 ) { - throw new Error('expired challenge'); + throw ErrorHandling('expired challenge'); } const credentialIdString = credentialId.toString('hex'); 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 0e57f07e5e8b..994dbc6f2ff6 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -11,6 +11,7 @@ import type { UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { requireCredential: true, @@ -43,7 +44,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw new Error('incorrect password'); + throw ErrorHandling('incorrect password'); } // 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 6402f3440f00..80a58d115825 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 @@ -10,6 +10,7 @@ import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/model import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { requireCredential: true, @@ -46,7 +47,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw new Error('incorrect password'); + throw ErrorHandling('incorrect password'); } // 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 c0e9ff5ec061..81e840e4b946 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -10,6 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { requireCredential: true, @@ -42,7 +43,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw new Error('incorrect password'); + throw ErrorHandling('incorrect password'); } await this.userProfilesRepository.update(me.id, { 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 721eb01c5147..3db056302db9 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -9,6 +9,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/index.js' import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { requireCredential: true, @@ -47,7 +48,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw new Error('incorrect password'); + throw ErrorHandling('incorrect password'); } await this.deleteAccountService.deleteAccount(me); diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index dcf3b8772a95..96c5c169b089 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -9,6 +9,7 @@ import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { resetDb } from '@/misc/reset-db.js'; +import { ErrorHandling } from '@/error.js'; export const meta = { tags: ['non-productive'], @@ -39,7 +40,7 @@ export default class extends Endpoint { private redisClient: Redis.Redis, ) { super(meta, paramDef, async (ps, me) => { - if (process.env.NODE_ENV !== 'test') throw new Error('NODE_ENV is not a test'); + if (process.env.NODE_ENV !== 'test') throw ErrorHandling('NODE_ENV is not a test'); await redisClient.flushdb(); await resetDb(this.db); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 4bbd3d0ef18d..187998ff05a2 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { ErrorHandling } from '@/error.js'; // TODO Line 51 export const meta = { tags: ['reset password'], diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 8fd106c10cf1..8bdd4f7c0874 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -19,6 +19,7 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class ChannelsService { @@ -59,7 +60,7 @@ export class ChannelsService { case 'admin': return this.adminChannelService; default: - throw new Error(`no such channel: ${name}`); + throw ErrorHandling(`no such channel: ${name}`); } } } diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 61c89a425875..2bb801b0389a 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -33,6 +33,7 @@ import Logger from '@/logger.js'; import { StatusError } from '@/misc/status-error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; +import { ErrorHandling } from '@/error.js'; // TODO: Consider migrating to @node-oauth/oauth2-server once // https://github.com/node-oauth/node-oauth2-server/issues/180 is figured out. @@ -370,7 +371,7 @@ export class OAuth2ProviderService { fastify.get('/oauth/authorize', async (request, reply) => { const oauth2 = (request.raw as MiddlewareRequest).oauth2; if (!oauth2) { - throw new Error('Unexpected lack of authorization information'); + throw ErrorHandling('Unexpected lack of authorization information'); } this.#logger.info(`Rendering authorization page for "${oauth2.client.name}"`); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index ebc1b72761aa..a5687c74f32a 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -42,6 +42,7 @@ import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; +import { ErrorHandling } from '@/error.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -147,17 +148,17 @@ export class ClientServerService { const token = request.cookies.token; if (token == null) { reply.code(401); - throw new Error('login required'); + throw ErrorHandling('login required'); } const user = await this.usersRepository.findOneBy({ token }); if (user == null) { reply.code(403); - throw new Error('no such user'); + throw ErrorHandling('no such user'); } const isAdministrator = await this.roleService.isAdministrator(user); if (!isAdministrator) { reply.code(403); - throw new Error('access denied'); + throw ErrorHandling('access denied'); } } }); diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index d590244e34a7..0db5c6de564b 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; import type { FastifyRequest, FastifyReply } from 'fastify'; +import { ErrorHandling } from '@/error.js'; @Injectable() export class UrlPreviewService { @@ -84,11 +85,11 @@ export class UrlPreviewService { this.logger.succ(`Got preview of ${url}: ${summary.title}`); if (!(summary.url.startsWith('http://') || summary.url.startsWith('https://'))) { - throw new Error('unsupported schema included'); + throw ErrorHandling('unsupported schema included'); } if (summary.player.url && !(summary.player.url.startsWith('http://') || summary.player.url.startsWith('https://'))) { - throw new Error('unsupported schema included'); + throw ErrorHandling('unsupported schema included'); } summary.icon = this.wrap(summary.icon); diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 1a194ae9db3c..cc2229a81999 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -85,6 +85,7 @@ import { deviceKind } from '@/scripts/device-kind'; import MkButton from '@/components/MkButton.vue'; import { versatileLang } from '@/scripts/intl-const'; import { defaultStore } from '@/store'; +import { ErrorHandling } from '@/error'; type SummalyResult = Awaited>; @@ -124,7 +125,7 @@ let tweetHeight = $ref(150); let unknownUrl = $ref(false); const requestUrl = new URL(props.url); -if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); +if (!['http:', 'https:'].includes(requestUrl.protocol)) throw ErrorHandling('invalid url'); if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') { const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue index 9b9c3d5ec4d1..64e715ea2011 100644 --- a/packages/frontend/src/components/MkYouTubePlayer.vue +++ b/packages/frontend/src/components/MkYouTubePlayer.vue @@ -27,13 +27,14 @@ SPDX-License-Identifier: AGPL-3.0-only import MkWindow from '@/components/MkWindow.vue'; import { versatileLang } from '@/scripts/intl-const'; import { defaultStore } from '@/store'; +import { ErrorHandling } from '@/error'; const props = defineProps<{ url: string; }>(); const requestUrl = new URL(props.url); -if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); +if (!['http:', 'https:'].includes(requestUrl.protocol)) throw ErrorHandling('invalid url'); let fetching = $ref(true); let title = $ref(null); diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue index 6bcfe3e1a5a1..0f76e436e071 100644 --- a/packages/frontend/src/components/global/MkUrl.vue +++ b/packages/frontend/src/components/global/MkUrl.vue @@ -30,6 +30,7 @@ import { url as local } from '@/config'; import * as os from '@/os'; import { useTooltip } from '@/scripts/use-tooltip'; import { safeURIDecode } from '@/scripts/safe-uri-decode'; +import { ErrorHandling } from '@/error'; const props = defineProps<{ url: string; @@ -38,7 +39,7 @@ const props = defineProps<{ const self = props.url.startsWith(local); const url = new URL(props.url); -if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url'); +if (!['http:', 'https:'].includes(url.protocol)) throw ErrorHandling('invalid url'); const el = ref(); useTooltip(el, (showing) => { diff --git a/packages/frontend/src/error.ts b/packages/frontend/src/error.ts new file mode 100644 index 000000000000..5e85978e2698 --- /dev/null +++ b/packages/frontend/src/error.ts @@ -0,0 +1,7 @@ +export function ErrorHandling(message: string): Error { + const error = new Error(message); + if (process.env.NODE_ENV === "production") { + error.stack = undefined; + } + return error; +} \ No newline at end of file diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index 2cc396fc2416..82a6eefe029c 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -50,6 +50,7 @@ import * as os from '@/os'; import { $i, login } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; +import { ErrorHandling } from '@/error'; const props = defineProps<{ token: string; @@ -62,7 +63,7 @@ function accepted() { state = 'accepted'; if (session && session.app.callbackUrl) { const url = new URL(session.app.callbackUrl); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw ErrorHandling('invalid url'); location.href = `${session.app.callbackUrl}?token=${session.token}`; } } diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue index de3b4a57aced..76c2157f912f 100644 --- a/packages/frontend/src/pages/follow.vue +++ b/packages/frontend/src/pages/follow.vue @@ -14,6 +14,7 @@ import * as Acct from 'misskey-js/built/acct'; import * as os from '@/os'; import { mainRouter } from '@/router'; import { i18n } from '@/i18n'; +import { ErrorHandling } from '@/error'; async function follow(user): Promise { const { canceled } = await os.confirm({ @@ -33,7 +34,7 @@ async function follow(user): Promise { const acct = new URL(location.href).searchParams.get('acct'); if (acct == null) { - throw new Error('acct required'); + throw ErrorHandling('acct required'); } let promise; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 24355c05562b..c68c21fa81df 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -136,6 +136,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy'; import { dateString } from '@/filters/date'; +import { ErrorHandling } from '@/error'; const props = defineProps<{ host: string; @@ -173,8 +174,8 @@ async function fetch(): Promise { } async function toggleBlock(): Promise { - if (!meta) throw new Error('No meta?'); - if (!instance) throw new Error('No instance?'); + if (!meta) throw ErrorHandling('No meta?'); + if (!instance) throw ErrorHandling('No instance?'); const { host } = instance; await os.api('admin/update-meta', { blockedHosts: isBlocked ? meta.blockedHosts.concat([host]) : meta.blockedHosts.filter(x => x !== host), @@ -182,7 +183,7 @@ async function toggleBlock(): Promise { } async function toggleSuspend(): Promise { - if (!instance) throw new Error('No instance?'); + if (!instance) throw ErrorHandling('No instance?'); await os.api('admin/federation/update-instance', { host: instance.host, isSuspended: suspended, @@ -190,7 +191,7 @@ async function toggleSuspend(): Promise { } function refreshMetadata(): void { - if (!instance) throw new Error('No instance?'); + if (!instance) throw ErrorHandling('No instance?'); os.api('admin/federation/refresh-remote-instance-metadata', { host: instance.host, }); diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 4df40db91747..0a392f50a6cc 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -50,6 +50,7 @@ import * as os from '@/os'; import { $i, login } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { ErrorHandling } from '@/error'; const props = defineProps<{ session: string; @@ -75,7 +76,7 @@ async function accept(): Promise { state = 'accepted'; if (props.callback) { const cbUrl = new URL(props.callback); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw ErrorHandling('invalid url'); cbUrl.searchParams.set('session', props.session); location.href = cbUrl.href; } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 3b39a5c00abc..859051c406c2 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -185,6 +185,7 @@ import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; +import { ErrorHandling } from '@/error'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -276,7 +277,7 @@ function downloadEmojiIndex(lang: string) { function download() { switch (lang) { case 'en-US': return import('../../unicode-emoji-indexes/en-US.json').then(x => x.default); - default: throw new Error('unrecognized lang: ' + lang); + default: throw ErrorHandling('unrecognized lang: ' + lang); } } currentIndexes[lang] = await download(); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index eb33cbd3a4c3..fb5d70af8f24 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -51,6 +51,8 @@ import { i18n } from '@/i18n'; import { version, host } from '@/config'; import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; +import { ErrorHandling } from '@/error'; + const { t, ts } = i18n; const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ @@ -133,27 +135,27 @@ function isObject(value: unknown): value is Record { } function validate(profile: any): void { - if (!isObject(profile)) throw new Error('not an object'); + if (!isObject(profile)) throw ErrorHandling('not an object'); // Check if unnecessary properties exist - if (Object.keys(profile).some(key => !profileProps.includes(key))) throw new Error('Unnecessary properties exist'); + if (Object.keys(profile).some(key => !profileProps.includes(key))) throw ErrorHandling('Unnecessary properties exist'); - if (!profile.name) throw new Error('Missing required prop: name'); - if (!profile.misskeyVersion) throw new Error('Missing required prop: misskeyVersion'); + if (!profile.name) throw ErrorHandling('Missing required prop: name'); + if (!profile.misskeyVersion) throw ErrorHandling('Missing required prop: misskeyVersion'); // Check if createdAt and updatedAt is Date // https://zenn.dev/lollipop_onl/articles/eoz-judge-js-invalid-date - if (!profile.createdAt || Number.isNaN(new Date(profile.createdAt as any).getTime())) throw new Error('createdAt is falsy or not Date'); + if (!profile.createdAt || Number.isNaN(new Date(profile.createdAt as any).getTime())) throw ErrorHandling('createdAt is falsy or not Date'); if (profile.updatedAt) { if (Number.isNaN(new Date(profile.updatedAt as any).getTime())) { - throw new Error('updatedAt is not Date'); + throw ErrorHandling('updatedAt is not Date'); } } else if (profile.updatedAt !== null) { - throw new Error('updatedAt is not null'); + throw ErrorHandling('updatedAt is not null'); } - if (!profile.settings) throw new Error('Missing required prop: settings'); - if (!isObject(profile.settings)) throw new Error('Invalid prop: settings'); + if (!profile.settings) throw ErrorHandling('Missing required prop: settings'); + if (!isObject(profile.settings)) throw ErrorHandling('Invalid prop: settings'); } function getSettings(): Profile['settings'] { diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts index f0eebf22426d..7b63f775ac36 100644 --- a/packages/frontend/src/scripts/please-login.ts +++ b/packages/frontend/src/scripts/please-login.ts @@ -22,5 +22,5 @@ export function pleaseLogin(path?: string) { }, }, 'closed'); - throw new Error('signin required'); + console.log('signin required'); } diff --git a/packages/frontend/src/scripts/time.ts b/packages/frontend/src/scripts/time.ts index 4479db10818f..506c7a9da706 100644 --- a/packages/frontend/src/scripts/time.ts +++ b/packages/frontend/src/scripts/time.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ErrorHandling } from '@/error'; + const dateTimeIntervals = { 'day': 86400000, 'hour': 3600000, @@ -19,7 +21,7 @@ export function dateUTC(time: number[]): Date { : time.length === 7 ? Date.UTC(time[0], time[1], time[2], time[3], time[4], time[5], time[6]) : null; - if (!d) throw new Error('wrong number of arguments'); + if (!d) throw ErrorHandling('wrong number of arguments'); return new Date(d); } diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index f5ec4b60b4a5..cb7ebeaffa4c 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -12,6 +12,7 @@ import { apiUrl } from '@/config'; import { $i } from '@/account'; import { alert } from '@/os'; import { i18n } from '@/i18n'; +import { ErrorHandling } from '@/error'; type Uploading = { id: string; @@ -34,7 +35,7 @@ export function uploadFile( name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading, ): Promise { - if ($i == null) throw new Error('Not logged in'); + if ($i == null) console.log('Not logged in'); if (folder && typeof folder === 'object') folder = folder.id; From 6feda8469a7dee4b3999fb7e3d3c4a69c33fa0b1 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Thu, 10 Aug 2023 18:03:18 +0800 Subject: [PATCH 02/15] sytle --- packages/backend/src/core/DriveService.ts | 12 ++++++------ packages/backend/src/core/NoteCreateService.ts | 2 +- .../src/core/activitypub/models/ApNoteService.ts | 12 ++++++------ packages/backend/src/error.ts | 2 +- packages/frontend/src/error.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index caaeb95445ff..69340731d408 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -752,12 +752,12 @@ export class DriveService { return; } else { const error = new Error(`Failed to delete the file from the object storage with the given key: ${key}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: ''}); - Object.defineProperty(err, 'stack', { value: ''}); - } - error['cause'] = err; - throw error; + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: '' }); + Object.defineProperty(err, 'stack', { value: '' }); + } + error['cause'] = err; + throw error; } } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index fa1604b1d88a..596cf3853a0a 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -438,7 +438,7 @@ export class NoteCreateService implements OnApplicationShutdown { const err = new Error('Duplicated note'); err.name = 'duplicated'; if (process.env.NODE_ENV === 'production') { - Object.defineProperty(err, 'stack', { value: ''}); + Object.defineProperty(err, 'stack', { value: '' }); } throw err; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index fc06515ac5e3..3b5b04fe5e16 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -83,26 +83,26 @@ export class ApNoteService { if (!validPost.includes(getApType(object))) { const error = new Error(`invalid Note: invalid object type ${getApType(object)}`); if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: ''}); + Object.defineProperty(error, 'stack', { value: '' }); } - return error + return error; } if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { const error = new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: ''}); + Object.defineProperty(error, 'stack', { value: '' }); } - return error + return error; } const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)); if (object.attributedTo && actualHost !== expectHost) { const error = new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: ''}); + Object.defineProperty(error, 'stack', { value: '' }); } - return error + return error; } return null; diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts index 5e85978e2698..929554708497 100644 --- a/packages/backend/src/error.ts +++ b/packages/backend/src/error.ts @@ -4,4 +4,4 @@ export function ErrorHandling(message: string): Error { error.stack = undefined; } return error; -} \ No newline at end of file +} diff --git a/packages/frontend/src/error.ts b/packages/frontend/src/error.ts index 5e85978e2698..929554708497 100644 --- a/packages/frontend/src/error.ts +++ b/packages/frontend/src/error.ts @@ -4,4 +4,4 @@ export function ErrorHandling(message: string): Error { error.stack = undefined; } return error; -} \ No newline at end of file +} From 0ddbeab9c17b69136bcf5a869309f375c5c5ab73 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Thu, 10 Aug 2023 18:13:48 +0800 Subject: [PATCH 03/15] style --- packages/backend/src/core/DriveService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 69340731d408..1b05dd651a10 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -752,12 +752,12 @@ export class DriveService { return; } else { const error = new Error(`Failed to delete the file from the object storage with the given key: ${key}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: '' }); + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(error, 'stack', { value: '' }); Object.defineProperty(err, 'stack', { value: '' }); - } - error['cause'] = err; - throw error; + } + error['cause'] = err; + throw error; } } } From 5428019061536c89f35028adc23cf8729781e49e Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Fri, 11 Aug 2023 00:17:23 +0800 Subject: [PATCH 04/15] style --- packages/backend/src/error.ts | 10 +++++----- packages/frontend/src/error.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts index 929554708497..865d276496e7 100644 --- a/packages/backend/src/error.ts +++ b/packages/backend/src/error.ts @@ -1,7 +1,7 @@ export function ErrorHandling(message: string): Error { - const error = new Error(message); - if (process.env.NODE_ENV === "production") { - error.stack = undefined; - } - return error; + const error = new Error(message); + if (process.env.NODE_ENV === 'production') { + error.stack = undefined; + } + return error; } diff --git a/packages/frontend/src/error.ts b/packages/frontend/src/error.ts index 929554708497..865d276496e7 100644 --- a/packages/frontend/src/error.ts +++ b/packages/frontend/src/error.ts @@ -1,7 +1,7 @@ export function ErrorHandling(message: string): Error { - const error = new Error(message); - if (process.env.NODE_ENV === "production") { - error.stack = undefined; - } - return error; + const error = new Error(message); + if (process.env.NODE_ENV === 'production') { + error.stack = undefined; + } + return error; } From f82dfdfbd26286c87beb33b5a1e41f636480945b Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 11 Aug 2023 10:54:15 +0000 Subject: [PATCH 05/15] add SPDX --- packages/backend/src/error.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts index 865d276496e7..000c4862fc57 100644 --- a/packages/backend/src/error.ts +++ b/packages/backend/src/error.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: MomentQYC and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export function ErrorHandling(message: string): Error { const error = new Error(message); if (process.env.NODE_ENV === 'production') { From 6b47cd7b941726109c14f24527dc140da7659262 Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 11 Aug 2023 10:57:58 +0000 Subject: [PATCH 06/15] move ./error.js to ./misc/error.js --- packages/backend/src/core/CaptchaService.ts | 2 +- packages/backend/src/core/CreateSystemUserService.ts | 2 +- packages/backend/src/core/CustomEmojiService.ts | 2 +- packages/backend/src/core/DriveService.ts | 2 +- packages/backend/src/core/IdService.ts | 2 +- packages/backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/PollService.ts | 2 +- packages/backend/src/core/RelayService.ts | 2 +- .../backend/src/core/RemoteUserResolveService.ts | 2 +- packages/backend/src/core/SearchService.ts | 2 +- .../src/core/TwoFactorAuthenticationService.ts | 2 +- packages/backend/src/core/UserFollowingService.ts | 2 +- .../src/core/activitypub/ApRendererService.ts | 2 +- .../src/core/activitypub/ApResolverService.ts | 2 +- .../src/core/activitypub/models/ApNoteService.ts | 2 +- packages/backend/src/error.ts | 10 ++-------- packages/backend/src/misc/error.ts | 12 ++++++++++++ packages/backend/src/server/api/GetterService.ts | 2 +- .../src/server/api/endpoints/admin/relays/add.ts | 2 +- .../src/server/api/endpoints/admin/show-user.ts | 2 +- .../src/server/api/endpoints/antennas/create.ts | 2 +- .../backend/src/server/api/endpoints/i/2fa/done.ts | 2 +- .../src/server/api/endpoints/i/2fa/key-done.ts | 2 +- .../src/server/api/endpoints/i/2fa/register.ts | 2 +- .../src/server/api/endpoints/i/2fa/remove-key.ts | 2 +- .../src/server/api/endpoints/i/2fa/unregister.ts | 2 +- .../src/server/api/endpoints/i/delete-account.ts | 2 +- .../backend/src/server/api/endpoints/reset-db.ts | 2 +- .../src/server/api/endpoints/reset-password.ts | 2 +- .../backend/src/server/api/stream/ChannelsService.ts | 2 +- .../src/server/oauth/OAuth2ProviderService.ts | 2 +- .../backend/src/server/web/ClientServerService.ts | 2 +- packages/backend/src/server/web/UrlPreviewService.ts | 2 +- 33 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 packages/backend/src/misc/error.ts diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index 12eb20466c88..8b9f7dbc8afc 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -6,7 +6,7 @@ import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; type CaptchaResponse = { success: boolean; diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 5ba301d2d8c5..5f768e9ee205 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -16,7 +16,7 @@ import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class CreateSystemUserService { diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 478d7e416d3a..9c2957eb3fd5 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -18,7 +18,7 @@ import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/server/api/stream/types.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 1b05dd651a10..ad4be4157c9d 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -42,7 +42,7 @@ 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 { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; type AddFileArgs = { /** User who wish to add file */ diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 11da4c74d27b..cd36b154e557 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -13,7 +13,7 @@ import { genMeidg, parseMeidg } from '@/misc/id/meidg.js'; import { genObjectId, parseObjectId } from '@/misc/id/object-id.js'; import { bindThis } from '@/decorators.js'; import { parseUlid } from '@/misc/id/ulid.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class IdService { diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 596cf3853a0a..3c57143aab4f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -53,7 +53,7 @@ import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index d0327dcdbe81..ff5e4973318b 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -15,7 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { bindThis } from '@/decorators.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class PollService { diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 13fc24092d0c..069893d6bfc9 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -16,7 +16,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const ACTOR_USERNAME = 'relay.actor' as const; diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index d4dd9c4ff9c3..fb2655ceed7e 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -18,7 +18,7 @@ import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class RemoteUserResolveService { diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 6b745a3152e0..9e282a196cda 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -15,7 +15,7 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { QueryService } from '@/core/QueryService.js'; import { IdService } from '@/core/IdService.js'; import type { Index, MeiliSearch } from 'meilisearch'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; type K = string; type V = string | number | boolean; diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 7329697bffee..2943aa1dc49c 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -9,7 +9,7 @@ import * as jsrsasign from 'jsrsasign'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 33aa1e44abd1..afd096d02500 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -29,7 +29,7 @@ import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import Logger from '../logger.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const logger = new Logger('following/create'); diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 911a0207615d..bed26e90f413 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -30,7 +30,7 @@ import { isNotNull } from '@/misc/is-not-null.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ApRendererService { diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index ca85a3111b00..623dd289e03e 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -20,7 +20,7 @@ import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export class Resolver { private history: Set; diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 3b5b04fe5e16..d1fdb87946ed 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -37,7 +37,7 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ApNoteService { diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts index 000c4862fc57..b9061476279e 100644 --- a/packages/backend/src/error.ts +++ b/packages/backend/src/error.ts @@ -1,12 +1,6 @@ /* - * SPDX-FileCopyrightText: MomentQYC and other misskey contributors + * SPDX-FileCopyrightText: tamaina and other misskey contributors * SPDX-License-Identifier: AGPL-3.0-only */ -export function ErrorHandling(message: string): Error { - const error = new Error(message); - if (process.env.NODE_ENV === 'production') { - error.stack = undefined; - } - return error; -} +// Never use `./error.js` because jest can't use it. diff --git a/packages/backend/src/misc/error.ts b/packages/backend/src/misc/error.ts new file mode 100644 index 000000000000..000c4862fc57 --- /dev/null +++ b/packages/backend/src/misc/error.ts @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: MomentQYC and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function ErrorHandling(message: string): Error { + const error = new Error(message); + if (process.env.NODE_ENV === 'production') { + error.stack = undefined; + } + return error; +} diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index a619abc8239a..052a9da6fa3d 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -11,7 +11,7 @@ import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class GetterService { 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 25ecf9901cea..571ad4810255 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -8,7 +8,7 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; import { ApiError } from '../../../error.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['admin'], 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 de060248408e..37548fc10bce 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -9,7 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index e82e3b78a549..5f4e0982f52b 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -12,7 +12,7 @@ import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['antennas'], 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 0cf0061487d0..256ff29f0f2b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -10,7 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, 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 07d9aff311f3..8c585f8328be 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 @@ -14,7 +14,7 @@ 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'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; 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 994dbc6f2ff6..217230cdc15b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -11,7 +11,7 @@ import type { UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, 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 80a58d115825..86b0ad9e355e 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 @@ -10,7 +10,7 @@ import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/model import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, 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 81e840e4b946..4c1a6b136686 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -10,7 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, 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 3db056302db9..afc28e509c4d 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -9,7 +9,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/index.js' import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 96c5c169b089..242c38c278e5 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -9,7 +9,7 @@ import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { resetDb } from '@/misc/reset-db.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['non-productive'], diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 187998ff05a2..17569103c5d4 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -8,7 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/error.js'; // TODO Line 51 +import { ErrorHandling } from '@/misc/error.js'; // TODO Line 51 export const meta = { tags: ['reset password'], diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 8bdd4f7c0874..4b9c71878a9d 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -19,7 +19,7 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ChannelsService { diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 2bb801b0389a..9f8e8a3eb190 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -33,7 +33,7 @@ import Logger from '@/logger.js'; import { StatusError } from '@/misc/status-error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; // TODO: Consider migrating to @node-oauth/oauth2-server once // https://github.com/node-oauth/node-oauth2-server/issues/180 is figured out. diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index a5687c74f32a..5ed12000a082 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -42,7 +42,7 @@ import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 0db5c6de564b..604bb9e97b23 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -15,7 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; import type { FastifyRequest, FastifyReply } from 'fastify'; -import { ErrorHandling } from '@/error.js'; +import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class UrlPreviewService { From fed33b92e27f61d576c683b3ddfb82553a8d01ff Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Fri, 11 Aug 2023 21:54:24 +0800 Subject: [PATCH 07/15] revert: remove frontend changes --- .../frontend/src/components/MkUrlPreview.vue | 3 +-- .../src/components/MkYouTubePlayer.vue | 3 +-- .../frontend/src/components/global/MkUrl.vue | 3 +-- packages/frontend/src/error.ts | 7 ------- packages/frontend/src/pages/auth.vue | 3 +-- packages/frontend/src/pages/follow.vue | 3 +-- packages/frontend/src/pages/instance-info.vue | 9 ++++----- packages/frontend/src/pages/miauth.vue | 3 +-- .../frontend/src/pages/settings/general.vue | 3 +-- .../pages/settings/preferences-backups.vue | 20 +++++++++---------- packages/frontend/src/scripts/please-login.ts | 2 +- packages/frontend/src/scripts/time.ts | 4 +--- packages/frontend/src/scripts/upload.ts | 3 +-- 13 files changed, 23 insertions(+), 43 deletions(-) delete mode 100644 packages/frontend/src/error.ts diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index cc2229a81999..1a194ae9db3c 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -85,7 +85,6 @@ import { deviceKind } from '@/scripts/device-kind'; import MkButton from '@/components/MkButton.vue'; import { versatileLang } from '@/scripts/intl-const'; import { defaultStore } from '@/store'; -import { ErrorHandling } from '@/error'; type SummalyResult = Awaited>; @@ -125,7 +124,7 @@ let tweetHeight = $ref(150); let unknownUrl = $ref(false); const requestUrl = new URL(props.url); -if (!['http:', 'https:'].includes(requestUrl.protocol)) throw ErrorHandling('invalid url'); +if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') { const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue index 64e715ea2011..9b9c3d5ec4d1 100644 --- a/packages/frontend/src/components/MkYouTubePlayer.vue +++ b/packages/frontend/src/components/MkYouTubePlayer.vue @@ -27,14 +27,13 @@ SPDX-License-Identifier: AGPL-3.0-only import MkWindow from '@/components/MkWindow.vue'; import { versatileLang } from '@/scripts/intl-const'; import { defaultStore } from '@/store'; -import { ErrorHandling } from '@/error'; const props = defineProps<{ url: string; }>(); const requestUrl = new URL(props.url); -if (!['http:', 'https:'].includes(requestUrl.protocol)) throw ErrorHandling('invalid url'); +if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); let fetching = $ref(true); let title = $ref(null); diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue index 0f76e436e071..6bcfe3e1a5a1 100644 --- a/packages/frontend/src/components/global/MkUrl.vue +++ b/packages/frontend/src/components/global/MkUrl.vue @@ -30,7 +30,6 @@ import { url as local } from '@/config'; import * as os from '@/os'; import { useTooltip } from '@/scripts/use-tooltip'; import { safeURIDecode } from '@/scripts/safe-uri-decode'; -import { ErrorHandling } from '@/error'; const props = defineProps<{ url: string; @@ -39,7 +38,7 @@ const props = defineProps<{ const self = props.url.startsWith(local); const url = new URL(props.url); -if (!['http:', 'https:'].includes(url.protocol)) throw ErrorHandling('invalid url'); +if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url'); const el = ref(); useTooltip(el, (showing) => { diff --git a/packages/frontend/src/error.ts b/packages/frontend/src/error.ts deleted file mode 100644 index 865d276496e7..000000000000 --- a/packages/frontend/src/error.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function ErrorHandling(message: string): Error { - const error = new Error(message); - if (process.env.NODE_ENV === 'production') { - error.stack = undefined; - } - return error; -} diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index 82a6eefe029c..2cc396fc2416 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -50,7 +50,6 @@ import * as os from '@/os'; import { $i, login } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; -import { ErrorHandling } from '@/error'; const props = defineProps<{ token: string; @@ -63,7 +62,7 @@ function accepted() { state = 'accepted'; if (session && session.app.callbackUrl) { const url = new URL(session.app.callbackUrl); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw ErrorHandling('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); location.href = `${session.app.callbackUrl}?token=${session.token}`; } } diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue index 76c2157f912f..de3b4a57aced 100644 --- a/packages/frontend/src/pages/follow.vue +++ b/packages/frontend/src/pages/follow.vue @@ -14,7 +14,6 @@ import * as Acct from 'misskey-js/built/acct'; import * as os from '@/os'; import { mainRouter } from '@/router'; import { i18n } from '@/i18n'; -import { ErrorHandling } from '@/error'; async function follow(user): Promise { const { canceled } = await os.confirm({ @@ -34,7 +33,7 @@ async function follow(user): Promise { const acct = new URL(location.href).searchParams.get('acct'); if (acct == null) { - throw ErrorHandling('acct required'); + throw new Error('acct required'); } let promise; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index c68c21fa81df..24355c05562b 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -136,7 +136,6 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy'; import { dateString } from '@/filters/date'; -import { ErrorHandling } from '@/error'; const props = defineProps<{ host: string; @@ -174,8 +173,8 @@ async function fetch(): Promise { } async function toggleBlock(): Promise { - if (!meta) throw ErrorHandling('No meta?'); - if (!instance) throw ErrorHandling('No instance?'); + if (!meta) throw new Error('No meta?'); + if (!instance) throw new Error('No instance?'); const { host } = instance; await os.api('admin/update-meta', { blockedHosts: isBlocked ? meta.blockedHosts.concat([host]) : meta.blockedHosts.filter(x => x !== host), @@ -183,7 +182,7 @@ async function toggleBlock(): Promise { } async function toggleSuspend(): Promise { - if (!instance) throw ErrorHandling('No instance?'); + if (!instance) throw new Error('No instance?'); await os.api('admin/federation/update-instance', { host: instance.host, isSuspended: suspended, @@ -191,7 +190,7 @@ async function toggleSuspend(): Promise { } function refreshMetadata(): void { - if (!instance) throw ErrorHandling('No instance?'); + if (!instance) throw new Error('No instance?'); os.api('admin/federation/refresh-remote-instance-metadata', { host: instance.host, }); diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 0a392f50a6cc..4df40db91747 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -50,7 +50,6 @@ import * as os from '@/os'; import { $i, login } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { ErrorHandling } from '@/error'; const props = defineProps<{ session: string; @@ -76,7 +75,7 @@ async function accept(): Promise { state = 'accepted'; if (props.callback) { const cbUrl = new URL(props.callback); - if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw ErrorHandling('invalid url'); + if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url'); cbUrl.searchParams.set('session', props.session); location.href = cbUrl.href; } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 859051c406c2..3b39a5c00abc 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -185,7 +185,6 @@ import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; -import { ErrorHandling } from '@/error'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -277,7 +276,7 @@ function downloadEmojiIndex(lang: string) { function download() { switch (lang) { case 'en-US': return import('../../unicode-emoji-indexes/en-US.json').then(x => x.default); - default: throw ErrorHandling('unrecognized lang: ' + lang); + default: throw new Error('unrecognized lang: ' + lang); } } currentIndexes[lang] = await download(); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index fb5d70af8f24..eb33cbd3a4c3 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -51,8 +51,6 @@ import { i18n } from '@/i18n'; import { version, host } from '@/config'; import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; -import { ErrorHandling } from '@/error'; - const { t, ts } = i18n; const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ @@ -135,27 +133,27 @@ function isObject(value: unknown): value is Record { } function validate(profile: any): void { - if (!isObject(profile)) throw ErrorHandling('not an object'); + if (!isObject(profile)) throw new Error('not an object'); // Check if unnecessary properties exist - if (Object.keys(profile).some(key => !profileProps.includes(key))) throw ErrorHandling('Unnecessary properties exist'); + if (Object.keys(profile).some(key => !profileProps.includes(key))) throw new Error('Unnecessary properties exist'); - if (!profile.name) throw ErrorHandling('Missing required prop: name'); - if (!profile.misskeyVersion) throw ErrorHandling('Missing required prop: misskeyVersion'); + if (!profile.name) throw new Error('Missing required prop: name'); + if (!profile.misskeyVersion) throw new Error('Missing required prop: misskeyVersion'); // Check if createdAt and updatedAt is Date // https://zenn.dev/lollipop_onl/articles/eoz-judge-js-invalid-date - if (!profile.createdAt || Number.isNaN(new Date(profile.createdAt as any).getTime())) throw ErrorHandling('createdAt is falsy or not Date'); + if (!profile.createdAt || Number.isNaN(new Date(profile.createdAt as any).getTime())) throw new Error('createdAt is falsy or not Date'); if (profile.updatedAt) { if (Number.isNaN(new Date(profile.updatedAt as any).getTime())) { - throw ErrorHandling('updatedAt is not Date'); + throw new Error('updatedAt is not Date'); } } else if (profile.updatedAt !== null) { - throw ErrorHandling('updatedAt is not null'); + throw new Error('updatedAt is not null'); } - if (!profile.settings) throw ErrorHandling('Missing required prop: settings'); - if (!isObject(profile.settings)) throw ErrorHandling('Invalid prop: settings'); + if (!profile.settings) throw new Error('Missing required prop: settings'); + if (!isObject(profile.settings)) throw new Error('Invalid prop: settings'); } function getSettings(): Profile['settings'] { diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts index 7b63f775ac36..f0eebf22426d 100644 --- a/packages/frontend/src/scripts/please-login.ts +++ b/packages/frontend/src/scripts/please-login.ts @@ -22,5 +22,5 @@ export function pleaseLogin(path?: string) { }, }, 'closed'); - console.log('signin required'); + throw new Error('signin required'); } diff --git a/packages/frontend/src/scripts/time.ts b/packages/frontend/src/scripts/time.ts index 506c7a9da706..4479db10818f 100644 --- a/packages/frontend/src/scripts/time.ts +++ b/packages/frontend/src/scripts/time.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ErrorHandling } from '@/error'; - const dateTimeIntervals = { 'day': 86400000, 'hour': 3600000, @@ -21,7 +19,7 @@ export function dateUTC(time: number[]): Date { : time.length === 7 ? Date.UTC(time[0], time[1], time[2], time[3], time[4], time[5], time[6]) : null; - if (!d) throw ErrorHandling('wrong number of arguments'); + if (!d) throw new Error('wrong number of arguments'); return new Date(d); } diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index cb7ebeaffa4c..f5ec4b60b4a5 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -12,7 +12,6 @@ import { apiUrl } from '@/config'; import { $i } from '@/account'; import { alert } from '@/os'; import { i18n } from '@/i18n'; -import { ErrorHandling } from '@/error'; type Uploading = { id: string; @@ -35,7 +34,7 @@ export function uploadFile( name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading, ): Promise { - if ($i == null) console.log('Not logged in'); + if ($i == null) throw new Error('Not logged in'); if (folder && typeof folder === 'object') folder = folder.id; From 8fc0a3b5908127a25396c38ccca13eefa2d05ff4 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Sun, 13 Aug 2023 19:25:58 +0800 Subject: [PATCH 08/15] feat: Hiding stack traces in production env --- packages/backend/src/misc/fastify-reply-error.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/src/misc/fastify-reply-error.ts b/packages/backend/src/misc/fastify-reply-error.ts index 7c889bab7a00..7f18cec8d547 100644 --- a/packages/backend/src/misc/fastify-reply-error.ts +++ b/packages/backend/src/misc/fastify-reply-error.ts @@ -12,5 +12,9 @@ export class FastifyReplyError extends Error { super(message); this.message = message; this.statusCode = statusCode; + + if (process.env.NODE_ENV === 'production') { + Object.defineProperty(this, 'stack', { value: '' }); + } } } From 34b2f2f7480e045d0ff4f534b1af2b1e4151eeac Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 00:42:47 +0800 Subject: [PATCH 09/15] feat: Hiding stack traces in production env --- packages/backend/src/misc/error.ts | 10 +++++++++- .../src/server/ActivityPubServerService.ts | 3 +++ packages/backend/src/server/FileServerService.ts | 2 ++ .../backend/src/server/NodeinfoServerService.ts | 3 +++ packages/backend/src/server/ServerService.ts | 2 ++ .../backend/src/server/WellKnownServerService.ts | 3 +++ .../backend/src/server/api/ApiServerService.ts | 3 +++ .../server/api/openapi/OpenApiServerService.ts | 2 ++ .../src/server/oauth/OAuth2ProviderService.ts | 5 ++++- .../src/server/web/ClientServerService.ts | 16 ++++++++-------- 10 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/misc/error.ts b/packages/backend/src/misc/error.ts index 000c4862fc57..2ea56dd96c49 100644 --- a/packages/backend/src/misc/error.ts +++ b/packages/backend/src/misc/error.ts @@ -2,11 +2,19 @@ * SPDX-FileCopyrightText: MomentQYC and other misskey contributors * SPDX-License-Identifier: AGPL-3.0-only */ +import { FastifyReply, FastifyRequest } from 'fastify'; -export function ErrorHandling(message: string): Error { +export function ErrorHandling(message: string, reply?: FastifyReply, statusCode?: number): Error { const error = new Error(message); if (process.env.NODE_ENV === 'production') { error.stack = undefined; } + if (reply) { + reply.code(statusCode ?? 500); + } return error; } + +export function ErrorHandler(error: Error, request: FastifyRequest, reply: FastifyReply): void { + throw ErrorHandling(error.message, reply); +} diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index c9ce1dcaee6a..6ad18768502d 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -26,6 +26,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import { IActivity } from '@/core/activitypub/type.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; @@ -462,6 +463,8 @@ export class ActivityPubServerService { fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); + fastify.setErrorHandler(ErrorHandler); + fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index c52ce4568e9d..036d746f37eb 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { correctFilename } from '@/misc/correct-filename.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; const _filename = fileURLToPath(import.meta.url); @@ -59,6 +60,7 @@ export class FileServerService { @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { + fastify.setErrorHandler(ErrorHandler); fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); done(); diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 95cc697768ea..7fbb11a89ec4 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -14,6 +14,7 @@ import { bindThis } from '@/decorators.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; @@ -118,6 +119,8 @@ export class NodeinfoServerService { const cache = new MemorySingleCache>>(1000 * 60 * 10); + fastify.setErrorHandler(ErrorHandler); + fastify.get(nodeinfo2_1path, async (request, reply) => { const base = await cache.fetch(() => nodeinfo2()); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index b40eeb31b174..1cf7c7e859e0 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -22,6 +22,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; +import { ErrorHandler } from '@/misc/error.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -76,6 +77,7 @@ export class ServerService implements OnApplicationShutdown { logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), }); this.#fastify = fastify; + fastify.setErrorHandler(ErrorHandler); // HSTS // 6months (15552000sec) diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 27139a0d6971..bd4bbea3720b 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -15,6 +15,7 @@ import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -51,6 +52,8 @@ export class WellKnownServerService { fastify.register(fastifyAccepts); + fastify.setErrorHandler(ErrorHandler); + fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 96b6a821d509..6bdb017d83ee 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -13,6 +13,7 @@ import type { InstancesRepository, AccessTokensRepository } from '@/models/index import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import endpoints from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; @@ -56,6 +57,8 @@ export class ApiServerService { fastify.register(fastifyCookie, {}); + fastify.setErrorHandler(ErrorHandler); + // Prevent cache fastify.addHook('onRequest', (request, reply, done) => { reply.header('Cache-Control', 'private, max-age=0, must-revalidate'); diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts index cb22d0f7c922..6ce766e1bf73 100644 --- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts +++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts @@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import { genOpenapiSpec } from './gen-spec.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -23,6 +24,7 @@ export class OpenApiServerService { @bindThis public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) { + fastify.setErrorHandler(ErrorHandler); fastify.get('/api-doc', async (_request, reply) => { reply.header('Cache-Control', 'public, max-age=86400'); return await reply.sendFile('/redoc.html', staticAssets); diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 9f8e8a3eb190..726c7298bb3e 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -31,9 +31,9 @@ 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 { ErrorHandler, ErrorHandling } from '@/misc/error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; -import { ErrorHandling } from '@/misc/error.js'; // TODO: Consider migrating to @node-oauth/oauth2-server once // https://github.com/node-oauth/node-oauth2-server/issues/180 is figured out. @@ -354,6 +354,9 @@ export class OAuth2ProviderService { public async createServer(fastify: FastifyInstance): Promise { // https://datatracker.ietf.org/doc/html/rfc8414.html // https://indieauth.spec.indieweb.org/#indieauth-server-metadata + + fastify.setErrorHandler(ErrorHandler); + fastify.get('/.well-known/oauth-authorization-server', async (_request, reply) => { reply.send({ issuer: this.config.url, diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 5ed12000a082..7614b2cf9b3f 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -42,7 +42,6 @@ import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; -import { ErrorHandling } from '@/misc/error.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -147,18 +146,18 @@ export class ClientServerService { if (request.url === bullBoardPath || request.url.startsWith(bullBoardPath + '/')) { const token = request.cookies.token; if (token == null) { - reply.code(401); - throw ErrorHandling('login required'); + reply.code(401).send('Login required'); + return; } const user = await this.usersRepository.findOneBy({ token }); if (user == null) { - reply.code(403); - throw ErrorHandling('no such user'); + reply.code(403).send('No such user'); + return; } const isAdministrator = await this.roleService.isAdministrator(user); if (!isAdministrator) { - reply.code(403); - throw ErrorHandling('access denied'); + reply.code(403).send('Access denied'); + return; } } }); @@ -683,12 +682,13 @@ export class ClientServerService { fastify.setErrorHandler(async (error, request, reply) => { const errId = randomUUID(); + const stack = (process.env.NODE_ENV === 'production') ? '' : error.stack; this.clientLoggerService.logger.error(`Internal error occurred in ${request.routerPath}: ${error.message}`, { path: request.routerPath, params: request.params, query: request.query, code: error.name, - stack: error.stack, + stack, id: errId, }); reply.code(500); From 0c84d97cbb9cfd8f783b4dfe381b536037a4cd17 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 01:09:37 +0800 Subject: [PATCH 10/15] revert --- packages/backend/src/server/ActivityPubServerService.ts | 3 --- packages/backend/src/server/FileServerService.ts | 2 -- packages/backend/src/server/NodeinfoServerService.ts | 3 --- packages/backend/src/server/WellKnownServerService.ts | 3 --- packages/backend/src/server/api/ApiServerService.ts | 3 --- .../backend/src/server/api/openapi/OpenApiServerService.ts | 2 -- packages/backend/src/server/oauth/OAuth2ProviderService.ts | 4 +--- 7 files changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 6ad18768502d..c9ce1dcaee6a 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -26,7 +26,6 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import { IActivity } from '@/core/activitypub/type.js'; -import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; @@ -463,8 +462,6 @@ export class ActivityPubServerService { fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); - fastify.setErrorHandler(ErrorHandler); - fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 036d746f37eb..c52ce4568e9d 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -27,7 +27,6 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { correctFilename } from '@/misc/correct-filename.js'; -import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; const _filename = fileURLToPath(import.meta.url); @@ -60,7 +59,6 @@ export class FileServerService { @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.setErrorHandler(ErrorHandler); fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); done(); diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 7fbb11a89ec4..95cc697768ea 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -14,7 +14,6 @@ import { bindThis } from '@/decorators.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; @@ -119,8 +118,6 @@ export class NodeinfoServerService { const cache = new MemorySingleCache>>(1000 * 60 * 10); - fastify.setErrorHandler(ErrorHandler); - fastify.get(nodeinfo2_1path, async (request, reply) => { const base = await cache.fetch(() => nodeinfo2()); diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index bd4bbea3720b..27139a0d6971 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -15,7 +15,6 @@ import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandler } from '@/misc/error.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -52,8 +51,6 @@ export class WellKnownServerService { fastify.register(fastifyAccepts); - fastify.setErrorHandler(ErrorHandler); - fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 6bdb017d83ee..96b6a821d509 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -13,7 +13,6 @@ import type { InstancesRepository, AccessTokensRepository } from '@/models/index import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandler } from '@/misc/error.js'; import endpoints from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; @@ -57,8 +56,6 @@ export class ApiServerService { fastify.register(fastifyCookie, {}); - fastify.setErrorHandler(ErrorHandler); - // Prevent cache fastify.addHook('onRequest', (request, reply, done) => { reply.header('Cache-Control', 'private, max-age=0, must-revalidate'); diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts index 6ce766e1bf73..cb22d0f7c922 100644 --- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts +++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts @@ -8,7 +8,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandler } from '@/misc/error.js'; import { genOpenapiSpec } from './gen-spec.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -24,7 +23,6 @@ export class OpenApiServerService { @bindThis public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.setErrorHandler(ErrorHandler); fastify.get('/api-doc', async (_request, reply) => { reply.header('Cache-Control', 'public, max-age=86400'); return await reply.sendFile('/redoc.html', staticAssets); diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 726c7298bb3e..0073acb6ab19 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -31,7 +31,7 @@ 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 { ErrorHandler, ErrorHandling } from '@/misc/error.js'; +import { ErrorHandling } from '@/misc/error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; @@ -355,8 +355,6 @@ export class OAuth2ProviderService { // https://datatracker.ietf.org/doc/html/rfc8414.html // https://indieauth.spec.indieweb.org/#indieauth-server-metadata - fastify.setErrorHandler(ErrorHandler); - fastify.get('/.well-known/oauth-authorization-server', async (_request, reply) => { reply.send({ issuer: this.config.url, From 01f587ebe23ea74530c99e832653f225cc56b15e Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 16:36:35 +0800 Subject: [PATCH 11/15] revert --- packages/backend/src/core/CaptchaService.ts | 21 ++++----- .../src/core/CreateSystemUserService.ts | 3 +- .../backend/src/core/CustomEmojiService.ts | 3 +- packages/backend/src/core/DriveService.ts | 13 ++--- packages/backend/src/core/IdService.ts | 5 +- .../backend/src/core/NoteCreateService.ts | 7 ++- packages/backend/src/core/PollService.ts | 15 +++--- packages/backend/src/core/RelayService.ts | 3 +- .../src/core/RemoteUserResolveService.ts | 15 +++--- packages/backend/src/core/SearchService.ts | 5 +- .../core/TwoFactorAuthenticationService.ts | 47 +++++++++---------- .../backend/src/core/UserFollowingService.ts | 5 +- .../src/core/activitypub/ApRendererService.ts | 5 +- .../src/core/activitypub/ApResolverService.ts | 19 ++++---- .../core/activitypub/models/ApNoteService.ts | 19 ++++---- .../backend/src/server/api/GetterService.ts | 5 +- .../server/api/endpoints/admin/relays/add.ts | 3 +- .../server/api/endpoints/admin/show-user.ts | 5 +- .../server/api/endpoints/antennas/create.ts | 3 +- .../src/server/api/endpoints/i/2fa/done.ts | 5 +- .../server/api/endpoints/i/2fa/key-done.ts | 23 +++++---- .../server/api/endpoints/i/2fa/register.ts | 3 +- .../server/api/endpoints/i/2fa/remove-key.ts | 3 +- .../server/api/endpoints/i/2fa/unregister.ts | 3 +- .../server/api/endpoints/i/delete-account.ts | 3 +- .../src/server/api/endpoints/reset-db.ts | 3 +- .../server/api/endpoints/reset-password.ts | 1 - .../src/server/api/stream/ChannelsService.ts | 3 +- .../src/server/oauth/OAuth2ProviderService.ts | 3 +- .../src/server/web/UrlPreviewService.ts | 5 +- 30 files changed, 111 insertions(+), 145 deletions(-) diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index 8b9f7dbc8afc..f64196f4fcae 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -6,7 +6,6 @@ import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; type CaptchaResponse = { success: boolean; @@ -36,7 +35,7 @@ export class CaptchaService { }, { throwErrorWhenResponseNotOk: false }); if (!res.ok) { - throw ErrorHandling(`${res.status}`); + throw new Error(`${res.status}`); } return await res.json() as CaptchaResponse; @@ -45,48 +44,48 @@ export class CaptchaService { @bindThis public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw ErrorHandling('recaptcha-failed: no response provided'); + throw new Error('recaptcha-failed: no response provided'); } const result = await this.getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(err => { - throw ErrorHandling(`recaptcha-request-failed: ${err}`); + throw new Error(`recaptcha-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw ErrorHandling(`recaptcha-failed: ${errorCodes}`); + throw new Error(`recaptcha-failed: ${errorCodes}`); } } @bindThis public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw ErrorHandling('hcaptcha-failed: no response provided'); + throw new Error('hcaptcha-failed: no response provided'); } const result = await this.getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(err => { - throw ErrorHandling(`hcaptcha-request-failed: ${err}`); + throw new Error(`hcaptcha-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw ErrorHandling(`hcaptcha-failed: ${errorCodes}`); + throw new Error(`hcaptcha-failed: ${errorCodes}`); } } @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise { if (response == null) { - throw ErrorHandling('turnstile-failed: no response provided'); + throw new Error('turnstile-failed: no response provided'); } const result = await this.getCaptchaResponse('https://challenges.cloudflare.com/turnstile/v0/siteverify', secret, response).catch(err => { - throw ErrorHandling(`turnstile-request-failed: ${err}`); + throw new Error(`turnstile-request-failed: ${err}`); }); if (result.success !== true) { const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; - throw ErrorHandling(`turnstile-failed: ${errorCodes}`); + throw new Error(`turnstile-failed: ${errorCodes}`); } } } diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 5f768e9ee205..5eece8cd4657 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -16,7 +16,6 @@ import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class CreateSystemUserService { @@ -50,7 +49,7 @@ export class CreateSystemUserService { host: IsNull(), }); - if (exist) throw ErrorHandling('the user is already exists'); + if (exist) throw new Error('the user is already exists'); account = await transactionalEntityManager.insert(User, { id: this.idService.genId(), diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 9c2957eb3fd5..f24a880914df 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -18,7 +18,6 @@ import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/server/api/stream/types.js'; -import { ErrorHandling } from '@/misc/error.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -108,7 +107,7 @@ export class CustomEmojiService implements OnApplicationShutdown { }): Promise { 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 ErrorHandling('name already exists'); + if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); await this.emojisRepository.update(emoji.id, { updatedAt: new Date(), diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index ad4be4157c9d..8c5c41ca0122 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -42,7 +42,6 @@ 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 { ErrorHandling } from '@/misc/error.js'; type AddFileArgs = { /** User who wish to add file */ @@ -536,7 +535,7 @@ export class DriveService { userId: user ? user.id : IsNull(), }); - if (driveFolder == null) throw ErrorHandling('folder-not-found'); + if (driveFolder == null) throw new Error('folder-not-found'); return driveFolder; }; @@ -751,13 +750,9 @@ export class DriveService { this.deleteLogger.warn(`The object storage had no such key to delete: ${key}. Skipping this.`, err as Error); return; } else { - const error = new Error(`Failed to delete the file from the object storage with the given key: ${key}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: '' }); - Object.defineProperty(err, 'stack', { value: '' }); - } - error['cause'] = err; - throw error; + throw new Error(`Failed to delete the file from the object storage with the given key: ${key}`, { + cause: err, + }); } } } diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index cd36b154e557..186fd36b42ef 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -13,7 +13,6 @@ import { genMeidg, parseMeidg } from '@/misc/id/meidg.js'; import { genObjectId, parseObjectId } from '@/misc/id/object-id.js'; import { bindThis } from '@/decorators.js'; import { parseUlid } from '@/misc/id/ulid.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class IdService { @@ -36,7 +35,7 @@ export class IdService { case 'meidg': return genMeidg(date); case 'ulid': return ulid(date.getTime()); case 'objectid': return genObjectId(date); - default: throw ErrorHandling('unrecognized id generation method'); + default: throw new Error('unrecognized id generation method'); } } @@ -48,7 +47,7 @@ export class IdService { case 'meid': return parseMeid(id); case 'meidg': return parseMeidg(id); case 'ulid': return parseUlid(id); - default: throw ErrorHandling('unrecognized id generation method'); + default: throw new Error('unrecognized id generation method'); } } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 3c57143aab4f..93ccf35a062c 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -53,7 +53,6 @@ import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; -import { ErrorHandling } from '@/misc/error.js'; const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -252,7 +251,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Renote対象が「ホームまたは全体」以外の公開範囲ならreject if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) { - throw ErrorHandling('Renote target is not public or home'); + throw new Error('Renote target is not public or home'); } // Renote対象がpublicではないならhomeにする @@ -317,7 +316,7 @@ export class NoteCreateService implements OnApplicationShutdown { } if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw ErrorHandling('invalid param'); + if (data.visibleUsers == null) throw new Error('invalid param'); for (const u of data.visibleUsers) { if (!mentionedUsers.some(x => x.id === u.id)) { @@ -529,7 +528,7 @@ export class NoteCreateService implements OnApplicationShutdown { // 未読通知を作成 if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw ErrorHandling('invalid param'); + if (data.visibleUsers == null) throw new Error('invalid param'); for (const u of data.visibleUsers) { // ローカルユーザーのみ diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index ff5e4973318b..f317087b4175 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -15,7 +15,6 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { bindThis } from '@/decorators.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class PollService { @@ -46,16 +45,16 @@ export class PollService { public async vote(user: User, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); - if (poll == null) throw ErrorHandling('poll not found'); + if (poll == null) throw new Error('poll not found'); // Check whether is valid choice - if (poll.choices[choice] == null) throw ErrorHandling('invalid choice param'); + if (poll.choices[choice] == null) throw new Error('invalid choice param'); // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); if (blocked) { - throw ErrorHandling('blocked'); + throw new Error('blocked'); } } @@ -67,10 +66,10 @@ export class PollService { if (poll.multiple) { if (exist.some(x => x.choice === choice)) { - throw ErrorHandling('already voted'); + throw new Error('already voted'); } } else if (exist.length !== 0) { - throw ErrorHandling('already voted'); + throw new Error('already voted'); } // Create vote @@ -95,10 +94,10 @@ export class PollService { @bindThis public async deliverQuestionUpdate(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); - if (note == null) throw ErrorHandling('note not found'); + if (note == null) throw new Error('note not found'); const user = await this.usersRepository.findOneBy({ id: note.userId }); - if (user == null) throw ErrorHandling('note not found'); + if (user == null) throw new Error('note not found'); if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 069893d6bfc9..1ac906991bf9 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -16,7 +16,6 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; const ACTOR_USERNAME = 'relay.actor' as const; @@ -75,7 +74,7 @@ export class RelayService { }); if (relay == null) { - throw ErrorHandling('relay not found'); + throw new Error('relay not found'); } const relayActor = await this.getRelayActor(); diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index fb2655ceed7e..31682ea98d89 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -18,7 +18,6 @@ import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class RemoteUserResolveService { @@ -48,7 +47,7 @@ export class RemoteUserResolveService { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { - throw ErrorHandling('user not found'); + throw new Error('user not found'); } else { return u; } @@ -61,7 +60,7 @@ export class RemoteUserResolveService { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { - throw ErrorHandling('user not found'); + throw new Error('user not found'); } else { return u; } @@ -83,7 +82,7 @@ export class RemoteUserResolveService { .getUserFromApId(self.href) .then((u) => { if (u == null) { - throw ErrorHandling('local user not found'); + throw new Error('local user not found'); } else { return u; } @@ -113,7 +112,7 @@ export class RemoteUserResolveService { // validate uri const uri = new URL(self.href); if (uri.hostname !== host) { - throw ErrorHandling('Invalid uri'); + throw new Error('Invalid uri'); } await this.usersRepository.update({ @@ -131,7 +130,7 @@ export class RemoteUserResolveService { this.logger.info(`return resynced remote user: ${acctLower}`); return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { if (u == null) { - throw ErrorHandling('user not found'); + throw new Error('user not found'); } else { return u as LocalUser | RemoteUser; } @@ -147,12 +146,12 @@ export class RemoteUserResolveService { this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await this.webfingerService.webfinger(acctLower).catch(err => { this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`); - throw ErrorHandling(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`); + throw new Error(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`); }); const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); if (!self) { this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); - throw ErrorHandling('self link not found'); + throw new Error('self link not found'); } return self; } diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 9e282a196cda..88b368dd2230 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -15,7 +15,6 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { QueryService } from '@/core/QueryService.js'; import { IdService } from '@/core/IdService.js'; import type { Index, MeiliSearch } from 'meilisearch'; -import { ErrorHandling } from '@/misc/error.js'; type K = string; type V = string | number | boolean; @@ -38,7 +37,7 @@ function compileValue(value: V): string { } else if (typeof value === 'boolean') { return value.toString(); } - throw ErrorHandling('unrecognized value'); + throw new Error('unrecognized value'); } function compileQuery(q: Q): string { @@ -52,7 +51,7 @@ function compileQuery(q: Q): string { 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 'not': return `(NOT ${compileQuery(q.q)})`; - default: throw ErrorHandling('unrecognized query operator'); + default: throw new Error('unrecognized query operator'); } } diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 2943aa1dc49c..ecf7676f4b8a 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -9,7 +9,6 @@ import * as jsrsasign from 'jsrsasign'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); @@ -73,7 +72,7 @@ function verifyCertificateChain(certificates: string[]) { const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1]; const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); - if (certStruct == null) throw ErrorHandling('certStruct is null'); + if (certStruct == null) throw new Error('certStruct is null'); const algorithm = certificate.getSignatureAlgorithmField(); const signatureHex = certificate.getSignatureValueHex(); @@ -143,14 +142,14 @@ export class TwoFactorAuthenticationService { challenge: string }) { if (clientData.type !== 'webauthn.get') { - throw ErrorHandling('type is not webauthn.get'); + throw new Error('type is not webauthn.get'); } if (this.hash(clientData.challenge).toString('hex') !== challenge) { - throw ErrorHandling('challenge mismatch'); + throw new Error('challenge mismatch'); } if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw ErrorHandling('origin mismatch'); + throw new Error('origin mismatch'); } const verificationData = Buffer.concat( @@ -172,11 +171,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw ErrorHandling('invalid or no -2 key given'); + throw new Error('invalid or no -2 key given'); } const negThree = publicKey.get(-3); if (!negThree || negThree.length !== 32) { - throw ErrorHandling('invalid or no -3 key given'); + throw new Error('invalid or no -3 key given'); } const publicKeyU2F = Buffer.concat( @@ -207,7 +206,7 @@ export class TwoFactorAuthenticationService { credentialId: Buffer, }) { if (attStmt.alg !== -7) { - throw ErrorHandling('alg mismatch'); + throw new Error('alg mismatch'); } const verificationData = Buffer.concat([ @@ -220,11 +219,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw ErrorHandling('invalid or no -2 key given'); + throw new Error('invalid or no -2 key given'); } const negThree = publicKey.get(-3); if (!negThree || negThree.length !== 32) { - throw ErrorHandling('invalid or no -3 key given'); + throw new Error('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -233,7 +232,7 @@ export class TwoFactorAuthenticationService { ); if (!attCert.equals(publicKeyData)) { - throw ErrorHandling('public key mismatch'); + throw new Error('public key mismatch'); } const isValid = crypto @@ -279,7 +278,7 @@ export class TwoFactorAuthenticationService { const signature = jwsParts[2]; if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { - throw ErrorHandling('invalid nonce'); + throw new Error('invalid nonce'); } const certificateChain = header.x5c @@ -287,11 +286,11 @@ export class TwoFactorAuthenticationService { .concat([GSR2]); if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { - throw ErrorHandling('invalid common name'); + throw new Error('invalid common name'); } if (!verifyCertificateChain(certificateChain)) { - throw ErrorHandling('Invalid certificate chain!'); + throw new Error('Invalid certificate chain!'); } const signatureBase = Buffer.from( @@ -307,11 +306,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw ErrorHandling('invalid or no -2 key given'); + throw new Error('invalid or no -2 key given'); } const negThree = publicKey.get(-3); if (!negThree || negThree.length !== 32) { - throw ErrorHandling('invalid or no -3 key given'); + throw new Error('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -356,11 +355,11 @@ export class TwoFactorAuthenticationService { const negTwo = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw ErrorHandling('invalid or no -2 key given'); + throw new Error('invalid or no -2 key given'); } const negThree = publicKey.get(-3); if (!negThree || negThree.length !== 32) { - throw ErrorHandling('invalid or no -3 key given'); + throw new Error('invalid or no -3 key given'); } const publicKeyData = Buffer.concat( @@ -374,11 +373,11 @@ export class TwoFactorAuthenticationService { }; } 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 ErrorHandling('ECDAA-Verify is not supported'); + throw new Error('ECDAA-Verify is not supported'); } else { - if (attStmt.alg !== -7) throw ErrorHandling('alg mismatch'); + if (attStmt.alg !== -7) throw new Error('alg mismatch'); - throw ErrorHandling('self attestation is not supported'); + throw new Error('self attestation is not supported'); } }, }, @@ -401,7 +400,7 @@ export class TwoFactorAuthenticationService { }) { const x5c: Buffer[] = attStmt.x5c; if (x5c.length !== 1) { - throw ErrorHandling('x5c length does not match expectation'); + throw new Error('x5c length does not match expectation'); } const attCert = x5c[0]; @@ -411,11 +410,11 @@ export class TwoFactorAuthenticationService { const negTwo: Buffer = publicKey.get(-2); if (!negTwo || negTwo.length !== 32) { - throw ErrorHandling('invalid or no -2 key given'); + throw new Error('invalid or no -2 key given'); } const negThree: Buffer = publicKey.get(-3); if (!negThree || negThree.length !== 32) { - throw ErrorHandling('invalid or no -3 key given'); + throw new Error('invalid or no -3 key given'); } const publicKeyU2F = Buffer.concat( diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index afd096d02500..8e356d19bb78 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -29,7 +29,6 @@ import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import Logger from '../logger.js'; -import { ErrorHandling } from '@/misc/error.js'; const logger = new Logger('following/create'); @@ -460,8 +459,8 @@ export class UserFollowingService implements OnModuleInit { this.userBlockingService.checkBlocked(followee.id, follower.id), ]); - if (blocking) throw ErrorHandling('blocking'); - if (blocked) throw ErrorHandling('blocked'); + if (blocking) throw new Error('blocking'); + if (blocked) throw new Error('blocked'); const followRequest = await this.followRequestsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index bed26e90f413..f31d5f84e549 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -30,7 +30,6 @@ import { isNotNull } from '@/misc/is-not-null.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ApRendererService { @@ -99,7 +98,7 @@ export class ApRendererService { to = [`${attributedTo}/followers`]; cc = []; } else { - throw ErrorHandling('renderAnnounce: cannot render non-public note'); + throw new Error('renderAnnounce: cannot render non-public note'); } return { @@ -121,7 +120,7 @@ export class ApRendererService { @bindThis public renderBlock(block: Blocking): IBlock { if (block.blockee?.uri == null) { - throw ErrorHandling('renderBlock: missing blockee uri'); + throw new Error('renderBlock: missing blockee uri'); } return { diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 623dd289e03e..31bea744e2b2 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -20,7 +20,6 @@ import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -import { ErrorHandling } from '@/misc/error.js'; export class Resolver { private history: Set; @@ -61,7 +60,7 @@ export class Resolver { if (isCollectionOrOrderedCollection(collection)) { return collection; } else { - throw ErrorHandling(`unrecognized collection type: ${collection.type}`); + throw new Error(`unrecognized collection type: ${collection.type}`); } } @@ -75,15 +74,15 @@ export class Resolver { // URLs with fragment parts cannot be resolved correctly because // the fragment part does not get transmitted over HTTP(S). // Avoid strange behaviour by not trying to resolve these at all. - throw ErrorHandling(`cannot resolve URL with fragment: ${value}`); + throw new Error(`cannot resolve URL with fragment: ${value}`); } if (this.history.has(value)) { - throw ErrorHandling('cannot resolve already resolved one'); + throw new Error('cannot resolve already resolved one'); } if (this.history.size > this.recursionLimit) { - throw ErrorHandling(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); + throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); } this.history.add(value); @@ -95,7 +94,7 @@ export class Resolver { const meta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(meta.blockedHosts, host)) { - throw ErrorHandling('Instance is blocked'); + throw new Error('Instance is blocked'); } if (this.config.signToActivityPubGet && !this.user) { @@ -111,7 +110,7 @@ export class Resolver { !(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') : object['@context'] !== 'https://www.w3.org/ns/activitystreams' ) { - throw ErrorHandling('invalid response'); + throw new Error('invalid response'); } return object; @@ -120,7 +119,7 @@ export class Resolver { @bindThis private resolveLocal(url: string): Promise { const parsed = this.apDbResolverService.parseUri(url); - if (!parsed.local) throw ErrorHandling('resolveLocal: not local'); + if (!parsed.local) throw new Error('resolveLocal: not local'); switch (parsed.type) { case 'notes': @@ -148,14 +147,14 @@ export class Resolver { this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null }))); case 'follows': // rest should be - if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw ErrorHandling('resolveLocal: invalid follow URI'); + 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))); default: - throw ErrorHandling(`resolveLocal: type ${parsed.type} unhandled`); + throw new Error(`resolveLocal: type ${parsed.type} unhandled`); } } } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index d1fdb87946ed..fd6814f936b8 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -37,7 +37,6 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ApNoteService { @@ -136,7 +135,7 @@ export class ApNoteService { value, object, }); - throw ErrorHandling('invalid note'); + throw new Error('invalid note'); } const note = object as IPost; @@ -144,27 +143,27 @@ export class ApNoteService { this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); if (note.id && !checkHttps(note.id)) { - throw ErrorHandling('unexpected schema 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 ErrorHandling('unexpected schema of note url: ' + url); + throw new Error('unexpected schema of note url: ' + url); } this.logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ if (note.attributedTo == null) { - throw ErrorHandling('invalid note.attributedTo: ' + note.attributedTo); + throw new Error('invalid note.attributedTo: ' + note.attributedTo); } const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { - throw ErrorHandling('actor has been suspended'); + throw new Error('actor has been suspended'); } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); @@ -199,7 +198,7 @@ export class ApNoteService { .then(x => { if (x == null) { this.logger.warn('Specified inReplyTo, but not found'); - throw ErrorHandling('inReplyTo not found'); + throw new Error('inReplyTo not found'); } return x; @@ -236,7 +235,7 @@ export class ApNoteService { quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0); if (!quote) { if (results.some(x => x.status === 'temperror')) { - throw ErrorHandling('quote resolve failed'); + throw new Error('quote resolve failed'); } } } @@ -310,7 +309,7 @@ export class ApNoteService { this.logger.info('The note is already inserted while creating itself, reading again'); const duplicate = await this.fetchNote(value); if (!duplicate) { - throw ErrorHandling('The note creation failed with duplication error even when there is no duplication'); + throw new Error('The note creation failed with duplication error even when there is no duplication'); } return duplicate; } @@ -389,7 +388,7 @@ export class ApNoteService { }); const emoji = await this.emojisRepository.findOneBy({ host, name }); - if (emoji == null) throw ErrorHandling('emoji update failed'); + if (emoji == null) throw new Error('emoji update failed'); return emoji; } diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index 052a9da6fa3d..c16f2f504fb3 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -11,7 +11,6 @@ import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class GetterService { @@ -62,7 +61,7 @@ export class GetterService { const user = await this.getUser(userId); if (!this.userEntityService.isRemoteUser(user)) { - throw ErrorHandling('user is not a remote user'); + throw new Error('user is not a remote user'); } return user; @@ -76,7 +75,7 @@ export class GetterService { const user = await this.getUser(userId); if (!this.userEntityService.isLocalUser(user)) { - throw ErrorHandling('user is not a local user'); + throw new Error('user is not a local user'); } return user; 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 571ad4810255..e420a42ab345 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -8,7 +8,6 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; import { ApiError } from '../../../error.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['admin'], @@ -68,7 +67,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { try { - if (new URL(ps.inbox).protocol !== 'https:') throw ErrorHandling('https only'); + if (new URL(ps.inbox).protocol !== 'https:') throw new Error('https only'); } catch { throw new ApiError(meta.errors.invalidUrl); } 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 37548fc10bce..1e8c38c96bd4 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -9,7 +9,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['admin'], @@ -54,7 +53,7 @@ export default class extends Endpoint { ]); if (user == null || profile == null) { - throw ErrorHandling('user not found'); + throw new Error('user not found'); } const isModerator = await this.roleService.isModerator(user); @@ -62,7 +61,7 @@ export default class extends Endpoint { const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { - throw ErrorHandling('cannot show info of admin'); + throw new Error('cannot show info of admin'); } const signins = await this.signinsRepository.findBy({ userId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 5f4e0982f52b..89245684409d 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -12,7 +12,6 @@ import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['antennas'], @@ -88,7 +87,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { if ((ps.keywords.length === 0) || ps.keywords[0].every(x => x === '')) { - throw ErrorHandling('invalid param'); + throw new Error('invalid param'); } const currentAntennasCount = await this.antennasRepository.countBy({ 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 256ff29f0f2b..d7ba21c25986 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -10,7 +10,6 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, @@ -42,7 +41,7 @@ export default class extends Endpoint { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); if (profile.twoFactorTempSecret == null) { - throw ErrorHandling('二段階認証の設定が開始されていません'); + throw new Error('二段階認証の設定が開始されていません'); } const delta = OTPAuth.TOTP.validate({ @@ -53,7 +52,7 @@ export default class extends Endpoint { }); if (delta === null) { - throw ErrorHandling('not verified'); + throw new Error('not verified'); } await this.userProfilesRepository.update(me.id, { 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 8c585f8328be..fd6e70a9dea1 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 @@ -14,7 +14,6 @@ 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'; -import { ErrorHandling } from '@/misc/error.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; @@ -65,20 +64,20 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw ErrorHandling('incorrect password'); + throw new Error('incorrect password'); } if (!profile.twoFactorEnabled) { - throw ErrorHandling('2fa not enabled'); + throw new Error('2fa not enabled'); } const clientData = JSON.parse(ps.clientDataJSON); if (clientData.type !== 'webauthn.create') { - throw ErrorHandling('not a creation attestation'); + throw new Error('not a creation attestation'); } if (clientData.origin !== this.config.scheme + '://' + this.config.host) { - throw ErrorHandling('origin mismatch'); + throw new Error('origin mismatch'); } const clientDataJSONHash = this.twoFactorAuthenticationService.hash(Buffer.from(ps.clientDataJSON, 'utf-8')); @@ -87,14 +86,14 @@ export default class extends Endpoint { const rpIdHash = attestation.authData.slice(0, 32); if (!rpIdHashReal.equals(rpIdHash)) { - throw ErrorHandling('rpIdHash mismatch'); + throw new Error('rpIdHash mismatch'); } const flags = attestation.authData[32]; // eslint:disable-next-line:no-bitwise if (!(flags & 1)) { - throw ErrorHandling('user not present'); + throw new Error('user not present'); } const authData = Buffer.from(attestation.authData); @@ -103,13 +102,13 @@ export default class extends Endpoint { const publicKeyData = authData.slice(55 + credentialIdLength); const publicKey: Map = await cborDecodeFirst(publicKeyData); if (publicKey.get(3) !== -7) { - throw ErrorHandling('alg mismatch'); + throw new Error('alg mismatch'); } const procedures = this.twoFactorAuthenticationService.getProcedures(); if (!(procedures as any)[attestation.fmt]) { - throw ErrorHandling(`unsupported fmt: ${attestation.fmt}. Supported ones: ${Object.keys(procedures)}`); + throw new Error(`unsupported fmt: ${attestation.fmt}. Supported ones: ${Object.keys(procedures)}`); } const verificationData = (procedures as any)[attestation.fmt].verify({ @@ -120,7 +119,7 @@ export default class extends Endpoint { publicKey, rpIdHash, }); - if (!verificationData.valid) throw ErrorHandling('signature invalid'); + if (!verificationData.valid) throw new Error('signature invalid'); const attestationChallenge = await this.attestationChallengesRepository.findOneBy({ userId: me.id, @@ -130,7 +129,7 @@ export default class extends Endpoint { }); if (!attestationChallenge) { - throw ErrorHandling('non-existent challenge'); + throw new Error('non-existent challenge'); } await this.attestationChallengesRepository.delete({ @@ -143,7 +142,7 @@ export default class extends Endpoint { new Date().getTime() - attestationChallenge.createdAt.getTime() >= 5 * 60 * 1000 ) { - throw ErrorHandling('expired challenge'); + throw new Error('expired challenge'); } const credentialIdString = credentialId.toString('hex'); 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 217230cdc15b..0e57f07e5e8b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -11,7 +11,6 @@ import type { UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, @@ -44,7 +43,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw ErrorHandling('incorrect password'); + throw new Error('incorrect password'); } // 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 86b0ad9e355e..6402f3440f00 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 @@ -10,7 +10,6 @@ import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/model import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, @@ -47,7 +46,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw ErrorHandling('incorrect password'); + throw new Error('incorrect password'); } // 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 4c1a6b136686..c0e9ff5ec061 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -10,7 +10,6 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, @@ -43,7 +42,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw ErrorHandling('incorrect password'); + throw new Error('incorrect password'); } await this.userProfilesRepository.update(me.id, { 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 afc28e509c4d..721eb01c5147 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -9,7 +9,6 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/index.js' import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { requireCredential: true, @@ -48,7 +47,7 @@ export default class extends Endpoint { const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { - throw ErrorHandling('incorrect password'); + throw new Error('incorrect password'); } await this.deleteAccountService.deleteAccount(me); diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 242c38c278e5..dcf3b8772a95 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -9,7 +9,6 @@ import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { resetDb } from '@/misc/reset-db.js'; -import { ErrorHandling } from '@/misc/error.js'; export const meta = { tags: ['non-productive'], @@ -40,7 +39,7 @@ export default class extends Endpoint { private redisClient: Redis.Redis, ) { super(meta, paramDef, async (ps, me) => { - if (process.env.NODE_ENV !== 'test') throw ErrorHandling('NODE_ENV is not a test'); + if (process.env.NODE_ENV !== 'test') throw new Error('NODE_ENV is not a test'); await redisClient.flushdb(); await resetDb(this.db); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 17569103c5d4..4bbd3d0ef18d 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -8,7 +8,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import { ErrorHandling } from '@/misc/error.js'; // TODO Line 51 export const meta = { tags: ['reset password'], diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 4b9c71878a9d..8fd106c10cf1 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -19,7 +19,6 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class ChannelsService { @@ -60,7 +59,7 @@ export class ChannelsService { case 'admin': return this.adminChannelService; default: - throw ErrorHandling(`no such channel: ${name}`); + throw new Error(`no such channel: ${name}`); } } } diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 0073acb6ab19..9eedded83071 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -31,7 +31,6 @@ 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 { ErrorHandling } from '@/misc/error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; @@ -372,7 +371,7 @@ export class OAuth2ProviderService { fastify.get('/oauth/authorize', async (request, reply) => { const oauth2 = (request.raw as MiddlewareRequest).oauth2; if (!oauth2) { - throw ErrorHandling('Unexpected lack of authorization information'); + throw new Error('Unexpected lack of authorization information'); } this.#logger.info(`Rendering authorization page for "${oauth2.client.name}"`); diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 604bb9e97b23..d590244e34a7 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -15,7 +15,6 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; import type { FastifyRequest, FastifyReply } from 'fastify'; -import { ErrorHandling } from '@/misc/error.js'; @Injectable() export class UrlPreviewService { @@ -85,11 +84,11 @@ export class UrlPreviewService { this.logger.succ(`Got preview of ${url}: ${summary.title}`); if (!(summary.url.startsWith('http://') || summary.url.startsWith('https://'))) { - throw ErrorHandling('unsupported schema included'); + throw new Error('unsupported schema included'); } if (summary.player.url && !(summary.player.url.startsWith('http://') || summary.player.url.startsWith('https://'))) { - throw ErrorHandling('unsupported schema included'); + throw new Error('unsupported schema included'); } summary.icon = this.wrap(summary.icon); From 0ea386effe98ce5f2836c172c9f75c2d8b79255e Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 16:40:01 +0800 Subject: [PATCH 12/15] revert --- packages/backend/src/core/NoteCreateService.ts | 3 --- .../core/activitypub/models/ApNoteService.ts | 18 +++--------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 93ccf35a062c..001947322d5d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -436,9 +436,6 @@ export class NoteCreateService implements OnApplicationShutdown { if (isDuplicateKeyValueError(e)) { const err = new Error('Duplicated note'); err.name = 'duplicated'; - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(err, 'stack', { value: '' }); - } throw err; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index fd6814f936b8..41d1bc48a7ca 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -80,28 +80,16 @@ export class ApNoteService { const expectHost = this.utilityService.extractDbHost(uri); if (!validPost.includes(getApType(object))) { - const error = new Error(`invalid Note: invalid object type ${getApType(object)}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: '' }); - } - return error; + return new Error(`invalid Note: invalid object type ${getApType(object)}`); } if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { - const error = new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: '' }); - } - return error; + return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); } const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)); if (object.attributedTo && actualHost !== expectHost) { - const error = new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(error, 'stack', { value: '' }); - } - return error; + return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); } return null; From 14df563c7d9e8b8373397a1b059da0cc79fe5f82 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 21:43:12 +0800 Subject: [PATCH 13/15] change and fix --- packages/backend/src/error.ts | 6 ------ packages/backend/src/misc/error.ts | 13 +++---------- packages/backend/src/misc/fastify-reply-error.ts | 2 +- .../src/server/oauth/OAuth2ProviderService.ts | 1 - 4 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 packages/backend/src/error.ts diff --git a/packages/backend/src/error.ts b/packages/backend/src/error.ts deleted file mode 100644 index b9061476279e..000000000000 --- a/packages/backend/src/error.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * SPDX-FileCopyrightText: tamaina and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -// Never use `./error.js` because jest can't use it. diff --git a/packages/backend/src/misc/error.ts b/packages/backend/src/misc/error.ts index 2ea56dd96c49..57df15c30b4c 100644 --- a/packages/backend/src/misc/error.ts +++ b/packages/backend/src/misc/error.ts @@ -4,17 +4,10 @@ */ import { FastifyReply, FastifyRequest } from 'fastify'; -export function ErrorHandling(message: string, reply?: FastifyReply, statusCode?: number): Error { - const error = new Error(message); +export async function ErrorHandler(error: Error, request: FastifyRequest, reply: FastifyReply): Promise { if (process.env.NODE_ENV === 'production') { error.stack = undefined; } - if (reply) { - reply.code(statusCode ?? 500); - } - return error; -} - -export function ErrorHandler(error: Error, request: FastifyRequest, reply: FastifyReply): void { - throw ErrorHandling(error.message, reply); + reply.send(error); + throw error; } diff --git a/packages/backend/src/misc/fastify-reply-error.ts b/packages/backend/src/misc/fastify-reply-error.ts index 7f18cec8d547..8edfa87bdc61 100644 --- a/packages/backend/src/misc/fastify-reply-error.ts +++ b/packages/backend/src/misc/fastify-reply-error.ts @@ -14,7 +14,7 @@ export class FastifyReplyError extends Error { this.statusCode = statusCode; if (process.env.NODE_ENV === 'production') { - Object.defineProperty(this, 'stack', { value: '' }); + Object.defineProperty(this, 'stack', { value: undefined }); } } } diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 9eedded83071..61c89a425875 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -353,7 +353,6 @@ export class OAuth2ProviderService { public async createServer(fastify: FastifyInstance): Promise { // 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, From d1f7f22068e2c746862df94979cee3aebb831d78 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Wed, 16 Aug 2023 11:36:02 +0800 Subject: [PATCH 14/15] revert --- packages/backend/src/misc/error.ts | 13 ------------- packages/backend/src/misc/fastify-reply-error.ts | 4 ---- packages/backend/src/server/ServerService.ts | 2 -- .../backend/src/server/web/ClientServerService.ts | 3 +-- 4 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 packages/backend/src/misc/error.ts diff --git a/packages/backend/src/misc/error.ts b/packages/backend/src/misc/error.ts deleted file mode 100644 index 57df15c30b4c..000000000000 --- a/packages/backend/src/misc/error.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: MomentQYC and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { FastifyReply, FastifyRequest } from 'fastify'; - -export async function ErrorHandler(error: Error, request: FastifyRequest, reply: FastifyReply): Promise { - if (process.env.NODE_ENV === 'production') { - error.stack = undefined; - } - reply.send(error); - throw error; -} diff --git a/packages/backend/src/misc/fastify-reply-error.ts b/packages/backend/src/misc/fastify-reply-error.ts index 8edfa87bdc61..7c889bab7a00 100644 --- a/packages/backend/src/misc/fastify-reply-error.ts +++ b/packages/backend/src/misc/fastify-reply-error.ts @@ -12,9 +12,5 @@ export class FastifyReplyError extends Error { super(message); this.message = message; this.statusCode = statusCode; - - if (process.env.NODE_ENV === 'production') { - Object.defineProperty(this, 'stack', { value: undefined }); - } } } diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 1cf7c7e859e0..b40eeb31b174 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -22,7 +22,6 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; -import { ErrorHandler } from '@/misc/error.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -77,7 +76,6 @@ export class ServerService implements OnApplicationShutdown { logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), }); this.#fastify = fastify; - fastify.setErrorHandler(ErrorHandler); // HSTS // 6months (15552000sec) diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 7614b2cf9b3f..7e9f2a5eba65 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -682,13 +682,12 @@ export class ClientServerService { fastify.setErrorHandler(async (error, request, reply) => { const errId = randomUUID(); - const stack = (process.env.NODE_ENV === 'production') ? '' : error.stack; this.clientLoggerService.logger.error(`Internal error occurred in ${request.routerPath}: ${error.message}`, { path: request.routerPath, params: request.params, query: request.query, code: error.name, - stack, + stack: error.stack, id: errId, }); reply.code(500); From 918efd770cf00dcc865f595cc13948e58fdcc07e Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Tue, 15 Aug 2023 23:08:37 +0200 Subject: [PATCH 15/15] fix queue endpoint test --- packages/backend/test/e2e/fetch-resource.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts index 96683ce5947e..1cbfec3e5f7f 100644 --- a/packages/backend/test/e2e/fetch-resource.ts +++ b/packages/backend/test/e2e/fetch-resource.ts @@ -34,6 +34,8 @@ describe('Webリソース', () => { let aliceGalleryPost: any; let aliceChannel: any; + let bob: misskey.entities.MeSignup; + type Request = { path: string, accept?: string, @@ -90,6 +92,8 @@ describe('Webリソース', () => { fileIds: [aliceUploadedFile.body.id], }); aliceChannel = await channel(alice, {}); + + bob = await signup({ username: 'alice' }); }, 1000 * 60 * 2); afterAll(async () => { @@ -163,9 +167,15 @@ describe('Webリソース', () => { }); describe.each([{ path: '/queue' }])('$path', ({ path }) => { + test('はログインしないとGETできない。', async () => await notOk({ + path, + status: 401, + })); + test('はadminでなければGETできない。', async () => await notOk({ path, - status: 500, // FIXME? 403ではない。 + cookie: cookie(bob), + status: 403, })); test('はadminならGETできる。', async () => await ok({