feat: Hiding stack traces in production env
This commit is contained in:
parent
827616f630
commit
191c5f07a4
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
||||
if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists');
|
||||
if (sameNameEmoji != null && sameNameEmoji.id !== id) throw ErrorHandling('name already exists');
|
||||
|
||||
await this.emojisRepository.update(emoji.id, {
|
||||
updatedAt: new Date(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
// ローカルユーザーのみ
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<string>;
|
||||
|
|
@ -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<IObject> {
|
||||
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 <followee id>
|
||||
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`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
) {
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
]);
|
||||
|
||||
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<typeof meta, typeof paramDef> {
|
|||
|
||||
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 });
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
) {
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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<typeof meta, typeof paramDef> {
|
|||
});
|
||||
|
||||
if (delta === null) {
|
||||
throw new Error('not verified');
|
||||
throw ErrorHandling('not verified');
|
||||
}
|
||||
|
||||
await this.userProfilesRepository.update(me.id, {
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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<typeof meta, typeof paramDef> {
|
|||
|
||||
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<typeof meta, typeof paramDef> {
|
|||
const publicKeyData = authData.slice(55 + credentialIdLength);
|
||||
const publicKey: Map<number, any> = 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<typeof meta, typeof paramDef> {
|
|||
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<typeof meta, typeof paramDef> {
|
|||
});
|
||||
|
||||
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<typeof meta, typeof paramDef> {
|
|||
new Date().getTime() - attestationChallenge.createdAt.getTime() >=
|
||||
5 * 60 * 1000
|
||||
) {
|
||||
throw new Error('expired challenge');
|
||||
throw ErrorHandling('expired challenge');
|
||||
}
|
||||
|
||||
const credentialIdString = credentialId.toString('hex');
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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, {
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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<typeof meta, typeof paramDef> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}"`);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<ReturnType<typeof summaly>>;
|
||||
|
||||
|
|
@ -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+)/);
|
||||
|
|
|
|||
|
|
@ -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<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
const { canceled } = await os.confirm({
|
||||
|
|
@ -33,7 +34,7 @@ async function follow(user): Promise<void> {
|
|||
|
||||
const acct = new URL(location.href).searchParams.get('acct');
|
||||
if (acct == null) {
|
||||
throw new Error('acct required');
|
||||
throw ErrorHandling('acct required');
|
||||
}
|
||||
|
||||
let promise;
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
|||
}
|
||||
|
||||
async function toggleBlock(): Promise<void> {
|
||||
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<void> {
|
|||
}
|
||||
|
||||
async function toggleSuspend(): Promise<void> {
|
||||
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<void> {
|
|||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<string, unknown> {
|
|||
}
|
||||
|
||||
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'] {
|
||||
|
|
|
|||
|
|
@ -22,5 +22,5 @@ export function pleaseLogin(path?: string) {
|
|||
},
|
||||
}, 'closed');
|
||||
|
||||
throw new Error('signin required');
|
||||
console.log('signin required');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Misskey.entities.DriveFile> {
|
||||
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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue