1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2024-12-12 13:48:28 +09:00

Merge branch 'develop'

This commit is contained in:
syuilo 2019-07-05 02:13:02 +09:00
commit d078f78602
191 changed files with 4346 additions and 1410 deletions

View File

@ -8,6 +8,9 @@
# Final accessible URL seen by a user. # Final accessible URL seen by a user.
url: https://example.tld/ url: https://example.tld/
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# URL SETTINGS AFTER THAT!
# ┌───────────────────────┐ # ┌───────────────────────┐
#───┘ Port and TLS settings └─────────────────────────────────── #───┘ Port and TLS settings └───────────────────────────────────

View File

@ -1,23 +1,23 @@
{ {
"parserOptions": { "parserOptions": {
"parser": "typescript-eslint-parser" "parser": "@typescript-eslint/parser"
}, },
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:vue/recommended" "plugin:vue/recommended"
], ],
"rules": { "rules": {
"vue/require-v-for-key": false, "vue/require-v-for-key": 0,
"vue/max-attributes-per-line": false, "vue/max-attributes-per-line": 0,
"vue/html-indent": false, "vue/html-indent": 0,
"vue/html-self-closing": false, "vue/html-self-closing": 0,
"vue/no-unused-vars": false, "vue/no-unused-vars": 0,
"vue/attributes-order": false, "vue/attributes-order": 0,
"vue/require-prop-types": false, "vue/require-prop-types": 0,
"vue/require-default-prop": false, "vue/require-default-prop": 0,
"vue/html-closing-bracket-spacing": false, "vue/html-closing-bracket-spacing": 0,
"vue/singleline-html-element-content-newline": false, "vue/singleline-html-element-content-newline": 0,
"vue/no-v-html": false, "vue/no-v-html": 0,
"no-console": 0, "no-console": 0,
"no-unused-vars": 0, "no-unused-vars": 0,
"no-empty": 0 "no-empty": 0

View File

@ -17,6 +17,29 @@ npm i -g ts-node
npm run migrate npm run migrate
``` ```
11.24.0 (2019/07/05)
--------------------
注意: このアップデート後に、`node built/tools/accept-migration Init 1000000000000`してください。
### ✨Improvements
* WebAuthnサポート
* APNGサポート
* アバターファイル選択でimage以外は表示しないように
* データベース手動バキューム機能
* メールサーバー設定が正しいか確認できるように
* ピン止めの上限に達したときエラーを表示するように
* AP: attributedTo, to, cc が Array や Object のパターンに対応
* AP: object type が Audio, Document, Image, Page, Video のパターンに対応
* フォロー解除時に確認ダイアログを表示するように
* ユーザー入力ダイアログで、入力時にユーザーの存在を確認するように
### 🐛Fixes
* 自分のインスタンスのルートURIへのリンクが無になる問題を修正
* リモートファイルの削除が重い問題を修正
* 投稿などの連続したスペースがひとつに纏まるのを修正
* MFMのパース修正
* マイグレーション時にエラーが出る問題を修正
11.23.1 (2019/06/25) 11.23.1 (2019/06/25)
-------------------- --------------------
### 🐛Fixes ### 🐛Fixes
@ -588,10 +611,9 @@ mongodb:
3. migration ブランチに切り替え 3. migration ブランチに切り替え
4. `npm i` 4. `npm i`
5. `npm run build` 5. `npm run build`
6. `npm run init` 6. `npm run migrate`
7. `npm run migrate` 7. master ブランチに戻す
8. master ブランチに戻す 8. enjoy
9. enjoy
10.100.0 10.100.0
---------- ----------

View File

@ -219,8 +219,6 @@ const user = await Users.findOne(userId).then(ensure);
``` ```
### Migration作成方法 ### Migration作成方法
コードの変更をした後、`ormconfig.json``npm run ormconfig`で生成)を用意し、
``` ```
npm i -g ts-node npm i -g ts-node
ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前

View File

@ -1,4 +1,4 @@
FROM node:12.1-alpine AS base FROM node:12.5-alpine AS base
ENV NODE_ENV=production ENV NODE_ENV=production

View File

@ -145,7 +145,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
@ -156,7 +155,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td> <td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
<td><a href="https://www.patreon.com/takimura">takimura</a></td> <td><a href="https://www.patreon.com/takimura">takimura</a></td>
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td> <td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td> <td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td> <td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
@ -177,7 +175,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table> </tr></table>
**Last updated:** Tue, 11 Jun 2019 01:46:06 UTC **Last updated:** Mon, 01 Jul 2019 21:44:06 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -260,6 +260,7 @@ common:
disable-via-mobile: "Neoznačovat příspěvky jako „z mobilu“" disable-via-mobile: "Neoznačovat příspěvky jako „z mobilu“"
load-raw-images: "Zobrazovat obrázky v původní kvalitě" load-raw-images: "Zobrazovat obrázky v původní kvalitě"
load-remote-media: "Zobrazovat média ze vzdáleného serveru" load-remote-media: "Zobrazovat média ze vzdáleného serveru"
sync: "Synchronizace"
search: "Hledání" search: "Hledání"
delete: "Odstranit" delete: "Odstranit"
loading: "Načítám..." loading: "Načítám..."
@ -449,6 +450,7 @@ common/views/components/messaging.vue:
you: "Vy" you: "Vy"
no-history: "Žádná historie" no-history: "Žádná historie"
user: "Uživatel" user: "Uživatel"
group: "Skupina"
common/views/components/messaging-room.vue: common/views/components/messaging-room.vue:
new-message: "Máte novou zprávu" new-message: "Máte novou zprávu"
common/views/components/messaging-room.form.vue: common/views/components/messaging-room.form.vue:
@ -466,6 +468,7 @@ common/views/components/nav.vue:
repository: "Úložiště" repository: "Úložiště"
develop: "Vývojáři" develop: "Vývojáři"
feedback: "Zpětná vazba" feedback: "Zpětná vazba"
tos: "Podmínky užívání"
common/views/components/note-menu.vue: common/views/components/note-menu.vue:
mention: "Zmínění" mention: "Zmínění"
detail: "Více" detail: "Více"
@ -984,6 +987,7 @@ admin/views/instance.vue:
smtp-auth: "Provést SMTP autentikaci" smtp-auth: "Provést SMTP autentikaci"
smtp-user: "SMTP uživatel" smtp-user: "SMTP uživatel"
smtp-pass: "SMTP heslo" smtp-pass: "SMTP heslo"
test-email: "Test"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "Povolit ServiceWorker" enable-serviceworker: "Povolit ServiceWorker"
vapid-publickey: "VAPID veřejný klíč" vapid-publickey: "VAPID veřejný klíč"

View File

@ -1195,6 +1195,7 @@ admin/views/instance.vue:
smtp-auth: "Udfør autentifikation af SMTP" smtp-auth: "Udfør autentifikation af SMTP"
smtp-user: "SMTP bruger" smtp-user: "SMTP bruger"
smtp-pass: "SMTP adgangskode" smtp-pass: "SMTP adgangskode"
test-email: "Test"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "Aktiver ServiceWorker" enable-serviceworker: "Aktiver ServiceWorker"
serviceworker-info: "Skal være aktiveret for at give push notifikationer." serviceworker-info: "Skal være aktiveret for at give push notifikationer."

View File

@ -775,6 +775,7 @@ admin/views/instance.vue:
invite: "Einladen" invite: "Einladen"
save: "Speichern" save: "Speichern"
saved: "Gespeichert" saved: "Gespeichert"
test-email: "Test"
admin/views/charts.vue: admin/views/charts.vue:
drive: "Drive" drive: "Drive"
admin/views/drive.vue: admin/views/drive.vue:

View File

@ -523,6 +523,7 @@ common/views/components/note-menu.vue:
delete: "Delete" delete: "Delete"
delete-confirm: "Are you sure you want to delete this post?" delete-confirm: "Are you sure you want to delete this post?"
remote: "Show original note" remote: "Show original note"
pin-limit-exceeded: "You can't pin any more posts."
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
mention: "Mention" mention: "Mention"
mute: "Mute" mute: "Mute"
@ -601,6 +602,8 @@ common/views/components/signin.vue:
signin-with-github: "Sign in with GitHub" signin-with-github: "Sign in with GitHub"
signin-with-discord: "Sign in with Discord" signin-with-discord: "Sign in with Discord"
login-failed: "Logging in has failed. Make sure you have entered the correct username and password." login-failed: "Logging in has failed. Make sure you have entered the correct username and password."
tap-key: "Click on the Security Key to log in"
enter-2fa-code: "Enter your verification code"
common/views/components/signup.vue: common/views/components/signup.vue:
invitation-code: "Invitation code" invitation-code: "Invitation code"
invitation-info: "If you do not have an invitation code, please contact an <a href=\"{}\">administrator</a>." invitation-info: "If you do not have an invitation code, please contact an <a href=\"{}\">administrator</a>."
@ -984,7 +987,7 @@ desktop/views/components/settings.2fa.vue:
url: "https://www.google.com/landing/2step/" url: "https://www.google.com/landing/2step/"
caution: "If you lose access to your registered device, you won't be able to connect to Misskey anymore!" caution: "If you lose access to your registered device, you won't be able to connect to Misskey anymore!"
register: "Register a device" register: "Register a device"
already-registered: "This device is already registered" already-registered: "Your account is currently registered to an authenticator application"
unregister: "Unregister" unregister: "Unregister"
unregistered: "Two-factor authentication has been disabled." unregistered: "Two-factor authentication has been disabled."
enter-password: "Enter the password" enter-password: "Enter the password"
@ -997,6 +1000,15 @@ desktop/views/components/settings.2fa.vue:
success: "Settings saved!" success: "Settings saved!"
failed: "Failed to setup. Please ensure that the token is correct." failed: "Failed to setup. Please ensure that the token is correct."
info: "From the next time you sign in to Misskey, the token displayed on your device will be necessary too, as well as the password." info: "From the next time you sign in to Misskey, the token displayed on your device will be necessary too, as well as the password."
totp-header: "Authenticator App"
security-key-header: "Security Key"
security-key: "For additional security, you can log in to your account using a hardware Security Key that supports FIDO2. When you then sign in, you'll need the registered Security Key, or an authenticator app with you."
last-used: "Last used:"
activate-key: "Click to activate the Security Key"
security-key-name: "Name the Key"
register-security-key: "Complete Key registration"
something-went-wrong: "Wow! There was a problem registering the Key:"
key-unregistered: "The Key has been deleted"
common/views/components/media-image.vue: common/views/components/media-image.vue:
sensitive: "NSFW" sensitive: "NSFW"
click-to-show: "Click to show" click-to-show: "Click to show"
@ -1110,6 +1122,9 @@ admin/views/index.vue:
back-to-misskey: "Back to Misskey" back-to-misskey: "Back to Misskey"
admin/views/db.vue: admin/views/db.vue:
tables: "Tables" tables: "Tables"
vacuum: "Vacuum"
vacuum-info: "Tidies up the database. Keeps the data intact and reduces disk usage. This is usually done automatically and periodically."
vacuum-exclamation: "Vacuuming can overload the database for a while, and cause users not to be able to participate in interactions."
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "Dashboard" dashboard: "Dashboard"
accounts: "Accounts" accounts: "Accounts"
@ -1244,6 +1259,7 @@ admin/views/instance.vue:
smtp-auth: "Perform SMTP authentication" smtp-auth: "Perform SMTP authentication"
smtp-user: "SMTP User" smtp-user: "SMTP User"
smtp-pass: "SMTP Password" smtp-pass: "SMTP Password"
test-email: "Test"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "Enable ServiceWorker" enable-serviceworker: "Enable ServiceWorker"
serviceworker-info: "Must be enabled for push notifications." serviceworker-info: "Must be enabled for push notifications."
@ -1947,6 +1963,7 @@ pages:
_pick: _pick:
arg1: "Lists" arg1: "Lists"
arg2: "Position" arg2: "Position"
listLen: "Get length of list"
_listLen: _listLen:
arg1: "Lists" arg1: "Lists"
number: "Number" number: "Number"

View File

@ -939,6 +939,7 @@ admin/views/instance.vue:
smtp-port: "Puerto SMTP" smtp-port: "Puerto SMTP"
smtp-user: "Usuario SMTP" smtp-user: "Usuario SMTP"
smtp-pass: "Contraseña SMTP" smtp-pass: "Contraseña SMTP"
test-email: "Prueba"
admin/views/charts.vue: admin/views/charts.vue:
title: "Gráficos" title: "Gráficos"
per-day: "Por día" per-day: "Por día"

View File

@ -1217,6 +1217,7 @@ admin/views/instance.vue:
smtp-auth: "Effectuer une authentification SMTP" smtp-auth: "Effectuer une authentification SMTP"
smtp-user: "Utilisateur SMTP" smtp-user: "Utilisateur SMTP"
smtp-pass: "Mot de passe SMTP" smtp-pass: "Mot de passe SMTP"
test-email: "Test"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "Activer ServiceWorker" enable-serviceworker: "Activer ServiceWorker"
serviceworker-info: "Devrait être activé pour les notifications push." serviceworker-info: "Devrait être activé pour les notifications push."

View File

@ -36,6 +36,7 @@ common:
signout: "ログアウト" signout: "ログアウト"
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?" reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
fetching-as-ap-object: "連合に照会中" fetching-as-ap-object: "連合に照会中"
unfollow-confirm: "{name}さんをフォロー解除しますか?"
got-it: "わかった" got-it: "わかった"
customization-tips: customization-tips:
@ -561,6 +562,7 @@ common/views/components/note-menu.vue:
delete: "削除" delete: "削除"
delete-confirm: "この投稿を削除しますか?" delete-confirm: "この投稿を削除しますか?"
remote: "投稿元で見る" remote: "投稿元で見る"
pin-limit-exceeded: "これ以上ピン留めできません。"
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
mention: "メンション" mention: "メンション"
@ -645,6 +647,8 @@ common/views/components/signin.vue:
signin-with-github: "GitHubでログイン" signin-with-github: "GitHubでログイン"
signin-with-discord: "Discordでログイン" signin-with-discord: "Discordでログイン"
login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。" login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
tap-key: "セキュリティキーをクリックしてログイン"
enter-2fa-code: "認証コードを入力してください"
common/views/components/signup.vue: common/views/components/signup.vue:
invitation-code: "招待コード" invitation-code: "招待コード"
@ -1099,6 +1103,15 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!" success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。" failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。" info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
totp-header: "認証アプリ"
security-key-header: "セキュリティキー"
security-key: "セキュリティを強化するために、FIDO2をサポートするハードウェアセキュリティキーを使用してアカウントにログインできます。 サインインの際は、登録されたセキュリティキーまたは認証アプリが必要になります。"
last-used: "最後の使用:"
activate-key: "クリックしてセキュリティキーをアクティベートしてください"
security-key-name: "キー名"
register-security-key: "キーの登録を完了"
something-went-wrong: "わー! キーを登録する際に問題が発生しました:"
key-unregistered: "キーが削除されました"
common/views/components/media-image.vue: common/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
@ -1234,6 +1247,9 @@ admin/views/index.vue:
admin/views/db.vue: admin/views/db.vue:
tables: "テーブル" tables: "テーブル"
vacuum: "バキューム"
vacuum-info: "データベースの掃除を行います。データはそのままで、ディスク使用量を減らします。通常この操作は自動で定期的に行われます。"
vacuum-exclamation: "バキュームを行うと、しばらくの間データベースの負荷が高くなり、ユーザーの操作を受け付けなくなる場合があります。"
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "ダッシュボード" dashboard: "ダッシュボード"
@ -1373,6 +1389,7 @@ admin/views/instance.vue:
smtp-auth: "SMTP認証を行う" smtp-auth: "SMTP認証を行う"
smtp-user: "SMTPユーザー" smtp-user: "SMTPユーザー"
smtp-pass: "SMTPパスワード" smtp-pass: "SMTPパスワード"
test-email: "テスト"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "ServiceWorkerを有効にする" enable-serviceworker: "ServiceWorkerを有効にする"
serviceworker-info: "プッシュ通知を行うには有効する必要があります。" serviceworker-info: "プッシュ通知を行うには有効する必要があります。"

View File

@ -35,6 +35,7 @@ common:
signout: "로그아웃" signout: "로그아웃"
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?" reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
fetching-as-ap-object: "연합에서 조회 중" fetching-as-ap-object: "연합에서 조회 중"
unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?"
got-it: "알겠습니다" got-it: "알겠습니다"
customization-tips: customization-tips:
title: "커스터마이징 도움말" title: "커스터마이징 도움말"
@ -523,6 +524,7 @@ common/views/components/note-menu.vue:
delete: "삭제" delete: "삭제"
delete-confirm: "이 글을 삭제하시겠습니까?" delete-confirm: "이 글을 삭제하시겠습니까?"
remote: "글 원본 보기" remote: "글 원본 보기"
pin-limit-exceeded: "더 이상 고정할 수 없습니다."
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
mention: "멘션" mention: "멘션"
mute: "뮤트" mute: "뮤트"
@ -601,6 +603,8 @@ common/views/components/signin.vue:
signin-with-github: "GitHub으로 로그인" signin-with-github: "GitHub으로 로그인"
signin-with-discord: "Discord로 로그인" signin-with-discord: "Discord로 로그인"
login-failed: "로그인할 수 없습니다. 사용자명과 비밀번호를 확인하여 주십시오." login-failed: "로그인할 수 없습니다. 사용자명과 비밀번호를 확인하여 주십시오."
tap-key: "보안 키를 클릭하여 로그인"
enter-2fa-code: "인증 코드를 입력하여 주십시오"
common/views/components/signup.vue: common/views/components/signup.vue:
invitation-code: "초대 코드" invitation-code: "초대 코드"
invitation-info: "초대 코드가 없으신 분은 <a href=\"{}\">관리자</a>에게 연락하시기 바랍니다." invitation-info: "초대 코드가 없으신 분은 <a href=\"{}\">관리자</a>에게 연락하시기 바랍니다."
@ -997,6 +1001,15 @@ desktop/views/components/settings.2fa.vue:
success: "설정이 완료되었습니다!" success: "설정이 완료되었습니다!"
failed: "설정에 실패했습니다. 토큰이 잘못되었는지 확인해주십시오." failed: "설정에 실패했습니다. 토큰이 잘못되었는지 확인해주십시오."
info: "다음 로그인부터는 이와 동일하게 비밀번호에 더해 장치에 표시된 토큰을 입력합니다." info: "다음 로그인부터는 이와 동일하게 비밀번호에 더해 장치에 표시된 토큰을 입력합니다."
totp-header: "인증 앱"
security-key-header: "보안 키"
security-key: "보안을 강화하려면 FIDO2를 지원하는 하드웨어 보안 키를 사용하여 계정에 로그인할 수 있습니다. 로그인 시 등록하였던 보안 키 또는 인증 앱이 필요하게 됩니다."
last-used: "마지막 사용:"
activate-key: "클릭하여 보안 키를 활성화하여 주십시오"
security-key-name: "키 이름"
register-security-key: "키 등록 완료"
something-went-wrong: "으악! 키를 등록하는 도중 문제가 발생하였습니다:"
key-unregistered: "키가 등록되어 있지 않습니다"
common/views/components/media-image.vue: common/views/components/media-image.vue:
sensitive: "열람주의" sensitive: "열람주의"
click-to-show: "클릭하여 보기" click-to-show: "클릭하여 보기"
@ -1110,6 +1123,9 @@ admin/views/index.vue:
back-to-misskey: "Misskey로 돌아가기" back-to-misskey: "Misskey로 돌아가기"
admin/views/db.vue: admin/views/db.vue:
tables: "테이블" tables: "테이블"
vacuum: "청소"
vacuum-info: "데이터베이스를 청소합니다. 데이터는 그대로인 채로 데이터의 사용량을 줄입니다. 일반적으로 이 작업은 자동으로 정기적으로 수행됩니다."
vacuum-exclamation: "청소를 수행하면 한동안 데이터베이스의 부하가 높아져 사용자 조작을 처리하지 못 할 수 있습니다."
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "대시보드" dashboard: "대시보드"
accounts: "계정" accounts: "계정"
@ -1244,6 +1260,7 @@ admin/views/instance.vue:
smtp-auth: "SMTP 인증 수행" smtp-auth: "SMTP 인증 수행"
smtp-user: "SMTP 사용자" smtp-user: "SMTP 사용자"
smtp-pass: "SMTP 비밀번호" smtp-pass: "SMTP 비밀번호"
test-email: "테스트"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "ServiceWorker 사용" enable-serviceworker: "ServiceWorker 사용"
serviceworker-info: "푸시알림을 수행하려면 사용해야 합니다." serviceworker-info: "푸시알림을 수행하려면 사용해야 합니다."
@ -1947,6 +1964,7 @@ pages:
_pick: _pick:
arg1: "리스트" arg1: "리스트"
arg2: "위치" arg2: "위치"
listLen: "리스트의 길이 가져오기"
_listLen: _listLen:
arg1: "리스트" arg1: "리스트"
number: "수치" number: "수치"

View File

@ -65,6 +65,7 @@ common:
favorites: "Moje ulubione" favorites: "Moje ulubione"
permissions: permissions:
"read:drive": "Wyświetl dysk" "read:drive": "Wyświetl dysk"
"read:messaging": "Zobacz konwersację"
"write:votes": "Zagłosuj" "write:votes": "Zagłosuj"
empty-timeline-info: empty-timeline-info:
explore: "Poznaj" explore: "Poznaj"
@ -72,6 +73,7 @@ common:
hide-contents: "Ukryj zawartość" hide-contents: "Ukryj zawartość"
reply-placeholder: "Odpowiedź na ten wpis…" reply-placeholder: "Odpowiedź na ten wpis…"
quote-placeholder: "Zacytuj ten wpis…" quote-placeholder: "Zacytuj ten wpis…"
quote-attached: "Z cytatem"
submit: "Wpis" submit: "Wpis"
reply: "Odpowiedz" reply: "Odpowiedz"
renote: "Udostępnij" renote: "Udostępnij"
@ -880,6 +882,7 @@ admin/views/instance.vue:
save: "Zapisz" save: "Zapisz"
saved: "Zapisano" saved: "Zapisano"
email: "Adres e-mail" email: "Adres e-mail"
test-email: "Test"
admin/views/charts.vue: admin/views/charts.vue:
notes: "Wpisy" notes: "Wpisy"
users: "Użytkownicy" users: "Użytkownicy"

View File

@ -1244,6 +1244,7 @@ admin/views/instance.vue:
smtp-auth: "SMTP身份验证" smtp-auth: "SMTP身份验证"
smtp-user: "SMTP 用户名" smtp-user: "SMTP 用户名"
smtp-pass: "SMTP 密码" smtp-pass: "SMTP 密码"
test-email: "测试"
serviceworker-config: "ServiceWorker" serviceworker-config: "ServiceWorker"
enable-serviceworker: "启用ServiceWorker" enable-serviceworker: "启用ServiceWorker"
serviceworker-info: "您需要启用推送通知" serviceworker-info: "您需要启用推送通知"

View File

@ -0,0 +1,485 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class Init1000000000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TYPE "log_level_enum" AS ENUM('error', 'warning', 'info', 'success', 'debug')`);
await queryRunner.query(`CREATE TABLE "log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "domain" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "level" "log_level_enum" NOT NULL, "worker" character varying(8) NOT NULL, "machine" character varying(128) NOT NULL, "message" character varying(1024) NOT NULL, "data" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_350604cbdf991d5930d9e618fbd" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_8e4eb51a35d81b64dda28eed0a" ON "log" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_8cb40cfc8f3c28261e6f887b03" ON "log" ("domain") `);
await queryRunner.query(`CREATE INDEX "IDX_584b536b49e53ac81beb39a177" ON "log" ("level") `);
await queryRunner.query(`CREATE TABLE "drive_folder" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(128) NOT NULL, "userId" character varying(32), "parentId" character varying(32), CONSTRAINT "PK_7a0c089191f5ebdc214e0af808a" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_02878d441ceae15ce060b73daf" ON "drive_folder" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_f4fc06e49c0171c85f1c48060d" ON "drive_folder" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_00ceffb0cdc238b3233294f08f" ON "drive_folder" ("parentId") `);
await queryRunner.query(`CREATE TABLE "drive_file" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "userHost" character varying(128), "md5" character varying(32) NOT NULL, "name" character varying(256) NOT NULL, "type" character varying(128) NOT NULL, "size" integer NOT NULL, "comment" character varying(512), "properties" jsonb NOT NULL DEFAULT '{}', "storedInternal" boolean NOT NULL, "url" character varying(512) NOT NULL, "thumbnailUrl" character varying(512), "webpublicUrl" character varying(512), "accessKey" character varying(256), "thumbnailAccessKey" character varying(256), "webpublicAccessKey" character varying(256), "uri" character varying(512), "src" character varying(512), "folderId" character varying(32), "isSensitive" boolean NOT NULL DEFAULT false, "isLink" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_43ddaaaf18c9e68029b7cbb032e" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_860fa6f6c7df5bb887249fba22" ON "drive_file" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_92779627994ac79277f070c91e" ON "drive_file" ("userHost") `);
await queryRunner.query(`CREATE INDEX "IDX_37bb9a1b4585f8a3beb24c62d6" ON "drive_file" ("md5") `);
await queryRunner.query(`CREATE INDEX "IDX_a40b8df8c989d7db937ea27cf6" ON "drive_file" ("type") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d85a184c2540d2deba33daf642" ON "drive_file" ("accessKey") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e74022ce9a074b3866f70e0d27" ON "drive_file" ("thumbnailAccessKey") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c55b2b7c284d9fef98026fc88e" ON "drive_file" ("webpublicAccessKey") `);
await queryRunner.query(`CREATE INDEX "IDX_e5848eac4940934e23dbc17581" ON "drive_file" ("uri") `);
await queryRunner.query(`CREATE INDEX "IDX_bb90d1956dafc4068c28aa7560" ON "drive_file" ("folderId") `);
await queryRunner.query(`CREATE TABLE "user" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "lastFetchedAt" TIMESTAMP WITH TIME ZONE, "username" character varying(128) NOT NULL, "usernameLower" character varying(128) NOT NULL, "name" character varying(128), "followersCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "avatarId" character varying(32), "bannerId" character varying(32), "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "avatarUrl" character varying(512), "bannerUrl" character varying(512), "avatarColor" character varying(32), "bannerColor" character varying(32), "isSuspended" boolean NOT NULL DEFAULT false, "isSilenced" boolean NOT NULL DEFAULT false, "isLocked" boolean NOT NULL DEFAULT false, "isBot" boolean NOT NULL DEFAULT false, "isCat" boolean NOT NULL DEFAULT false, "isAdmin" boolean NOT NULL DEFAULT false, "isModerator" boolean NOT NULL DEFAULT false, "isVerified" boolean NOT NULL DEFAULT false, "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "host" character varying(128), "inbox" character varying(512), "sharedInbox" character varying(512), "featured" character varying(512), "uri" character varying(512), "token" character(16), CONSTRAINT "UQ_a854e557b1b14814750c7c7b0c9" UNIQUE ("token"), CONSTRAINT "REL_58f5c71eaab331645112cf8cfa" UNIQUE ("avatarId"), CONSTRAINT "REL_afc64b53f8db3707ceb34eb28e" UNIQUE ("bannerId"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_e11e649824a45d8ed01d597fd9" ON "user" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4" ON "user" ("updatedAt") `);
await queryRunner.query(`CREATE INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8" ON "user" ("usernameLower") `);
await queryRunner.query(`CREATE INDEX "IDX_fa99d777623947a5b05f394cae" ON "user" ("tags") `);
await queryRunner.query(`CREATE INDEX "IDX_3252a5df8d5bbd16b281f7799e" ON "user" ("host") `);
await queryRunner.query(`CREATE INDEX "IDX_be623adaa4c566baf5d29ce0c8" ON "user" ("uri") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a854e557b1b14814750c7c7b0c" ON "user" ("token") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5deb01ae162d1d70b80d064c27" ON "user" ("usernameLower", "host") `);
await queryRunner.query(`CREATE TABLE "app" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "secret" character varying(64) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(512) NOT NULL, "permission" character varying(64) array NOT NULL, "callbackUrl" character varying(512), CONSTRAINT "PK_9478629fc093d229df09e560aea" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_048a757923ed8b157e9895da53" ON "app" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_3f5b0899ef90527a3462d7c2cb" ON "app" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_f49922d511d666848f250663c4" ON "app" ("secret") `);
await queryRunner.query(`CREATE TABLE "access_token" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "hash" character varying(128) NOT NULL, "userId" character varying(32) NOT NULL, "appId" character varying(32) NOT NULL, CONSTRAINT "PK_f20f028607b2603deabd8182d12" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_70ba8f6af34bc924fc9e12adb8" ON "access_token" ("token") `);
await queryRunner.query(`CREATE INDEX "IDX_64c327441248bae40f7d92f34f" ON "access_token" ("hash") `);
await queryRunner.query(`CREATE INDEX "IDX_9949557d0e1b2c19e5344c171e" ON "access_token" ("userId") `);
await queryRunner.query(`CREATE TYPE "note_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`);
await queryRunner.query(`CREATE TABLE "note" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "replyId" character varying(32), "renoteId" character varying(32), "text" text, "name" character varying(256), "cw" character varying(512), "appId" character varying(32), "userId" character varying(32) NOT NULL, "viaMobile" boolean NOT NULL DEFAULT false, "localOnly" boolean NOT NULL DEFAULT false, "renoteCount" smallint NOT NULL DEFAULT 0, "repliesCount" smallint NOT NULL DEFAULT 0, "reactions" jsonb NOT NULL DEFAULT '{}', "visibility" "note_visibility_enum" NOT NULL, "uri" character varying(512), "score" integer NOT NULL DEFAULT 0, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "attachedFileTypes" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "visibleUserIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentions" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentionedRemoteUsers" text NOT NULL DEFAULT '[]', "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "hasPoll" boolean NOT NULL DEFAULT false, "geo" jsonb DEFAULT null, "userHost" character varying(128), "replyUserId" character varying(32), "replyUserHost" character varying(128), "renoteUserId" character varying(32), "renoteUserHost" character varying(128), CONSTRAINT "PK_96d0c172a4fba276b1bbed43058" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_e7c0567f5261063592f022e9b5" ON "note" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_17cb3553c700a4985dff5a30ff" ON "note" ("replyId") `);
await queryRunner.query(`CREATE INDEX "IDX_52ccc804d7c69037d558bac4c9" ON "note" ("renoteId") `);
await queryRunner.query(`CREATE INDEX "IDX_5b87d9d19127bd5d92026017a7" ON "note" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_153536c67d05e9adb24e99fc2b" ON "note" ("uri") `);
await queryRunner.query(`CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds") `);
await queryRunner.query(`CREATE INDEX "IDX_25dfc71b0369b003a4cd434d0b" ON "note" ("attachedFileTypes") `);
await queryRunner.query(`CREATE INDEX "IDX_796a8c03959361f97dc2be1d5c" ON "note" ("visibleUserIds") `);
await queryRunner.query(`CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions") `);
await queryRunner.query(`CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags") `);
await queryRunner.query(`CREATE INDEX "IDX_7125a826ab192eb27e11d358a5" ON "note" ("userHost") `);
await queryRunner.query(`CREATE TABLE "poll_vote" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "choice" integer NOT NULL, CONSTRAINT "PK_fd002d371201c472490ba89c6a0" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_0fb627e1c2f753262a74f0562d" ON "poll_vote" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_66d2bd2ee31d14bcc23069a89f" ON "poll_vote" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_aecfbd5ef60374918e63ee95fa" ON "poll_vote" ("noteId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_50bd7164c5b78f1f4a42c4d21f" ON "poll_vote" ("userId", "noteId", "choice") `);
await queryRunner.query(`CREATE TABLE "note_reaction" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "reaction" character varying(128) NOT NULL, CONSTRAINT "PK_767ec729b108799b587a3fcc9cf" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_13761f64257f40c5636d0ff95e" ON "note_reaction" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_45145e4953780f3cd5656f0ea6" ON "note_reaction" ("noteId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") `);
await queryRunner.query(`CREATE TABLE "note_watching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, CONSTRAINT "PK_49286fdb23725945a74aa27d757" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_318cdf42a9cfc11f479bd802bb" ON "note_watching" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_b0134ec406e8d09a540f818288" ON "note_watching" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_03e7028ab8388a3f5e3ce2a861" ON "note_watching" ("noteId") `);
await queryRunner.query(`CREATE INDEX "IDX_44499765eec6b5489d72c4253b" ON "note_watching" ("noteUserId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a42c93c69989ce1d09959df4cf" ON "note_watching" ("userId", "noteId") `);
await queryRunner.query(`CREATE TABLE "note_unread" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, "isSpecified" boolean NOT NULL, CONSTRAINT "PK_1904eda61a784f57e6e51fa9c1f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_56b0166d34ddae49d8ef7610bb" ON "note_unread" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_e637cba4dc4410218c4251260e" ON "note_unread" ("noteId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d908433a4953cc13216cd9c274" ON "note_unread" ("userId", "noteId") `);
await queryRunner.query(`CREATE TABLE "notification" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "notifieeId" character varying(32) NOT NULL, "notifierId" character varying(32) NOT NULL, "type" character varying(32) NOT NULL, "isRead" boolean NOT NULL DEFAULT false, "noteId" character varying(32), "reaction" character varying(128), "choice" integer, CONSTRAINT "PK_705b6c7cdf9b2c2ff7ac7872cb7" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b11a5e627c41d4dc3170f1d370" ON "notification" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_3c601b70a1066d2c8b517094cb" ON "notification" ("notifieeId") `);
await queryRunner.query(`CREATE TABLE "meta" ("id" character varying(32) NOT NULL, "name" character varying(128), "description" character varying(1024), "maintainerName" character varying(128), "maintainerEmail" character varying(128), "announcements" jsonb NOT NULL DEFAULT '[]', "disableRegistration" boolean NOT NULL DEFAULT false, "disableLocalTimeline" boolean NOT NULL DEFAULT false, "disableGlobalTimeline" boolean NOT NULL DEFAULT false, "enableEmojiReaction" boolean NOT NULL DEFAULT true, "useStarForReactionFallback" boolean NOT NULL DEFAULT false, "langs" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "hiddenTags" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "blockedHosts" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "mascotImageUrl" character varying(512) DEFAULT '/assets/ai.png', "bannerUrl" character varying(512), "errorImageUrl" character varying(512) DEFAULT 'https://xn--931a.moe/aiart/yubitun.png', "iconUrl" character varying(512), "cacheRemoteFiles" boolean NOT NULL DEFAULT true, "proxyAccount" character varying(128), "enableRecaptcha" boolean NOT NULL DEFAULT false, "recaptchaSiteKey" character varying(64), "recaptchaSecretKey" character varying(64), "localDriveCapacityMb" integer NOT NULL DEFAULT 1024, "remoteDriveCapacityMb" integer NOT NULL DEFAULT 32, "maxNoteTextLength" integer NOT NULL DEFAULT 500, "summalyProxy" character varying(128), "enableEmail" boolean NOT NULL DEFAULT false, "email" character varying(128), "smtpSecure" boolean NOT NULL DEFAULT false, "smtpHost" character varying(128), "smtpPort" integer, "smtpUser" character varying(128), "smtpPass" character varying(128), "enableServiceWorker" boolean NOT NULL DEFAULT false, "swPublicKey" character varying(128), "swPrivateKey" character varying(128), "enableTwitterIntegration" boolean NOT NULL DEFAULT false, "twitterConsumerKey" character varying(128), "twitterConsumerSecret" character varying(128), "enableGithubIntegration" boolean NOT NULL DEFAULT false, "githubClientId" character varying(128), "githubClientSecret" character varying(128), "enableDiscordIntegration" boolean NOT NULL DEFAULT false, "discordClientId" character varying(128), "discordClientSecret" character varying(128), CONSTRAINT "PK_c4c17a6c2bd7651338b60fc590b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_c76c6e044bdf76ecf8bfb82a645" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_582f8fab771a9040a12961f3e7" ON "following" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_24e0042143a18157b234df186c" ON "following" ("followeeId") `);
await queryRunner.query(`CREATE INDEX "IDX_6516c5a6f3c015b4eed39978be" ON "following" ("followerId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_307be5f1d1252e0388662acb96" ON "following" ("followerId", "followeeId") `);
await queryRunner.query(`CREATE TABLE "instance" ("id" character varying(32) NOT NULL, "caughtAt" TIMESTAMP WITH TIME ZONE NOT NULL, "host" character varying(128) NOT NULL, "system" character varying(64), "usersCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "followersCount" integer NOT NULL DEFAULT 0, "driveUsage" integer NOT NULL DEFAULT 0, "driveFiles" integer NOT NULL DEFAULT 0, "latestRequestSentAt" TIMESTAMP WITH TIME ZONE, "latestStatus" integer, "latestRequestReceivedAt" TIMESTAMP WITH TIME ZONE, "lastCommunicatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "isNotResponding" boolean NOT NULL DEFAULT false, "isMarkedAsClosed" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_eaf60e4a0c399c9935413e06474" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_2cd3b2a6b4cf0b910b260afe08" ON "instance" ("caughtAt") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8d5afc98982185799b160e10eb" ON "instance" ("host") `);
await queryRunner.query(`CREATE TABLE "muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_2e92d06c8b5c602eeb27ca9ba48" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_f86d57fbca33c7a4e6897490cc" ON "muting" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_ec96b4fed9dae517e0dbbe0675" ON "muting" ("muteeId") `);
await queryRunner.query(`CREATE INDEX "IDX_93060675b4a79a577f31d260c6" ON "muting" ("muterId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1eb9d9824a630321a29fd3b290" ON "muting" ("muterId", "muteeId") `);
await queryRunner.query(`CREATE TABLE "sw_subscription" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "endpoint" character varying(512) NOT NULL, "auth" character varying(256) NOT NULL, "publickey" character varying(128) NOT NULL, CONSTRAINT "PK_e8f763631530051b95eb6279b91" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_97754ca6f2baff9b4abb7f853d" ON "sw_subscription" ("userId") `);
await queryRunner.query(`CREATE TABLE "blocking" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "blockeeId" character varying(32) NOT NULL, "blockerId" character varying(32) NOT NULL, CONSTRAINT "PK_e5d9a541cc1965ee7e048ea09dd" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b9a354f7941c1e779f3b33aea6" ON "blocking" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_2cd4a2743a99671308f5417759" ON "blocking" ("blockeeId") `);
await queryRunner.query(`CREATE INDEX "IDX_0627125f1a8a42c9a1929edb55" ON "blocking" ("blockerId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_98a1bc5cb30dfd159de056549f" ON "blocking" ("blockerId", "blockeeId") `);
await queryRunner.query(`CREATE TABLE "user_list" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, CONSTRAINT "PK_87bab75775fd9b1ff822b656402" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b7fcefbdd1c18dce86687531f9" ON "user_list" ("userId") `);
await queryRunner.query(`CREATE TABLE "user_list_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userListId" character varying(32) NOT NULL, CONSTRAINT "PK_11abb3768da1c5f8de101c9df45" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_d844bfc6f3f523a05189076efa" ON "user_list_joining" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_605472305f26818cc93d1baaa7" ON "user_list_joining" ("userListId") `);
await queryRunner.query(`CREATE TABLE "hashtag" ("id" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "mentionedUserIds" character varying(32) array NOT NULL, "mentionedUsersCount" integer NOT NULL DEFAULT 0, "mentionedLocalUserIds" character varying(32) array NOT NULL, "mentionedLocalUsersCount" integer NOT NULL DEFAULT 0, "mentionedRemoteUserIds" character varying(32) array NOT NULL, "mentionedRemoteUsersCount" integer NOT NULL DEFAULT 0, "attachedUserIds" character varying(32) array NOT NULL, "attachedUsersCount" integer NOT NULL DEFAULT 0, "attachedLocalUserIds" character varying(32) array NOT NULL, "attachedLocalUsersCount" integer NOT NULL DEFAULT 0, "attachedRemoteUserIds" character varying(32) array NOT NULL, "attachedRemoteUsersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_cb36eb8af8412bfa978f1165d78" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_347fec870eafea7b26c8a73bac" ON "hashtag" ("name") `);
await queryRunner.query(`CREATE INDEX "IDX_2710a55f826ee236ea1a62698f" ON "hashtag" ("mentionedUsersCount") `);
await queryRunner.query(`CREATE INDEX "IDX_0e206cec573f1edff4a3062923" ON "hashtag" ("mentionedLocalUsersCount") `);
await queryRunner.query(`CREATE INDEX "IDX_4c02d38a976c3ae132228c6fce" ON "hashtag" ("mentionedRemoteUsersCount") `);
await queryRunner.query(`CREATE INDEX "IDX_d57f9030cd3af7f63ffb1c267c" ON "hashtag" ("attachedUsersCount") `);
await queryRunner.query(`CREATE INDEX "IDX_0c44bf4f680964145f2a68a341" ON "hashtag" ("attachedLocalUsersCount") `);
await queryRunner.query(`CREATE INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2" ON "hashtag" ("attachedRemoteUsersCount") `);
await queryRunner.query(`CREATE TABLE "note_favorite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_af0da35a60b9fa4463a62082b36" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_47f4b1892f5d6ba8efb3057d81" ON "note_favorite" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0f4fb9ad355f3effff221ef245" ON "note_favorite" ("userId", "noteId") `);
await queryRunner.query(`CREATE TABLE "abuse_user_report" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "reporterId" character varying(32) NOT NULL, "comment" character varying(512) NOT NULL, CONSTRAINT "PK_87873f5f5cc5c321a1306b2d18c" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_db2098070b2b5a523c58181f74" ON "abuse_user_report" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_04cc96756f89d0b7f9473e8cdf" ON "abuse_user_report" ("reporterId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `);
await queryRunner.query(`CREATE TABLE "registration_ticket" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(64) NOT NULL, CONSTRAINT "PK_f11696b6fafcf3662d4292734f8" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59" ON "registration_ticket" ("code") `);
await queryRunner.query(`CREATE TABLE "messaging_message" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "recipientId" character varying(32) NOT NULL, "text" character varying(4096), "isRead" boolean NOT NULL DEFAULT false, "fileId" character varying(32), CONSTRAINT "PK_db398fd79dc95d0eb8c30456eaa" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_e21cd3646e52ef9c94aaf17c2e" ON "messaging_message" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_5377c307783fce2b6d352e1203" ON "messaging_message" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_cac14a4e3944454a5ce7daa514" ON "messaging_message" ("recipientId") `);
await queryRunner.query(`CREATE TABLE "signin" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "ip" character varying(128) NOT NULL, "headers" jsonb NOT NULL, "success" boolean NOT NULL, CONSTRAINT "PK_9e96ddc025712616fc492b3b588" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_2c308dbdc50d94dc625670055f" ON "signin" ("userId") `);
await queryRunner.query(`CREATE TABLE "auth_session" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "userId" character varying(32), "appId" character varying(32) NOT NULL, CONSTRAINT "PK_19354ed146424a728c1112a8cbf" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_62cb09e1129f6ec024ef66e183" ON "auth_session" ("token") `);
await queryRunner.query(`CREATE TABLE "follow_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "requestId" character varying(128), "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_53a9aa3725f7a3deb150b39dbfc" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_12c01c0d1a79f77d9f6c15fadd" ON "follow_request" ("followeeId") `);
await queryRunner.query(`CREATE INDEX "IDX_a7fd92dd6dc519e6fb435dd108" ON "follow_request" ("followerId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d54a512b822fac7ed52800f6b4" ON "follow_request" ("followerId", "followeeId") `);
await queryRunner.query(`CREATE TABLE "emoji" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "host" character varying(128), "url" character varying(512) NOT NULL, "uri" character varying(512), "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_df74ce05e24999ee01ea0bc50a3" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b37dafc86e9af007e3295c2781" ON "emoji" ("name") `);
await queryRunner.query(`CREATE INDEX "IDX_5900e907bb46516ddf2871327c" ON "emoji" ("host") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4f4d35e1256c84ae3d1f0eab10" ON "emoji" ("name", "host") `);
await queryRunner.query(`CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt") `);
await queryRunner.query(`CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId") `);
await queryRunner.query(`CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId") `);
await queryRunner.query(`CREATE TABLE "user_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_a6a2dad4ae000abce2ea9d9b103" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_bfbc6f79ba4007b4ce5097f08d" ON "user_note_pining" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_410cd649884b501c02d6e72738" ON "user_note_pining" ("userId", "noteId") `);
await queryRunner.query(`CREATE TYPE "poll_notevisibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`);
await queryRunner.query(`CREATE TABLE "poll" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "multiple" boolean NOT NULL, "choices" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "votes" integer array NOT NULL, "noteVisibility" "poll_notevisibility_enum" NOT NULL, "userId" character varying(32) NOT NULL, "userHost" character varying(128), CONSTRAINT "REL_da851e06d0dfe2ef397d8b1bf1" UNIQUE ("noteId"), CONSTRAINT "PK_da851e06d0dfe2ef397d8b1bf1b" PRIMARY KEY ("noteId"))`);
await queryRunner.query(`CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_7fa20a12319c7f6dc3aed98c0a" ON "poll" ("userHost") `);
await queryRunner.query(`CREATE TABLE "user_keypair" ("userId" character varying(32) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096) NOT NULL, CONSTRAINT "REL_f4853eb41ab722fe05f81cedeb" UNIQUE ("userId"), CONSTRAINT "PK_f4853eb41ab722fe05f81cedeb6" PRIMARY KEY ("userId"))`);
await queryRunner.query(`CREATE TABLE "user_publickey" ("userId" character varying(32) NOT NULL, "keyId" character varying(256) NOT NULL, "keyPem" character varying(4096) NOT NULL, CONSTRAINT "REL_10c146e4b39b443ede016f6736" UNIQUE ("userId"), CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
await queryRunner.query(`CREATE TABLE "user_profile" ("userId" character varying(32) NOT NULL, "location" character varying(128), "birthday" character(10), "description" character varying(1024), "fields" jsonb NOT NULL DEFAULT '[]', "url" character varying(512), "email" character varying(128), "emailVerifyCode" character varying(128), "emailVerified" boolean NOT NULL DEFAULT false, "twoFactorTempSecret" character varying(128), "twoFactorSecret" character varying(128), "twoFactorEnabled" boolean NOT NULL DEFAULT false, "password" character varying(128), "clientData" jsonb NOT NULL DEFAULT '{}', "autoWatch" boolean NOT NULL DEFAULT false, "autoAcceptFollowed" boolean NOT NULL DEFAULT false, "alwaysMarkNsfw" boolean NOT NULL DEFAULT false, "carefulBot" boolean NOT NULL DEFAULT false, "twitter" boolean NOT NULL DEFAULT false, "twitterAccessToken" character varying(64) DEFAULT null, "twitterAccessTokenSecret" character varying(64) DEFAULT null, "twitterUserId" character varying(64) DEFAULT null, "twitterScreenName" character varying(64) DEFAULT null, "github" boolean NOT NULL DEFAULT false, "githubAccessToken" character varying(64) DEFAULT null, "githubId" integer DEFAULT null, "githubLogin" character varying(64) DEFAULT null, "discord" boolean NOT NULL DEFAULT false, "discordAccessToken" character varying(64) DEFAULT null, "discordRefreshToken" character varying(64) DEFAULT null, "discordExpiresDate" integer DEFAULT null, "discordId" character varying(64) DEFAULT null, "discordUsername" character varying(64) DEFAULT null, "discordDiscriminator" character varying(64) DEFAULT null, "userHost" character varying(128), CONSTRAINT "REL_51cb79b5555effaf7d69ba1cff" UNIQUE ("userId"), CONSTRAINT "PK_51cb79b5555effaf7d69ba1cff9" PRIMARY KEY ("userId"))`);
await queryRunner.query(`CREATE INDEX "IDX_dce530b98e454793dac5ec2f5a" ON "user_profile" ("userHost") `);
await queryRunner.query(`CREATE TYPE "__chart__active_users_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__active_users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_317237a9f733b970604a11e314f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__drive_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "PK_f96bc548a765cd4b3b354221ce7" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__federation_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__federation_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "PK_b39dcd31a0fe1a7757e348e85fd" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__hashtag_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__hashtag_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_c32f1ea2b44a5d2f7881e37f8f9" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__instance_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__instance_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "PK_1267c67c7c2d47b4903975f2c00" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__network_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__network_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "PK_bc4290c2e27fad14ef0c1ca93f3" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__notes_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "PK_0aec823fa85c7f901bdb3863b14" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "PK_d0ef23d24d666e1a44a0cd3d208" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_following_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "PK_85bb1b540363a29c2fec83bd907" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "PK_334acf6e915af2f29edc11b8e50" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_reaction_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_984f54dae441e65b633e8d27a7f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__test_grouped" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_grouped_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_f4a2b175d308695af30d4293272" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__test_unique_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__test_unique" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_unique_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo" bigint NOT NULL, CONSTRAINT "PK_409bac9c97cc612d8500012319d" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__test_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__test" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_b4bc31dffbd1b785276a3ecfc1e" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TYPE "__chart__users_span_enum" AS ENUM('hour', 'day')`);
await queryRunner.query(`CREATE TABLE "__chart__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "PK_4dfcf2c78d03524b9eb2c99d328" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2" FOREIGN KEY ("parentId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "drive_file" ADD CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "drive_file" ADD CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a" FOREIGN KEY ("folderId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5" FOREIGN KEY ("avatarId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "app" ADD CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_52ccc804d7c69037d558bac4c96" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_ec5c201576192ba8904c345c5cc" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_13761f64257f40c5636d0ff95ee" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_watching" ADD CONSTRAINT "FK_b0134ec406e8d09a540f8182888" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_watching" ADD CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_unread" ADD CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_unread" ADD CONSTRAINT "FK_e637cba4dc4410218c4251260e4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9" FOREIGN KEY ("notifieeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_24e0042143a18157b234df186c3" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "muting" ADD CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "muting" ADD CONSTRAINT "FK_93060675b4a79a577f31d260c67" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "sw_subscription" ADD CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "blocking" ADD CONSTRAINT "FK_2cd4a2743a99671308f5417759e" FOREIGN KEY ("blockeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "blocking" ADD CONSTRAINT "FK_0627125f1a8a42c9a1929edb552" FOREIGN KEY ("blockerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_list" ADD CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_d844bfc6f3f523a05189076efaa" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_605472305f26818cc93d1baaa74" FOREIGN KEY ("userListId") REFERENCES "user_list"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_0e00498f180193423c992bc4370" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3" FOREIGN KEY ("reporterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_5377c307783fce2b6d352e1203b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142" FOREIGN KEY ("recipientId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "signin" ADD CONSTRAINT "FK_2c308dbdc50d94dc625670055f7" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_c072b729d71697f959bde66ade0" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "follow_request" ADD CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "follow_request" ADD CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_68881008f7c3588ad7ecae471cf" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9"`);
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
await queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6"`);
await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`);
await queryRunner.query(`ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_68881008f7c3588ad7ecae471cf"`);
await queryRunner.query(`ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6"`);
await queryRunner.query(`ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066"`);
await queryRunner.query(`ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_3b25402709dd9882048c2bbade0"`);
await queryRunner.query(`ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6"`);
await queryRunner.query(`ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_f7467510c60a45ce5aca6292743"`);
await queryRunner.query(`ALTER TABLE "follow_request" DROP CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f"`);
await queryRunner.query(`ALTER TABLE "follow_request" DROP CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2"`);
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee"`);
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_c072b729d71697f959bde66ade0"`);
await queryRunner.query(`ALTER TABLE "signin" DROP CONSTRAINT "FK_2c308dbdc50d94dc625670055f7"`);
await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"`);
await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142"`);
await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_5377c307783fce2b6d352e1203b"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`);
await queryRunner.query(`ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_0e00498f180193423c992bc4370"`);
await queryRunner.query(`ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a"`);
await queryRunner.query(`ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_605472305f26818cc93d1baaa74"`);
await queryRunner.query(`ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_d844bfc6f3f523a05189076efaa"`);
await queryRunner.query(`ALTER TABLE "user_list" DROP CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99"`);
await queryRunner.query(`ALTER TABLE "blocking" DROP CONSTRAINT "FK_0627125f1a8a42c9a1929edb552"`);
await queryRunner.query(`ALTER TABLE "blocking" DROP CONSTRAINT "FK_2cd4a2743a99671308f5417759e"`);
await queryRunner.query(`ALTER TABLE "sw_subscription" DROP CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd"`);
await queryRunner.query(`ALTER TABLE "muting" DROP CONSTRAINT "FK_93060675b4a79a577f31d260c67"`);
await queryRunner.query(`ALTER TABLE "muting" DROP CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c"`);
await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5"`);
await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_24e0042143a18157b234df186c3"`);
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453"`);
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`);
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9"`);
await queryRunner.query(`ALTER TABLE "note_unread" DROP CONSTRAINT "FK_e637cba4dc4410218c4251260e4"`);
await queryRunner.query(`ALTER TABLE "note_unread" DROP CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9"`);
await queryRunner.query(`ALTER TABLE "note_watching" DROP CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619"`);
await queryRunner.query(`ALTER TABLE "note_watching" DROP CONSTRAINT "FK_b0134ec406e8d09a540f8182888"`);
await queryRunner.query(`ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a"`);
await queryRunner.query(`ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_13761f64257f40c5636d0ff95ee"`);
await queryRunner.query(`ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7"`);
await queryRunner.query(`ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8"`);
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b"`);
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_ec5c201576192ba8904c345c5cc"`);
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_52ccc804d7c69037d558bac4c96"`);
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5"`);
await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`);
await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9"`);
await queryRunner.query(`ALTER TABLE "app" DROP CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3"`);
await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2"`);
await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5"`);
await queryRunner.query(`ALTER TABLE "drive_file" DROP CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a"`);
await queryRunner.query(`ALTER TABLE "drive_file" DROP CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e"`);
await queryRunner.query(`ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2"`);
await queryRunner.query(`ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2"`);
await queryRunner.query(`DROP TABLE "__chart__users"`);
await queryRunner.query(`DROP TYPE "__chart__users_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__test"`);
await queryRunner.query(`DROP TYPE "__chart__test_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__test_unique"`);
await queryRunner.query(`DROP TYPE "__chart__test_unique_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__test_grouped"`);
await queryRunner.query(`DROP TYPE "__chart__test_grouped_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__per_user_reaction"`);
await queryRunner.query(`DROP TYPE "__chart__per_user_reaction_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__per_user_notes"`);
await queryRunner.query(`DROP TYPE "__chart__per_user_notes_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__per_user_following"`);
await queryRunner.query(`DROP TYPE "__chart__per_user_following_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__per_user_drive"`);
await queryRunner.query(`DROP TYPE "__chart__per_user_drive_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__notes"`);
await queryRunner.query(`DROP TYPE "__chart__notes_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__network"`);
await queryRunner.query(`DROP TYPE "__chart__network_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__instance"`);
await queryRunner.query(`DROP TYPE "__chart__instance_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__hashtag"`);
await queryRunner.query(`DROP TYPE "__chart__hashtag_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__federation"`);
await queryRunner.query(`DROP TYPE "__chart__federation_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__drive"`);
await queryRunner.query(`DROP TYPE "__chart__drive_span_enum"`);
await queryRunner.query(`DROP TABLE "__chart__active_users"`);
await queryRunner.query(`DROP TYPE "__chart__active_users_span_enum"`);
await queryRunner.query(`DROP INDEX "IDX_dce530b98e454793dac5ec2f5a"`);
await queryRunner.query(`DROP TABLE "user_profile"`);
await queryRunner.query(`DROP INDEX "IDX_171e64971c780ebd23fae140bb"`);
await queryRunner.query(`DROP TABLE "user_publickey"`);
await queryRunner.query(`DROP TABLE "user_keypair"`);
await queryRunner.query(`DROP INDEX "IDX_7fa20a12319c7f6dc3aed98c0a"`);
await queryRunner.query(`DROP INDEX "IDX_0610ebcfcfb4a18441a9bcdab2"`);
await queryRunner.query(`DROP TABLE "poll"`);
await queryRunner.query(`DROP TYPE "poll_notevisibility_enum"`);
await queryRunner.query(`DROP INDEX "IDX_410cd649884b501c02d6e72738"`);
await queryRunner.query(`DROP INDEX "IDX_bfbc6f79ba4007b4ce5097f08d"`);
await queryRunner.query(`DROP TABLE "user_note_pining"`);
await queryRunner.query(`DROP INDEX "IDX_e247b23a3c9b45f89ec1299d06"`);
await queryRunner.query(`DROP INDEX "IDX_3b25402709dd9882048c2bbade"`);
await queryRunner.query(`DROP INDEX "IDX_b604d92d6c7aec38627f6eaf16"`);
await queryRunner.query(`DROP TABLE "reversi_matching"`);
await queryRunner.query(`DROP INDEX "IDX_b46ec40746efceac604142be1c"`);
await queryRunner.query(`DROP TABLE "reversi_game"`);
await queryRunner.query(`DROP INDEX "IDX_4f4d35e1256c84ae3d1f0eab10"`);
await queryRunner.query(`DROP INDEX "IDX_5900e907bb46516ddf2871327c"`);
await queryRunner.query(`DROP INDEX "IDX_b37dafc86e9af007e3295c2781"`);
await queryRunner.query(`DROP TABLE "emoji"`);
await queryRunner.query(`DROP INDEX "IDX_d54a512b822fac7ed52800f6b4"`);
await queryRunner.query(`DROP INDEX "IDX_a7fd92dd6dc519e6fb435dd108"`);
await queryRunner.query(`DROP INDEX "IDX_12c01c0d1a79f77d9f6c15fadd"`);
await queryRunner.query(`DROP TABLE "follow_request"`);
await queryRunner.query(`DROP INDEX "IDX_62cb09e1129f6ec024ef66e183"`);
await queryRunner.query(`DROP TABLE "auth_session"`);
await queryRunner.query(`DROP INDEX "IDX_2c308dbdc50d94dc625670055f"`);
await queryRunner.query(`DROP TABLE "signin"`);
await queryRunner.query(`DROP INDEX "IDX_cac14a4e3944454a5ce7daa514"`);
await queryRunner.query(`DROP INDEX "IDX_5377c307783fce2b6d352e1203"`);
await queryRunner.query(`DROP INDEX "IDX_e21cd3646e52ef9c94aaf17c2e"`);
await queryRunner.query(`DROP TABLE "messaging_message"`);
await queryRunner.query(`DROP INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59"`);
await queryRunner.query(`DROP TABLE "registration_ticket"`);
await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`);
await queryRunner.query(`DROP INDEX "IDX_04cc96756f89d0b7f9473e8cdf"`);
await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`);
await queryRunner.query(`DROP INDEX "IDX_db2098070b2b5a523c58181f74"`);
await queryRunner.query(`DROP TABLE "abuse_user_report"`);
await queryRunner.query(`DROP INDEX "IDX_0f4fb9ad355f3effff221ef245"`);
await queryRunner.query(`DROP INDEX "IDX_47f4b1892f5d6ba8efb3057d81"`);
await queryRunner.query(`DROP TABLE "note_favorite"`);
await queryRunner.query(`DROP INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2"`);
await queryRunner.query(`DROP INDEX "IDX_0c44bf4f680964145f2a68a341"`);
await queryRunner.query(`DROP INDEX "IDX_d57f9030cd3af7f63ffb1c267c"`);
await queryRunner.query(`DROP INDEX "IDX_4c02d38a976c3ae132228c6fce"`);
await queryRunner.query(`DROP INDEX "IDX_0e206cec573f1edff4a3062923"`);
await queryRunner.query(`DROP INDEX "IDX_2710a55f826ee236ea1a62698f"`);
await queryRunner.query(`DROP INDEX "IDX_347fec870eafea7b26c8a73bac"`);
await queryRunner.query(`DROP TABLE "hashtag"`);
await queryRunner.query(`DROP INDEX "IDX_605472305f26818cc93d1baaa7"`);
await queryRunner.query(`DROP INDEX "IDX_d844bfc6f3f523a05189076efa"`);
await queryRunner.query(`DROP TABLE "user_list_joining"`);
await queryRunner.query(`DROP INDEX "IDX_b7fcefbdd1c18dce86687531f9"`);
await queryRunner.query(`DROP TABLE "user_list"`);
await queryRunner.query(`DROP INDEX "IDX_98a1bc5cb30dfd159de056549f"`);
await queryRunner.query(`DROP INDEX "IDX_0627125f1a8a42c9a1929edb55"`);
await queryRunner.query(`DROP INDEX "IDX_2cd4a2743a99671308f5417759"`);
await queryRunner.query(`DROP INDEX "IDX_b9a354f7941c1e779f3b33aea6"`);
await queryRunner.query(`DROP TABLE "blocking"`);
await queryRunner.query(`DROP INDEX "IDX_97754ca6f2baff9b4abb7f853d"`);
await queryRunner.query(`DROP TABLE "sw_subscription"`);
await queryRunner.query(`DROP INDEX "IDX_1eb9d9824a630321a29fd3b290"`);
await queryRunner.query(`DROP INDEX "IDX_93060675b4a79a577f31d260c6"`);
await queryRunner.query(`DROP INDEX "IDX_ec96b4fed9dae517e0dbbe0675"`);
await queryRunner.query(`DROP INDEX "IDX_f86d57fbca33c7a4e6897490cc"`);
await queryRunner.query(`DROP TABLE "muting"`);
await queryRunner.query(`DROP INDEX "IDX_8d5afc98982185799b160e10eb"`);
await queryRunner.query(`DROP INDEX "IDX_2cd3b2a6b4cf0b910b260afe08"`);
await queryRunner.query(`DROP TABLE "instance"`);
await queryRunner.query(`DROP INDEX "IDX_307be5f1d1252e0388662acb96"`);
await queryRunner.query(`DROP INDEX "IDX_6516c5a6f3c015b4eed39978be"`);
await queryRunner.query(`DROP INDEX "IDX_24e0042143a18157b234df186c"`);
await queryRunner.query(`DROP INDEX "IDX_582f8fab771a9040a12961f3e7"`);
await queryRunner.query(`DROP TABLE "following"`);
await queryRunner.query(`DROP TABLE "meta"`);
await queryRunner.query(`DROP INDEX "IDX_3c601b70a1066d2c8b517094cb"`);
await queryRunner.query(`DROP INDEX "IDX_b11a5e627c41d4dc3170f1d370"`);
await queryRunner.query(`DROP TABLE "notification"`);
await queryRunner.query(`DROP INDEX "IDX_d908433a4953cc13216cd9c274"`);
await queryRunner.query(`DROP INDEX "IDX_e637cba4dc4410218c4251260e"`);
await queryRunner.query(`DROP INDEX "IDX_56b0166d34ddae49d8ef7610bb"`);
await queryRunner.query(`DROP TABLE "note_unread"`);
await queryRunner.query(`DROP INDEX "IDX_a42c93c69989ce1d09959df4cf"`);
await queryRunner.query(`DROP INDEX "IDX_44499765eec6b5489d72c4253b"`);
await queryRunner.query(`DROP INDEX "IDX_03e7028ab8388a3f5e3ce2a861"`);
await queryRunner.query(`DROP INDEX "IDX_b0134ec406e8d09a540f818288"`);
await queryRunner.query(`DROP INDEX "IDX_318cdf42a9cfc11f479bd802bb"`);
await queryRunner.query(`DROP TABLE "note_watching"`);
await queryRunner.query(`DROP INDEX "IDX_ad0c221b25672daf2df320a817"`);
await queryRunner.query(`DROP INDEX "IDX_45145e4953780f3cd5656f0ea6"`);
await queryRunner.query(`DROP INDEX "IDX_13761f64257f40c5636d0ff95e"`);
await queryRunner.query(`DROP INDEX "IDX_01f4581f114e0ebd2bbb876f0b"`);
await queryRunner.query(`DROP TABLE "note_reaction"`);
await queryRunner.query(`DROP INDEX "IDX_50bd7164c5b78f1f4a42c4d21f"`);
await queryRunner.query(`DROP INDEX "IDX_aecfbd5ef60374918e63ee95fa"`);
await queryRunner.query(`DROP INDEX "IDX_66d2bd2ee31d14bcc23069a89f"`);
await queryRunner.query(`DROP INDEX "IDX_0fb627e1c2f753262a74f0562d"`);
await queryRunner.query(`DROP TABLE "poll_vote"`);
await queryRunner.query(`DROP INDEX "IDX_7125a826ab192eb27e11d358a5"`);
await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`);
await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`);
await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`);
await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`);
await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`);
await queryRunner.query(`DROP INDEX "IDX_153536c67d05e9adb24e99fc2b"`);
await queryRunner.query(`DROP INDEX "IDX_5b87d9d19127bd5d92026017a7"`);
await queryRunner.query(`DROP INDEX "IDX_52ccc804d7c69037d558bac4c9"`);
await queryRunner.query(`DROP INDEX "IDX_17cb3553c700a4985dff5a30ff"`);
await queryRunner.query(`DROP INDEX "IDX_e7c0567f5261063592f022e9b5"`);
await queryRunner.query(`DROP TABLE "note"`);
await queryRunner.query(`DROP TYPE "note_visibility_enum"`);
await queryRunner.query(`DROP INDEX "IDX_9949557d0e1b2c19e5344c171e"`);
await queryRunner.query(`DROP INDEX "IDX_64c327441248bae40f7d92f34f"`);
await queryRunner.query(`DROP INDEX "IDX_70ba8f6af34bc924fc9e12adb8"`);
await queryRunner.query(`DROP TABLE "access_token"`);
await queryRunner.query(`DROP INDEX "IDX_f49922d511d666848f250663c4"`);
await queryRunner.query(`DROP INDEX "IDX_3f5b0899ef90527a3462d7c2cb"`);
await queryRunner.query(`DROP INDEX "IDX_048a757923ed8b157e9895da53"`);
await queryRunner.query(`DROP TABLE "app"`);
await queryRunner.query(`DROP INDEX "IDX_5deb01ae162d1d70b80d064c27"`);
await queryRunner.query(`DROP INDEX "IDX_a854e557b1b14814750c7c7b0c"`);
await queryRunner.query(`DROP INDEX "IDX_be623adaa4c566baf5d29ce0c8"`);
await queryRunner.query(`DROP INDEX "IDX_3252a5df8d5bbd16b281f7799e"`);
await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`);
await queryRunner.query(`DROP INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8"`);
await queryRunner.query(`DROP INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4"`);
await queryRunner.query(`DROP INDEX "IDX_e11e649824a45d8ed01d597fd9"`);
await queryRunner.query(`DROP TABLE "user"`);
await queryRunner.query(`DROP INDEX "IDX_bb90d1956dafc4068c28aa7560"`);
await queryRunner.query(`DROP INDEX "IDX_e5848eac4940934e23dbc17581"`);
await queryRunner.query(`DROP INDEX "IDX_c55b2b7c284d9fef98026fc88e"`);
await queryRunner.query(`DROP INDEX "IDX_e74022ce9a074b3866f70e0d27"`);
await queryRunner.query(`DROP INDEX "IDX_d85a184c2540d2deba33daf642"`);
await queryRunner.query(`DROP INDEX "IDX_a40b8df8c989d7db937ea27cf6"`);
await queryRunner.query(`DROP INDEX "IDX_37bb9a1b4585f8a3beb24c62d6"`);
await queryRunner.query(`DROP INDEX "IDX_92779627994ac79277f070c91e"`);
await queryRunner.query(`DROP INDEX "IDX_860fa6f6c7df5bb887249fba22"`);
await queryRunner.query(`DROP INDEX "IDX_c8dfad3b72196dd1d6b5db168a"`);
await queryRunner.query(`DROP TABLE "drive_file"`);
await queryRunner.query(`DROP INDEX "IDX_00ceffb0cdc238b3233294f08f"`);
await queryRunner.query(`DROP INDEX "IDX_f4fc06e49c0171c85f1c48060d"`);
await queryRunner.query(`DROP INDEX "IDX_02878d441ceae15ce060b73daf"`);
await queryRunner.query(`DROP TABLE "drive_folder"`);
await queryRunner.query(`DROP INDEX "IDX_584b536b49e53ac81beb39a177"`);
await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`);
await queryRunner.query(`DROP INDEX "IDX_8e4eb51a35d81b64dda28eed0a"`);
await queryRunner.query(`DROP TABLE "log"`);
await queryRunner.query(`DROP TYPE "log_level_enum"`);
}
}

View File

@ -0,0 +1,29 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class webauthn1561706992953 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "attestation_challenge" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "challenge" character varying(64) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "registrationChallenge" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_d0ba6786e093f1bcb497572a6b5" PRIMARY KEY ("id", "userId"))`);
await queryRunner.query(`CREATE INDEX "IDX_f1a461a618fa1755692d0e0d59" ON "attestation_challenge" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_47efb914aed1f72dd39a306c7b" ON "attestation_challenge" ("challenge") `);
await queryRunner.query(`CREATE TABLE "user_security_key" ("id" character varying NOT NULL, "userId" character varying(32) NOT NULL, "publicKey" character varying NOT NULL, "lastUsed" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(30) NOT NULL, CONSTRAINT "PK_3e508571121ab39c5f85d10c166" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44" ON "user_security_key" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7" ON "user_security_key" ("publicKey") `);
await queryRunner.query(`ALTER TABLE "user_profile" ADD "securityKeysAvailable" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "attestation_challenge" ADD CONSTRAINT "FK_f1a461a618fa1755692d0e0d592" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_security_key" ADD CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_security_key" DROP CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447"`);
await queryRunner.query(`ALTER TABLE "attestation_challenge" DROP CONSTRAINT "FK_f1a461a618fa1755692d0e0d592"`);
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "securityKeysAvailable"`);
await queryRunner.query(`DROP INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7"`);
await queryRunner.query(`DROP INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44"`);
await queryRunner.query(`DROP TABLE "user_security_key"`);
await queryRunner.query(`DROP INDEX "IDX_47efb914aed1f72dd39a306c7b"`);
await queryRunner.query(`DROP INDEX "IDX_f1a461a618fa1755692d0e0d59"`);
await queryRunner.query(`DROP TABLE "attestation_challenge"`);
}
}

