diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ffe6dc2e1..d9d6702e80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,36 +240,6 @@ SQLでは配列のインデックスは**1始まり**。 MongoDBの時とは違い、findOneでレコードを取得する時に対象レコードが存在しない場合 **`undefined`** が返ってくるので注意。 MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください -### 簡素な`undefined`チェック -データベースからレコードを取得するときに、プログラムの流れ的に(ほぼ)絶対`undefined`にはならない場合でも、`undefined`チェックしないとTypeScriptに怒られます。 -でもいちいち複数行を費やして、発生するはずのない`undefined`をチェックするのも面倒なので、`ensure`というユーティリティ関数を用意しています。 -例えば、 -``` ts -const user = await Users.findOne(userId); -// この時点で user の型は User | undefined -if (user == null) { - throw 'missing user'; -} -// この時点で user の型は User -``` -という処理を`ensure`を使うと -``` ts -const user = await Users.findOne(userId).then(ensure); -// この時点で user の型は User -``` -という風に書けます。 -もちろん`ensure`内部でエラーを握りつぶすようなことはしておらず、万が一`undefined`だった場合はPromiseがRejectされ後続の処理は実行されません。 -``` ts -const user = await Users.findOne(userId).then(ensure); -// 万が一 Users.findOne の結果が undefined だったら、ensure でエラーが発生するので -// この行に到達することは無い -// なので、.then(ensure) は -// if (user == null) { -// throw 'missing user'; -// } -// の糖衣構文のような扱いです -``` - ### Migration作成方法 ``` npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 diff --git a/assets/mi-white.png b/assets/mi-white.png new file mode 100644 index 0000000000..1e57da6b38 Binary files /dev/null and b/assets/mi-white.png differ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5a7272b480..394577f378 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -437,6 +437,7 @@ signinWith: "{x}でログイン" signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。" tapSecurityKey: "セキュリティキーにタッチ" or: "もしくは" +language: "言語" uiLanguage: "UIの表示言語" groupInvited: "グループに招待されました" aboutX: "{x}について" @@ -701,6 +702,13 @@ inUse: "使用中" editCode: "コードを編集" apply: "適用" receiveAnnouncementFromInstance: "インスタンスからのお知らせを受け取る" +emailNotification: "メール通知" + +_email: + _follow: + title: "フォローされました" + _receiveFollowRequest: + title: "フォローリクエストを受け取りました" _plugin: install: "プラグインのインストール" diff --git a/migration/1613155914446-emailNotificationTypes.ts b/migration/1613155914446-emailNotificationTypes.ts new file mode 100644 index 0000000000..d6908aecfa --- /dev/null +++ b/migration/1613155914446-emailNotificationTypes.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class emailNotificationTypes1613155914446 implements MigrationInterface { + name = 'emailNotificationTypes1613155914446' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "emailNotificationTypes" jsonb NOT NULL DEFAULT '["follow","receiveFollowRequest","groupInvited"]'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "emailNotificationTypes"`); + } + +} diff --git a/migration/1613181457597-user-lang.ts b/migration/1613181457597-user-lang.ts new file mode 100644 index 0000000000..ac1fc88c99 --- /dev/null +++ b/migration/1613181457597-user-lang.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class userLang1613181457597 implements MigrationInterface { + name = 'userLang1613181457597' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "lang" character varying(32)`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`); + } + +} diff --git a/package.json b/package.json index 6a4dbdc6a9..9062fbbec9 100644 --- a/package.json +++ b/package.json @@ -180,13 +180,13 @@ "markdown-it": "12.0.4", "markdown-it-anchor": "7.0.2", "matter-js": "0.16.1", - "mocha": "8.2.1", + "mocha": "8.3.0", "moji": "0.5.1", "ms": "2.1.3", "multer": "1.4.2", "nested-property": "4.0.0", "node-fetch": "2.6.1", - "nodemailer": "6.4.17", + "nodemailer": "6.4.18", "object-assign-deep": "0.4.0", "os-utils": "0.0.14", "p-cancelable": "2.0.0", @@ -239,7 +239,7 @@ "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "typeorm": "0.2.30", - "typescript": "4.1.3", + "typescript": "4.1.5", "ulid": "2.3.0", "url-loader": "4.1.1", "uuid": "8.3.2", @@ -254,7 +254,7 @@ "vue-style-loader": "4.1.2", "vuedraggable": "4.0.1", "web-push": "3.4.4", - "webpack": "5.21.1", + "webpack": "5.21.2", "webpack-cli": "4.5.0", "websocket": "1.0.33", "ws": "7.4.3", diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue index c8c22e95c7..f0aa6b0534 100644 --- a/src/client/components/form/input.vue +++ b/src/client/components/form/input.vue @@ -1,63 +1,50 @@ @@ -112,11 +144,6 @@ export default defineComponent({ } } - > .save { - margin: 6px 0 0 0; - font-size: 0.8em; - } - &.tall { > .input { > textarea { diff --git a/src/client/components/sample.vue b/src/client/components/sample.vue index 8fd79ceec9..0f29fc69bb 100644 --- a/src/client/components/sample.vue +++ b/src/client/components/sample.vue @@ -51,7 +51,7 @@ export default defineComponent({ text: '', flag: true, radio: 'misskey', - mfm: `Hello world! This is an @example mention. BTW you are @${this.$i.username}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.` + mfm: `Hello world! This is an @example mention. BTW you are @${this.$i ? this.$i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.` } }, diff --git a/src/client/i18n.ts b/src/client/i18n.ts index fbc10a0bad..6c29ef153f 100644 --- a/src/client/i18n.ts +++ b/src/client/i18n.ts @@ -1,6 +1,6 @@ import { markRaw } from 'vue'; import { locale } from '@/config'; -import { I18n } from '@/scripts/i18n'; +import { I18n } from '../misc/i18n'; export const i18n = markRaw(new I18n(locale)); diff --git a/src/client/pages/settings/email-notification.vue b/src/client/pages/settings/email-notification.vue new file mode 100644 index 0000000000..de2cfd3912 --- /dev/null +++ b/src/client/pages/settings/email-notification.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/client/pages/settings/email.vue b/src/client/pages/settings/email.vue index 5ccb79a41d..e334e23cbd 100644 --- a/src/client/pages/settings/email.vue +++ b/src/client/pages/settings/email.vue @@ -9,6 +9,11 @@ + + + {{ $ts.emailNotification }} + + {{ $ts.receiveAnnouncementFromInstance }} @@ -43,7 +48,7 @@ export default defineComponent({ title: this.$ts.email, icon: faEnvelope }, - faCog, faExclamationTriangle, faCheck + faCog, faExclamationTriangle, faCheck, faBell } }, diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 32cbce2f96..1b4aa70eca 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -100,6 +100,7 @@ export default defineComponent({ case 'general': return defineAsyncComponent(() => import('./general.vue')); case 'email': return defineAsyncComponent(() => import('./email.vue')); case 'email/address': return defineAsyncComponent(() => import('./email-address.vue')); + case 'email/notification': return defineAsyncComponent(() => import('./email-notification.vue')); case 'theme': return defineAsyncComponent(() => import('./theme.vue')); case 'theme/install': return defineAsyncComponent(() => import('./theme.install.vue')); case 'theme/manage': return defineAsyncComponent(() => import('./theme.manage.vue')); diff --git a/src/client/pages/settings/plugin.install.vue b/src/client/pages/settings/plugin.install.vue index e78ce06aa5..0f6393f73c 100644 --- a/src/client/pages/settings/plugin.install.vue +++ b/src/client/pages/settings/plugin.install.vue @@ -1,6 +1,6 @@ @@ -50,10 +53,10 @@ import FormButton from '@/components/form/button.vue'; import FormInput from '@/components/form/input.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormSwitch from '@/components/form/switch.vue'; -import FormTuple from '@/components/form/tuple.vue'; +import FormSelect from '@/components/form/select.vue'; import FormBase from '@/components/form/base.vue'; import FormGroup from '@/components/form/group.vue'; -import { host } from '@/config'; +import { host, langs } from '@/config'; import { selectFile } from '@/scripts/select-file'; import * as os from '@/os'; @@ -63,7 +66,7 @@ export default defineComponent({ FormInput, FormTextarea, FormSwitch, - FormTuple, + FormSelect, FormBase, FormGroup, }, @@ -77,9 +80,11 @@ export default defineComponent({ icon: faUser }, host, + langs, name: null, description: null, birthday: null, + lang: null, location: null, fieldName0: null, fieldValue0: null, @@ -104,6 +109,7 @@ export default defineComponent({ this.description = this.$i.description; this.location = this.$i.location; this.birthday = this.$i.birthday; + this.lang = this.$i.lang; this.avatarId = this.$i.avatarId; this.bannerId = this.$i.bannerId; this.isBot = this.$i.isBot; @@ -118,6 +124,15 @@ export default defineComponent({ this.fieldValue2 = this.$i.fields[2] ? this.$i.fields[2].value : null; this.fieldName3 = this.$i.fields[3] ? this.$i.fields[3].name : null; this.fieldValue3 = this.$i.fields[3] ? this.$i.fields[3].value : null; + + this.$watch('name', this.save); + this.$watch('description', this.save); + this.$watch('location', this.save); + this.$watch('birthday', this.save); + this.$watch('lang', this.save); + this.$watch('isBot', this.save); + this.$watch('isCat', this.save); + this.$watch('alwaysMarkNsfw', this.save); }, mounted() { @@ -214,14 +229,15 @@ export default defineComponent({ }); }, - save(notify) { + save() { this.saving = true; - os.api('i/update', { + os.apiWithDialog('i/update', { name: this.name || null, description: this.description || null, location: this.location || null, birthday: this.birthday || null, + lang: this.lang || null, isBot: !!this.isBot, isCat: !!this.isCat, alwaysMarkNsfw: !!this.alwaysMarkNsfw, @@ -231,16 +247,8 @@ export default defineComponent({ this.$i.avatarUrl = i.avatarUrl; this.$i.bannerId = i.bannerId; this.$i.bannerUrl = i.bannerUrl; - - if (notify) { - os.success(); - } }).catch(err => { this.saving = false; - os.dialog({ - type: 'error', - text: err.id - }); }); }, } diff --git a/src/client/sw/sw.ts b/src/client/sw/sw.ts index a18d305ea1..ec4de17551 100644 --- a/src/client/sw/sw.ts +++ b/src/client/sw/sw.ts @@ -5,12 +5,11 @@ declare var self: ServiceWorkerGlobalScope; import { get, set } from 'idb-keyval'; import composeNotification from '@/sw/compose-notification'; -import { I18n } from '@/scripts/i18n'; +import { I18n } from '../../misc/i18n'; //#region Variables const version = _VERSION_; const cacheName = `mk-cache-${version}`; -const apiUrl = `${location.origin}/api/`; let lang: string; let i18n: I18n; @@ -27,15 +26,7 @@ get('lang').then(async prelang => { //#region Lifecycle: Install self.addEventListener('install', ev => { - ev.waitUntil( - caches.open(cacheName) - .then(cache => { - return cache.addAll([ - `/?v=${version}` - ]); - }) - .then(() => self.skipWaiting()) - ); + self.skipWaiting(); }); //#endregion @@ -53,19 +44,9 @@ self.addEventListener('activate', ev => { }); //#endregion -// TODO: 消せるかも ref. https://github.com/syuilo/misskey/pull/7108#issuecomment-774573666 //#region When: Fetching self.addEventListener('fetch', ev => { - if (ev.request.method !== 'GET' || ev.request.url.startsWith(apiUrl)) return; - ev.respondWith( - caches.match(ev.request) - .then(response => { - return response || fetch(ev.request); - }) - .catch(() => { - return caches.match(`/?v=${version}`); - }) - ); + // Nothing to do }); //#endregion diff --git a/src/misc/check-hit-antenna.ts b/src/misc/check-hit-antenna.ts index 562d054563..0464f14131 100644 --- a/src/misc/check-hit-antenna.ts +++ b/src/misc/check-hit-antenna.ts @@ -4,7 +4,6 @@ import { User } from '../models/entities/user'; import { UserListJoinings, UserGroupJoinings } from '../models'; import parseAcct from './acct/parse'; import { getFullApAccount } from './convert-host'; -import { ensure } from '../prelude/ensure'; export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: User, followers: User['id'][]): Promise { if (note.visibility === 'specified') return false; @@ -24,7 +23,7 @@ export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: Us if (!listUsers.includes(note.userId)) return false; } else if (antenna.src === 'group') { - const joining = await UserGroupJoinings.findOne(antenna.userGroupJoiningId!).then(ensure); + const joining = await UserGroupJoinings.findOneOrFail(antenna.userGroupJoiningId!); const groupUsers = (await UserGroupJoinings.find({ userGroupId: joining.userGroupId diff --git a/src/misc/fetch-proxy-account.ts b/src/misc/fetch-proxy-account.ts index 0094ab8a56..537edf2891 100644 --- a/src/misc/fetch-proxy-account.ts +++ b/src/misc/fetch-proxy-account.ts @@ -1,10 +1,9 @@ import { fetchMeta } from './fetch-meta'; import { ILocalUser } from '../models/entities/user'; import { Users } from '../models'; -import { ensure } from '../prelude/ensure'; export async function fetchProxyAccount(): Promise { const meta = await fetchMeta(); if (meta.proxyAccountId == null) return null; - return await Users.findOne(meta.proxyAccountId).then(ensure) as ILocalUser; + return await Users.findOneOrFail(meta.proxyAccountId) as ILocalUser; } diff --git a/src/client/scripts/i18n.ts b/src/misc/i18n.ts similarity index 66% rename from src/client/scripts/i18n.ts rename to src/misc/i18n.ts index d535e236bb..4fa398763a 100644 --- a/src/client/scripts/i18n.ts +++ b/src/misc/i18n.ts @@ -1,14 +1,9 @@ -// Notice: Service Workerでも使用します export class I18n> { public locale: T; constructor(locale: T) { this.locale = locale; - if (_DEV_) { - console.log('i18n', this.locale); - } - //#region BIND this.t = this.t.bind(this); //#endregion @@ -20,12 +15,6 @@ export class I18n> { try { let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; - if (_DEV_) { - if (!str.includes('{')) { - console.warn(`i18n: '${key}' has no any arg. so ref prop directly instead of call this method.`); - } - } - if (args) { for (const [k, v] of Object.entries(args)) { str = str.replace(`{${k}}`, v); @@ -33,11 +22,7 @@ export class I18n> { } return str; } catch (e) { - if (_DEV_) { - console.warn(`missing localization '${key}'`); - return `⚠'${key}'⚠`; - } - + console.warn(`missing localization '${key}'`); return key; } } diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index 4fab52868f..3a9043fac6 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -4,6 +4,8 @@ import { User } from './user'; import { Page } from './page'; import { notificationTypes } from '../../types'; +// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも +// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン @Entity() export class UserProfile { @PrimaryColumn(id()) @@ -41,6 +43,11 @@ export class UserProfile { value: string; }[]; + @Column('varchar', { + length: 32, nullable: true, + }) + public lang: string | null; + @Column('varchar', { length: 512, nullable: true, comment: 'Remote URL of the user.' @@ -63,6 +70,11 @@ export class UserProfile { }) public emailVerified: boolean; + @Column('jsonb', { + default: ['follow', 'receiveFollowRequest', 'groupInvited'] + }) + public emailNotificationTypes: string[]; + @Column('varchar', { length: 128, nullable: true, }) diff --git a/src/models/repositories/abuse-user-report.ts b/src/models/repositories/abuse-user-report.ts index dbdaa5ee15..cb33d2506e 100644 --- a/src/models/repositories/abuse-user-report.ts +++ b/src/models/repositories/abuse-user-report.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import { AbuseUserReport } from '../entities/abuse-user-report'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; @EntityRepository(AbuseUserReport) @@ -9,7 +8,7 @@ export class AbuseUserReportRepository extends Repository { public async pack( src: AbuseUserReport['id'] | AbuseUserReport, ) { - const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const report = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: report.id, diff --git a/src/models/repositories/antenna.ts b/src/models/repositories/antenna.ts index 16ef2e5a39..b20da26c5d 100644 --- a/src/models/repositories/antenna.ts +++ b/src/models/repositories/antenna.ts @@ -1,6 +1,5 @@ import { EntityRepository, Repository } from 'typeorm'; import { Antenna } from '../entities/antenna'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; import { AntennaNotes, UserGroupJoinings } from '..'; @@ -11,7 +10,7 @@ export class AntennaRepository extends Repository { public async pack( src: Antenna['id'] | Antenna, ): Promise { - const antenna = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const antenna = typeof src === 'object' ? src : await this.findOneOrFail(src); const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null; const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOne(antenna.userGroupJoiningId) : null; diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts index 45d8d16c51..f5cc10787a 100644 --- a/src/models/repositories/app.ts +++ b/src/models/repositories/app.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { App } from '../entities/app'; import { AccessTokens } from '..'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; export type PackedApp = SchemaType; @@ -23,7 +22,7 @@ export class AppRepository extends Repository { includeProfileImageIds: false }, options); - const app = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const app = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: app.id, diff --git a/src/models/repositories/auth-session.ts b/src/models/repositories/auth-session.ts index a6a4d46de6..e985d6925f 100644 --- a/src/models/repositories/auth-session.ts +++ b/src/models/repositories/auth-session.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Apps } from '..'; import { AuthSession } from '../entities/auth-session'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; @EntityRepository(AuthSession) @@ -10,7 +9,7 @@ export class AuthSessionRepository extends Repository { src: AuthSession['id'] | AuthSession, me?: any ) { - const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const session = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: session.id, diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts index 9ebe6bbf59..314f459e65 100644 --- a/src/models/repositories/blocking.ts +++ b/src/models/repositories/blocking.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import { Blocking } from '../entities/blocking'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; @@ -13,7 +12,7 @@ export class BlockingRepository extends Repository { src: Blocking['id'] | Blocking, me?: any ): Promise { - const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const blocking = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: blocking.id, diff --git a/src/models/repositories/channel.ts b/src/models/repositories/channel.ts index 2a90419922..2654c9d6f3 100644 --- a/src/models/repositories/channel.ts +++ b/src/models/repositories/channel.ts @@ -1,6 +1,5 @@ import { EntityRepository, Repository } from 'typeorm'; import { Channel } from '../entities/channel'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; import { DriveFiles, ChannelFollowings, NoteUnreads } from '..'; import { User } from '../entities/user'; @@ -13,7 +12,7 @@ export class ChannelRepository extends Repository { src: Channel['id'] | Channel, me?: User['id'] | User | null | undefined, ): Promise { - const channel = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const channel = typeof src === 'object' ? src : await this.findOneOrFail(src); const meId = me ? typeof me === 'string' ? me : me.id : null; const banner = channel.bannerId ? await DriveFiles.findOne(channel.bannerId) : null; diff --git a/src/models/repositories/clip.ts b/src/models/repositories/clip.ts index 11f743349f..84891a4372 100644 --- a/src/models/repositories/clip.ts +++ b/src/models/repositories/clip.ts @@ -1,6 +1,5 @@ import { EntityRepository, Repository } from 'typeorm'; import { Clip } from '../entities/clip'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; import { Users } from '..'; import { awaitAll } from '../../prelude/await-all'; @@ -12,7 +11,7 @@ export class ClipRepository extends Repository { public async pack( src: Clip['id'] | Clip, ): Promise { - const clip = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const clip = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: clip.id, diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index ab22d2dc09..3d013b8bb2 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -3,7 +3,6 @@ import { DriveFile } from '../entities/drive-file'; import { Users, DriveFolders } from '..'; import { User } from '../entities/user'; import { toPuny } from '../../misc/convert-host'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; import config from '../../config'; @@ -103,7 +102,7 @@ export class DriveFileRepository extends Repository { self: false }, options); - const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const file = typeof src === 'object' ? src : await this.findOneOrFail(src); const meta = await fetchMeta(); diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts index dee424cccb..2a18220384 100644 --- a/src/models/repositories/drive-folder.ts +++ b/src/models/repositories/drive-folder.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { DriveFolders, DriveFiles } from '..'; import { DriveFolder } from '../entities/drive-folder'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; @@ -26,7 +25,7 @@ export class DriveFolderRepository extends Repository { detail: false }, options); - const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const folder = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: folder.id, diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts index b485b37da5..3490a6ac86 100644 --- a/src/models/repositories/emoji.ts +++ b/src/models/repositories/emoji.ts @@ -1,13 +1,12 @@ import { EntityRepository, Repository } from 'typeorm'; import { Emoji } from '../entities/emoji'; -import { ensure } from '../../prelude/ensure'; @EntityRepository(Emoji) export class EmojiRepository extends Repository { public async pack( src: Emoji['id'] | Emoji, ) { - const emoji = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: emoji.id, diff --git a/src/models/repositories/follow-request.ts b/src/models/repositories/follow-request.ts index 451ed8e2d5..0d96b8eb53 100644 --- a/src/models/repositories/follow-request.ts +++ b/src/models/repositories/follow-request.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { FollowRequest } from '../entities/follow-request'; import { Users } from '..'; -import { ensure } from '../../prelude/ensure'; @EntityRepository(FollowRequest) export class FollowRequestRepository extends Repository { @@ -9,7 +8,7 @@ export class FollowRequestRepository extends Repository { src: FollowRequest['id'] | FollowRequest, me?: any ) { - const request = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const request = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: request.id, diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts index 58728a3380..986f107e7d 100644 --- a/src/models/repositories/following.ts +++ b/src/models/repositories/following.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import { Following } from '../entities/following'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; @@ -57,7 +56,7 @@ export class FollowingRepository extends Repository { populateFollower?: boolean; } ): Promise { - const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const following = typeof src === 'object' ? src : await this.findOneOrFail(src); if (opts == null) opts = {}; diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts index c380f5251e..e23247f664 100644 --- a/src/models/repositories/games/reversi/game.ts +++ b/src/models/repositories/games/reversi/game.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '../../..'; import { ReversiGame } from '../../../entities/games/reversi/game'; -import { ensure } from '../../../../prelude/ensure'; @EntityRepository(ReversiGame) export class ReversiGameRepository extends Repository { @@ -16,7 +15,7 @@ export class ReversiGameRepository extends Repository { detail: true }, options); - const game = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const game = typeof src === 'object' ? src : await this.findOneOrFail(src); const meId = me ? typeof me === 'string' ? me : me.id : null; return { diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts index 86c9204456..51f17c9a4e 100644 --- a/src/models/repositories/games/reversi/matching.ts +++ b/src/models/repositories/games/reversi/matching.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { ReversiMatching } from '../../../entities/games/reversi/matching'; import { Users } from '../../..'; -import { ensure } from '../../../../prelude/ensure'; import { awaitAll } from '../../../../prelude/await-all'; @EntityRepository(ReversiMatching) @@ -10,7 +9,7 @@ export class ReversiMatchingRepository extends Repository { src: ReversiMatching['id'] | ReversiMatching, me: any ) { - const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const matching = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: matching.id, diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts index d70e95bc12..0e04c25864 100644 --- a/src/models/repositories/messaging-message.ts +++ b/src/models/repositories/messaging-message.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { MessagingMessage } from '../entities/messaging-message'; import { Users, DriveFiles, UserGroups } from '..'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; export type PackedMessagingMessage = SchemaType; @@ -25,7 +24,7 @@ export class MessagingMessageRepository extends Repository { populateGroup: true, }; - const message = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const message = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: message.id, diff --git a/src/models/repositories/moderation-logs.ts b/src/models/repositories/moderation-logs.ts index d6e04795bb..3d4c075701 100644 --- a/src/models/repositories/moderation-logs.ts +++ b/src/models/repositories/moderation-logs.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import { ModerationLog } from '../entities/moderation-log'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; @EntityRepository(ModerationLog) @@ -9,7 +8,7 @@ export class ModerationLogRepository extends Repository { public async pack( src: ModerationLog['id'] | ModerationLog, ) { - const log = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const log = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: log.id, diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts index 763e04bb3d..5fd409df78 100644 --- a/src/models/repositories/muting.ts +++ b/src/models/repositories/muting.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import { Muting } from '../entities/muting'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; @@ -13,7 +12,7 @@ export class MutingRepository extends Repository { src: Muting['id'] | Muting, me?: any ): Promise { - const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const muting = typeof src === 'object' ? src : await this.findOneOrFail(src); return await awaitAll({ id: muting.id, diff --git a/src/models/repositories/note-favorite.ts b/src/models/repositories/note-favorite.ts index 37cfbc0025..eb2ffff4c1 100644 --- a/src/models/repositories/note-favorite.ts +++ b/src/models/repositories/note-favorite.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { NoteFavorite } from '../entities/note-favorite'; import { Notes } from '..'; -import { ensure } from '../../prelude/ensure'; @EntityRepository(NoteFavorite) export class NoteFavoriteRepository extends Repository { @@ -9,7 +8,7 @@ export class NoteFavoriteRepository extends Repository { src: NoteFavorite['id'] | NoteFavorite, me?: any ) { - const favorite = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const favorite = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: favorite.id, diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts index 3439f3c8cb..785a876bf8 100644 --- a/src/models/repositories/note-reaction.ts +++ b/src/models/repositories/note-reaction.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { NoteReaction } from '../entities/note-reaction'; import { Users } from '..'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; import { convertLegacyReaction } from '../../misc/reaction-lib'; @@ -13,7 +12,7 @@ export class NoteReactionRepository extends Repository { src: NoteReaction['id'] | NoteReaction, me?: any ): Promise { - const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const reaction = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: reaction.id, diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index b60744bb2b..32552db2fe 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -2,7 +2,6 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { Note } from '../entities/note'; import { User } from '../entities/user'; import { Emojis, Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '..'; -import { ensure } from '../../prelude/ensure'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '../../misc/reaction-lib'; @@ -92,11 +91,11 @@ export class NoteRepository extends Repository { }, options); const meId = me ? typeof me === 'string' ? me : me.id : null; - const note = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const note = typeof src === 'object' ? src : await this.findOneOrFail(src); const host = note.userHost; async function populatePoll() { - const poll = await Polls.findOne(note.id).then(ensure); + const poll = await Polls.findOneOrFail(note.id); const choices = poll.choices.map(c => ({ text: c, votes: poll.votes[poll.choices.indexOf(c)], @@ -413,5 +412,63 @@ export const packedNoteSchema = { optional: true as const, nullable: true as const, ref: 'Channel' }, + localOnly: { + type: 'boolean' as const, + optional: false as const, nullable: true as const, + }, + emojis: { + type: 'array' as const, + optional: false as const, nullable: false as const, + items: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + name: { + type: 'string' as const, + optional: false as const, nullable: false as const, + }, + url: { + type: 'string' as const, + optional: false as const, nullable: false as const, + }, + }, + }, + }, + reactions: { + type: 'object' as const, + optional: false as const, nullable: false as const, + description: 'Key is either Unicode emoji or custom emoji, value is count of that emoji reaction.', + }, + renoteCount: { + type: 'number' as const, + optional: false as const, nullable: false as const, + }, + repliesCount: { + type: 'number' as const, + optional: false as const, nullable: false as const, + }, + uri: { + type: 'string' as const, + optional: false as const, nullable: true as const, + description: 'The URI of a note. it will be null when the note is local.', + }, + url: { + type: 'string' as const, + optional: false as const, nullable: true as const, + description: 'The human readable url of a note. it will be null when the note is local.', + }, + _featuredId_: { + type: 'string' as const, + optional: false as const, nullable: true as const, + }, + _prId_: { + type: 'string' as const, + optional: false as const, nullable: true as const, + }, + myReaction: { + type: 'object' as const, + optional: true as const, nullable: true as const, + description: 'Key is either Unicode emoji or custom emoji, value is count of that emoji reaction.', + }, }, }; diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 40f43d6c15..16de6c8c25 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users, Notes, UserGroupInvitations, AccessTokens } from '..'; import { Notification } from '../entities/notification'; -import { ensure } from '../../prelude/ensure'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; @@ -12,8 +11,8 @@ export class NotificationRepository extends Repository { public async pack( src: Notification['id'] | Notification, ): Promise { - const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - const token = notification.appAccessTokenId ? await AccessTokens.findOne(notification.appAccessTokenId).then(ensure) : null; + const notification = typeof src === 'object' ? src : await this.findOneOrFail(src); + const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null; return await awaitAll({ id: notification.id, diff --git a/src/models/repositories/page-like.ts b/src/models/repositories/page-like.ts index 3e7e803fdb..94b1685e5e 100644 --- a/src/models/repositories/page-like.ts +++ b/src/models/repositories/page-like.ts @@ -1,7 +1,6 @@ import { EntityRepository, Repository } from 'typeorm'; import { PageLike } from '../entities/page-like'; import { Pages } from '..'; -import { ensure } from '../../prelude/ensure'; @EntityRepository(PageLike) export class PageLikeRepository extends Repository { @@ -9,7 +8,7 @@ export class PageLikeRepository extends Repository { src: PageLike['id'] | PageLike, me?: any ) { - const like = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const like = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: like.id, diff --git a/src/models/repositories/page.ts b/src/models/repositories/page.ts index 3889bf59a7..1b30b6645b 100644 --- a/src/models/repositories/page.ts +++ b/src/models/repositories/page.ts @@ -5,7 +5,6 @@ import { Users, DriveFiles, PageLikes } from '..'; import { awaitAll } from '../../prelude/await-all'; import { DriveFile } from '../entities/drive-file'; import { User } from '../entities/user'; -import { ensure } from '../../prelude/ensure'; export type PackedPage = SchemaType; @@ -16,7 +15,7 @@ export class PageRepository extends Repository { me?: User['id'] | User | null | undefined, ): Promise { const meId = me ? typeof me === 'string' ? me : me.id : null; - const page = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const page = typeof src === 'object' ? src : await this.findOneOrFail(src); const attachedFiles: Promise[] = []; const collectFile = (xs: any[]) => { diff --git a/src/models/repositories/user-group-invitation.ts b/src/models/repositories/user-group-invitation.ts index 0d3ad525c3..2aa890361c 100644 --- a/src/models/repositories/user-group-invitation.ts +++ b/src/models/repositories/user-group-invitation.ts @@ -1,14 +1,13 @@ import { EntityRepository, Repository } from 'typeorm'; import { UserGroupInvitation } from '../entities/user-group-invitation'; import { UserGroups } from '..'; -import { ensure } from '../../prelude/ensure'; @EntityRepository(UserGroupInvitation) export class UserGroupInvitationRepository extends Repository { public async pack( src: UserGroupInvitation['id'] | UserGroupInvitation, ) { - const invitation = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const invitation = typeof src === 'object' ? src : await this.findOneOrFail(src); return { id: invitation.id, diff --git a/src/models/repositories/user-group.ts b/src/models/repositories/user-group.ts index 5ff75047c8..a1b226f154 100644 --- a/src/models/repositories/user-group.ts +++ b/src/models/repositories/user-group.ts @@ -1,6 +1,5 @@ import { EntityRepository, Repository } from 'typeorm'; import { UserGroup } from '../entities/user-group'; -import { ensure } from '../../prelude/ensure'; import { UserGroupJoinings } from '..'; import { SchemaType } from '../../misc/schema'; @@ -11,7 +10,7 @@ export class UserGroupRepository extends Repository { public async pack( src: UserGroup['id'] | UserGroup, ): Promise { - const userGroup = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const userGroup = typeof src === 'object' ? src : await this.findOneOrFail(src); const users = await UserGroupJoinings.find({ userGroupId: userGroup.id diff --git a/src/models/repositories/user-list.ts b/src/models/repositories/user-list.ts index 8842118be4..9421aeb0c7 100644 --- a/src/models/repositories/user-list.ts +++ b/src/models/repositories/user-list.ts @@ -1,6 +1,5 @@ import { EntityRepository, Repository } from 'typeorm'; import { UserList } from '../entities/user-list'; -import { ensure } from '../../prelude/ensure'; import { UserListJoinings } from '..'; import { SchemaType } from '../../misc/schema'; @@ -11,7 +10,7 @@ export class UserListRepository extends Repository { public async pack( src: UserList['id'] | UserList, ): Promise { - const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const userList = typeof src === 'object' ? src : await this.findOneOrFail(src); const users = await UserListJoinings.find({ userListId: userList.id diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 88861224a4..7502e7a08e 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '..'; -import { ensure } from '../../prelude/ensure'; import config from '../../config'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; @@ -157,7 +156,7 @@ export class UserRepository extends Repository { includeSecrets: false }, options); - const user = typeof src === 'object' ? src : await this.findOne(src).then(ensure); + const user = typeof src === 'object' ? src : await this.findOneOrFail(src); const meId = me ? typeof me === 'string' ? me : me.id : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; @@ -165,7 +164,7 @@ export class UserRepository extends Repository { where: { userId: user.id }, order: { id: 'DESC' } }) : []; - const profile = opts.detail ? await UserProfiles.findOne(user.id).then(ensure) : null; + const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; const falsy = opts.detail ? false : undefined; @@ -213,6 +212,7 @@ export class UserRepository extends Repository { description: profile!.description, location: profile!.location, birthday: profile!.birthday, + lang: profile!.lang, fields: profile!.fields, followersCount: user.followersCount, followingCount: user.followingCount, @@ -258,7 +258,8 @@ export class UserRepository extends Repository { hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), integrations: profile!.integrations, mutedWords: profile!.mutedWords, - mutingNotificationTypes: profile?.mutingNotificationTypes, + mutingNotificationTypes: profile!.mutingNotificationTypes, + emailNotificationTypes: profile!.emailNotificationTypes, } : {}), ...(opts.includeSecrets ? { diff --git a/src/prelude/ensure.ts b/src/prelude/ensure.ts deleted file mode 100644 index 1af281c056..0000000000 --- a/src/prelude/ensure.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * 値が null または undefined の場合はエラーを発生させ、そうでない場合は値をそのまま返します - */ -export function ensure(x: T): NonNullable { - if (x == null) { - throw new Error('ぬるぽ'); - } else { - return x!; - } -} diff --git a/src/queue/processors/db/export-notes.ts b/src/queue/processors/db/export-notes.ts index 0fd8c02c4a..f76a47aacd 100644 --- a/src/queue/processors/db/export-notes.ts +++ b/src/queue/processors/db/export-notes.ts @@ -9,7 +9,6 @@ import { Users, Notes, Polls } from '../../../models'; import { MoreThan } from 'typeorm'; import { Note } from '../../../models/entities/note'; import { Poll } from '../../../models/entities/poll'; -import { ensure } from '../../../prelude/ensure'; const logger = queueLogger.createSubLogger('export-notes'); @@ -70,7 +69,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise { for (const note of notes) { let poll: Poll | undefined; if (note.hasPoll) { - poll = await Polls.findOne({ noteId: note.id }).then(ensure); + poll = await Polls.findOneOrFail({ noteId: note.id }); } const content = JSON.stringify(serialize(note, poll)); await new Promise((res, rej) => { diff --git a/src/remote/activitypub/db-resolver.ts b/src/remote/activitypub/db-resolver.ts index cad2212b70..26162b6b23 100644 --- a/src/remote/activitypub/db-resolver.ts +++ b/src/remote/activitypub/db-resolver.ts @@ -6,7 +6,6 @@ import { MessagingMessage } from '../../models/entities/messaging-message'; import { Notes, Users, UserPublickeys, MessagingMessages } from '../../models'; import { IObject, getApId } from './type'; import { resolvePerson } from './models/person'; -import { ensure } from '../../prelude/ensure'; import escapeRegexp = require('escape-regexp'); export default class DbResolver { @@ -99,7 +98,7 @@ export default class DbResolver { if (user == null) return null; - const key = await UserPublickeys.findOne(user.id).then(ensure); + const key = await UserPublickeys.findOneOrFail(user.id); return { user, diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index 84a1040b2e..dd8086cbeb 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -5,7 +5,6 @@ import { fetchMeta } from '../../../misc/fetch-meta'; import { apLogger } from '../logger'; import { DriveFile } from '../../../models/entities/drive-file'; import { DriveFiles } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; const logger = apLogger; @@ -40,7 +39,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 73a2ebc023..5840d67020 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -24,7 +24,6 @@ import { toPuny } from '../../../misc/convert-host'; import { UserProfile } from '../../../models/entities/user-profile'; import { validActor } from '../../../remote/activitypub/type'; import { getConnection } from 'typeorm'; -import { ensure } from '../../../prelude/ensure'; import { toArray } from '../../../prelude/array'; import { fetchInstanceMetadata } from '../../../services/fetch-instance-metadata'; import { normalizeForSearch } from '../../../misc/normalize-for-search'; @@ -457,7 +456,7 @@ export function analyzeAttachments(attachments: IObject | IObject[] | undefined) } export async function updateFeatured(userId: User['id']) { - const user = await Users.findOne(userId).then(ensure); + const user = await Users.findOneOrFail(userId); if (!Users.isRemoteUser(user)) return; if (!user.featured) return; diff --git a/src/remote/activitypub/renderer/follow-user.ts b/src/remote/activitypub/renderer/follow-user.ts index 6d354803e5..bfc91bb4cb 100644 --- a/src/remote/activitypub/renderer/follow-user.ts +++ b/src/remote/activitypub/renderer/follow-user.ts @@ -1,13 +1,12 @@ import config from '../../../config'; import { Users } from '../../../models'; import { User } from '../../../models/entities/user'; -import { ensure } from '../../../prelude/ensure'; /** * Convert (local|remote)(Follower|Followee)ID to URL * @param id Follower|Followee ID */ export default async function renderFollowUser(id: User['id']): Promise { - const user = await Users.findOne(id).then(ensure); + const user = await Users.findOneOrFail(id); return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri; } diff --git a/src/remote/activitypub/renderer/index.ts b/src/remote/activitypub/renderer/index.ts index a34febff2f..e74affdadf 100644 --- a/src/remote/activitypub/renderer/index.ts +++ b/src/remote/activitypub/renderer/index.ts @@ -4,7 +4,6 @@ import { IActivity } from '../type'; import { LdSignature } from '../misc/ld-signature'; import { ILocalUser } from '../../../models/entities/user'; import { UserKeypairs } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; export const renderActivity = (x: any): IActivity | null => { if (x == null) return null; @@ -24,9 +23,9 @@ export const renderActivity = (x: any): IActivity | null => { export const attachLdSignature = async (activity: any, user: ILocalUser): Promise => { if (activity == null) return null; - const keypair = await UserKeypairs.findOne({ + const keypair = await UserKeypairs.findOneOrFail({ userId: user.id - }).then(ensure); + }); const obj = { // as non-standards diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts index 6878a402b8..8e3e0e9ba1 100644 --- a/src/remote/activitypub/renderer/note.ts +++ b/src/remote/activitypub/renderer/note.ts @@ -10,7 +10,6 @@ import { DriveFiles, Notes, Users, Emojis, Polls } from '../../../models'; import { In } from 'typeorm'; import { Emoji } from '../../../models/entities/emoji'; import { Poll } from '../../../models/entities/poll'; -import { ensure } from '../../../prelude/ensure'; export default async function renderNote(note: Note, dive = true, isTalk = false): Promise { const getPromisedFiles = async (ids: string[]) => { @@ -54,7 +53,7 @@ export default async function renderNote(note: Note, dive = true, isTalk = false } } - const user = await Users.findOne(note.userId).then(ensure); + const user = await Users.findOneOrFail(note.userId); const attributedTo = `${config.url}/users/${user.id}`; diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index 4462f88315..4907e3bc6f 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -9,7 +9,6 @@ import renderEmoji from './emoji'; import { IIdentifier } from '../models/identifier'; import renderHashtag from './hashtag'; import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; @@ -18,7 +17,7 @@ export async function renderPerson(user: ILocalUser) { const [avatar, banner, profile] = await Promise.all([ user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined), user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined), - UserProfiles.findOne(user.id).then(ensure) + UserProfiles.findOneOrFail(user.id) ]); const attachment: { @@ -50,7 +49,7 @@ export async function renderPerson(user: ILocalUser) { ...hashtagTags, ]; - const keypair = await UserKeypairs.findOne(user.id).then(ensure); + const keypair = await UserKeypairs.findOneOrFail(user.id); const person = { type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person', diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 0edfcee1e3..2f07351635 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -6,7 +6,6 @@ import * as crypto from 'crypto'; import config from '../../config'; import { ILocalUser } from '../../models/entities/user'; import { UserKeypairs } from '../../models'; -import { ensure } from '../../prelude/ensure'; import { getAgentByUrl } from '../../misc/fetch'; import { URL } from 'url'; import got from 'got'; @@ -23,9 +22,9 @@ export default async (user: ILocalUser, url: string, object: any) => { sha256.update(data); const hash = sha256.digest('base64'); - const keypair = await UserKeypairs.findOne({ + const keypair = await UserKeypairs.findOneOrFail({ userId: user.id - }).then(ensure); + }); await new Promise((resolve, reject) => { const req = https.request({ @@ -75,9 +74,9 @@ export default async (user: ILocalUser, url: string, object: any) => { export async function signedGet(url: string, user: ILocalUser) { const timeout = 10 * 1000; - const keypair = await UserKeypairs.findOne({ + const keypair = await UserKeypairs.findOneOrFail({ userId: user.id - }).then(ensure); + }); const req = got.get(url, { headers: { diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts index c665fe28ca..bf71258625 100644 --- a/src/server/activitypub.ts +++ b/src/server/activitypub.ts @@ -16,7 +16,6 @@ import { isSelfHost } from '../misc/convert-host'; import { Notes, Users, Emojis, UserKeypairs, NoteReactions } from '../models'; import { ILocalUser, User } from '../models/entities/user'; import { In } from 'typeorm'; -import { ensure } from '../prelude/ensure'; import { renderLike } from '../remote/activitypub/renderer/like'; // Init router @@ -136,7 +135,7 @@ router.get('/users/:user/publickey', async ctx => { return; } - const keypair = await UserKeypairs.findOne(user.id).then(ensure); + const keypair = await UserKeypairs.findOneOrFail(user.id); if (Users.isLocalUser(user)) { ctx.body = renderActivity(renderKey(user, keypair)); diff --git a/src/server/activitypub/featured.ts b/src/server/activitypub/featured.ts index 80a7852f59..66ad2aa86e 100644 --- a/src/server/activitypub/featured.ts +++ b/src/server/activitypub/featured.ts @@ -5,7 +5,6 @@ import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-c import { setResponseType } from '../activitypub'; import renderNote from '../../remote/activitypub/renderer/note'; import { Users, Notes, UserNotePinings } from '../../models'; -import { ensure } from '../../prelude/ensure'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; @@ -27,7 +26,7 @@ export default async (ctx: Router.RouterContext) => { }); const pinnedNotes = await Promise.all(pinings.map(pining => - Notes.findOne(pining.noteId).then(ensure))); + Notes.findOneOrFail(pining.noteId))); const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note))); diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts index 03cf65bce6..3c1b07a679 100644 --- a/src/server/activitypub/outbox.ts +++ b/src/server/activitypub/outbox.ts @@ -15,7 +15,6 @@ import { Users, Notes } from '../../models'; import { makePaginationQuery } from '../api/common/make-pagination-query'; import { Brackets } from 'typeorm'; import { Note } from '../../models/entities/note'; -import { ensure } from '../../prelude/ensure'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; @@ -101,7 +100,7 @@ export default async (ctx: Router.RouterContext) => { */ export async function packActivity(note: Note): Promise { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { - const renote = await Notes.findOne(note.renoteId).then(ensure); + const renote = await Notes.findOneOrFail(note.renoteId); return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); } diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts index 0785372b3b..0374ca35ea 100644 --- a/src/server/api/authenticate.ts +++ b/src/server/api/authenticate.ts @@ -1,7 +1,6 @@ import isNativeToken from './common/is-native-token'; import { User } from '../../models/entities/user'; import { Users, AccessTokens, Apps } from '../../models'; -import { ensure } from '../../prelude/ensure'; import { AccessToken } from '../../models/entities/access-token'; export default async (token: string): Promise<[User | null | undefined, AccessToken | null | undefined]> => { @@ -43,7 +42,7 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo if (accessToken.appId) { const app = await Apps - .findOne(accessToken.appId).then(ensure); + .findOneOrFail(accessToken.appId); return [user, { id: accessToken.id, diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index 098d20e72d..3f47c13385 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -3,7 +3,6 @@ import { Note } from '../../../models/entities/note'; import { User } from '../../../models/entities/user'; import { Notes, UserProfiles, NoteReactions } from '../../../models'; import { generateMutedUserQuery } from './generate-muted-user-query'; -import { ensure } from '../../../prelude/ensure'; // TODO: リアクション、Renote、返信などをしたノートは除外する @@ -11,7 +10,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { if (timeline.length < 5) return; if (user) { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); if (!profile.injectFeaturedNote) return; } diff --git a/src/server/api/common/inject-promo.ts b/src/server/api/common/inject-promo.ts index f694ce6ea0..2c16ca4cf7 100644 --- a/src/server/api/common/inject-promo.ts +++ b/src/server/api/common/inject-promo.ts @@ -2,7 +2,6 @@ import rndstr from 'rndstr'; import { Note } from '../../../models/entities/note'; import { User } from '../../../models/entities/user'; import { PromoReads, PromoNotes, Notes, Users } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; export async function injectPromo(timeline: Note[], user?: User | null) { if (timeline.length < 5) return; @@ -23,10 +22,10 @@ export async function injectPromo(timeline: Note[], user?: User | null) { // Pick random promo const promo = promos[Math.floor(Math.random() * promos.length)]; - const note = await Notes.findOne(promo.noteId).then(ensure); + const note = await Notes.findOneOrFail(promo.noteId); // Join - note.user = await Users.findOne(note.userId).then(ensure); + note.user = await Users.findOneOrFail(note.userId); (note as any)._prId_ = rndstr('a-z0-9', 8); diff --git a/src/server/api/endpoints/admin/federation/remove-all-following.ts b/src/server/api/endpoints/admin/federation/remove-all-following.ts index 76497c0dd8..e593193552 100644 --- a/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import define from '../../../define'; import deleteFollowing from '../../../../../services/following/delete'; import { Followings, Users } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { tags: ['admin'], @@ -23,8 +22,8 @@ export default define(meta, async (ps, me) => { }); const pairs = await Promise.all(followings.map(f => Promise.all([ - Users.findOne(f.followerId).then(ensure), - Users.findOne(f.followeeId).then(ensure) + Users.findOneOrFail(f.followerId), + Users.findOneOrFail(f.followeeId) ]))); for (const pair of pairs) { diff --git a/src/server/api/endpoints/admin/send-email.ts b/src/server/api/endpoints/admin/send-email.ts index 9af931ad99..c0e77e1621 100644 --- a/src/server/api/endpoints/admin/send-email.ts +++ b/src/server/api/endpoints/admin/send-email.ts @@ -22,5 +22,5 @@ export const meta = { }; export default define(meta, async (ps) => { - await sendEmail(ps.to, ps.subject, ps.text); + await sendEmail(ps.to, ps.subject, ps.text, ps.text); }); diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index e98242a3c3..6d4d31fa1e 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -4,7 +4,6 @@ import define from '../../define'; import { ApiError } from '../../error'; import { AuthSessions, AccessTokens, Apps } from '../../../../models'; import { genId } from '../../../../misc/gen-id'; -import { ensure } from '../../../../prelude/ensure'; import { secureRndstr } from '../../../../misc/secure-rndstr'; export const meta = { @@ -49,7 +48,7 @@ export default define(meta, async (ps, user) => { if (exist == null) { // Lookup app - const app = await Apps.findOne(session.appId).then(ensure); + const app = await Apps.findOneOrFail(session.appId); // Generate Hash const sha256 = crypto.createHash('sha256'); diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts index 7b474c8295..68d0c7bdf1 100644 --- a/src/server/api/endpoints/auth/session/userkey.ts +++ b/src/server/api/endpoints/auth/session/userkey.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import define from '../../../define'; import { ApiError } from '../../../error'; import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { tags: ['auth'], @@ -92,10 +91,10 @@ export default define(meta, async (ps) => { } // Lookup access token - const accessToken = await AccessTokens.findOne({ + const accessToken = await AccessTokens.findOneOrFail({ appId: app.id, userId: session.userId - }).then(ensure); + }); // Delete session AuthSessions.delete(session.id); diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index 3d0c092adb..e5b65e0930 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,6 +1,5 @@ import define from '../define'; import { RegistryItems, UserProfiles, Users } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; import { genId } from '../../../misc/gen-id'; export const meta = { @@ -25,7 +24,7 @@ export default define(meta, async (ps, user, token) => { const isSecure = token == null; // TODO: そのうち消す - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); for (const [k, v] of Object.entries(profile.clientData)) { await RegistryItems.insert({ id: genId(), diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index 7d35f929e0..9a74d7675b 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as speakeasy from 'speakeasy'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true as const, @@ -19,7 +18,7 @@ export const meta = { export default define(meta, async (ps, user) => { const token = ps.token.replace(/\s/g, ''); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); if (profile.twoFactorTempSecret == null) { throw new Error('二段階認証の設定が開始されていません'); diff --git a/src/server/api/endpoints/i/2fa/key-done.ts b/src/server/api/endpoints/i/2fa/key-done.ts index 8ac165e629..4634944ca7 100644 --- a/src/server/api/endpoints/i/2fa/key-done.ts +++ b/src/server/api/endpoints/i/2fa/key-done.ts @@ -9,7 +9,6 @@ import { AttestationChallenges, Users } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; import config from '../../../../../config'; import { procedures, hash } from '../../../2fa'; import { publishMainStream } from '../../../../../services/stream'; @@ -43,7 +42,7 @@ export const meta = { const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/2fa/register-key.ts b/src/server/api/endpoints/i/2fa/register-key.ts index e189519a4c..d5cc11c7fb 100644 --- a/src/server/api/endpoints/i/2fa/register-key.ts +++ b/src/server/api/endpoints/i/2fa/register-key.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles, AttestationChallenges } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; import { promisify } from 'util'; import * as crypto from 'crypto'; import { genId } from '../../../../../misc/gen-id'; @@ -23,7 +22,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index 784b276a26..a39b2963e9 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -5,7 +5,6 @@ import * as QRCode from 'qrcode'; import config from '../../../../../config'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true as const, @@ -20,7 +19,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/2fa/remove-key.ts b/src/server/api/endpoints/i/2fa/remove-key.ts index 3eb92ba19d..135f0eb284 100644 --- a/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/src/server/api/endpoints/i/2fa/remove-key.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles, UserSecurityKeys, Users } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; import { publishMainStream } from '../../../../../services/stream'; export const meta = { @@ -21,7 +20,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index f1287b2dca..e809f40c71 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true as const, @@ -17,7 +16,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/change-password.ts b/src/server/api/endpoints/i/change-password.ts index bc2ec3d7b5..0a8b86e665 100644 --- a/src/server/api/endpoints/i/change-password.ts +++ b/src/server/api/endpoints/i/change-password.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { UserProfiles } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true as const, @@ -21,7 +20,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.currentPassword, profile.password!); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts index 49a3349170..0f04c4c92d 100644 --- a/src/server/api/endpoints/i/delete-account.ts +++ b/src/server/api/endpoints/i/delete-account.ts @@ -2,7 +2,6 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { Users, UserProfiles } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; import { doPostSuspend } from '../../../../services/suspend-user'; export const meta = { @@ -18,7 +17,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/regenerate-token.ts b/src/server/api/endpoints/i/regenerate-token.ts index 5d322aa122..3596e20197 100644 --- a/src/server/api/endpoints/i/regenerate-token.ts +++ b/src/server/api/endpoints/i/regenerate-token.ts @@ -4,7 +4,6 @@ import { publishMainStream } from '../../../../services/stream'; import generateUserToken from '../../common/generate-native-user-token'; import define from '../../define'; import { Users, UserProfiles } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true as const, @@ -19,7 +18,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index 20d9703320..730918aebe 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -6,7 +6,6 @@ import config from '../../../../config'; import * as ms from 'ms'; import * as bcrypt from 'bcryptjs'; import { Users, UserProfiles } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; import { sendEmail } from '../../../../services/send-email'; import { ApiError } from '../../error'; @@ -40,7 +39,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -72,7 +71,9 @@ export default define(meta, async (ps, user) => { const link = `${config.url}/verify-email/${code}`; - sendEmail(ps.email, 'Email verification', `To verify email, please click this link: ${link}`); + sendEmail(ps.email, 'Email verification', + `To verify email, please click this link:
${link}`, + `To verify email, please click this link: ${link}`); } return iObj; diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index e4c0e8cec9..3d7f1fa76f 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -13,7 +13,6 @@ import { ApiError } from '../../error'; import { Users, DriveFiles, UserProfiles, Pages } from '../../../../models'; import { User } from '../../../../models/entities/user'; import { UserProfile } from '../../../../models/entities/user-profile'; -import { ensure } from '../../../../prelude/ensure'; import { notificationTypes } from '../../../../types'; import { normalizeForSearch } from '../../../../misc/normalize-for-search'; @@ -161,6 +160,10 @@ export const meta = { mutingNotificationTypes: { validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])) }, + + emailNotificationTypes: { + validator: $.optional.arr($.str) + }, }, errors: { @@ -202,11 +205,11 @@ export default define(meta, async (ps, user, token) => { const updates = {} as Partial; const profileUpdates = {} as Partial; - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); if (ps.name !== undefined) updates.name = ps.name; if (ps.description !== undefined) profileUpdates.description = ps.description; - //if (ps.lang !== undefined) updates.lang = ps.lang; + if (ps.lang !== undefined) profileUpdates.lang = ps.lang; if (ps.location !== undefined) profileUpdates.location = ps.location; if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; @@ -226,6 +229,7 @@ export default define(meta, async (ps, user, token) => { if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; + if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; if (ps.avatarId) { const avatar = await DriveFiles.findOne(ps.avatarId); diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index 65565ee3ab..5afd911ca9 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -6,7 +6,6 @@ import * as ms from 'ms'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; import { Users } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; export const meta = { desc: { @@ -62,5 +61,5 @@ export default define(meta, async (ps, user) => { } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await deleteNote(await Users.findOne(note.userId).then(ensure), note); + await deleteNote(await Users.findOneOrFail(note.userId), note); }); diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index 1e6ab472cc..6113d7ea9a 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -13,7 +13,6 @@ import { PollVotes, NoteWatchings, Users, Polls } from '../../../../../models'; import { Not } from 'typeorm'; import { IRemoteUser } from '../../../../../models/entities/user'; import { genId } from '../../../../../misc/gen-id'; -import { ensure } from '../../../../../prelude/ensure'; export const meta = { desc: { @@ -87,7 +86,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.noPoll); } - const poll = await Polls.findOne({ noteId: note.id }).then(ensure); + const poll = await Polls.findOneOrFail({ noteId: note.id }); if (poll.expiresAt && poll.expiresAt < createdAt) { throw new ApiError(meta.errors.alreadyExpired); @@ -153,7 +152,7 @@ export default define(meta, async (ps, user) => { // リモート投票の場合リプライ送信 if (note.userHost != null) { - const pollOwner = await Users.findOne(note.userId).then(ensure) as IRemoteUser; + const pollOwner = await Users.findOneOrFail(note.userId) as IRemoteUser; deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); } diff --git a/src/server/api/endpoints/room/show.ts b/src/server/api/endpoints/room/show.ts index 96248a8c0c..e8af68956d 100644 --- a/src/server/api/endpoints/room/show.ts +++ b/src/server/api/endpoints/room/show.ts @@ -3,7 +3,6 @@ import define from '../../define'; import { ApiError } from '../../error'; import { Users, UserProfiles } from '../../../../models'; import { ID } from '../../../../misc/cafy-id'; -import { ensure } from '../../../../prelude/ensure'; import { toPunyNullable } from '../../../../misc/convert-host'; export const meta = { @@ -51,7 +50,7 @@ export default define(meta, async (ps, me) => { throw new ApiError(meta.errors.noSuchUser); } - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); if (profile.room.furnitures == null) { await UserProfiles.update(user.id, { diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index 734758d63d..7a5efc6cc9 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -6,7 +6,6 @@ import config from '../../../config'; import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { genId } from '../../../misc/gen-id'; -import { ensure } from '../../../prelude/ensure'; import { verifyLogin, hash } from '../2fa'; import { randomBytes } from 'crypto'; @@ -47,7 +46,7 @@ export default async (ctx: Koa.Context) => { return; } - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); // Compare password const same = await bcrypt.compare(password, profile.password!); diff --git a/src/server/api/service/discord.ts b/src/server/api/service/discord.ts index 007458066d..fce840cde5 100644 --- a/src/server/api/service/discord.ts +++ b/src/server/api/service/discord.ts @@ -10,7 +10,6 @@ import signin from '../common/signin'; import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; -import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.Context) { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; @@ -41,12 +40,12 @@ router.get('/disconnect/discord', async ctx => { return; } - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); delete profile.integrations.discord; @@ -253,12 +252,12 @@ router.get('/dc/cb', async ctx => { return; } - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); await UserProfiles.update(user.id, { integrations: { diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index 663c3cc754..2b10fa02a0 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -10,7 +10,6 @@ import signin from '../common/signin'; import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; -import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.Context) { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; @@ -41,12 +40,12 @@ router.get('/disconnect/github', async ctx => { return; } - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); delete profile.integrations.github; @@ -227,12 +226,12 @@ router.get('/gh/cb', async ctx => { return; } - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); await UserProfiles.update(user.id, { integrations: { diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index 000eb57c1b..97e9d3a7fc 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -9,7 +9,6 @@ import signin from '../common/signin'; import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; -import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.Context) { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; @@ -40,12 +39,12 @@ router.get('/disconnect/twitter', async ctx => { return; } - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); delete profile.integrations.twitter; @@ -163,12 +162,12 @@ router.get('/tw/cb', async ctx => { const result = await twAuth!.done(JSON.parse(twCtx), verifier); - const user = await Users.findOne({ + const user = await Users.findOneOrFail({ host: null, token: userToken - }).then(ensure); + }); - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); await UserProfiles.update(user.id, { integrations: { diff --git a/src/server/web/feed.ts b/src/server/web/feed.ts index 9c7591d744..6078fbe0f3 100644 --- a/src/server/web/feed.ts +++ b/src/server/web/feed.ts @@ -3,7 +3,6 @@ import config from '../../config'; import { User } from '../../models/entities/user'; import { Notes, DriveFiles, UserProfiles } from '../../models'; import { In } from 'typeorm'; -import { ensure } from '../../prelude/ensure'; export default async function(user: User) { const author = { @@ -11,7 +10,7 @@ export default async function(user: User) { name: user.name || user.username }; - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); const notes = await Notes.find({ where: { diff --git a/src/server/web/index.ts b/src/server/web/index.ts index f3442c6199..8ea7e15751 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -20,7 +20,6 @@ import config from '../../config'; import { Users, Notes, Emojis, UserProfiles, Pages, Channels, Clips } from '../../models'; import parseAcct from '../../misc/acct/parse'; import { getNoteSummary } from '../../misc/get-note-summary'; -import { ensure } from '../../prelude/ensure'; import { getConnection } from 'typeorm'; import redis from '../../db/redis'; import locales = require('../../../locales'); @@ -199,7 +198,7 @@ router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { }); if (user != null) { - const profile = await UserProfiles.findOne(user.id).then(ensure); + const profile = await UserProfiles.findOneOrFail(user.id); const meta = await fetchMeta(); const me = profile.fields ? profile.fields @@ -242,7 +241,7 @@ router.get('/notes/:note', async ctx => { if (note) { const _note = await Notes.pack(note); - const profile = await UserProfiles.findOne(note.userId).then(ensure); + const profile = await UserProfiles.findOneOrFail(note.userId); const meta = await fetchMeta(); await ctx.render('note', { note: _note, @@ -282,7 +281,7 @@ router.get('/@:user/pages/:page', async ctx => { if (page) { const _page = await Pages.pack(page); - const profile = await UserProfiles.findOne(page.userId).then(ensure); + const profile = await UserProfiles.findOneOrFail(page.userId); const meta = await fetchMeta(); await ctx.render('page', { page: _page, @@ -311,7 +310,7 @@ router.get('/clips/:clip', async ctx => { if (clip) { const _clip = await Clips.pack(clip); - const profile = await UserProfiles.findOne(clip.userId).then(ensure); + const profile = await UserProfiles.findOneOrFail(clip.userId); const meta = await fetchMeta(); await ctx.render('clip', { clip: _clip, diff --git a/src/services/add-note-to-antenna.ts b/src/services/add-note-to-antenna.ts index f11607fd43..2c893488c3 100644 --- a/src/services/add-note-to-antenna.ts +++ b/src/services/add-note-to-antenna.ts @@ -3,7 +3,6 @@ import { Note } from '../models/entities/note'; import { AntennaNotes, Mutings, Notes } from '../models'; import { genId } from '../misc/gen-id'; import { isMutedUserRelated } from '../misc/is-muted-user-related'; -import { ensure } from '../prelude/ensure'; import { publishAntennaStream, publishMainStream } from './stream'; import { User } from '../models/entities/user'; @@ -34,10 +33,10 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: U }; if (note.replyId != null) { - _note.reply = await Notes.findOne(note.replyId).then(ensure); + _note.reply = await Notes.findOneOrFail(note.replyId); } if (note.renoteId != null) { - _note.renote = await Notes.findOne(note.renoteId).then(ensure); + _note.renote = await Notes.findOneOrFail(note.renoteId); } if (isMutedUserRelated(_note, new Set(mutings.map(x => x.muteeId)))) { diff --git a/src/services/create-notification.ts b/src/services/create-notification.ts index 5dddaa5727..6cd116040a 100644 --- a/src/services/create-notification.ts +++ b/src/services/create-notification.ts @@ -4,6 +4,7 @@ import { Notifications, Mutings, UserProfiles } from '../models'; import { genId } from '../misc/gen-id'; import { User } from '../models/entities/user'; import { Notification } from '../models/entities/notification'; +import { sendEmailNotification } from './send-email-notification'; export async function createNotification( notifieeId: User['id'], @@ -38,20 +39,22 @@ export async function createNotification( setTimeout(async () => { const fresh = await Notifications.findOne(notification.id); if (fresh == null) return; // 既に削除されているかもしれない - if (!fresh.isRead) { - //#region ただしミュートしているユーザーからの通知なら無視 - const mutings = await Mutings.find({ - muterId: notifieeId - }); - if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { - return; - } - //#endregion + if (fresh.isRead) return; - publishMainStream(notifieeId, 'unreadNotification', packed); - - pushSw(notifieeId, 'notification', packed); + //#region ただしミュートしているユーザーからの通知なら無視 + const mutings = await Mutings.find({ + muterId: notifieeId + }); + if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { + return; } + //#endregion + + publishMainStream(notifieeId, 'unreadNotification', packed); + + pushSw(notifieeId, 'notification', packed); + if (type === 'follow') sendEmailNotification.follow(notifieeId, data); + if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, data); }, 2000); return notification; diff --git a/src/services/following/create.ts b/src/services/following/create.ts index c5f130f49f..c0583cdb86 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -14,7 +14,6 @@ import { instanceChart, perUserFollowingChart } from '../chart'; import { genId } from '../../misc/gen-id'; import { createNotification } from '../create-notification'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; -import { ensure } from '../../prelude/ensure'; const logger = new Logger('following/create'); @@ -130,7 +129,7 @@ export default async function(follower: User, followee: User, requestId?: string if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } - const followeeProfile = await UserProfiles.findOne(followee.id).then(ensure); + const followeeProfile = await UserProfiles.findOneOrFail(followee.id); // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or diff --git a/src/services/following/requests/accept-all.ts b/src/services/following/requests/accept-all.ts index 70e7448aad..da39965349 100644 --- a/src/services/following/requests/accept-all.ts +++ b/src/services/following/requests/accept-all.ts @@ -1,7 +1,6 @@ import accept from './accept'; import { User } from '../../../models/entities/user'; import { FollowRequests, Users } from '../../../models'; -import { ensure } from '../../../prelude/ensure'; /** * 指定したユーザー宛てのフォローリクエストをすべて承認 @@ -13,7 +12,7 @@ export default async function(user: User) { }); for (const request of requests) { - const follower = await Users.findOne(request.followerId).then(ensure); + const follower = await Users.findOneOrFail(request.followerId); accept(user, follower); } } diff --git a/src/services/messages/delete.ts b/src/services/messages/delete.ts index 0efff85f39..c94a7b67db 100644 --- a/src/services/messages/delete.ts +++ b/src/services/messages/delete.ts @@ -1,5 +1,4 @@ import config from '../../config'; -import { ensure } from '../../prelude/ensure'; import { MessagingMessages, Users } from '../../models'; import { MessagingMessage } from '../../models/entities/messaging-message'; import { publishGroupMessagingStream, publishMessagingStream } from '../stream'; @@ -15,8 +14,8 @@ export async function deleteMessage(message: MessagingMessage) { async function postDeleteMessage(message: MessagingMessage) { if (message.recipientId) { - const user = await Users.findOne(message.userId).then(ensure); - const recipient = await Users.findOne(message.recipientId).then(ensure); + const user = await Users.findOneOrFail(message.userId); + const recipient = await Users.findOneOrFail(message.recipientId); if (Users.isLocalUser(user)) publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id); if (Users.isLocalUser(recipient)) publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id); diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 62ec92f2bc..563eaac758 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -26,7 +26,6 @@ import { notesChart, perUserNotesChart, activeUsersChart, instanceChart } from ' import { Poll, IPoll } from '../../models/entities/poll'; import { createNotification } from '../create-notification'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; -import { ensure } from '../../prelude/ensure'; import { checkHitAntenna } from '../../misc/check-hit-antenna'; import { checkWordMute } from '../../misc/check-word-mute'; import { addNoteToAntenna } from '../add-note-to-antenna'; @@ -200,7 +199,7 @@ export default async (user: User, data: Option, silent = false) => new Promise Array.from(tag || '').length <= 128).splice(0, 32); if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { - mentionedUsers.push(await Users.findOne(data.reply.userId).then(ensure)); + mentionedUsers.push(await Users.findOneOrFail(data.reply.userId)); } if (data.visibility == 'specified') { @@ -213,7 +212,7 @@ export default async (user: User, data: Option, silent = false) => new Promise x.id === data.reply!.userId)) { - data.visibleUsers.push(await Users.findOne(data.reply.userId).then(ensure)); + data.visibleUsers.push(await Users.findOneOrFail(data.reply.userId)); } } diff --git a/src/services/send-email-notification.ts b/src/services/send-email-notification.ts new file mode 100644 index 0000000000..7579d5b674 --- /dev/null +++ b/src/services/send-email-notification.ts @@ -0,0 +1,28 @@ +import { UserProfiles } from '../models'; +import { User } from '../models/entities/user'; +import { sendEmail } from './send-email'; +import * as locales from '../../locales/'; +import { I18n } from '../misc/i18n'; + +// TODO: locale ファイルをクライアント用とサーバー用で分けたい + +async function follow(userId: User['id'], args: {}) { + const userProfile = await UserProfiles.findOneOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + sendEmail(userProfile.email, i18n.t('_email._follow.title'), 'test', 'test'); +} + +async function receiveFollowRequest(userId: User['id'], args: {}) { + const userProfile = await UserProfiles.findOneOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), 'test', 'test'); +} + +export const sendEmailNotification = { + follow, + receiveFollowRequest, +}; diff --git a/src/services/send-email.ts b/src/services/send-email.ts index 5a8f92be54..c716b36715 100644 --- a/src/services/send-email.ts +++ b/src/services/send-email.ts @@ -5,9 +5,12 @@ import config from '../config'; export const logger = new Logger('email'); -export async function sendEmail(to: string, subject: string, text: string) { +export async function sendEmail(to: string, subject: string, html: string, text: string) { const meta = await fetchMeta(true); + const iconUrl = `${config.url}/assets/mi-white.png`; + const emailSettingUrl = `${config.url}/settings/email`; + const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; const transporter = nodemailer.createTransport({ @@ -23,11 +26,93 @@ export async function sendEmail(to: string, subject: string, text: string) { } as any); try { + // TODO: htmlサニタイズ const info = await transporter.sendMail({ from: meta.email!, to: to, - subject: subject || 'Misskey', - text: text + subject: subject, + text: text, + html: ` + + + + ${ subject } + + + +
+
+ +
+
+