View File

@ -0,0 +1,201 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class ChartIndexes1561873850023 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_00ed5f86db1f7efafb1978bf21" ON "__chart__active_users" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_7a170f67425e62a8fabb76c872" ON "__chart__drive" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_76e87c7bfc5d925fcbba405d84" ON "__chart__federation" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_07747a1038c05f532a718fe1de" ON "__chart__hashtag" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_99a7d2faaef84a6f728d714ad6" ON "__chart__hashtag" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_6b8f34a1a64b06014b6fb66824" ON "__chart__instance" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63" ON "__chart__instance" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_7b5da130992ec9df96712d4290" ON "__chart__network" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_7036f2957151588b813185c794" ON "__chart__notes" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_5f86db6492274e07c1a3cdf286" ON "__chart__per_user_drive" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_e496ca8096d28f6b9b509264dc" ON "__chart__per_user_drive" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_7af07790712aa3438ff6773f3b" ON "__chart__per_user_following" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_4b3593098b6edc9c5afe36b18b" ON "__chart__per_user_following" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_84234bd1abb873f07329681c83" ON "__chart__per_user_notes" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_55bf20f366979f2436de99206b" ON "__chart__per_user_notes" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_f7bf4c62059764c2c2bb40fdab" ON "__chart__per_user_reaction" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_8cf3156fd7a6b15c43459c6e3b" ON "__chart__per_user_reaction" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_0c641990ecf47d2545df4edb75" ON "__chart__test_grouped" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_234dff3c0b56a6150b95431ab9" ON "__chart__test_grouped" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_437bab3c6061d90f6bb65fd2cc" ON "__chart__test_unique" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_bbfa573a8181018851ed0b6357" ON "__chart__test_unique" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_b070a906db04b44c67c6c2144d" ON "__chart__test" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_d41cce6aee1a50bfc062038f9b" ON "__chart__test" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("span", "date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `);
await queryRunner.query(`CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `);
await queryRunner.query(`CREATE INDEX "IDX_ed9b95919c672a13008e9487ee" ON "__chart__users" ("group") `);
await queryRunner.query(`CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("span", "date") `);
await queryRunner.query(`CREATE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `);
await queryRunner.query(`CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("span", "date", "group") `);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`);
await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`);
await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`);
await queryRunner.query(`DROP INDEX "IDX_ed9b95919c672a13008e9487ee"`);
await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`);
await queryRunner.query(`DROP INDEX "IDX_845254b3eaf708ae8a6cac3026"`);
await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`);
await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`);
await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`);
await queryRunner.query(`DROP INDEX "IDX_d41cce6aee1a50bfc062038f9b"`);
await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`);
await queryRunner.query(`DROP INDEX "IDX_b070a906db04b44c67c6c2144d"`);
await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`);
await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`);
await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`);
await queryRunner.query(`DROP INDEX "IDX_bbfa573a8181018851ed0b6357"`);
await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`);
await queryRunner.query(`DROP INDEX "IDX_437bab3c6061d90f6bb65fd2cc"`);
await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`);
await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`);
await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`);
await queryRunner.query(`DROP INDEX "IDX_234dff3c0b56a6150b95431ab9"`);
await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`);
await queryRunner.query(`DROP INDEX "IDX_0c641990ecf47d2545df4edb75"`);
await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`);
await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`);
await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`);
await queryRunner.query(`DROP INDEX "IDX_8cf3156fd7a6b15c43459c6e3b"`);
await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`);
await queryRunner.query(`DROP INDEX "IDX_f7bf4c62059764c2c2bb40fdab"`);
await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`);
await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`);
await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`);
await queryRunner.query(`DROP INDEX "IDX_55bf20f366979f2436de99206b"`);
await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`);
await queryRunner.query(`DROP INDEX "IDX_84234bd1abb873f07329681c83"`);
await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`);
await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`);
await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`);
await queryRunner.query(`DROP INDEX "IDX_4b3593098b6edc9c5afe36b18b"`);
await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`);
await queryRunner.query(`DROP INDEX "IDX_7af07790712aa3438ff6773f3b"`);
await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`);
await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`);
await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`);
await queryRunner.query(`DROP INDEX "IDX_e496ca8096d28f6b9b509264dc"`);
await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`);
await queryRunner.query(`DROP INDEX "IDX_5f86db6492274e07c1a3cdf286"`);
await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`);
await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`);
await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`);
await queryRunner.query(`DROP INDEX "IDX_7036f2957151588b813185c794"`);
await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`);
await queryRunner.query(`DROP INDEX "IDX_42eb716a37d381cdf566192b2b"`);
await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`);
await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`);
await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`);
await queryRunner.query(`DROP INDEX "IDX_7b5da130992ec9df96712d4290"`);
await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`);
await queryRunner.query(`DROP INDEX "IDX_a1efd3e0048a5f2793a47360dc"`);
await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`);
await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`);
await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`);
await queryRunner.query(`DROP INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63"`);
await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`);
await queryRunner.query(`DROP INDEX "IDX_6b8f34a1a64b06014b6fb66824"`);
await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`);
await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`);
await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`);
await queryRunner.query(`DROP INDEX "IDX_99a7d2faaef84a6f728d714ad6"`);
await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`);
await queryRunner.query(`DROP INDEX "IDX_07747a1038c05f532a718fe1de"`);
await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`);
await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`);
await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`);
await queryRunner.query(`DROP INDEX "IDX_76e87c7bfc5d925fcbba405d84"`);
await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`);
await queryRunner.query(`DROP INDEX "IDX_36cb699c49580d4e6c2e6159f9"`);
await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`);
await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`);
await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`);
await queryRunner.query(`DROP INDEX "IDX_7a170f67425e62a8fabb76c872"`);
await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`);
await queryRunner.query(`DROP INDEX "IDX_13565815f618a1ff53886c5b28"`);
await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`);
await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`);
await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`);
await queryRunner.query(`DROP INDEX "IDX_00ed5f86db1f7efafb1978bf21"`);
await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`);
await queryRunner.query(`DROP INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc"`);
await queryRunner.query(`DROP INDEX "IDX_90148bbc2bf0854428786bfc15"`);
await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`);
await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`);
await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`);
await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`);
await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`);
await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`);
await queryRunner.query(`DROP INDEX "IDX_315c779174fe8247ab324f036e"`);
await queryRunner.query(`DROP INDEX "IDX_c5d46cbfda48b1c33ed852e21b"`);
await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`);
}
}

View File

@ -1,7 +1,7 @@
import * as fs from 'fs'; const config = require('./built/config').default;
import config from './config'; const entities = require('./built/db/postgre').entities;
const json = { module.exports = {
type: 'postgres', type: 'postgres',
host: config.db.host, host: config.db.host,
port: config.db.port, port: config.db.port,
@ -9,11 +9,9 @@ const json = {
password: config.db.pass, password: config.db.pass,
database: config.db.db, database: config.db.db,
extra: config.db.extra, extra: config.db.extra,
entities: ['src/models/entities/*.ts'], entities: entities,
migrations: ['migration/*.ts'], migrations: ['migration/*.ts'],
cli: { cli: {
migrationsDir: 'migration' migrationsDir: 'migration'
} }
}; };
fs.writeFileSync('ormconfig.json', JSON.stringify(json));

View File

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.23.1", "version": "11.24.0",
"codename": "daybreak", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
@ -11,9 +11,9 @@
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./index.js", "start": "node ./index.js",
"init": "node ./built/init.js", "init": "npm run migrate",
"ormconfig": "node ./built/ormconfig.js", "ormconfig": "node ./built/ormconfig.js",
"migrate": "npm run ormconfig && ts-node ./node_modules/typeorm/cli.js migration:run", "migrate": "ts-node ./node_modules/typeorm/cli.js migration:run",
"migrateandstart": "npm run migrate && npm run start", "migrateandstart": "npm run migrate && npm run start",
"build": "webpack && gulp build", "build": "webpack && gulp build",
"webpack": "webpack", "webpack": "webpack",
@ -30,105 +30,109 @@
"video-thumbnail-generator/lodash": "^4.17.11" "video-thumbnail-generator/lodash": "^4.17.11"
}, },
"dependencies": { "dependencies": {
"@elastic/elasticsearch": "7.0.0-rc.2", "@elastic/elasticsearch": "7.1.0",
"@fortawesome/fontawesome-svg-core": "1.2.15", "@fortawesome/fontawesome-svg-core": "1.2.19",
"@fortawesome/free-brands-svg-icons": "5.7.2", "@fortawesome/free-brands-svg-icons": "5.9.0",
"@fortawesome/free-regular-svg-icons": "5.7.2", "@fortawesome/free-regular-svg-icons": "5.9.0",
"@fortawesome/free-solid-svg-icons": "5.7.2", "@fortawesome/free-solid-svg-icons": "5.9.0",
"@fortawesome/vue-fontawesome": "0.1.5", "@fortawesome/vue-fontawesome": "0.1.6",
"@koa/cors": "2.2.3", "@koa/cors": "3.0.0",
"@typescript-eslint/parser": "1.11.0",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.11", "@types/bull": "3.5.15",
"@types/cbor": "2.0.0",
"@types/dateformat": "3.0.0", "@types/dateformat": "3.0.0",
"@types/deep-equal": "1.0.1", "@types/deep-equal": "1.0.1",
"@types/double-ended-queue": "2.1.0", "@types/double-ended-queue": "2.1.1",
"@types/gulp": "4.0.6", "@types/gulp": "4.0.6",
"@types/gulp-mocha": "0.0.32", "@types/gulp-mocha": "0.0.32",
"@types/gulp-rename": "0.0.33", "@types/gulp-rename": "0.0.33",
"@types/gulp-replace": "0.0.31", "@types/gulp-replace": "0.0.31",
"@types/gulp-uglify": "3.0.6", "@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34", "@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0", "@types/is-root": "2.1.2",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.1", "@types/js-yaml": "3.12.1",
"@types/jsdom": "12.2.3", "@types/jsdom": "12.2.4",
"@types/katex": "0.10.1", "@types/katex": "0.10.1",
"@types/koa": "2.0.48", "@types/koa": "2.0.49",
"@types/koa-bodyparser": "5.0.2", "@types/koa-bodyparser": "5.0.2",
"@types/koa-compress": "2.0.9", "@types/koa-compress": "2.0.9",
"@types/koa-cors": "0.0.0", "@types/koa-cors": "0.0.0",
"@types/koa-favicon": "2.0.19", "@types/koa-favicon": "2.0.19",
"@types/koa-logger": "3.1.1", "@types/koa-logger": "3.1.1",
"@types/koa-mount": "3.0.1", "@types/koa-mount": "4.0.0",
"@types/koa-multer": "1.0.0", "@types/koa-multer": "1.0.0",
"@types/koa-router": "7.0.40", "@types/koa-router": "7.0.42",
"@types/koa-send": "4.1.2", "@types/koa-send": "4.1.2",
"@types/koa-views": "2.0.3", "@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.3", "@types/koa__cors": "2.2.3",
"@types/lolex": "3.1.1", "@types/lolex": "3.1.1",
"@types/minio": "7.0.1", "@types/minio": "7.0.2",
"@types/mocha": "5.2.6", "@types/mocha": "5.2.7",
"@types/node": "11.13.8", "@types/node": "12.0.10",
"@types/nodemailer": "4.6.8", "@types/nodemailer": "6.2.0",
"@types/nprogress": "0.0.29", "@types/nprogress": "0.2.0",
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
"@types/parse5": "5.0.0", "@types/parse5": "5.0.0",
"@types/parsimmon": "1.10.0", "@types/parsimmon": "1.10.0",
"@types/portscanner": "2.1.0", "@types/portscanner": "2.1.0",
"@types/pug": "2.0.4", "@types/pug": "2.0.4",
"@types/qrcode": "1.3.2", "@types/qrcode": "1.3.3",
"@types/random-seed": "0.3.3", "@types/random-seed": "0.3.3",
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.12", "@types/redis": "2.8.13",
"@types/rename": "1.0.1", "@types/rename": "1.0.1",
"@types/request": "2.48.1", "@types/request": "2.48.1",
"@types/request-promise-native": "1.0.16", "@types/request-promise-native": "1.0.16",
"@types/request-stats": "3.0.0", "@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.28", "@types/seedrandom": "2.4.28",
"@types/sharp": "0.22.1", "@types/sharp": "0.22.2",
"@types/showdown": "1.9.2", "@types/showdown": "1.9.3",
"@types/speakeasy": "2.0.4", "@types/speakeasy": "2.0.4",
"@types/systeminformation": "3.23.1", "@types/systeminformation": "3.23.1",
"@types/tinycolor2": "1.4.1", "@types/tinycolor2": "1.4.2",
"@types/tmp": "0.1.0", "@types/tmp": "0.1.0",
"@types/uuid": "3.4.4", "@types/uuid": "3.4.5",
"@types/web-push": "3.3.0", "@types/web-push": "3.3.0",
"@types/webpack": "4.4.29", "@types/webpack": "4.4.34",
"@types/webpack-stream": "3.2.10", "@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"animejs": "3.0.1", "animejs": "3.0.1",
"apexcharts": "3.8.0", "apexcharts": "3.8.1",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "4.0.2", "autosize": "4.0.2",
"autwh": "0.1.0", "autwh": "0.1.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bootstrap": "4.3.1",
"bootstrap-vue": "2.0.0-rc.13", "bootstrap-vue": "2.0.0-rc.13",
"bull": "3.10.0", "bull": "3.10.0",
"cafy": "15.1.1", "cafy": "15.1.1",
"cbor": "4.1.5",
"chai": "4.2.0", "chai": "4.2.0",
"chalk": "2.4.2", "chalk": "2.4.2",
"cli-highlight": "2.1.1", "cli-highlight": "2.1.1",
"commander": "2.20.0", "commander": "2.20.0",
"content-disposition": "0.5.3", "content-disposition": "0.5.3",
"crc-32": "1.2.0", "crc-32": "1.2.0",
"css-loader": "2.1.1", "css-loader": "3.0.0",
"cssnano": "4.1.10", "cssnano": "4.1.10",
"dateformat": "3.0.3", "dateformat": "3.0.3",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
"diskusage": "1.1.1", "diskusage": "1.1.2",
"double-ended-queue": "2.1.0-0", "double-ended-queue": "2.1.0-0",
"emojilib": "2.4.0", "emojilib": "2.4.0",
"eslint": "5.16.0", "eslint": "6.0.1",
"eslint-plugin-vue": "5.2.2", "eslint-plugin-vue": "5.2.3",
"eventemitter3": "3.1.2", "eventemitter3": "4.0.0",
"feed": "2.0.4", "feed": "3.0.0",
"file-type": "10.11.0", "file-type": "12.0.0",
"fuckadblock": "3.2.1", "fuckadblock": "3.2.1",
"gulp": "4.0.2", "gulp": "4.0.2",
"gulp-cssnano": "2.1.3", "gulp-cssnano": "2.1.3",
"gulp-imagemin": "5.0.3", "gulp-imagemin": "6.0.0",
"gulp-mocha": "6.0.0", "gulp-mocha": "6.0.0",
"gulp-rename": "1.4.0", "gulp-rename": "1.4.0",
"gulp-replace": "1.0.0", "gulp-replace": "1.0.0",
@ -147,7 +151,8 @@
"js-yaml": "3.13.1", "js-yaml": "3.13.1",
"jsdom": "15.1.1", "jsdom": "15.1.1",
"json5": "2.1.0", "json5": "2.1.0",
"json5-loader": "2.0.0", "json5-loader": "3.0.0",
"jsrsasign": "8.0.12",
"katex": "0.10.2", "katex": "0.10.2",
"koa": "2.7.0", "koa": "2.7.0",
"koa-bodyparser": "4.2.1", "koa-bodyparser": "4.2.1",
@ -163,14 +168,14 @@
"koa-views": "6.2.0", "koa-views": "6.2.0",
"langmap": "0.0.16", "langmap": "0.0.16",
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"lolex": "3.1.0", "lolex": "4.1.0",
"lookup-dns-cache": "2.1.0", "lookup-dns-cache": "2.1.0",
"minio": "7.0.10", "minio": "7.0.10",
"mocha": "6.1.4", "mocha": "6.1.4",
"moji": "0.5.1", "moji": "0.5.1",
"moment": "2.24.0", "moment": "2.24.0",
"ms": "2.1.2", "ms": "2.1.2",
"nested-property": "0.0.7", "nested-property": "1.0.1",
"node-fetch": "2.6.0", "node-fetch": "2.6.0",
"nodemailer": "6.2.1", "nodemailer": "6.2.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
@ -186,7 +191,7 @@
"promise-any": "0.2.0", "promise-any": "0.2.0",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"promise-sequential": "1.1.1", "promise-sequential": "1.1.1",
"pug": "2.0.3", "pug": "2.0.4",
"punycode": "2.1.1", "punycode": "2.1.1",
"pureimage": "0.1.6", "pureimage": "0.1.6",
"qrcode": "1.3.3", "qrcode": "1.3.3",
@ -210,12 +215,12 @@
"showdown": "1.9.0", "showdown": "1.9.0",
"showdown-highlightjs-extension": "0.1.2", "showdown-highlightjs-extension": "0.1.2",
"speakeasy": "2.0.0", "speakeasy": "2.0.0",
"stringz": "1.0.0", "stringz": "2.0.0",
"style-loader": "0.23.1", "style-loader": "0.23.1",
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"summaly": "2.3.0", "summaly": "2.3.0",
"systeminformation": "4.11.1", "systeminformation": "4.13.1",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.3.0", "terser-webpack-plugin": "1.3.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
@ -223,14 +228,13 @@
"tmp": "0.1.0", "tmp": "0.1.0",
"ts-loader": "5.3.3", "ts-loader": "5.3.3",
"ts-node": "7.0.1", "ts-node": "7.0.1",
"tslint": "5.15.0", "tslint": "5.18.0",
"tslint-sonarts": "1.9.0", "tslint-sonarts": "1.9.0",
"typeorm": "0.2.16-rc.1", "typeorm": "0.2.18",
"typescript": "3.3.3333", "typescript": "3.5.2",
"typescript-eslint-parser": "22.0.0",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"ulid": "2.3.0", "ulid": "2.3.0",
"url-loader": "1.1.2", "url-loader": "2.0.1",
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.3", "v-animate-css": "0.0.3",
"v-debounce": "0.1.2", "v-debounce": "0.1.2",
@ -238,7 +242,7 @@
"vue": "2.6.10", "vue": "2.6.10",
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.6.0", "vue-content-loading": "1.6.0",
"vue-cropperjs": "3.0.0", "vue-cropperjs": "4.0.0",
"vue-i18n": "8.11.2", "vue-i18n": "8.11.2",
"vue-js-modal": "1.3.31", "vue-js-modal": "1.3.31",
"vue-json-pretty": "1.6.0", "vue-json-pretty": "1.6.0",
@ -248,17 +252,17 @@
"vue-router": "3.0.6", "vue-router": "3.0.6",
"vue-sequential-entrance": "1.1.3", "vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.15", "vue-svg-inline-loader": "1.2.16",
"vue-template-compiler": "2.6.10", "vue-template-compiler": "2.6.10",
"vuedraggable": "2.21.0", "vuedraggable": "2.23.0",
"vuewordcloud": "18.7.11", "vuewordcloud": "18.7.11",
"vuex": "3.1.1", "vuex": "3.1.1",
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"web-push": "3.3.5", "web-push": "3.3.5",
"webpack": "4.34.0", "webpack": "4.35.2",
"webpack-cli": "3.3.4", "webpack-cli": "3.3.5",
"websocket": "1.0.28", "websocket": "1.0.28",
"ws": "7.0.0", "ws": "7.0.1",
"xev": "2.0.1" "xev": "2.0.1"
} }
} }

803
src/@types/jsrsasign.d.ts vendored Normal file
View File

@ -0,0 +1,803 @@
// Attention: Partial Type Definition
declare module 'jsrsasign' {
//// HELPER TYPES
/**
* Attention: The value might be changed by the function.
*/
type Mutable<T> = T;
/**
* Deprecated: The function might be deleted in future release.
*/
type Deprecated<T> = T;
//// COMMON TYPES
/**
* byte number
*/
type ByteNumber = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255;
/**
* hexadecimal string /[0-9A-F]/
*/
type HexString = string;
/**
* binary string /[01]/
*/
type BinString = string;
/**
* base64 string /[A-Za-z0-9+/]=+/
*/
type Base64String = string;
/**
* base64 URL encoded string /[A-Za-z0-9_-]/
*/
type Base64URLString = string;
/**
* time value (ex. "151231235959Z")
*/
type TimeValue = string;
/**
* OID string (ex. '1.2.3.4.567')
*/
type OID = string;
/**
* OID name
*/
type OIDName = string;
/**
* PEM formatted string
*/
type PEM = string;
//// ASN1 TYPES
class ASN1Object {
public isModified: boolean;
public hTLV: ASN1TLV;
public hT: ASN1T;
public hL: ASN1L;
public hV: ASN1V;
public getLengthHexFromValue(): HexString;
public getEncodedHex(): ASN1TLV;
public getValueHex(): ASN1V;
public getFreshValueHex(): ASN1V;
}
class DERAbstractStructured extends ASN1Object {
constructor(params?: Partial<Record<'array', ASN1Object[]>>);
public setByASN1ObjectArray(asn1ObjectArray: ASN1Object[]): void;
public appendASN1Object(asn1Object: ASN1Object): void;
}
class DERSequence extends DERAbstractStructured {
constructor(params?: Partial<Record<'array', ASN1Object[]>>);
public getFreshValueHex(): ASN1V;
}
//// ASN1HEX TYPES
/**
* ASN.1 DER encoded data (hexadecimal string)
*/
type ASN1S = HexString;
/**
* index of something
*/
type Idx<T extends { [idx: string]: unknown } | { [idx: number]: unknown }> = ASN1S extends { [idx: string]: unknown } ? string : ASN1S extends { [idx: number]: unknown } ? number : never;
/**
* byte length of something
*/
type ByteLength<T extends { length: unknown }> = T['length'];
/**
* ASN.1 L(length) (hexadecimal string)
*/
type ASN1L = HexString;
/**
* ASN.1 T(tag) (hexadecimal string)
*/
type ASN1T = HexString;
/**
* ASN.1 V(value) (hexadecimal string)
*/
type ASN1V = HexString;
/**
* ASN.1 TLV (hexadecimal string)
*/
type ASN1TLV = HexString;
/**
* ASN.1 object string
*/
type ASN1ObjectString = string;
/**
* nth
*/
type Nth = number;
/**
* ASN.1 DER encoded OID value (hexadecimal string)
*/
type ASN1OIDV = HexString;
class ASN1HEX {
public static getLblen(s: ASN1S, idx: Idx<ASN1S>): ByteLength<ASN1L>;
public static getL(s: ASN1S, idx: Idx<ASN1S>): ASN1L;
public static getVblen(s: ASN1S, idx: Idx<ASN1S>): ByteLength<ASN1V>;
public static getVidx(s: ASN1S, idx: Idx<ASN1S>): Idx<ASN1V>;
public static getV(s: ASN1S, idx: Idx<ASN1S>): ASN1V;
public static getTLV(s: ASN1S, idx: Idx<ASN1S>): ASN1TLV;
public static getNextSiblingIdx(s: ASN1S, idx: Idx<ASN1S>): Idx<ASN1ObjectString>;
public static getChildIdx(h: ASN1S, pos: Idx<ASN1S>): Idx<ASN1ObjectString>[];
public static getNthChildIdx(h: ASN1S, idx: Idx<ASN1S>, nth: Nth): Idx<ASN1ObjectString>;
public static getIdxbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string): Idx<Mutable<Nth[]>>;
public static getTLVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string): ASN1TLV;
public static getVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string, removeUnusedbits?: boolean): ASN1V;
public static hextooidstr(hex: ASN1OIDV): OID;
public static dump(hexOrObj: ASN1S | ASN1Object, flags?: Record<string, unknown>, idx?: Idx<ASN1S>, indent?: string): string;
public static isASN1HEX(hex: string): hex is HexString;
public static oidname(oidDotOrHex: OID | ASN1OIDV): OIDName;
}
//// BIG INTEGER TYPES (PARTIAL)
class BigInteger {
constructor(a: null);
constructor(a: number, b: SecureRandom);
constructor(a: number, b: number, c: SecureRandom);
constructor(a: unknown);
constructor(a: string, b: number);
public am(i: number, x: number, w: number, j: number, c: number, n: number): number;
public DB: number;
public DM: number;
public DV: number;
public FV: number;
public F1: number;
public F2: number;
protected copyTo(r: Mutable<BigInteger>): void;
protected fromInt(x: number): void;
protected fromString(s: string, b: number): void;
protected clamp(): void;
public toString(b: number): string;
public negate(): BigInteger;
public abs(): BigInteger;
public compareTo(a: BigInteger): number;
public bitLength(): number;
protected dlShiftTo(n: number, r: Mutable<BigInteger>): void;
protected drShiftTo(n: number, r: Mutable<BigInteger>): void;
protected lShiftTo(n: number, r: Mutable<BigInteger>): void;
protected rShiftTo(n: number, r: Mutable<BigInteger>): void;
protected subTo(a: BigInteger, r: Mutable<BigInteger>): void;
protected multiplyTo(a: BigInteger, r: Mutable<BigInteger>): void;
protected squareTo(r: Mutable<BigInteger>): void;
protected divRemTo(m: BigInteger, q: Mutable<BigInteger>, r: Mutable<BigInteger>): void;
public mod(a: BigInteger): BigInteger;
protected invDigit(): number;
protected isEven(): boolean;
protected exp(e: number, z: Classic | Montgomery): BigInteger;
public modPowInt(e: number, m: BigInteger): BigInteger;
public static ZERO: BigInteger;
public static ONE: BigInteger;
}
class Classic {
constructor(m: BigInteger);
public convert(x: BigInteger): BigInteger;
public revert(x: BigInteger): BigInteger;
public reduce(x: Mutable<BigInteger>): void;
public mulTo(x: BigInteger, r: Mutable<BigInteger>): void;
public sqrTo(x: BigInteger, y: BigInteger, r: Mutable<BigInteger>): void;
}
class Montgomery {
constructor(m: BigInteger);
public convert(x: BigInteger): BigInteger;
public revert(x: BigInteger): BigInteger;
public reduce(x: Mutable<BigInteger>): void;
public mulTo(x: BigInteger, r: Mutable<BigInteger>): void;
public sqrTo(x: BigInteger, y: BigInteger, r: Mutable<BigInteger>): void;
}
//// KEYUTIL TYPES
type DecryptAES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type Decrypt3DES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type DecryptDES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type EncryptAES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type Encrypt3DES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type EncryptDES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
type AlgList = {
'AES-256-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 32; ivlen: 16; };
'AES-192-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 24; ivlen: 16; };
'AES-128-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 16; ivlen: 16; };
'DES-EDE3-CBC': { 'proc': Decrypt3DES; 'eproc': Encrypt3DES; keylen: 24; ivlen: 8; };
'DES-CBC': { 'proc': DecryptDES; 'eproc': EncryptDES; keylen: 8; ivlen: 8; };
};
type AlgName = keyof AlgList;
type PEMHeadAlgName = 'RSA' | 'EC' | 'DSA';
type GetKeyRSAParam = RSAKey | {
n: BigInteger;
e: number;
} | Record<'n' | 'e', HexString> | Record<'n' | 'e', HexString> & Record<'d' | 'p' | 'q' | 'dp' | 'dq' | 'co', HexString | null> | {
n: BigInteger;
e: number;
d: BigInteger;
} | {
kty: 'RSA';
} & Record<'n' | 'e', Base64URLString> | {
kty: 'RSA';
} & Record<'n' | 'e' | 'd' | 'p' | 'q' | 'dp' | 'dq' | 'qi', Base64URLString> | {
kty: 'RSA';
} & Record<'n' | 'e' | 'd', Base64URLString>;
type GetKeyECDSAParam = KJUR.crypto.ECDSA | {
curve: KJUR.crypto.CurveName;
xy: HexString;
} | {
curve: KJUR.crypto.CurveName;
d: HexString;
} | {
kty: 'EC';
crv: KJUR.crypto.CurveName;
x: Base64URLString;
y: Base64URLString;
} | {
kty: 'EC';
crv: KJUR.crypto.CurveName;
x: Base64URLString;
y: Base64URLString;
d: Base64URLString;
};
type GetKeyDSAParam = KJUR.crypto.DSA | Record<'p' | 'q' | 'g', BigInteger> & Record<'y', BigInteger | null> | Record<'p' | 'q' | 'g' | 'x', BigInteger> & Record<'y', BigInteger | null>;
type GetKeyParam = GetKeyRSAParam | GetKeyECDSAParam | GetKeyDSAParam | string;
class KEYUTIL {
public version: '1.0.0';
public parsePKCS5PEM(sPKCS5PEM: PEM): Partial<Record<'type' | 's', string>> & (Record<'cipher' | 'ivsalt', string> | Record<'cipher' | 'ivsalt', undefined>);
public getKeyAndUnusedIvByPasscodeAndIvsalt(algName: AlgName, passcode: string, ivsaltHex: HexString): Record<'keyhex' | 'ivhex', HexString>;
public decryptKeyB64(privateKeyB64: Base64String, sharedKeyAlgName: AlgName, sharedKeyHex: HexString, ivsaltHex: HexString): Base64String;
public getDecryptedKeyHex(sEncryptedPEM: PEM, passcode: string): HexString;
public getEncryptedPKCS5PEMFromPrvKeyHex(pemHeadAlg: PEMHeadAlgName, hPrvKey: string, passcode: string, sharedKeyAlgName?: AlgName | null, ivsaltHex?: HexString | null): PEM;
public parseHexOfEncryptedPKCS8(sHEX: HexString): {
ciphertext: ASN1V;
encryptionSchemeAlg: 'TripleDES';
encryptionSchemeIV: ASN1V;
pbkdf2Salt: ASN1V;
pbkdf2Iter: number;
};
public getPBKDF2KeyHexFromParam(info: ReturnType<this['parseHexOfEncryptedPKCS8']>, passcode: string): HexString;
private _getPlainPKCS8HexFromEncryptedPKCS8PEM(pkcs8PEM: PEM, passcode: string): HexString;
public getKeyFromEncryptedPKCS8PEM(prvKeyHex: HexString): ReturnType<this['getKeyFromPlainPrivatePKCS8Hex']>;
public parsePlainPrivatePKCS8Hex(pkcs8PrvHex: HexString): {
algparam: ASN1V | null;
algoid: ASN1V;
keyidx: Idx<ASN1V>;
};
public getKeyFromPlainPrivatePKCS8PEM(prvKeyHex: HexString): ReturnType<this['getKeyFromPlainPrivatePKCS8Hex']>;
public getKeyFromPlainPrivatePKCS8Hex(prvKeyHex: HexString): RSAKey | KJUR.crypto.DSA | KJUR.crypto.ECDSA;
private _getKeyFromPublicPKCS8Hex(h: HexString): RSAKey | KJUR.crypto.DSA | KJUR.crypto.ECDSA;
public parsePublicRawRSAKeyHex(pubRawRSAHex: HexString): Record<'n' | 'e', ASN1V>;
public parsePublicPKCS8Hex(pkcs8PubHex: HexString): {
algparam: ASN1V | Record<'p' | 'q' | 'g', ASN1V> | null;
algoid: ASN1V;
key: ASN1V;
};
public static getKey(param: GetKeyRSAParam): RSAKey;
public static getKey(param: GetKeyECDSAParam): KJUR.crypto.ECDSA;
public static getKey(param: GetKeyDSAParam): KJUR.crypto.DSA;
public static getKey(param: string, passcode?: string, hextype?: string): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
public static generateKeypair(alg: 'RSA', keylen: number): Record<'prvKeyObj' | 'pubKeyObj', RSAKey>;
public static generateKeypair(alg: 'EC', curve: KJUR.crypto.CurveName): Record<'prvKeyObj' | 'pubKeyObj', KJUR.crypto.ECDSA>;
public static getPEM(keyObjOrHex: RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA, formatType?: 'PKCS1PRV' | 'PKCS5PRV' | 'PKCS8PRV', passwd?: string, encAlg?: 'DES-CBC' | 'DES-EDE3-CBC' | 'AES-128-CBC' | 'AES-192-CBC' | 'AES-256-CBC', hexType?: string, ivsaltHex?: HexString): object; // To Do
public static getKeyFromCSRPEM(csrPEM: PEM): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
public static getKeyFromCSRHex(csrHex: HexString): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
public static parseCSRHex(csrHex: HexString): Record<'p8pubkeyhex', ASN1TLV>;
public static getJWKFromKey(keyObj: RSAKey): {
kty: 'RSA';
} & Record<'n' | 'e' | 'd' | 'p' | 'q' | 'dp' | 'dq' | 'qi', Base64URLString> | {
kty: 'RSA';
} & Record<'n' | 'e', Base64URLString>;
public static getJWKFromKey(keyObj: KJUR.crypto.ECDSA): {
kty: 'EC';
crv: KJUR.crypto.CurveName;
x: Base64URLString;
y: Base64URLString;
d: Base64URLString;
} | {
kty: 'EC';
crv: KJUR.crypto.CurveName;
x: Base64URLString;
y: Base64URLString;
};
}
//// KJUR NAMESPACE (PARTIAL)
namespace KJUR {
namespace crypto {
type CurveName = 'secp128r1' | 'secp160k1' | 'secp160r1' | 'secp192k1' | 'secp192r1' | 'secp224r1' | 'secp256k1' | 'secp256r1' | 'secp384r1' | 'secp521r1';
class DSA {
public p: BigInteger | null;
public q: BigInteger | null;
public g: BigInteger | null;
public y: BigInteger | null;
public x: BigInteger | null;
public type: 'DSA';
public isPrivate: boolean;
public isPublic: boolean;
public setPrivate(p: BigInteger, q: BigInteger, g: BigInteger, y: BigInteger | null, x: BigInteger): void;
public setPrivateHex(hP: HexString, hQ: HexString, hG: HexString, hY: HexString | null, hX: HexString): void;
public setPublic(p: BigInteger, q: BigInteger, g: BigInteger, y: BigInteger): void;
public setPublicHex(hP: HexString, hQ: HexString, hG: HexString, hY: HexString): void;
public signWithMessageHash(sHashHex: HexString): HexString;
public verifyWithMessageHash(sHashHex: HexString, hSigVal: HexString): boolean;
public parseASN1Signature(hSigVal: HexString): [BigInteger, BigInteger];
public readPKCS5PrvKeyHex(h: HexString): void;
public readPKCS8PrvKeyHex(h: HexString): void;
public readPKCS8PubKeyHex(h: HexString): void;
public readCertPubKeyHex(h: HexString, nthPKI: number): void;
}
class ECDSA {
constructor(params?: {
curve?: CurveName;
prv?: HexString;
pub?: HexString;
});
public p: BigInteger | null;
public q: BigInteger | null;
public g: BigInteger | null;
public y: BigInteger | null;
public x: BigInteger | null;
public type: 'EC';
public isPrivate: boolean;
public isPublic: boolean;
public getBigRandom(limit: BigInteger): BigInteger;
public setNamedCurve(curveName: CurveName): void;
public setPrivateKeyHex(prvKeyHex: HexString): void;
public setPublicKeyHex(pubKeyHex: HexString): void;
public getPublicKeyXYHex(): Record<'x' | 'y', HexString>;
public getShortNISTPCurveName(): 'P-256' | 'P-384' | null;
public generateKeyPairHex(): Record<'ecprvhex' | 'ecpubhex', HexString>;
public signWithMessageHash(hashHex: HexString): HexString;
public signHex(hashHex: HexString, privHex: HexString): HexString;
public verifyWithMessageHash(sHashHex: HexString, hSigVal: HexString): boolean;
public parseASN1Signature(hSigVal: HexString): [BigInteger, BigInteger];
public readPKCS5PrvKeyHex(h: HexString): void;
public readPKCS8PrvKeyHex(h: HexString): void;
public readPKCS8PubKeyHex(h: HexString): void;
public readCertPubKeyHex(h: HexString, nthPKI: number): void;
public static parseSigHex(sigHex: HexString): Record<'r' | 's', BigInteger>;
public static parseSigHexInHexRS(sigHex: HexString): Record<'r' | 's', ASN1V>;
public static asn1SigToConcatSig(asn1Sig: HexString): HexString;
public static concatSigToASN1Sig(concatSig: HexString): ASN1TLV;
public static hexRSSigToASN1Sig(hR: HexString, hS: HexString): ASN1TLV;
public static biRSSigToASN1Sig(biR: BigInteger, biS: BigInteger): ASN1TLV;
public static getName(s: CurveName | HexString): 'secp256r1' | 'secp256k1' | 'secp384r1' | null;
}
class Signature {
constructor(params?: ({
alg: string;
prov?: string;
} | {}) & ({
psssaltlen: number;
} | {}) & ({
prvkeypem: PEM;
prvkeypas?: never;
} | {}));
private _setAlgNames(): void;
private _zeroPaddingOfSignature(hex: HexString, bitLength: number): HexString;
public setAlgAndProvider(alg: string, prov: string): void;
public init(key: GetKeyParam, pass?: string): void;
public updateString(str: string): void;
public updateHex(hex: HexString): void;
public sign(): HexString;
public signString(str: string): HexString;
public signHex(hex: HexString): HexString;
public verify(hSigVal: string): boolean | 0;
}
}
}
//// RSAKEY TYPES
class RSAKey {
public n: BigInteger | null;
public e: number;
public d: BigInteger | null;
public p: BigInteger | null;
public q: BigInteger | null;
public dmp1: BigInteger | null;
public dmq1: BigInteger | null;
public coeff: BigInteger | null;
public type: 'RSA';
public isPrivate?: boolean;
public isPublic?: boolean;
//// RSA PUBLIC
protected doPublic(x: BigInteger): BigInteger;
public setPublic(N: BigInteger, E: number): void;
public setPublic(N: HexString, E: HexString): void;
public encrypt(text: string): HexString | null;
public encryptOAEP(text: string, hash?: string, hashLen?: number): HexString | null;
public encryptOAEP(text: string, hash?: (s: string) => string, hashLen?: number): HexString | null;
//// RSA PRIVATE
protected doPrivate(x: BigInteger): BigInteger;
public setPrivate(N: BigInteger, E: number, D: BigInteger): void;
public setPrivate(N: HexString, E: HexString, D: HexString): void;
public setPrivateEx(N: HexString, E: HexString, D?: HexString | null, P?: HexString | null, Q?: HexString | null, DP?: HexString | null, DQ?: HexString | null, C?: HexString | null): void;
public generate(B: number, E: HexString): void;
public decrypt(ctext: HexString): string;
public decryptOAEP(ctext: HexString, hash?: string, hashLen?: number): string | null;
public encryptOAEP(ctext: HexString, hash?: (s: string) => string, hashLen?: number): string | null;
//// RSA PEM
public getPosArrayOfChildrenFromHex(hPrivateKey: PEM): Idx<ASN1ObjectString>[];
public getHexValueArrayOfChildrenFromHex(hPrivateKey: PEM): Idx<ASN1ObjectString>[];
public readPrivateKeyFromPEMString(keyPEM: PEM): void;
public readPKCS5PrvKeyHex(h: HexString): void;
public readPKCS8PrvKeyHex(h: HexString): void;
public readPKCS5PubKeyHex(h: HexString): void;
public readPKCS8PubKeyHex(h: HexString): void;
public readCertPubKeyHex(h: HexString, nthPKI: Nth): void;
//// RSA SIGN
public sign(s: string, hashAlg: string): HexString;
public signWithMessageHash(sHashHex: HexString, hashAlg: string): HexString;
public signPSS(s: string, hashAlg: string, sLen: number): HexString;
public signWithMessageHashPSS(hHash: HexString, hashAlg: string, sLen: number): HexString;
public verify(sMsg: string, hSig: HexString): boolean | 0;
public verifyWithMessageHash(sHashHex: HexString, hSig: HexString): boolean | 0;
public verifyPSS(sMsg: string, hSig: HexString, hashAlg: string, sLen: number): boolean;
public verifyWithMessageHashPSS(hHash: HexString, hSig: HexString, hashAlg: string, sLen: number): boolean;
public static SALT_LEN_HLEN: -1;
public static SALT_LEN_MAX: -2;
public static SALT_LEN_RECOVER: -2;
}
/// RNG TYPES
class SecureRandom {
public nextBytes(ba: Mutable<ByteNumber[]>): void;
}
//// X509 TYPES
type ExtInfo = {
critical: boolean;
oid: OID;
vidx: Idx<ASN1V>;
};
type ExtAIAInfo = Record<'ocsp' | 'caissuer', string>;
type ExtCertificatePolicy = {
id: OIDName;
} & Partial<{
cps: string;
} | {
unotice: string;
}>;
class X509 {
public hex: HexString | null;
public version: number;
public foffset: number;
public aExtInfo: null;
public getVersion(): number;
public getSerialNumberHex(): ASN1V;
public getSignatureAlgorithmField(): OIDName;
public getIssuerHex(): ASN1TLV;
public getIssuerString(): HexString;
public getSubjectHex(): ASN1TLV;
public getSubjectString(): HexString;
public getNotBefore(): TimeValue;
public getNotAfter(): TimeValue;
public getPublicKeyHex(): ASN1TLV;
public getPublicKeyIdx(): Idx<Mutable<Nth[]>>;
public getPublicKeyContentIdx(): Idx<Mutable<Nth[]>>;
public getPublicKey(): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
public getSignatureAlgorithmName(): OIDName;
public getSignatureValueHex(): ASN1V;
public verifySignature(pubKey: GetKeyParam): boolean | 0;
public parseExt(): void;
public getExtInfo(oidOrName: OID | string): ExtInfo | undefined;
public getExtBasicConstraints(): ExtInfo | {} | {
cA: true;
pathLen?: number;
};
public getExtKeyUsageBin(): BinString;
public getExtKeyUsageString(): string;
public getExtSubjectKeyIdentifier(): ASN1V | undefined;
public getExtAuthorityKeyIdentifier(): {
kid: ASN1V;
} | undefined;
public getExtExtKeyUsageName(): OIDName[] | undefined;
public getExtSubjectAltName(): Deprecated<string[]>;
public getExtSubjectAltName2(): ['MAIL' | 'DNS' | 'DN' | 'URI' | 'IP', string][] | undefined;
public getExtCRLDistributionPointsURI(): string[] | undefined;
public getExtAIAInfo(): ExtAIAInfo | undefined;
public getExtCertificatePolicies(): ExtCertificatePolicy[] | undefined;
public readCertPEM(sCertPEM: PEM): void;
public readCertHex(sCertHex: HexString): void;
public getInfo(): string;
public static hex2dn(hex: HexString, idx?: Idx<HexString>): string;
public static hex2rdn(hex: HexString, idx?: Idx<HexString>): string;
public static hex2attrTypeValue(hex: HexString, idx?: Idx<HexString>): string;
public static getPublicKeyFromCertPEM(sCertPEM: PEM): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
public static getPublicKeyInfoPropOfCertPEM(sCertPEM: PEM): {
algparam: ASN1V | null;
leyhex: ASN1V;
algoid: ASN1V;
};
}
}

View File

@ -79,6 +79,7 @@ export async function masterMain() {
require('../daemons/server-stats').default(); require('../daemons/server-stats').default();
require('../daemons/notes-stats').default(); require('../daemons/notes-stats').default();
require('../daemons/queue-stats').default(); require('../daemons/queue-stats').default();
require('../daemons/janitor').default();
} }
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);

View File

@ -5,6 +5,14 @@
<section v-if="tables"> <section v-if="tables">
<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div> <div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
</section> </section>
<section>
<header><fa :icon="faBroom"/> {{ $t('vacuum') }}</header>
<ui-info>{{ $t('vacuum-info') }}</ui-info>
<ui-switch v-model="fullVacuum">FULL</ui-switch>
<ui-switch v-model="analyzeVacuum">ANALYZE</ui-switch>
<ui-button @click="vacuum()"><fa :icon="faBroom"/> {{ $t('vacuum') }}</ui-button>
<ui-info warn>{{ $t('vacuum-exclamation') }}</ui-info>
</section>
</ui-card> </ui-card>
</div> </div>
</template> </template>
@ -12,7 +20,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { faDatabase } from '@fortawesome/free-solid-svg-icons'; import { faDatabase, faBroom } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/db.vue'), i18n: i18n('admin/views/db.vue'),
@ -20,7 +28,9 @@ export default Vue.extend({
data() { data() {
return { return {
tables: null, tables: null,
faDatabase fullVacuum: true,
analyzeVacuum: true,
faDatabase, faBroom
}; };
}, },
@ -34,6 +44,18 @@ export default Vue.extend({
this.tables = tables; this.tables = tables;
}); });
}, },
vacuum() {
this.$root.api('admin/vacuum', {
full: this.fullVacuum,
analyze: this.analyzeVacuum,
}).then(() => {
this.$root.dialog({
type: 'success',
splash: true
});
});
},
} }
}); });
</script> </script>

View File

@ -129,6 +129,7 @@
<ui-input v-model="smtpPass" type="password" :with-password-toggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input> <ui-input v-model="smtpPass" type="password" :with-password-toggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input>
</ui-horizon-group> </ui-horizon-group>
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch> <ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch>
<ui-button @click="testEmail()">{{ $t('test-email') }}</ui-button>
</template> </template>
</section> </section>
<section> <section>
@ -424,6 +425,24 @@ export default Vue.extend({
}); });
}, },
async testEmail() {
this.$root.api('admin/send-email', {
to: this.maintainerEmail,
subject: 'Test email',
text: 'Yo'
}).then(x => {
this.$root.dialog({
type: 'success',
splash: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
updateMeta() { updateMeta() {
this.$root.api('admin/update-meta', { this.$root.api('admin/update-meta', {
maintainerName: this.maintainerName, maintainerName: this.maintainerName,

View File

@ -41,7 +41,6 @@
if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev'; if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev';
if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth'; if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth';
if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin'; if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin';
if (`${url.pathname}/`.startsWith('/test/')) app = 'test';
//#endregion //#endregion
// Script version // Script version

View File

@ -0,0 +1,5 @@
export function hexifyAB(buffer) {
return Array.from(new Uint8Array(buffer))
.map(item => item.toString(16).padStart(2, 0))
.join('');
}

View File

@ -33,7 +33,7 @@
</template> </template>
</ui-select> </ui-select>
<ui-horizon-group no-grow class="buttons fit-bottom" v-if="!splash && (showOkButton || showCancelButton)"> <ui-horizon-group no-grow class="buttons fit-bottom" v-if="!splash && (showOkButton || showCancelButton)">
<ui-button @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user">{{ (showCancelButton || input || select || user) ? $t('@.ok') : $t('@.got-it') }}</ui-button> <ui-button @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user" :disabled="!canOk">{{ (showCancelButton || input || select || user) ? $t('@.ok') : $t('@.got-it') }}</ui-button>
<ui-button @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('@.cancel') }}</ui-button> <ui-button @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('@.cancel') }}</ui-button>
</ui-horizon-group> </ui-horizon-group>
</template> </template>
@ -99,11 +99,26 @@ export default Vue.extend({
inputValue: this.input && this.input.default ? this.input.default : null, inputValue: this.input && this.input.default ? this.input.default : null,
userInputValue: null, userInputValue: null,
selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null, selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
canOk: true,
faTimesCircle, faQuestionCircle faTimesCircle, faQuestionCircle
}; };
}, },
watch: {
userInputValue() {
if (this.user) {
this.$root.api('users/show', parseAcct(this.userInputValue)).then(u => {
this.canOk = u != null;
}).catch(() => {
this.canOk = false;
});
}
}
},
mounted() { mounted() {
if (this.user) this.canOk = false;
this.$nextTick(() => { this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto'; (this.$refs.bg as any).style.pointerEvents = 'auto';
anime({ anime({
@ -131,6 +146,7 @@ export default Vue.extend({
methods: { methods: {
async ok() { async ok() {
if (!this.canOk) return;
if (!this.showOkButton) return; if (!this.showOkButton) return;
if (this.user) { if (this.user) {

View File

@ -92,6 +92,14 @@ export default Vue.extend({
try { try {
if (this.isFollowing) { if (this.isFollowing) {
const { canceled } = await this.$root.dialog({
type: 'warning',
text: this.$t('@.unfollow-confirm', { name: this.user.name || this.user.username }),
showCancelButton: true
});
if (canceled) return;
await this.$root.api('following/delete', { await this.$root.api('following/delete', {
userId: this.user.id userId: this.user.id
}); });

View File

@ -15,6 +15,8 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.havbbuyv .havbbuyv
white-space pre-wrap
>>> .title >>> .title
display block display block
margin-bottom 4px margin-bottom 4px

View File

@ -9,7 +9,6 @@ import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { url } from '../../../config'; import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import { concat, intersperse } from '../../../../../prelude/array';
import { faCopy, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'; import { faCopy, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({ export default Vue.extend({
@ -129,6 +128,13 @@ export default Vue.extend({
splash: true splash: true
}); });
this.destroyDom(); this.destroyDom();
}).catch(e => {
if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
this.$root.dialog({
type: 'error',
text: this.$t('pin-limit-exceeded')
});
}
}); });
}, },

View File

@ -1,11 +1,54 @@
<template> <template>
<div class="2fa"> <div class="2fa totp-section">
<p style="margin-top:0;">{{ $t('intro') }}<a :href="$t('url')" target="_blank">{{ $t('detail') }}</a></p> <p style="margin-top:0;">{{ $t('intro') }}<a :href="$t('url')" target="_blank">{{ $t('detail') }}</a></p>
<ui-info warn>{{ $t('caution') }}</ui-info> <ui-info warn>{{ $t('caution') }}</ui-info>
<p v-if="!data && !$store.state.i.twoFactorEnabled"><ui-button @click="register">{{ $t('register') }}</ui-button></p> <p v-if="!data && !$store.state.i.twoFactorEnabled"><ui-button @click="register">{{ $t('register') }}</ui-button></p>
<template v-if="$store.state.i.twoFactorEnabled"> <template v-if="$store.state.i.twoFactorEnabled">
<h2 class="heading">{{ $t('totp-header') }}</h2>
<p>{{ $t('already-registered') }}</p> <p>{{ $t('already-registered') }}</p>
<ui-button @click="unregister">{{ $t('unregister') }}</ui-button> <ui-button @click="unregister">{{ $t('unregister') }}</ui-button>
<template v-if="supportsCredentials">
<hr class="totp-method-sep">
<h2 class="heading">{{ $t('security-key-header') }}</h2>
<p>{{ $t('security-key') }}</p>
<div class="key-list">
<div class="key" v-for="key in $store.state.i.securityKeysList">
<h3>
{{ key.name }}
</h3>
<div class="last-used">
{{ $t('last-used') }}
<mk-time :time="key.lastUsed"/>
</div>
<ui-button @click="unregisterKey(key)">
{{ $t('unregister') }}
</ui-button>
</div>
</div>
<ui-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</ui-info>
<ui-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('register') }}</ui-button>
<ol v-if="registration && !registration.error">
<li v-if="registration.stage >= 0">
{{ $t('activate-key') }}
<fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" />
</li>
<li v-if="registration.stage >= 1">
<ui-form :disabled="registration.stage != 1 || registration.saving">
<ui-input v-model="keyName" :max="30">
<span>{{ $t('security-key-name') }}</span>
</ui-input>
<ui-button @click="registerKey" :disabled="this.keyName.length == 0">
{{ $t('register-security-key') }}
</ui-button>
<fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 1" />
</ui-form>
</li>
</ol>
</template>
</template> </template>
<div v-if="data && !$store.state.i.twoFactorEnabled"> <div v-if="data && !$store.state.i.twoFactorEnabled">
<ol> <ol>
@ -24,12 +67,21 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import { hostname } from '../../../../config';
import { hexifyAB } from '../../../scripts/2fa';
function stringifyAB(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/components/settings.2fa.vue'), i18n: i18n('desktop/views/components/settings.2fa.vue'),
data() { data() {
return { return {
data: null, data: null,
supportsCredentials: !!navigator.credentials,
registration: null,
keyName: '',
token: null token: null
}; };
}, },
@ -76,7 +128,116 @@ export default Vue.extend({
}).catch(() => { }).catch(() => {
this.$notify(this.$t('failed')); this.$notify(this.$t('failed'));
}); });
},
registerKey() {
this.registration.saving = true;
this.$root.api('i/2fa/key-done', {
password: this.registration.password,
name: this.keyName,
challengeId: this.registration.challengeId,
// we convert each 16 bits to a string to serialise
clientDataJSON: stringifyAB(this.registration.credential.response.clientDataJSON),
attestationObject: hexifyAB(this.registration.credential.response.attestationObject)
}).then(key => {
this.registration = null;
key.lastUsed = new Date();
this.$notify(this.$t('success'));
})
},
unregisterKey(key) {
this.$root.dialog({
title: this.$t('enter-password'),
input: {
type: 'password'
}
}).then(({ canceled, result: password }) => {
if (canceled) return;
return this.$root.api('i/2fa/remove-key', {
password,
credentialId: key.id
}).then(() => {
this.$notify(this.$t('key-unregistered'));
});
});
},
addSecurityKey() {
this.$root.dialog({
title: this.$t('enter-password'),
input: {
type: 'password'
}
}).then(({ canceled, result: password }) => {
if (canceled) return;
this.$root.api('i/2fa/register-key', {
password
}).then(registration => {
this.registration = {
password,
challengeId: registration.challengeId,
stage: 0,
publicKeyOptions: {
challenge: Buffer.from(
registration.challenge
.replace(/\-/g, "+")
.replace(/_/g, "/"),
'base64'
),
rp: {
id: hostname,
name: 'Misskey'
},
user: {
id: Uint8Array.from(this.$store.state.i.id, c => c.charCodeAt(0)),
name: this.$store.state.i.username,
displayName: this.$store.state.i.name,
},
pubKeyCredParams: [{alg: -7, type: 'public-key'}],
timeout: 60000,
attestation: 'direct'
},
saving: true
};
return navigator.credentials.create({
publicKey: this.registration.publicKeyOptions
});
}).then(credential => {
this.registration.credential = credential;
this.registration.saving = false;
this.registration.stage = 1;
}).catch(err => {
console.warn('Error while registering?', err);
this.registration.error = err.message;
this.registration.stage = -1;
});
});
} }
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.totp-section
.totp-method-sep
margin 1.5em 0 1em
border none
border-top solid var(--lineWidth) var(--faceDivider)
h2.heading
margin 0
.key
padding 1em
margin 0.5em 0
background #161616
border-radius 6px
h3
margin-top 0
margin-bottom .3em
.last-used
margin-bottom .5em
</style>

View File

@ -1,6 +1,7 @@
<template> <template>
<form class="mk-signin" :class="{ signing }" @submit.prevent="onSubmit"> <form class="mk-signin" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div> <div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange"> <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange">
<span>{{ $t('username') }}</span> <span>{{ $t('username') }}</span>
<template #prefix>@</template> <template #prefix>@</template>
@ -10,14 +11,30 @@
<span>{{ $t('password') }}</span> <span>{{ $t('password') }}</span>
<template #prefix><fa icon="lock"/></template> <template #prefix><fa icon="lock"/></template>
</ui-input> </ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('@.2fa') }}</span>
<template #prefix><fa icon="gavel"/></template>
</ui-input>
<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button> <ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p> <p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p> <p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p> <p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
<p>{{ $t('tap-key') }}</p>
<ui-button @click="queryKey" v-if="!queryingKey">
{{ $t('@.error.retry') }}
</ui-button>
</div>
<div class="or-hr" v-if="user && user.securityKeys">
<p class="or-msg">{{ $t('or') }}</p>
</div>
<div class="twofa-group totp-group">
<p style="margin-bottom:0;">{{ $t('enter-2fa-code') }}</p>
<ui-input v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('@.2fa') }}</span>
<template #prefix><fa icon="gavel"/></template>
</ui-input>
<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
</div>
</div>
</form> </form>
</template> </template>
@ -26,6 +43,7 @@ import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { apiUrl, host } from '../../../config'; import { apiUrl, host } from '../../../config';
import { toUnicode } from 'punycode'; import { toUnicode } from 'punycode';
import { hexifyAB } from '../../scripts/2fa';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/signin.vue'), i18n: i18n('common/views/components/signin.vue'),
@ -47,7 +65,11 @@ export default Vue.extend({
token: '', token: '',
apiUrl, apiUrl,
host: toUnicode(host), host: toUnicode(host),
meta: null meta: null,
totpLogin: false,
credential: null,
challengeData: null,
queryingKey: false,
}; };
}, },
@ -68,9 +90,72 @@ export default Vue.extend({
}); });
}, },
queryKey() {
this.queryingKey = true;
return navigator.credentials.get({
publicKey: {
challenge: Buffer.from(
this.challengeData.challenge
.replace(/\-/g, '+')
.replace(/_/g, '/'),
'base64'
),
allowCredentials: this.challengeData.securityKeys.map(key => ({
id: Buffer.from(key.id, 'hex'),
type: 'public-key',
transports: ['usb', 'ble', 'nfc']
})),
timeout: 60 * 1000
}
}).catch(err => {
this.queryingKey = false;
console.warn(err);
return Promise.reject(null);
}).then(credential => {
this.queryingKey = false;
this.signing = true;
return this.$root.api('signin', {
username: this.username,
password: this.password,
signature: hexifyAB(credential.response.signature),
authenticatorData: hexifyAB(credential.response.authenticatorData),
clientDataJSON: hexifyAB(credential.response.clientDataJSON),
credentialId: credential.id,
challengeId: this.challengeData.challengeId
});
}).then(res => {
localStorage.setItem('i', res.i);
location.reload();
}).catch(err => {
if(err === null) return;
console.error(err);
this.$root.dialog({
type: 'error',
text: this.$t('login-failed')
});
this.signing = false;
});
},
onSubmit() { onSubmit() {
this.signing = true; this.signing = true;
if (!this.totpLogin && this.user && this.user.twoFactorEnabled) {
if (window.PublicKeyCredential && this.user.securityKeys) {
this.$root.api('i/2fa/getkeys', {
username: this.username,
password: this.password
}).then(res => {
this.totpLogin = true;
this.signing = false;
this.challengeData = res;
return this.queryKey();
});
} else {
this.totpLogin = true;
this.signing = false;
}
} else {
this.$root.api('signin', { this.$root.api('signin', {
username: this.username, username: this.username,
password: this.password, password: this.password,
@ -87,6 +172,7 @@ export default Vue.extend({
}); });
} }
} }
}
}); });
</script> </script>
@ -94,6 +180,48 @@ export default Vue.extend({
.mk-signin .mk-signin
color #555 color #555
.or-hr,
.or-hr .or-msg,
.twofa-group,
.twofa-group p
color var(--text)
.tap-group > button
margin-bottom 1em
.securityKeys .or-hr
&
position relative
.or-msg
&:before
right 100%
margin-right 0.125em
&:after
left 100%
margin-left 0.125em
&:before, &:after
content ""
position absolute
top 50%
width 100%
height 2px
background #555
&
position relative
margin auto
left 0
right 0
top 0
bottom 0
font-size 1.5em
height 1.5em
width 3em
text-align center
&.signing &.signing
&, * &, *
cursor wait !important cursor wait !important

View File

@ -5,6 +5,9 @@
<span class="hostname">{{ hostname }}</span> <span class="hostname">{{ hostname }}</span>
<span class="port" v-if="port != ''">:{{ port }}</span> <span class="port" v-if="port != ''">:{{ port }}</span>
</template> </template>
<template v-if="pathname === '/' && self">
<span class="self">{{ hostname }}</span>
</template>
<span class="pathname" v-if="pathname != ''">{{ self ? pathname.substr(1) : pathname }}</span> <span class="pathname" v-if="pathname != ''">{{ self ? pathname.substr(1) : pathname }}</span>
<span class="query">{{ query }}</span> <span class="query">{{ query }}</span>
<span class="hash">{{ hash }}</span> <span class="hash">{{ hash }}</span>
@ -22,6 +25,7 @@ export default Vue.extend({
data() { data() {
const isSelf = this.url.startsWith(local); const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && ( const hasRoute = isSelf && (
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') || this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') || this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/pages/')); this.url.substr(local.length).startsWith('/pages/'));
@ -54,19 +58,28 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-url .mk-url
word-break break-all word-break break-all
> [data-icon] > [data-icon]
padding-left 2px padding-left 2px
font-size .9em font-size .9em
font-weight 400 font-weight 400
font-style normal font-style normal
> .self
font-weight bold
> .schema > .schema
opacity 0.5 opacity 0.5
> .hostname > .hostname
font-weight bold font-weight bold
> .pathname > .pathname
opacity 0.8 opacity 0.8
> .query > .query
opacity 0.5 opacity 0.5
> .hash > .hash
font-style italic font-style italic
</style> </style>

View File

@ -97,7 +97,9 @@ export default Vue.extend({
const image = [ const image = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif' 'image/gif',
'image/apng',
'image/vnd.mozilla.apng',
]; ];
this.$root.api('users/notes', { this.$root.api('users/notes', {

View File

@ -91,6 +91,7 @@ export default ($root: any) => {
? Promise.resolve(file) ? Promise.resolve(file)
: $root.$chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
type: 'image/*',
title: locale['desktop']['choose-avatar'] title: locale['desktop']['choose-avatar']
}); });

View File

@ -91,6 +91,7 @@ export default ($root: any) => {
? Promise.resolve(file) ? Promise.resolve(file)
: $root.$chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
type: 'image/*',
title: locale['desktop']['choose-banner'] title: locale['desktop']['choose-banner']
}); });

View File

@ -77,6 +77,7 @@ init(async (launch, os) => {
if (document.body.clientWidth > 800) { if (document.body.clientWidth > 800) {
const w = this.$root.new(MkChooseFileFromDriveWindow, { const w = this.$root.new(MkChooseFileFromDriveWindow, {
title: o.title, title: o.title,
type: o.type,
multiple: o.multiple, multiple: o.multiple,
initFolder: o.currentFolder initFolder: o.currentFolder
}); });

View File

@ -11,6 +11,7 @@
<x-drive <x-drive
ref="browser" ref="browser"
class="browser" class="browser"
:type="type"
:multiple="multiple" :multiple="multiple"
@selected="onSelected" @selected="onSelected"
@change-selection="onChangeSelection" @change-selection="onChangeSelection"
@ -33,6 +34,11 @@ export default Vue.extend({
XDrive: () => import('./drive.vue').then(m => m.default), XDrive: () => import('./drive.vue').then(m => m.default),
}, },
props: { props: {
type: {
type: String,
required: false,
default: undefined
},
multiple: { multiple: {
default: false default: false
} }

View File

@ -21,6 +21,7 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import VueCropper from 'vue-cropperjs'; import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.css';
import * as url from '../../../../../prelude/url'; import * as url from '../../../../../prelude/url';
export default Vue.extend({ export default Vue.extend({

View File

@ -80,6 +80,11 @@ export default Vue.extend({
type: Object, type: Object,
required: false required: false
}, },
type: {
type: String,
required: false,
default: undefined
},
multiple: { multiple: {
type: Boolean, type: Boolean,
default: false default: false
@ -540,6 +545,7 @@ export default Vue.extend({
// //
this.$root.api('drive/files', { this.$root.api('drive/files', {
folderId: this.folder ? this.folder.id : null, folderId: this.folder ? this.folder.id : null,
type: this.type,
limit: filesMax + 1 limit: filesMax + 1
}).then(files => { }).then(files => {
if (files.length == filesMax + 1) { if (files.length == filesMax + 1) {
@ -570,6 +576,7 @@ export default Vue.extend({
// //
this.$root.api('drive/files', { this.$root.api('drive/files', {
folderId: this.folder ? this.folder.id : null, folderId: this.folder ? this.folder.id : null,
type: this.type,
untilId: this.files[this.files.length - 1].id, untilId: this.files[this.files.length - 1].id,
limit: max + 1 limit: max + 1
}).then(files => { }).then(files => {

View File

@ -38,7 +38,9 @@ export default Vue.extend({
const image = [ const image = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif' 'image/gif',
'image/apng',
'image/vnd.mozilla.apng',
]; ];
this.$root.api('users/notes', { this.$root.api('users/notes', {

View File

@ -186,7 +186,9 @@ export default Vue.extend({
const image = [ const image = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif' 'image/gif',
'image/apng',
'image/vnd.mozilla.apng',
]; ];
this.$root.api('notes/local-timeline', { this.$root.api('notes/local-timeline', {

View File

@ -8,6 +8,7 @@
</header> </header>
<x-drive class="drive" ref="browser" <x-drive class="drive" ref="browser"
:select-file="true" :select-file="true"
:type="type"
:multiple="multiple" :multiple="multiple"
@change-selection="onChangeSelection" @change-selection="onChangeSelection"
@selected="onSelected" @selected="onSelected"
@ -25,7 +26,7 @@ export default Vue.extend({
components: { components: {
XDrive: () => import('./drive.vue').then(m => m.default), XDrive: () => import('./drive.vue').then(m => m.default),
}, },
props: ['multiple'], props: ['type', 'multiple'],
data() { data() {
return { return {
files: [] files: []

View File

@ -30,7 +30,9 @@ export default Vue.extend({
const image = [ const image = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif' 'image/gif',
'image/apng',
'image/vnd.mozilla.apng',
]; ];
this.$root.api('users/notes', { this.$root.api('users/notes', {
userId: this.user.id, userId: this.user.id,

View File

@ -110,7 +110,9 @@ export default Vue.extend({
const image = [ const image = [
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif' 'image/gif',
'image/apng',
'image/vnd.mozilla.apng',
]; ];
this.$root.api('notes/local-timeline', { this.$root.api('notes/local-timeline', {

View File

@ -1,25 +0,0 @@
import VueRouter from 'vue-router';
// Style
import './style.styl';
import init from '../init';
import Index from './views/index.vue';
import NotFound from '../common/views/pages/not-found.vue';
init(launch => {
document.title = 'Misskey';
// Init router
const router = new VueRouter({
mode: 'history',
base: '/test/',
routes: [
{ path: '/', component: Index },
{ path: '*', component: NotFound }
]
});
// Launch the app
launch(router);
});

View File

@ -1,6 +0,0 @@
@import "../app"
@import "../reset"
html
height 100%
background var(--bg)

View File

@ -1,82 +0,0 @@
<template>
<main>
<ui-card>
<template #title>MFM Playground</template>
<section class="fit-top">
<ui-textarea v-model="mfm">
<span>MFM</span>
</ui-textarea>
</section>
<section>
<header>Preview</header>
<mfm :text="mfm" :i="$store.state.i"/>
</section>
<section>
<header style="margin-bottom:0;">AST</header>
<ui-textarea v-model="mfmAst" readonly tall style="margin-top:16px;"></ui-textarea>
</section>
</ui-card>
<ui-card>
<template #title>Dialog Generator</template>
<section class="fit-top">
<ui-select v-model="dialogType" placeholder="">
<option value="info">Information</option>
<option value="success">Success</option>
<option value="warning">Warning</option>
<option value="error">Error</option>
</ui-select>
<ui-input v-model="dialogTitle">
<span>Title</span>
</ui-input>
<ui-input v-model="dialogText">
<span>Text</span>
</ui-input>
<ui-switch v-model="dialogShowCancelButton">With cancel button</ui-switch>
<ui-button @click="showDialog">Show</ui-button>
</section>
</ui-card>
</main>
</template>
<script lang="ts">
import Vue from 'vue';
import { parse } from '../../../../mfm/parse';
import * as JSON5 from 'json5';
export default Vue.extend({
data() {
return {
mfm: '',
dialogType: 'success',
dialogTitle: '',
dialogText: 'Hello World!',
dialogShowCancelButton: false
};
},
computed: {
mfmAst(): any {
return JSON5.stringify(parse(this.mfm), null, 2);
}
},
methods: {
showDialog() {
this.$root.dialog({
type: this.dialogType,
title: this.dialogTitle,
text: this.dialogText,
showCancelButton: this.dialogShowCancelButton
});
}
}
});
</script>
<style lang="stylus" scoped>
main
max-width 700px
margin 0 auto
</style>

18
src/daemons/janitor.ts Normal file
View File

@ -0,0 +1,18 @@
const interval = 30 * 60 * 1000;
import { AttestationChallenges } from '../models';
import { LessThan } from 'typeorm';
/**
* Clean up database occasionally
*/
export default function() {
async function tick() {
await AttestationChallenges.delete({
createdAt: LessThan(new Date(new Date().getTime() - 5 * 60 * 1000))
});
}
tick();
setInterval(tick, interval);
}

View File

@ -43,6 +43,8 @@ import { Poll } from '../models/entities/poll';
import { UserKeypair } from '../models/entities/user-keypair'; import { UserKeypair } from '../models/entities/user-keypair';
import { UserPublickey } from '../models/entities/user-publickey'; import { UserPublickey } from '../models/entities/user-publickey';
import { UserProfile } from '../models/entities/user-profile'; import { UserProfile } from '../models/entities/user-profile';
import { UserSecurityKey } from '../models/entities/user-security-key';
import { AttestationChallenge } from '../models/entities/attestation-challenge';
import { Page } from '../models/entities/page'; import { Page } from '../models/entities/page';
import { PageLike } from '../models/entities/page-like'; import { PageLike } from '../models/entities/page-like';
@ -80,37 +82,7 @@ class MyCustomLogger implements Logger {
} }
} }
export function initDb(justBorrow = false, sync = false, log = false) { export const entities = [
try {
const conn = getConnection();
return Promise.resolve(conn);
} catch (e) {}
return createConnection({
type: 'postgres',
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: config.db.extra,
synchronize: process.env.NODE_ENV === 'test' || sync,
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
options:{
password: config.redis.pass,
prefix: config.redis.prefix,
db: config.redis.db || 0
}
}
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
entities: [
Meta, Meta,
Instance, Instance,
App, App,
@ -126,6 +98,8 @@ export function initDb(justBorrow = false, sync = false, log = false) {
UserGroupJoining, UserGroupJoining,
UserGroupInvite, UserGroupInvite,
UserNotePining, UserNotePining,
UserSecurityKey,
AttestationChallenge,
Following, Following,
FollowRequest, FollowRequest,
Muting, Muting,
@ -153,6 +127,38 @@ export function initDb(justBorrow = false, sync = false, log = false) {
ReversiGame, ReversiGame,
ReversiMatching, ReversiMatching,
...charts as any ...charts as any
] ];
export function initDb(justBorrow = false, sync = false, log = false) {
try {
const conn = getConnection();
return Promise.resolve(conn);
} catch (e) {}
return createConnection({
type: 'postgres',
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: config.db.extra,
synchronize: process.env.NODE_ENV === 'test' || sync,
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
options: {
password: config.redis.pass,
prefix: config.redis.prefix,
db: config.redis.db || 0
}
}
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
entities: entities
}); });
} }

View File

@ -6,8 +6,8 @@ import { toUnicode } from 'punycode';
import { emojiRegex } from '../misc/emoji-regex'; import { emojiRegex } from '../misc/emoji-regex';
export function removeOrphanedBrackets(s: string): string { export function removeOrphanedBrackets(s: string): string {
const openBrackets = ['(', '「']; const openBrackets = ['(', '「', '['];
const closeBrackets = [')', '」']; const closeBrackets = [')', '」', ']'];
const xs = cumulativeSum(s.split('').map(c => { const xs = cumulativeSum(s.split('').map(c => {
if (openBrackets.includes(c)) return 1; if (openBrackets.includes(c)) return 1;
if (closeBrackets.includes(c)) return -1; if (closeBrackets.includes(c)) return -1;

View File

@ -1,5 +1,5 @@
import * as fs from 'fs'; import * as fs from 'fs';
import fileType from 'file-type'; import fileType = require('file-type');
import checkSvg from '../misc/check-svg'; import checkSvg from '../misc/check-svg';
export async function detectMine(path: string) { export async function detectMine(path: string) {

View File

@ -5,7 +5,7 @@ import chalk from 'chalk';
import Logger from '../services/logger'; import Logger from '../services/logger';
export async function downloadUrl(url: string, path: string) { export async function downloadUrl(url: string, path: string) {
const logger = new Logger('download-url'); const logger = new Logger('download');
await new Promise((res, rej) => { await new Promise((res, rej) => {
logger.info(`Downloading ${chalk.cyan(url)} ...`); logger.info(`Downloading ${chalk.cyan(url)} ...`);

View File

@ -1,17 +1,3 @@
export const types = {
boolean: 'boolean' as 'boolean',
string: 'string' as 'string',
number: 'number' as 'number',
array: 'array' as 'array',
object: 'object' as 'object',
any: 'any' as 'any',
};
export const bool = {
true: true as true,
false: false as false,
};
export type Schema = { export type Schema = {
type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any'; type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
nullable: boolean; nullable: boolean;

View File

@ -0,0 +1,46 @@
import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm';
import { User } from './user';
import { id } from '../id';
@Entity()
export class AttestationChallenge {
@PrimaryColumn(id())
public id: string;
@Index()
@PrimaryColumn(id())
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE'
})
@JoinColumn()
public user: User | null;
@Index()
@Column('varchar', {
length: 64,
comment: 'Hex-encoded sha256 hash of the challenge.'
})
public challenge: string;
@Column('timestamp with time zone', {
comment: 'The date challenge was created for expiry purposes.'
})
public createdAt: Date;
@Column('boolean', {
comment:
'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.',
default: false
})
public registrationChallenge: boolean;
constructor(data: Partial<AttestationChallenge>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@ -76,6 +76,11 @@ export class UserProfile {
}) })
public twoFactorEnabled: boolean; public twoFactorEnabled: boolean;
@Column('boolean', {
default: false,
})
public securityKeysAvailable: boolean;
@Column('varchar', { @Column('varchar', {
length: 128, nullable: true, length: 128, nullable: true,
comment: 'The password hash of the User. It will be null if the origin of the user is local.' comment: 'The password hash of the User. It will be null if the origin of the user is local.'

View File

@ -0,0 +1,48 @@
import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm';
import { User } from './user';
import { id } from '../id';
@Entity()
export class UserSecurityKey {
@PrimaryColumn('varchar', {
comment: 'Variable-length id given to navigator.credentials.get()'
})
public id: string;
@Index()
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE'
})
@JoinColumn()
public user: User | null;
@Index()
@Column('varchar', {
comment:
'Variable-length public key used to verify attestations (hex-encoded).'
})
public publicKey: string;
@Column('timestamp with time zone', {
comment:
'The date of the last time the UserSecurityKey was successfully validated.'
})
public lastUsed: Date;
@Column('varchar', {
comment: 'User-defined name for this key',
length: 30
})
public name: string;
constructor(data: Partial<UserSecurityKey>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@ -1,4 +1,4 @@
export const id = () => ({ export const id = () => ({
type: 'varchar' as 'varchar', type: 'varchar' as const,
length: 32 length: 32
}); });

View File

@ -37,6 +37,8 @@ import { FollowingRepository } from './repositories/following';
import { AbuseUserReportRepository } from './repositories/abuse-user-report'; import { AbuseUserReportRepository } from './repositories/abuse-user-report';
import { AuthSessionRepository } from './repositories/auth-session'; import { AuthSessionRepository } from './repositories/auth-session';
import { UserProfile } from './entities/user-profile'; import { UserProfile } from './entities/user-profile';
import { AttestationChallenge } from './entities/attestation-challenge';
import { UserSecurityKey } from './entities/user-security-key';
import { HashtagRepository } from './repositories/hashtag'; import { HashtagRepository } from './repositories/hashtag';
import { PageRepository } from './repositories/page'; import { PageRepository } from './repositories/page';
import { PageLikeRepository } from './repositories/page-like'; import { PageLikeRepository } from './repositories/page-like';
@ -52,6 +54,8 @@ export const PollVotes = getRepository(PollVote);
export const Users = getCustomRepository(UserRepository); export const Users = getCustomRepository(UserRepository);
export const UserProfiles = getRepository(UserProfile); export const UserProfiles = getRepository(UserProfile);
export const UserKeypairs = getRepository(UserKeypair); export const UserKeypairs = getRepository(UserKeypair);
export const AttestationChallenges = getRepository(AttestationChallenge);
export const UserSecurityKeys = getRepository(UserSecurityKey);
export const UserPublickeys = getRepository(UserPublickey); export const UserPublickeys = getRepository(UserPublickey);
export const UserLists = getCustomRepository(UserListRepository); export const UserLists = getCustomRepository(UserListRepository);
export const UserListJoinings = getRepository(UserListJoining); export const UserListJoinings = getRepository(UserListJoining);

View File

@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { App } from '../entities/app'; import { App } from '../entities/app';
import { AccessTokens } from '..'; import { AccessTokens } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedApp = SchemaType<typeof packedAppSchema>; export type PackedApp = SchemaType<typeof packedAppSchema>;
@ -42,37 +42,37 @@ export class AppRepository extends Repository<App> {
} }
export const packedAppSchema = { export const packedAppSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this Note.', description: 'The unique identifier for this Note.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'アプリケーションの名前' description: 'アプリケーションの名前'
}, },
callbackUrl: { callbackUrl: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
description: 'コールバックするURL' description: 'コールバックするURL'
}, },
permission: { permission: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
} }
}, },
secret: { secret: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
description: 'アプリケーションのシークレットキー' description: 'アプリケーションのシークレットキー'
} }
}, },

View File

@ -3,7 +3,7 @@ import { Users } from '..';
import { Blocking } from '../entities/blocking'; import { Blocking } from '../entities/blocking';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedBlocking = SchemaType<typeof packedBlockingSchema>; export type PackedBlocking = SchemaType<typeof packedBlockingSchema>;
@ -34,30 +34,30 @@ export class BlockingRepository extends Repository<Blocking> {
} }
export const packedBlockingSchema = { export const packedBlockingSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this blocking.', description: 'The unique identifier for this blocking.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the blocking was created.' description: 'The date that the blocking was created.'
}, },
blockeeId: { blockeeId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
blockee: { blockee: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'User', ref: 'User',
description: 'The blockee.' description: 'The blockee.'
}, },

View File

@ -5,7 +5,7 @@ import { User } from '../entities/user';
import { toPuny } from '../../misc/convert-host'; import { toPuny } from '../../misc/convert-host';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>; export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>;
@ -114,63 +114,63 @@ export class DriveFileRepository extends Repository<DriveFile> {
} }
export const packedDriveFileSchema = { export const packedDriveFileSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this Drive file.', description: 'The unique identifier for this Drive file.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the Drive file was created on Misskey.' description: 'The date that the Drive file was created on Misskey.'
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The file name with extension.', description: 'The file name with extension.',
example: 'lenna.jpg' example: 'lenna.jpg'
}, },
type: { type: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The MIME type of this Drive file.', description: 'The MIME type of this Drive file.',
example: 'image/jpeg' example: 'image/jpeg'
}, },
md5: { md5: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'md5', format: 'md5',
description: 'The MD5 hash of this Drive file.', description: 'The MD5 hash of this Drive file.',
example: '15eca7fba0480996e2245f5185bf39f2' example: '15eca7fba0480996e2245f5185bf39f2'
}, },
size: { size: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The size of this Drive file. (bytes)', description: 'The size of this Drive file. (bytes)',
example: 51469 example: 51469
}, },
url: { url: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
format: 'url', format: 'url',
description: 'The URL of this Drive file.', description: 'The URL of this Drive file.',
}, },
folderId: { folderId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
format: 'id', format: 'id',
description: 'The parent folder ID of this Drive file.', description: 'The parent folder ID of this Drive file.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
isSensitive: { isSensitive: {
type: types.boolean, type: 'boolean' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Whether this Drive file is sensitive.', description: 'Whether this Drive file is sensitive.',
}, },
}, },

View File

@ -3,7 +3,7 @@ import { DriveFolders, DriveFiles } from '..';
import { DriveFolder } from '../entities/drive-folder'; import { DriveFolder } from '../entities/drive-folder';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedDriveFolder = SchemaType<typeof packedDriveFolderSchema>; export type PackedDriveFolder = SchemaType<typeof packedDriveFolderSchema>;
@ -53,47 +53,47 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
} }
export const packedDriveFolderSchema = { export const packedDriveFolderSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this Drive folder.', description: 'The unique identifier for this Drive folder.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the Drive folder was created.' description: 'The date that the Drive folder was created.'
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The folder name.', description: 'The folder name.',
}, },
foldersCount: { foldersCount: {
type: types.number, type: 'number' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
description: 'The count of child folders.', description: 'The count of child folders.',
}, },
filesCount: { filesCount: {
type: types.number, type: 'number' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
description: 'The count of child files.', description: 'The count of child files.',
}, },
parentId: { parentId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
format: 'id', format: 'id',
description: 'The parent folder ID of this folder.', description: 'The parent folder ID of this folder.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
parent: { parent: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'DriveFolder' ref: 'DriveFolder'
}, },
}, },

View File

@ -3,7 +3,7 @@ import { Users } from '..';
import { Following } from '../entities/following'; import { Following } from '../entities/following';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
type LocalFollowerFollowing = Following & { type LocalFollowerFollowing = Following & {
followerHost: null; followerHost: null;
@ -88,41 +88,41 @@ export class FollowingRepository extends Repository<Following> {
} }
export const packedFollowingSchema = { export const packedFollowingSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this following.', description: 'The unique identifier for this following.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the following was created.' description: 'The date that the following was created.'
}, },
followeeId: { followeeId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
followee: { followee: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
ref: 'User', ref: 'User',
description: 'The followee.' description: 'The followee.'
}, },
followerId: { followerId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
follower: { follower: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
ref: 'User', ref: 'User',
description: 'The follower.' description: 'The follower.'
}, },

View File

@ -1,6 +1,6 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Hashtag } from '../entities/hashtag'; import { Hashtag } from '../entities/hashtag';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedHashtag = SchemaType<typeof packedHashtagSchema>; export type PackedHashtag = SchemaType<typeof packedHashtagSchema>;
@ -28,43 +28,43 @@ export class HashtagRepository extends Repository<Hashtag> {
} }
export const packedHashtagSchema = { export const packedHashtagSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
tag: { tag: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The hashtag name. No # prefixed.', description: 'The hashtag name. No # prefixed.',
example: 'misskey', example: 'misskey',
}, },
mentionedUsersCount: { mentionedUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of all users using this hashtag.' description: 'Number of all users using this hashtag.'
}, },
mentionedLocalUsersCount: { mentionedLocalUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of local users using this hashtag.' description: 'Number of local users using this hashtag.'
}, },
mentionedRemoteUsersCount: { mentionedRemoteUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of remote users using this hashtag.' description: 'Number of remote users using this hashtag.'
}, },
attachedUsersCount: { attachedUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of all users who attached this hashtag to profile.' description: 'Number of all users who attached this hashtag to profile.'
}, },
attachedLocalUsersCount: { attachedLocalUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of local users who attached this hashtag to profile.' description: 'Number of local users who attached this hashtag to profile.'
}, },
attachedRemoteUsersCount: { attachedRemoteUsersCount: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'Number of remote users who attached this hashtag to profile.' description: 'Number of remote users who attached this hashtag to profile.'
}, },
} }

View File

@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { MessagingMessage } from '../entities/messaging-message'; import { MessagingMessage } from '../entities/messaging-message';
import { Users, DriveFiles, UserGroups } from '..'; import { Users, DriveFiles, UserGroups } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSchema>; export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSchema>;
@ -46,76 +46,76 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
} }
export const packedMessagingMessageSchema = { export const packedMessagingMessageSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this MessagingMessage.', description: 'The unique identifier for this MessagingMessage.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the MessagingMessage was created.' description: 'The date that the MessagingMessage was created.'
}, },
userId: { userId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
user: { user: {
type: types.object, type: 'object' as const,
ref: 'User', ref: 'User',
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
}, },
text: { text: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
}, },
fileId: { fileId: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
format: 'id', format: 'id',
}, },
file: { file: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'DriveFile', ref: 'DriveFile',
}, },
recipientId: { recipientId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
format: 'id', format: 'id',
}, },
recipient: { recipient: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'User' ref: 'User'
}, },
groupId: { groupId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
format: 'id', format: 'id',
}, },
group: { group: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'UserGroup' ref: 'UserGroup'
}, },
isRead: { isRead: {
type: types.boolean, type: 'boolean' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
}, },
reads: { reads: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id' format: 'id'
} }
}, },

View File

@ -3,7 +3,7 @@ import { Users } from '..';
import { Muting } from '../entities/muting'; import { Muting } from '../entities/muting';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedMuting = SchemaType<typeof packedMutingSchema>; export type PackedMuting = SchemaType<typeof packedMutingSchema>;
@ -34,30 +34,30 @@ export class MutingRepository extends Repository<Muting> {
} }
export const packedMutingSchema = { export const packedMutingSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this muting.', description: 'The unique identifier for this muting.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the muting was created.' description: 'The date that the muting was created.'
}, },
muteeId: { muteeId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
mutee: { mutee: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'User', ref: 'User',
description: 'The mutee.' description: 'The mutee.'
}, },

View File

@ -2,7 +2,6 @@ import { EntityRepository, Repository } from 'typeorm';
import { NoteFavorite } from '../entities/note-favorite'; import { NoteFavorite } from '../entities/note-favorite';
import { Notes } from '..'; import { Notes } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool } from '../../misc/schema';
@EntityRepository(NoteFavorite) @EntityRepository(NoteFavorite)
export class NoteFavoriteRepository extends Repository<NoteFavorite> { export class NoteFavoriteRepository extends Repository<NoteFavorite> {
@ -29,30 +28,30 @@ export class NoteFavoriteRepository extends Repository<NoteFavorite> {
} }
export const packedNoteFavoriteSchema = { export const packedNoteFavoriteSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this favorite.', description: 'The unique identifier for this favorite.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the favorite was created.' description: 'The date that the favorite was created.'
}, },
note: { note: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'Note', ref: 'Note',
}, },
noteId: { noteId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
}, },

View File

@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { NoteReaction } from '../entities/note-reaction'; import { NoteReaction } from '../entities/note-reaction';
import { Users } from '..'; import { Users } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedNoteReaction = SchemaType<typeof packedNoteReactionSchema>; export type PackedNoteReaction = SchemaType<typeof packedNoteReactionSchema>;
@ -24,31 +24,31 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
} }
export const packedNoteReactionSchema = { export const packedNoteReactionSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this reaction.', description: 'The unique identifier for this reaction.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the reaction was created.' description: 'The date that the reaction was created.'
}, },
user: { user: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'User', ref: 'User',
description: 'User who performed this reaction.' description: 'User who performed this reaction.'
}, },
type: { type: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The reaction type.' description: 'The reaction type.'
}, },
}, },

View File

@ -5,7 +5,7 @@ import { unique, concat } from '../../prelude/array';
import { nyaize } from '../../misc/nyaize'; import { nyaize } from '../../misc/nyaize';
import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
export type PackedNote = SchemaType<typeof packedNoteSchema>; export type PackedNote = SchemaType<typeof packedNoteSchema>;
@ -144,8 +144,8 @@ export class NoteRepository extends Repository<Note> {
let text = note.text; let text = note.text;
if (note.name) { if (note.name && note.uri) {
text = `${note.name}\n${note.text}`; text = `${note.name}\n${(note.text || '').trim()}\n${note.uri}`;
} }
const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)])); const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)]));
@ -218,125 +218,125 @@ export class NoteRepository extends Repository<Note> {
} }
export const packedNoteSchema = { export const packedNoteSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this Note.', description: 'The unique identifier for this Note.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the Note was created on Misskey.' description: 'The date that the Note was created on Misskey.'
}, },
text: { text: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
}, },
cw: { cw: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
}, },
userId: { userId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
user: { user: {
type: types.object, type: 'object' as const,
ref: 'User', ref: 'User',
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
replyId: { replyId: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
format: 'id', format: 'id',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
renoteId: { renoteId: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
format: 'id', format: 'id',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
reply: { reply: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'Note' ref: 'Note'
}, },
renote: { renote: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
ref: 'Note' ref: 'Note'
}, },
viaMobile: { viaMobile: {
type: types.boolean, type: 'boolean' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
}, },
isHidden: { isHidden: {
type: types.boolean, type: 'boolean' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
}, },
visibility: { visibility: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
mentions: { mentions: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id' format: 'id'
} }
}, },
visibleUserIds: { visibleUserIds: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id' format: 'id'
} }
}, },
fileIds: { fileIds: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id' format: 'id'
} }
}, },
files: { files: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'DriveFile' ref: 'DriveFile'
} }
}, },
tags: { tags: {
type: types.array, type: 'array' as const,
optional: bool.true, nullable: bool.false, optional: true as const, nullable: false as const,
items: { items: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
} }
}, },
poll: { poll: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
}, },
geo: { geo: {
type: types.object, type: 'object' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
}, },
}, },
}; };

View File

@ -3,7 +3,7 @@ import { Users, Notes } from '..';
import { Notification } from '../entities/notification'; import { Notification } from '../entities/notification';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedNotification = SchemaType<typeof packedNotificationSchema>; export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
@ -51,37 +51,37 @@ export class NotificationRepository extends Repository<Notification> {
} }
export const packedNotificationSchema = { export const packedNotificationSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this notification.', description: 'The unique identifier for this notification.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the notification was created.' description: 'The date that the notification was created.'
}, },
type: { type: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
enum: ['follow', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'], enum: ['follow', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'],
description: 'The type of the notification.' description: 'The type of the notification.'
}, },
userId: { userId: {
type: types.string, type: 'string' as const,
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
format: 'id', format: 'id',
}, },
user: { user: {
type: types.object, type: 'object' as const,
ref: 'User', ref: 'User',
optional: bool.true, nullable: bool.true, optional: true as const, nullable: true as const,
}, },
} }
}; };

View File

@ -1,6 +1,6 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Page } from '../entities/page'; import { Page } from '../entities/page';
import { SchemaType, types, bool } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
import { Users, DriveFiles, PageLikes } from '..'; import { Users, DriveFiles, PageLikes } from '..';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
import { DriveFile } from '../entities/drive-file'; import { DriveFile } from '../entities/drive-file';
@ -89,54 +89,54 @@ export class PageRepository extends Repository<Page> {
} }
export const packedPageSchema = { export const packedPageSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
}, },
updatedAt: { updatedAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
}, },
title: { title: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
summary: { summary: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.true, optional: false as const, nullable: true as const,
}, },
content: { content: {
type: types.array, type: 'array' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
variables: { variables: {
type: types.array, type: 'array' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
userId: { userId: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
}, },
user: { user: {
type: types.object, type: 'object' as const,
ref: 'User', ref: 'User',
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
} }
}; };

View File

@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { UserGroup } from '../entities/user-group'; import { UserGroup } from '../entities/user-group';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { UserGroupJoinings } from '..'; import { UserGroupJoinings } from '..';
import { bool, types, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedUserGroup = SchemaType<typeof packedUserGroupSchema>; export type PackedUserGroup = SchemaType<typeof packedUserGroupSchema>;
@ -28,38 +28,38 @@ export class UserGroupRepository extends Repository<UserGroup> {
} }
export const packedUserGroupSchema = { export const packedUserGroupSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this UserGroup.', description: 'The unique identifier for this UserGroup.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the UserGroup was created.' description: 'The date that the UserGroup was created.'
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The name of the UserGroup.' description: 'The name of the UserGroup.'
}, },
ownerId: { ownerId: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
format: 'id', format: 'id',
}, },
userIds: { userIds: {
type: types.array, type: 'array' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
items: { items: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
format: 'id', format: 'id',
} }
}, },

View File

@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { UserList } from '../entities/user-list'; import { UserList } from '../entities/user-list';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { UserListJoinings } from '..'; import { UserListJoinings } from '..';
import { bool, types, SchemaType } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
export type PackedUserList = SchemaType<typeof packedUserListSchema>; export type PackedUserList = SchemaType<typeof packedUserListSchema>;
@ -27,33 +27,33 @@ export class UserListRepository extends Repository<UserList> {
} }
export const packedUserListSchema = { export const packedUserListSchema = {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this UserList.', description: 'The unique identifier for this UserList.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'date-time', format: 'date-time',
description: 'The date that the UserList was created.' description: 'The date that the UserList was created.'
}, },
name: { name: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'The name of the UserList.' description: 'The name of the UserList.'
}, },
userIds: { userIds: {
type: types.array, type: 'array' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
items: { items: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
format: 'id', format: 'id',
} }
}, },

View File

@ -1,10 +1,10 @@
import $ from 'cafy'; import $ from 'cafy';
import { EntityRepository, Repository, In } from 'typeorm'; import { EntityRepository, Repository, In } from 'typeorm';
import { User, ILocalUser, IRemoteUser } from '../entities/user'; import { User, ILocalUser, IRemoteUser } from '../entities/user';
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserGroupJoinings } from '..'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import config from '../../config'; import config from '../../config';
import { SchemaType, bool, types } from '../../misc/schema'; import { SchemaType } from '../../misc/schema';
import { awaitAll } from '../../prelude/await-all'; import { awaitAll } from '../../prelude/await-all';
export type PackedUser = SchemaType<typeof packedUserSchema>; export type PackedUser = SchemaType<typeof packedUserSchema>;
@ -156,6 +156,11 @@ export class UserRepository extends Repository<User> {
detail: true detail: true
}), }),
twoFactorEnabled: profile!.twoFactorEnabled, twoFactorEnabled: profile!.twoFactorEnabled,
securityKeys: profile!.twoFactorEnabled
? UserSecurityKeys.count({
userId: user.id
}).then(result => result >= 1)
: false,
twitter: profile!.twitter ? { twitter: profile!.twitter ? {
id: profile!.twitterUserId, id: profile!.twitterUserId,
screenName: profile!.twitterScreenName screenName: profile!.twitterScreenName
@ -195,6 +200,15 @@ export class UserRepository extends Repository<User> {
clientData: profile!.clientData, clientData: profile!.clientData,
email: profile!.email, email: profile!.email,
emailVerified: profile!.emailVerified, emailVerified: profile!.emailVerified,
securityKeysList: profile!.twoFactorEnabled
? UserSecurityKeys.find({
where: {
userId: user.id
},
select: ['id', 'name', 'lastUsed']
})
: []
} : {}), } : {}),
...(relation ? { ...(relation ? {
@ -243,150 +257,150 @@ export class UserRepository extends Repository<User> {
} }
export const packedUserSchema = { export const packedUserSchema = {
type: types.object, type: 'object' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
properties: { properties: {
id: { id: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
format: 'id', format: 'id',
description: 'The unique identifier for this User.', description: 'The unique identifier for this User.',
example: 'xxxxxxxxxx', example: 'xxxxxxxxxx',
}, },
username: { username: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
description: 'The screen name, handle, or alias that this user identifies themselves with.', description: 'The screen name, handle, or alias that this user identifies themselves with.',
example: 'ai' example: 'ai'
}, },
name: { name: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.false, nullable: true as const, optional: false as const,
description: 'The name of the user, as theyve defined it.', description: 'The name of the user, as theyve defined it.',
example: '藍' example: '藍'
}, },
url: { url: {
type: types.string, type: 'string' as const,
format: 'url', format: 'url',
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
}, },
avatarUrl: { avatarUrl: {
type: types.string, type: 'string' as const,
format: 'url', format: 'url',
nullable: bool.true, optional: bool.false, nullable: true as const, optional: false as const,
}, },
avatarColor: { avatarColor: {
type: types.any, type: 'any' as const,
nullable: bool.true, optional: bool.false, nullable: true as const, optional: false as const,
}, },
bannerUrl: { bannerUrl: {
type: types.string, type: 'string' as const,
format: 'url', format: 'url',
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
}, },
bannerColor: { bannerColor: {
type: types.any, type: 'any' as const,
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
}, },
emojis: { emojis: {
type: types.any, type: 'any' as const,
nullable: bool.true, optional: bool.false, nullable: true as const, optional: false as const,
}, },
host: { host: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.false, nullable: true as const, optional: false as const,
example: 'misskey.example.com' example: 'misskey.example.com'
}, },
description: { description: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
description: 'The user-defined UTF-8 string describing their account.', description: 'The user-defined UTF-8 string describing their account.',
example: 'Hi masters, I am Ai!' example: 'Hi masters, I am Ai!'
}, },
birthday: { birthday: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
example: '2018-03-12' example: '2018-03-12'
}, },
createdAt: { createdAt: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
format: 'date-time', format: 'date-time',
description: 'The date that the user account was created on Misskey.' description: 'The date that the user account was created on Misskey.'
}, },
updatedAt: { updatedAt: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
format: 'date-time', format: 'date-time',
}, },
location: { location: {
type: types.string, type: 'string' as const,
nullable: bool.true, optional: bool.true, nullable: true as const, optional: true as const,
}, },
followersCount: { followersCount: {
type: types.number, type: 'number' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'The number of followers this account currently has.' description: 'The number of followers this account currently has.'
}, },
followingCount: { followingCount: {
type: types.number, type: 'number' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'The number of users this account is following.' description: 'The number of users this account is following.'
}, },
notesCount: { notesCount: {
type: types.number, type: 'number' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'The number of Notes (including renotes) issued by the user.' description: 'The number of Notes (including renotes) issued by the user.'
}, },
isBot: { isBot: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'Whether this account is a bot.' description: 'Whether this account is a bot.'
}, },
pinnedNoteIds: { pinnedNoteIds: {
type: types.array, type: 'array' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
items: { items: {
type: types.string, type: 'string' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
format: 'id', format: 'id',
} }
}, },
pinnedNotes: { pinnedNotes: {
type: types.array, type: 'array' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
items: { items: {
type: types.object, type: 'object' as const,
nullable: bool.false, optional: bool.false, nullable: false as const, optional: false as const,
ref: 'Note' ref: 'Note'
} }
}, },
isCat: { isCat: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'Whether this account is a cat.' description: 'Whether this account is a cat.'
}, },
isAdmin: { isAdmin: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'Whether this account is the admin.' description: 'Whether this account is the admin.'
}, },
isModerator: { isModerator: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
description: 'Whether this account is a moderator.' description: 'Whether this account is a moderator.'
}, },
isLocked: { isLocked: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
}, },
hasUnreadSpecifiedNotes: { hasUnreadSpecifiedNotes: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
}, },
hasUnreadMentions: { hasUnreadMentions: {
type: types.boolean, type: 'boolean' as const,
nullable: bool.false, optional: bool.true, nullable: false as const, optional: true as const,
}, },
}, },
}; };

View File

@ -194,6 +194,13 @@ export function createDeleteObjectStorageFileJob(key: string) {
}); });
} }
export function createCleanRemoteFilesJob() {
return objectStorageQueue.add('cleanRemoteFiles', {}, {
removeOnComplete: true,
removeOnFail: true
});
}
export default function() { export default function() {
if (!program.onlyServer) { if (!program.onlyServer) {
deliverQueue.process(128, processDeliver); deliverQueue.process(128, processDeliver);

View File

@ -1,7 +1,7 @@
import * as Bull from 'bull'; import * as Bull from 'bull';
import { queueLogger } from '../../logger'; import { queueLogger } from '../../logger';
import { deleteFile } from '../../../services/drive/delete-file'; import { deleteFileSync } from '../../../services/drive/delete-file';
import { Users, DriveFiles } from '../../../models'; import { Users, DriveFiles } from '../../../models';
import { MoreThan } from 'typeorm'; import { MoreThan } from 'typeorm';
@ -39,7 +39,7 @@ export async function deleteDriveFiles(job: Bull.Job, done: any): Promise<void>
cursor = files[files.length - 1].id; cursor = files[files.length - 1].id;
for (const file of files) { for (const file of files) {
await deleteFile(file); await deleteFileSync(file);
deletedCount++; deletedCount++;
} }

View File

@ -0,0 +1,50 @@
import * as Bull from 'bull';
import { queueLogger } from '../../logger';
import { deleteFileSync } from '../../../services/drive/delete-file';
import { DriveFiles } from '../../../models';
import { MoreThan, Not, IsNull } from 'typeorm';
const logger = queueLogger.createSubLogger('clean-remote-files');
export default async function cleanRemoteFiles(job: Bull.Job, done: any): Promise<void> {
logger.info(`Deleting cached remote files...`);
let deletedCount = 0;
let cursor: any = null;
while (true) {
const files = await DriveFiles.find({
where: {
userHost: Not(IsNull()),
isLink: false,
...(cursor ? { id: MoreThan(cursor) } : {})
},
take: 8,
order: {
id: 1
}
});
if (files.length === 0) {
job.progress(100);
break;
}
cursor = files[files.length - 1].id;
await Promise.all(files.map(file => deleteFileSync(file, true)));
deletedCount += 8;
const total = await DriveFiles.count({
userHost: Not(IsNull()),
isLink: false,
});
job.progress(deletedCount / total);
}
logger.succ(`All cahced remote files has been deleted.`);
done();
}

View File

@ -1,22 +1,10 @@
import * as Bull from 'bull'; import * as Bull from 'bull';
import * as Minio from 'minio'; import { deleteObjectStorageFile } from '../../../services/drive/delete-file';
import { fetchMeta } from '../../../misc/fetch-meta';
export default async (job: Bull.Job) => { export default async (job: Bull.Job) => {
const meta = await fetchMeta();
const minio = new Minio.Client({
endPoint: meta.objectStorageEndpoint!,
region: meta.objectStorageRegion ? meta.objectStorageRegion : undefined,
port: meta.objectStoragePort ? meta.objectStoragePort : undefined,
useSSL: meta.objectStorageUseSSL,
accessKey: meta.objectStorageAccessKey!,
secretKey: meta.objectStorageSecretKey!,
});
const key: string = job.data.key; const key: string = job.data.key;
await minio.removeObject(meta.objectStorageBucket!, key); await deleteObjectStorageFile(key);
return 'Success'; return 'Success';
}; };

View File

@ -1,12 +1,14 @@
import * as Bull from 'bull'; import * as Bull from 'bull';
import deleteFile from './delete-file'; import deleteFile from './delete-file';
import cleanRemoteFiles from './clean-remote-files';
const jobs = { const jobs = {
deleteFile, deleteFile,
cleanRemoteFiles,
} as any; } as any;
export default function(q: Bull.Queue) { export default function(q: Bull.Queue) {
for (const [k, v] of Object.entries(jobs)) { for (const [k, v] of Object.entries(jobs)) {
q.process(k, v as any); q.process(k, 16, v as any);
} }
} }

View File

@ -1,13 +1,13 @@
import Resolver from '../../resolver'; import Resolver from '../../resolver';
import { IRemoteUser } from '../../../../models/entities/user'; import { IRemoteUser } from '../../../../models/entities/user';
import announceNote from './note'; import announceNote from './note';
import { IAnnounce, INote } from '../../type'; import { IAnnounce, INote, validPost, getApId } from '../../type';
import { apLogger } from '../../logger'; import { apLogger } from '../../logger';
const logger = apLogger; const logger = apLogger;
export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => { export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => {
const uri = activity.id || activity; const uri = getApId(activity);
logger.info(`Announce: ${uri}`); logger.info(`Announce: ${uri}`);
@ -22,15 +22,9 @@ export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> =>
throw e; throw e;
} }
switch (object.type) { if (validPost.includes(object.type)) {
case 'Note':
case 'Question':
case 'Article':
announceNote(resolver, actor, activity, object as INote); announceNote(resolver, actor, activity, object as INote);
break; } else {
default:
logger.warn(`Unknown announce type: ${object.type}`); logger.warn(`Unknown announce type: ${object.type}`);
break;
} }
}; };

View File

@ -1,7 +1,7 @@
import Resolver from '../../resolver'; import Resolver from '../../resolver';
import post from '../../../../services/note/create'; import post from '../../../../services/note/create';
import { IRemoteUser, User } from '../../../../models/entities/user'; import { IRemoteUser, User } from '../../../../models/entities/user';
import { IAnnounce, INote } from '../../type'; import { IAnnounce, INote, getApId, getApIds } from '../../type';
import { fetchNote, resolveNote } from '../../models/note'; import { fetchNote, resolveNote } from '../../models/note';
import { resolvePerson } from '../../models/person'; import { resolvePerson } from '../../models/person';
import { apLogger } from '../../logger'; import { apLogger } from '../../logger';
@ -14,17 +14,13 @@ const logger = apLogger;
* *
*/ */
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> { export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> {
const uri = activity.id || activity; const uri = getApId(activity);
// アナウンサーが凍結されていたらスキップ // アナウンサーが凍結されていたらスキップ
if (actor.isSuspended) { if (actor.isSuspended) {
return; return;
} }
if (typeof uri !== 'string') {
throw new Error('invalid announce');
}
// アナウンス先をブロックしてたら中断 // アナウンス先をブロックしてたら中断
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) return; if (meta.blockedHosts.includes(extractDbHost(uri))) return;
@ -52,11 +48,14 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity:
logger.info(`Creating the (Re)Note: ${uri}`); logger.info(`Creating the (Re)Note: ${uri}`);
//#region Visibility //#region Visibility
const visibility = getVisibility(activity.to || [], activity.cc || [], actor); const to = getApIds(activity.to);
const cc = getApIds(activity.cc);
const visibility = getVisibility(to, cc, actor);
let visibleUsers: User[] = []; let visibleUsers: User[] = [];
if (visibility == 'specified') { if (visibility == 'specified') {
visibleUsers = await Promise.all((note.to || []).map(uri => resolvePerson(uri))); visibleUsers = await Promise.all(to.map(uri => resolvePerson(uri)));
} }
//#endergion //#endergion

View File

@ -1,6 +0,0 @@
import { IRemoteUser } from '../../../../models/entities/user';
import { createImage } from '../../models/image';
export default async function(actor: IRemoteUser, image: any): Promise<void> {
await createImage(image.url, actor);
}

View File

@ -1,14 +1,13 @@
import Resolver from '../../resolver'; import Resolver from '../../resolver';
import { IRemoteUser } from '../../../../models/entities/user'; import { IRemoteUser } from '../../../../models/entities/user';
import createImage from './image';
import createNote from './note'; import createNote from './note';
import { ICreate } from '../../type'; import { ICreate, getApId, validPost } from '../../type';
import { apLogger } from '../../logger'; import { apLogger } from '../../logger';
const logger = apLogger; const logger = apLogger;
export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => { export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
const uri = activity.id || activity; const uri = getApId(activity);
logger.info(`Create: ${uri}`); logger.info(`Create: ${uri}`);
@ -23,19 +22,9 @@ export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
throw e; throw e;
} }
switch (object.type) { if (validPost.includes(object.type)) {
case 'Image':
createImage(actor, object);
break;
case 'Note':
case 'Question':
case 'Article':
createNote(resolver, actor, object); createNote(resolver, actor, object);
break; } else {
default:
logger.warn(`Unknown type: ${object.type}`); logger.warn(`Unknown type: ${object.type}`);
break;
} }
}; };

View File

@ -1,9 +1,8 @@
import Resolver from '../../resolver'; import Resolver from '../../resolver';
import deleteNote from './note'; import deleteNote from './note';
import { IRemoteUser } from '../../../../models/entities/user'; import { IRemoteUser } from '../../../../models/entities/user';
import { IDelete } from '../../type'; import { IDelete, getApId, validPost } from '../../type';
import { apLogger } from '../../logger'; import { apLogger } from '../../logger';
import { Notes } from '../../../../models';
/** /**
* *
@ -17,24 +16,11 @@ export default async (actor: IRemoteUser, activity: IDelete): Promise<void> => {
const object = await resolver.resolve(activity.object); const object = await resolver.resolve(activity.object);
const uri = (object as any).id; const uri = getApId(object);
switch (object.type) { if (validPost.includes(object.type) || object.type === 'Tombstone') {
case 'Note':
case 'Question':
case 'Article':
deleteNote(actor, uri); deleteNote(actor, uri);
break; } else {
case 'Tombstone':
const note = await Notes.findOne({ uri });
if (note != null) {
deleteNote(actor, uri);
}
break;
default:
apLogger.warn(`Unknown type: ${object.type}`); apLogger.warn(`Unknown type: ${object.type}`);
break;
} }
}; };

View File

@ -17,7 +17,7 @@ import { deliverQuestionUpdate } from '../../../services/note/polls/update';
import { extractDbHost, toPuny } from '../../../misc/convert-host'; import { extractDbHost, toPuny } from '../../../misc/convert-host';
import { Notes, Emojis, Polls } from '../../../models'; import { Notes, Emojis, Polls } from '../../../models';
import { Note } from '../../../models/entities/note'; import { Note } from '../../../models/entities/note';
import { IObject, INote } from '../type'; import { IObject, INote, getApIds, getOneApId, getApId, validPost } from '../type';
import { Emoji } from '../../../models/entities/emoji'; import { Emoji } from '../../../models/entities/emoji';
import { genId } from '../../../misc/gen-id'; import { genId } from '../../../misc/gen-id';
import { fetchMeta } from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
@ -32,7 +32,7 @@ export function validateNote(object: any, uri: string) {
return new Error('invalid Note: object is null'); return new Error('invalid Note: object is null');
} }
if (!['Note', 'Question', 'Article'].includes(object.type)) { if (!validPost.includes(object.type)) {
return new Error(`invalid Note: invalied object type ${object.type}`); return new Error(`invalid Note: invalied object type ${object.type}`);
} }
@ -40,7 +40,7 @@ export function validateNote(object: any, uri: string) {
return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`); return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`);
} }
if (object.attributedTo && extractDbHost(object.attributedTo) !== expectHost) { if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) {
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`); return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`);
} }
@ -53,8 +53,7 @@ export function validateNote(object: any, uri: string) {
* Misskeyに対象のNoteが登録されていればそれを返します * Misskeyに対象のNoteが登録されていればそれを返します
*/ */
export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
const uri = typeof value == 'string' ? value : value.id; const uri = getApId(value);
if (uri == null) throw new Error('missing uri');
// URIがこのサーバーを指しているならデータベースからフェッチ // URIがこのサーバーを指しているならデータベースからフェッチ
if (uri.startsWith(config.url + '/')) { if (uri.startsWith(config.url + '/')) {
@ -76,12 +75,12 @@ export async function fetchNote(value: string | IObject, resolver?: Resolver): P
/** /**
* Noteを作成します * Noteを作成します
*/ */
export async function createNote(value: any, resolver?: Resolver, silent = false): Promise<Note | null> { export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
if (resolver == null) resolver = new Resolver(); if (resolver == null) resolver = new Resolver();
const object: any = await resolver.resolve(value); const object: any = await resolver.resolve(value);
const entryUri = value.id || value; const entryUri = getApId(value);
const err = validateNote(object, entryUri); const err = validateNote(object, entryUri);
if (err) { if (err) {
logger.error(`${err.message}`, { logger.error(`${err.message}`, {
@ -101,7 +100,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
logger.info(`Creating the Note: ${note.id}`); logger.info(`Creating the Note: ${note.id}`);
// 投稿者をフェッチ // 投稿者をフェッチ
const actor = await resolvePerson(note.attributedTo, resolver) as IRemoteUser; const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as IRemoteUser;
// 投稿者が凍結されていたらスキップ // 投稿者が凍結されていたらスキップ
if (actor.isSuspended) { if (actor.isSuspended) {
@ -109,24 +108,24 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
} }
//#region Visibility //#region Visibility
note.to = note.to == null ? [] : typeof note.to == 'string' ? [note.to] : note.to; const to = getApIds(note.to);
note.cc = note.cc == null ? [] : typeof note.cc == 'string' ? [note.cc] : note.cc; const cc = getApIds(note.cc);
let visibility = 'public'; let visibility = 'public';
let visibleUsers: User[] = []; let visibleUsers: User[] = [];
if (!note.to.includes('https://www.w3.org/ns/activitystreams#Public')) { if (!to.includes('https://www.w3.org/ns/activitystreams#Public')) {
if (note.cc.includes('https://www.w3.org/ns/activitystreams#Public')) { if (cc.includes('https://www.w3.org/ns/activitystreams#Public')) {
visibility = 'home'; visibility = 'home';
} else if (note.to.includes(`${actor.uri}/followers`)) { // TODO: person.followerと照合するべき } else if (to.includes(`${actor.uri}/followers`)) { // TODO: person.followerと照合するべき
visibility = 'followers'; visibility = 'followers';
} else { } else {
visibility = 'specified'; visibility = 'specified';
visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, resolver))); visibleUsers = await Promise.all(to.map(uri => resolvePerson(uri, resolver)));
} }
} }
//#endergion //#endergion
const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver); const apMentions = await extractMentionedUsers(actor, to, cc, resolver);
const apHashtags = await extractHashtags(note.tag); const apHashtags = await extractHashtags(note.tag);
@ -217,11 +216,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
const apEmojis = emojis.map(emoji => emoji.name); const apEmojis = emojis.map(emoji => emoji.name);
const questionUri = note._misskey_question; const questionUri = note._misskey_question;
const poll = await extractPollFromQuestion(note._misskey_question || note).catch(() => undefined); const poll = await extractPollFromQuestion(note._misskey_question || note, resolver).catch(() => undefined);
// ユーザーの情報が古かったらついでに更新しておく // ユーザーの情報が古かったらついでに更新しておく
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
updatePerson(note.attributedTo); if (actor.uri) updatePerson(actor.uri);
} }
return await post(actor, { return await post(actor, {

View File

@ -1,12 +1,19 @@
import config from '../../../config'; import config from '../../../config';
import Resolver from '../resolver'; import Resolver from '../resolver';
import { IQuestion } from '../type'; import { IObject, IQuestion, isQuestion, } from '../type';
import { apLogger } from '../logger'; import { apLogger } from '../logger';
import { Notes, Polls } from '../../../models'; import { Notes, Polls } from '../../../models';
import { IPoll } from '../../../models/entities/poll'; import { IPoll } from '../../../models/entities/poll';
export async function extractPollFromQuestion(source: string | IQuestion): Promise<IPoll> { export async function extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
const question = typeof source === 'string' ? await new Resolver().resolve(source) as IQuestion : source; if (resolver == null) resolver = new Resolver();
const question = await resolver.resolve(source);
if (!isQuestion(question)) {
throw new Error('invalid type');
}
const multiple = !question.oneOf; const multiple = !question.oneOf;
const expiresAt = question.endTime ? new Date(question.endTime) : null; const expiresAt = question.endTime ? new Date(question.endTime) : null;

View File

@ -6,9 +6,9 @@ export interface IObject {
id?: string; id?: string;
summary?: string; summary?: string;
published?: string; published?: string;
cc?: string[]; cc?: IObject | string | (IObject | string)[];
to?: string[]; to?: IObject | string | (IObject | string)[];
attributedTo: string; attributedTo: IObject | string | (IObject | string)[];
attachment?: any[]; attachment?: any[];
inReplyTo?: any; inReplyTo?: any;
replies?: ICollection; replies?: ICollection;
@ -23,6 +23,32 @@ export interface IObject {
sensitive?: boolean; sensitive?: boolean;
} }
/**
* Get array of ActivityStreams Objects id
*/
export function getApIds(value: IObject | string | (IObject | string)[] | undefined): string[] {
if (value == null) return [];
const array = Array.isArray(value) ? value : [value];
return array.map(x => getApId(x));
}
/**
* Get first ActivityStreams Object id
*/
export function getOneApId(value: IObject | string | (IObject | string)[]): string {
const firstOne = Array.isArray(value) ? value[0] : value;
return getApId(firstOne);
}
/**
* Get ActivityStreams Object id
*/
export function getApId(value: string | IObject): string {
if (typeof value === 'string') return value;
if (typeof value.id === 'string') return value.id;
throw new Error(`cannot detemine id`);
}
export interface IActivity extends IObject { export interface IActivity extends IObject {
//type: 'Activity'; //type: 'Activity';
actor: IObject | string; actor: IObject | string;
@ -42,8 +68,10 @@ export interface IOrderedCollection extends IObject {
orderedItems: IObject | string | IObject[] | string[]; orderedItems: IObject | string | IObject[] | string[];
} }
export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video'];
export interface INote extends IObject { export interface INote extends IObject {
type: 'Note' | 'Question'; type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video';
_misskey_content?: string; _misskey_content?: string;
_misskey_quote?: string; _misskey_quote?: string;
_misskey_question?: string; _misskey_question?: string;
@ -59,6 +87,9 @@ export interface IQuestion extends IObject {
endTime?: Date; endTime?: Date;
} }
export const isQuestion = (object: IObject): object is IQuestion =>
object.type === 'Note' || object.type === 'Question';
interface IQuestionChoice { interface IQuestionChoice {
name?: string; name?: string;
replies?: ICollection; replies?: ICollection;

422
src/server/api/2fa.ts Normal file
View File

@ -0,0 +1,422 @@
import * as crypto from 'crypto';
import config from '../../config';
import * as jsrsasign from 'jsrsasign';
const ECC_PRELUDE = Buffer.from([0x04]);
const NULL_BYTE = Buffer.from([0]);
const PEM_PRELUDE = Buffer.from(
'3059301306072a8648ce3d020106082a8648ce3d030107034200',
'hex'
);
// Android Safetynet attestations are signed with this cert:
const GSR2 = `-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----\n`;
function base64URLDecode(source: string) {
return Buffer.from(source.replace(/\-/g, '+').replace(/_/g, '/'), 'base64');
}
function getCertSubject(certificate: string) {
const subjectCert = new jsrsasign.X509();
subjectCert.readCertPEM(certificate);
const subjectString = subjectCert.getSubjectString();
const subjectFields = subjectString.slice(1).split('/');
const fields = {} as Record<string, string>;
for (const field of subjectFields) {
const eqIndex = field.indexOf('=');
fields[field.substring(0, eqIndex)] = field.substring(eqIndex + 1);
}
return fields;
}
function verifyCertificateChain(certificates: string[]) {
let valid = true;
for (let i = 0; i < certificates.length; i++) {
const Cert = certificates[i];
const certificate = new jsrsasign.X509();
certificate.readCertPEM(Cert);
const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1];
const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]);
const algorithm = certificate.getSignatureAlgorithmField();
const signatureHex = certificate.getSignatureValueHex();
// Verify against CA
const Signature = new jsrsasign.KJUR.crypto.Signature({alg: algorithm});
Signature.init(CACert);
Signature.updateHex(certStruct);
valid = valid && !!Signature.verify(signatureHex); // true if CA signed the certificate
}
return valid;
}
function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') {
if (pemBuffer.length == 65 && pemBuffer[0] == 0x04) {
pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91);
type = 'PUBLIC KEY';
}
const cert = pemBuffer.toString('base64');
const keyParts = [];
const max = Math.ceil(cert.length / 64);
let start = 0;
for (let i = 0; i < max; i++) {
keyParts.push(cert.substring(start, start + 64));
start += 64;
}
return (
`-----BEGIN ${type}-----\n` +
keyParts.join('\n') +
`\n-----END ${type}-----\n`
);
}
export function hash(data: Buffer) {
return crypto
.createHash('sha256')
.update(data)
.digest();
}
export function verifyLogin({
publicKey,
authenticatorData,
clientDataJSON,
clientData,
signature,
challenge
}: {
publicKey: Buffer,
authenticatorData: Buffer,
clientDataJSON: Buffer,
clientData: any,
signature: Buffer,
challenge: string
}) {
if (clientData.type != 'webauthn.get') {
throw new Error('type is not webauthn.get');
}
if (hash(clientData.challenge).toString('hex') != challenge) {
throw new Error('challenge mismatch');
}
if (clientData.origin != config.scheme + '://' + config.host) {
throw new Error('origin mismatch');
}
const verificationData = Buffer.concat(
[authenticatorData, hash(clientDataJSON)],
32 + authenticatorData.length
);
return crypto
.createVerify('SHA256')
.update(verificationData)
.verify(PEMString(publicKey), signature);
}
export const procedures = {
none: {
verify({publicKey}: {publicKey: Map<number, Buffer>}) {
const negTwo = publicKey.get(-2);
if (!negTwo || negTwo.length != 32) {
throw new Error('invalid or no -2 key given');
}
const negThree = publicKey.get(-3);
if (!negThree || negThree.length != 32) {
throw new Error('invalid or no -3 key given');
}
const publicKeyU2F = Buffer.concat(
[ECC_PRELUDE, negTwo, negThree],
1 + 32 + 32
);
return {
publicKey: publicKeyU2F,
valid: true
};
}
},
'android-key': {
verify({
attStmt,
authenticatorData,
clientDataHash,
publicKey,
rpIdHash,
credentialId
}: {
attStmt: any,
authenticatorData: Buffer,
clientDataHash: Buffer,
publicKey: Map<number, any>;
rpIdHash: Buffer,
credentialId: Buffer,
}) {
if (attStmt.alg != -7) {
throw new Error('alg mismatch');
}
const verificationData = Buffer.concat([
authenticatorData,
clientDataHash
]);
const attCert: Buffer = attStmt.x5c[0];
const negTwo = publicKey.get(-2);
if (!negTwo || negTwo.length != 32) {
throw new Error('invalid or no -2 key given');
}
const negThree = publicKey.get(-3);
if (!negThree || negThree.length != 32) {
throw new Error('invalid or no -3 key given');
}
const publicKeyData = Buffer.concat(
[ECC_PRELUDE, negTwo, negThree],
1 + 32 + 32
);
if (!attCert.equals(publicKeyData)) {
throw new Error('public key mismatch');
}
const isValid = crypto
.createVerify('SHA256')
.update(verificationData)
.verify(PEMString(attCert), attStmt.sig);
// TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON)
return {
valid: isValid,
publicKey: publicKeyData
};
}
},
// what a stupid attestation
'android-safetynet': {
verify({
attStmt,
authenticatorData,
clientDataHash,
publicKey,
rpIdHash,
credentialId
}: {
attStmt: any,
authenticatorData: Buffer,
clientDataHash: Buffer,
publicKey: Map<number, any>;
rpIdHash: Buffer,
credentialId: Buffer,
}) {
const verificationData = hash(
Buffer.concat([authenticatorData, clientDataHash])
);
const jwsParts = attStmt.response.toString('utf-8').split('.');
const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8'));
const response = JSON.parse(
base64URLDecode(jwsParts[1]).toString('utf-8')
);
const signature = jwsParts[2];
if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) {
throw new Error('invalid nonce');
}
const certificateChain = header.x5c
.map((key: any) => PEMString(key))
.concat([GSR2]);
if (getCertSubject(certificateChain[0]).CN != 'attest.android.com') {
throw new Error('invalid common name');
}
if (!verifyCertificateChain(certificateChain)) {
throw new Error('Invalid certificate chain!');
}
const signatureBase = Buffer.from(
jwsParts[0] + '.' + jwsParts[1],
'utf-8'
);
const valid = crypto
.createVerify('sha256')
.update(signatureBase)
.verify(certificateChain[0], base64URLDecode(signature));
const negTwo = publicKey.get(-2);
if (!negTwo || negTwo.length != 32) {
throw new Error('invalid or no -2 key given');
}
const negThree = publicKey.get(-3);
if (!negThree || negThree.length != 32) {
throw new Error('invalid or no -3 key given');
}
const publicKeyData = Buffer.concat(
[ECC_PRELUDE, negTwo, negThree],
1 + 32 + 32
);
return {
valid,
publicKey: publicKeyData
};
}
},
packed: {
verify({
attStmt,
authenticatorData,
clientDataHash,
publicKey,
rpIdHash,
credentialId
}: {
attStmt: any,
authenticatorData: Buffer,
clientDataHash: Buffer,
publicKey: Map<number, any>;
rpIdHash: Buffer,
credentialId: Buffer,
}) {
const verificationData = Buffer.concat([
authenticatorData,
clientDataHash
]);
if (attStmt.x5c) {
const attCert = attStmt.x5c[0];
const validSignature = crypto
.createVerify('SHA256')
.update(verificationData)
.verify(PEMString(attCert), attStmt.sig);
const negTwo = publicKey.get(-2);
if (!negTwo || negTwo.length != 32) {
throw new Error('invalid or no -2 key given');
}
const negThree = publicKey.get(-3);
if (!negThree || negThree.length != 32) {
throw new Error('invalid or no -3 key given');
}
const publicKeyData = Buffer.concat(
[ECC_PRELUDE, negTwo, negThree],
1 + 32 + 32
);
return {
valid: validSignature,
publicKey: publicKeyData
};
} else if (attStmt.ecdaaKeyId) {
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
throw new Error('ECDAA-Verify is not supported');
} else {
if (attStmt.alg != -7) throw new Error('alg mismatch');
throw new Error('self attestation is not supported');
}
}
},
'fido-u2f': {
verify({
attStmt,
authenticatorData,
clientDataHash,
publicKey,
rpIdHash,
credentialId
}: {
attStmt: any,
authenticatorData: Buffer,
clientDataHash: Buffer,
publicKey: Map<number, any>,
rpIdHash: Buffer,
credentialId: Buffer
}) {
const x5c: Buffer[] = attStmt.x5c;
if (x5c.length != 1) {
throw new Error('x5c length does not match expectation');
}
const attCert = x5c[0];
// TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve
const negTwo: Buffer = publicKey.get(-2);
if (!negTwo || negTwo.length != 32) {
throw new Error('invalid or no -2 key given');
}
const negThree: Buffer = publicKey.get(-3);
if (!negThree || negThree.length != 32) {
throw new Error('invalid or no -3 key given');
}
const publicKeyU2F = Buffer.concat(
[ECC_PRELUDE, negTwo, negThree],
1 + 32 + 32
);
const verificationData = Buffer.concat([
NULL_BYTE,
rpIdHash,
clientDataHash,
credentialId,
publicKeyU2F
]);
const validSignature = crypto
.createVerify('SHA256')
.update(verificationData)
.verify(PEMString(attCert), attStmt.sig);
return {
valid: validSignature,
publicKey: publicKeyU2F
};
}
}
};

View File

@ -1,7 +1,5 @@
import { Not, IsNull } from 'typeorm';
import define from '../../../define'; import define from '../../../define';
import { deleteFile } from '../../../../../services/drive/delete-file'; import { createCleanRemoteFilesJob } from '../../../../../queue';
import { DriveFiles } from '../../../../../models';
export const meta = { export const meta = {
tags: ['admin'], tags: ['admin'],
@ -11,12 +9,5 @@ export const meta = {
}; };
export default define(meta, async (ps, me) => { export default define(meta, async (ps, me) => {
const files = await DriveFiles.find({ createCleanRemoteFilesJob();
userHost: Not(IsNull()),
isLink: false,
});
for (const file of files) {
deleteFile(file, true);
}
}); });

View File

@ -0,0 +1,26 @@
import $ from 'cafy';
import define from '../../define';
import { sendEmail } from '../../../../services/send-email';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
to: {
validator: $.str,
},
subject: {
validator: $.str,
},
text: {
validator: $.str,
},
}
};
export default define(meta, async (ps) => {
await sendEmail(ps.to, ps.subject, ps.text);
});

View File

@ -0,0 +1,33 @@
import $ from 'cafy';
import define from '../../define';
import { getConnection } from 'typeorm';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
full: {
validator: $.bool,
},
analyze: {
validator: $.bool,
},
}
};
export default define(meta, async (ps) => {
const params: string[] = [];
if (ps.full) {
params.push('FULL');
}
if (ps.analyze) {
params.push('ANALYZE');
}
getConnection().query('VACUUM ' + params.join(' '));
});

View File

@ -10,7 +10,7 @@ import { Users, Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note'; import { Note } from '../../../../models/entities/note';
import { User } from '../../../../models/entities/user'; import { User } from '../../../../models/entities/user';
import { fetchMeta } from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { validActor } from '../../../../remote/activitypub/type'; import { validActor, validPost } from '../../../../remote/activitypub/type';
export const meta = { export const meta = {
tags: ['federation'], tags: ['federation'],
@ -145,7 +145,7 @@ async function fetchAny(uri: string) {
}; };
} }
if (['Note', 'Question', 'Article'].includes(object.type)) { if (validPost.includes(object.type)) {
const note = await createNote(object.id, undefined, true); const note = await createNote(object.id, undefined, true);
return { return {
type: 'Note', type: 'Note',

View File

@ -4,7 +4,6 @@ import define from '../../define';
import { Apps } from '../../../../models'; import { Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id'; import { genId } from '../../../../misc/gen-id';
import { unique } from '../../../../prelude/array'; import { unique } from '../../../../prelude/array';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['app'], tags: ['app'],
@ -53,8 +52,8 @@ export const meta = {
}, },
res: { res: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'App', ref: 'App',
}, },
}; };

View File

@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { Apps } from '../../../../models'; import { Apps } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['app'], tags: ['app'],
@ -15,8 +14,8 @@ export const meta = {
}, },
res: { res: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'App', ref: 'App',
}, },

View File

@ -5,7 +5,6 @@ import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { Apps, AuthSessions } from '../../../../../models'; import { Apps, AuthSessions } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id'; import { genId } from '../../../../../misc/gen-id';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
tags: ['auth'], tags: ['auth'],
@ -28,17 +27,17 @@ export const meta = {
}, },
res: { res: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
token: { token: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'セッションのトークン' description: 'セッションのトークン'
}, },
url: { url: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
format: 'url', format: 'url',
description: 'セッションのURL' description: 'セッションのURL'
}, },

View File

@ -3,7 +3,6 @@ import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models';
import { ensure } from '../../../../../prelude/ensure'; import { ensure } from '../../../../../prelude/ensure';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
tags: ['auth'], tags: ['auth'],
@ -29,18 +28,18 @@ export const meta = {
}, },
res: { res: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
accessToken: { accessToken: {
type: types.string, type: 'string' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
description: 'ユーザーのアクセストークン', description: 'ユーザーのアクセストークン',
}, },
user: { user: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'User', ref: 'User',
description: '認証したユーザー' description: '認証したユーザー'
}, },

View File

@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { Blockings } from '../../../../models'; import { Blockings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -33,11 +32,11 @@ export const meta = {
}, },
res: { res: {
type: types.array, type: 'array' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
items: { items: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
ref: 'Blocking', ref: 'Blocking',
} }
}, },

View File

@ -1,7 +1,6 @@
import define from '../define'; import define from '../define';
import { fetchMeta } from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import { DriveFiles } from '../../../models'; import { DriveFiles } from '../../../models';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -16,16 +15,16 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
res: { res: {
type: types.object, type: 'object' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
properties: { properties: {
capacity: { capacity: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
}, },
usage: { usage: {
type: types.number, type: 'number' as const,
optional: bool.false, nullable: bool.false, optional: false as const, nullable: false as const,
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More