${ subject }

+
${ html }
+
+ +
+ + + + ` }); logger.info('Message sent: %s', info.messageId); diff --git a/yarn.lock b/yarn.lock index 44b7ec9277..783e17d4a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -437,12 +437,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "0.0.45" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" - integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== - -"@types/estree@^0.0.46": +"@types/estree@*", "@types/estree@^0.0.46": version "0.0.46" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== @@ -2395,7 +2390,7 @@ cheerio@^0.22.0: lodash.reject "^4.4.0" lodash.some "^4.4.0" -chokidar@3.4.3, "chokidar@>=2.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.3.1: +chokidar@3.5.1, "chokidar@>=2.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== @@ -2504,15 +2499,6 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.1.tgz#a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3" - integrity sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -3232,10 +3218,10 @@ debug@3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@4, debug@4.2.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== +debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" @@ -3426,7 +3412,12 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -diff@4.0.2, diff@^4.0.1: +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -3766,7 +3757,7 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.3" es6-symbol "^3.1.1" -escalade@^3.0.2, escalade@^3.1.0: +escalade@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== @@ -5608,14 +5599,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.0, js-yaml@^3.13.1, js-yaml@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" @@ -5623,6 +5606,14 @@ js-yaml@4.0.0: dependencies: argparse "^2.0.1" +js-yaml@^3.13.1, js-yaml@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" @@ -6595,35 +6586,35 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@8.2.1: - version "8.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39" - integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w== +mocha@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.0.tgz#a83a7432d382ae1ca29686062d7fdc2c36f63fe5" + integrity sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.4.3" - debug "4.2.0" - diff "4.0.2" + chokidar "3.5.1" + debug "4.3.1" + diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.1.6" growl "1.10.5" he "1.2.0" - js-yaml "3.14.0" + js-yaml "4.0.0" log-symbols "4.0.0" minimatch "3.0.4" - ms "2.1.2" - nanoid "3.1.12" + ms "2.1.3" + nanoid "3.1.20" serialize-javascript "5.0.1" strip-json-comments "3.1.1" - supports-color "7.2.0" + supports-color "8.1.1" which "2.0.2" wide-align "1.1.3" - workerpool "6.0.2" - yargs "13.3.2" - yargs-parser "13.1.2" + workerpool "6.1.0" + yargs "16.2.0" + yargs-parser "20.2.4" yargs-unparser "2.0.0" moji@0.5.1: @@ -6700,12 +6691,7 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanoid@3.1.12: - version "3.1.12" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" - integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== - -nanoid@^3.1.20: +nanoid@3.1.20, nanoid@^3.1.20: version "3.1.20" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== @@ -6836,10 +6822,10 @@ node-releases@^1.1.61: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e" integrity sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g== -nodemailer@6.4.17: - version "6.4.17" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.17.tgz#8de98618028953b80680775770f937243a7d7877" - integrity sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ== +nodemailer@6.4.18: + version "6.4.18" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.18.tgz#2788c85792844fc17befda019031609017f4b9a1" + integrity sha512-ht9cXxQ+lTC+t00vkSIpKHIyM4aXIsQ1tcbQCn5IOnxYHi81W2XOaU66EQBFFpbtzLEBTC94gmkbD4mGZQzVpA== nofilter@^1.0.4: version "1.0.4" @@ -9794,10 +9780,10 @@ summaly@2.4.0: require-all "2.2.0" trace-redirect "1.0.6" -supports-color@7.2.0, supports-color@^7.0.0, supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" @@ -9827,6 +9813,13 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + sver-compat@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" @@ -9959,12 +9952,7 @@ tapable@^1.0.0, tapable@^1.0.0-beta.5: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.1.1.tgz#b01cc1902d42a7bb30514e320ce21c456f72fd3f" - integrity sha512-Wib1S8m2wdpLbmQz0RBEVosIyvb/ykfKXf3ZIDqvWoMg/zTNm6G/tDSuUM61J1kNCDXWJrLHGSFeMhAG+gAGpQ== - -tapable@^2.2.0: +tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== @@ -10437,10 +10425,10 @@ typeorm@0.2.30: yargonaut "^1.1.2" yargs "^16.0.3" -typescript@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -10916,10 +10904,10 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@5.21.1: - version "5.21.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.21.1.tgz#a5a13965187deeaa674a0ecea219b61060a2c38f" - integrity sha512-H/fjQiDETEZDKoZm/LhvDBxOIKf9rfOdqb2pKTHRvBFMIRtwAwYlPCgBd0gc5xiDG5DqkxAiFZgAF/4H41wMuQ== +webpack@5.21.2: + version "5.21.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.21.2.tgz#647507e50d3637695be28af58a6a8246050394e7" + integrity sha512-xHflCenx+AM4uWKX71SWHhxml5aMXdy2tu/vdi4lClm7PADKxlyDAFFN1rEFzNV0MAoPpHtBeJnl/+K6F4QBPg== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.46" @@ -11047,10 +11035,10 @@ wordwrap@0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= -workerpool@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438" - integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== +workerpool@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" + integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== wrap-ansi@^2.0.0: version "2.1.0" @@ -11182,11 +11170,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -y18n@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.2.tgz#48218df5da2731b4403115c39a1af709c873f829" - integrity sha512-CkwaeZw6dQgqgPGeTWKMXCRmMcBgETFlTml1+ZOO+q7kGst8NREJ+eWwFNPVUQ4QGdAaklbqCZHH6Zuep1RjiA== - y18n@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" @@ -11226,13 +11209,10 @@ yargonaut@^1.1.2: figlet "^1.1.1" parent-require "^1.0.0" -yargs-parser@13.1.2, yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.2.4, yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@5.0.0-security.0, yargs-parser@^5.0.0: version "5.0.0-security.0" @@ -11242,15 +11222,13 @@ yargs-parser@5.0.0-security.0, yargs-parser@^5.0.0: camelcase "^3.0.0" object.assign "^4.1.0" -yargs-parser@^20.0.0: - version "20.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77" - integrity sha512-yYsjuSkjbLMBp16eaOt7/siKTjNVjMm3SoJnIg3sEh/JsvqVVDyjRKmaJV4cl+lNIgq6QEco2i3gDebJl7/vLA== - -yargs-parser@^20.2.2: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" yargs-unparser@2.0.0: version "2.0.0" @@ -11262,7 +11240,20 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@13.3.2, yargs@^13.2.4: +yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^13.2.4: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -11278,32 +11269,6 @@ yargs@13.3.2, yargs@^13.2.4: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^16.0.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^16.0.3: - version "16.0.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c" - integrity sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA== - dependencies: - cliui "^7.0.0" - escalade "^3.0.2" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.1" - yargs-parser "^20.0.0" - yargs@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"