Compare commits
170 Commits
13.14.0-be
...
mkjs-n
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c454a44785 | ||
![]() |
41250d997b | ||
![]() |
a97d379c84 | ||
![]() |
535e778033 | ||
![]() |
726fdb9e93 | ||
![]() |
ae3ce71bb2 | ||
![]() |
106062fa23 | ||
![]() |
42990fe6b6 | ||
![]() |
dc787f7a76 | ||
![]() |
83a6902997 | ||
![]() |
1c5606ca72 | ||
![]() |
02405287d1 | ||
![]() |
bd9c084a3a | ||
![]() |
cef5feaee2 | ||
![]() |
fe16b16fc3 | ||
![]() |
f4cc9e3d2e | ||
![]() |
4d021f315f | ||
![]() |
bad25a804c | ||
![]() |
019199168e | ||
![]() |
8122cb3a86 | ||
![]() |
ab02fd3ec4 | ||
![]() |
028e7c00ae | ||
![]() |
27b6921d3c | ||
![]() |
3ec19e0b00 | ||
![]() |
8401117293 | ||
![]() |
ced5638b80 | ||
![]() |
b532ad8cd4 | ||
![]() |
b818f2308d | ||
![]() |
b697b2f651 | ||
![]() |
0571b83978 | ||
![]() |
b519ff1e10 | ||
![]() |
601eb65cfb | ||
![]() |
59f4bf2a74 | ||
![]() |
a5a9540eac | ||
![]() |
bb46030677 | ||
![]() |
9530cb01b9 | ||
![]() |
fd223a8538 | ||
![]() |
4c52f3b286 | ||
![]() |
edc38fc2c7 | ||
![]() |
4a104af304 | ||
![]() |
a1b243bbda | ||
![]() |
6bd7a0d0aa | ||
![]() |
80bd84932c | ||
![]() |
7f28b7334c | ||
![]() |
669cfb1e29 | ||
![]() |
47fae30aba | ||
![]() |
64bbfc2107 | ||
![]() |
1e2178b717 | ||
![]() |
b5852f6053 | ||
![]() |
35b1f73b51 | ||
![]() |
f651ca4b48 | ||
![]() |
8bad11c559 | ||
![]() |
aead5305a7 | ||
![]() |
de9a5d515d | ||
![]() |
7f51ace0ff | ||
![]() |
586cfbe880 | ||
![]() |
f7f8845801 | ||
![]() |
93d111bfb6 | ||
![]() |
1f64b7c9ea | ||
![]() |
c4f434aa01 | ||
![]() |
4001c974a6 | ||
![]() |
2fc2f2d616 | ||
![]() |
f81479ad05 | ||
![]() |
6198313c49 | ||
![]() |
90a22fe32f | ||
![]() |
3918473e29 | ||
![]() |
931cea75b6 | ||
![]() |
68a2aa3efd | ||
![]() |
dbeb1856ac | ||
![]() |
07bbadbbdc | ||
![]() |
78558eb666 | ||
![]() |
bc15dde742 | ||
![]() |
9135d2757e | ||
![]() |
a1c96d9bcb | ||
![]() |
94f25d9cdd | ||
![]() |
e1b4a0438e | ||
![]() |
93ea327660 | ||
![]() |
9ce6f99cad | ||
![]() |
d647df7f63 | ||
![]() |
654c93b0ce | ||
![]() |
8d36911f90 | ||
![]() |
84835be483 | ||
![]() |
35f90e94c9 | ||
![]() |
5268a55996 | ||
![]() |
53ad4b18e5 | ||
![]() |
54b6b79559 | ||
![]() |
b4cfa412b0 | ||
![]() |
dfdfc8fab4 | ||
![]() |
b14d3cdc32 | ||
![]() |
82a17ea427 | ||
![]() |
a57df6f2ca | ||
![]() |
41c353e24d | ||
![]() |
bca9c54923 | ||
![]() |
ce5faa8ff5 | ||
![]() |
0ef7a3db0f | ||
![]() |
c264b01658 | ||
![]() |
e07ed1b312 | ||
![]() |
c0bd1b82ab | ||
![]() |
d7fe783c46 | ||
![]() |
1b6ac7888b | ||
![]() |
86f4e206f4 | ||
![]() |
e689dcdd73 | ||
![]() |
810d065d21 | ||
![]() |
8adf2701d2 | ||
![]() |
11f41a6e8d | ||
![]() |
8d1569567f | ||
![]() |
23545bcbbb | ||
![]() |
aea6950b46 | ||
![]() |
d7d3f00488 | ||
![]() |
307bc2a38e | ||
![]() |
919af7b967 | ||
![]() |
359f0b21b0 | ||
![]() |
79f71acd42 | ||
![]() |
89480d9029 | ||
![]() |
0853b2fe42 | ||
![]() |
13fe20d47e | ||
![]() |
361ab296c7 | ||
![]() |
e76c1d8956 | ||
![]() |
0709ec5d82 | ||
![]() |
f7a0213af9 | ||
![]() |
9b27fffb0e | ||
![]() |
c0cbbe6072 | ||
![]() |
13fc093602 | ||
![]() |
ca29fdc795 | ||
![]() |
03a2501b45 | ||
![]() |
69ba41bd8d | ||
![]() |
5f2bbea6de | ||
![]() |
49dc995f1e | ||
![]() |
15ef1a082d | ||
![]() |
cdfab72b35 | ||
![]() |
37142d7495 | ||
![]() |
b6d04f5d5b | ||
![]() |
5e268f0611 | ||
![]() |
2afa664864 | ||
![]() |
64f4bb6a90 | ||
![]() |
00cc846729 | ||
![]() |
4f5d77391f | ||
![]() |
ac99cdce8b | ||
![]() |
3324f3fa15 | ||
![]() |
56a1e436f3 | ||
![]() |
ad6c5275ec | ||
![]() |
0e08c56bc7 | ||
![]() |
3e13bb4cd7 | ||
![]() |
d3ecb02644 | ||
![]() |
f3bc232419 | ||
![]() |
00b1fd2ee3 | ||
![]() |
44e819bf6a | ||
![]() |
49e32ebf2b | ||
![]() |
66447d13c1 | ||
![]() |
5bb2d71231 | ||
![]() |
32e489e536 | ||
![]() |
fc10a9026c | ||
![]() |
9699bac704 | ||
![]() |
d328a8cefc | ||
![]() |
34edf950ff | ||
![]() |
7d8fa48d47 | ||
![]() |
96e04738c0 | ||
![]() |
e4c36bbd7a | ||
![]() |
0ee1dab2bc | ||
![]() |
47e3792105 | ||
![]() |
0040aca841 | ||
![]() |
41a822c34c | ||
![]() |
55c217da73 | ||
![]() |
ff85376306 | ||
![]() |
ef15b9c0d3 | ||
![]() |
abefddb90e | ||
![]() |
3fcee7c4bd | ||
![]() |
d18750cb97 | ||
![]() |
6c53d1fdb7 | ||
![]() |
e9355bf918 |
@@ -6,7 +6,7 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers-contrib/features/pnpm:2": {},
|
"ghcr.io/devcontainers-contrib/features/pnpm:2": {},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "20.3.1"
|
"version": "18.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
|
@@ -6,10 +6,6 @@ indent_size = 2
|
|||||||
charset = utf-8
|
charset = utf-8
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
[*.{yml,yaml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
2
.github/ISSUE_TEMPLATE/01_bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/01_bug-report.md
vendored
@@ -54,7 +54,7 @@ Please include errors from the developer console and/or server log files if you
|
|||||||
|
|
||||||
* Installation Method or Hosting Service: <!-- Example: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment -->
|
* Installation Method or Hosting Service: <!-- Example: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment -->
|
||||||
* Misskey: 13.x.x
|
* Misskey: 13.x.x
|
||||||
* Node: 20.x.x
|
* Node: 18.x.x
|
||||||
* PostgreSQL: 15.x.x
|
* PostgreSQL: 15.x.x
|
||||||
* Redis: 7.x.x
|
* Redis: 7.x.x
|
||||||
* OS and Architecture: <!-- Example: Ubuntu 22.04.2 LTS aarch64 -->
|
* OS and Architecture: <!-- Example: Ubuntu 22.04.2 LTS aarch64 -->
|
||||||
|
2
.github/workflows/storybook.yml
vendored
2
.github/workflows/storybook.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js 20.x
|
- name: Use Node.js 18.x
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
|
2
.github/workflows/test-backend.yml
vendored
2
.github/workflows/test-backend.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [20.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
4
.github/workflows/test-frontend.yml
vendored
4
.github/workflows/test-frontend.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [20.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.3.0
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [20.x]
|
node-version: [18.x]
|
||||||
browser: [chrome]
|
browser: [chrome]
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
2
.github/workflows/test-misskey-js.yml
vendored
2
.github/workflows/test-misskey-js.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [20.x]
|
node-version: [18.x]
|
||||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
2
.github/workflows/test-production.yml
vendored
2
.github/workflows/test-production.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [20.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.3.0
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -64,6 +64,3 @@ temp
|
|||||||
*.blend3
|
*.blend3
|
||||||
*.blend4
|
*.blend4
|
||||||
*.blend5
|
*.blend5
|
||||||
|
|
||||||
# VSCode addon
|
|
||||||
.favorites.json
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
20.3.1
|
18.16.0
|
||||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@@ -19,22 +19,8 @@
|
|||||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- deck UIのカラムのメニューからアンテナとリストの編集画面を開けるように
|
|
||||||
- ドライブファイルのメニューで画像をクロップできるように
|
|
||||||
- 画像を動画と同様に簡単に隠せるように
|
|
||||||
- オリジナル画像を保持せずにアップロードする場合webpでアップロードされるように(Safari以外)
|
|
||||||
- 見たことのあるRenoteを省略して表示をオンのときに自分のnoteのrenoteを省略するように
|
|
||||||
- Fix: サーバーメトリクスが90度傾いている
|
- Fix: サーバーメトリクスが90度傾いている
|
||||||
- Fix: 非ログイン時にクレデンシャルが必要なページに行くとエラーが出る問題を修正
|
|
||||||
- Fix: sparkle内にリンクを入れるとクリック不能になる問題の修正
|
- Fix: sparkle内にリンクを入れるとクリック不能になる問題の修正
|
||||||
- Fix: ZenUIでポップアップの表示位置がおかしい問題を修正
|
|
||||||
- Fix: ページ遷移でスクロール位置が保持されない問題を修正
|
|
||||||
|
|
||||||
### Server
|
|
||||||
- JSON.parse の回数を削減することで、ストリーミングのパフォーマンスを向上しました
|
|
||||||
- nsfwjs のモデルロードを排他することで、重複ロードによってメモリ使用量が増加しないように
|
|
||||||
- 連合の配送ジョブのパフォーマンスを向上(ロック機構の見直し、Redisキャッシュの活用)
|
|
||||||
- 全体的なDBクエリのパフォーマンスを向上
|
|
||||||
|
|
||||||
## 13.13.2
|
## 13.13.2
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# syntax = docker/dockerfile:1.4
|
# syntax = docker/dockerfile:1.4
|
||||||
|
|
||||||
ARG NODE_VERSION=20.3.1-bullseye
|
ARG NODE_VERSION=18.16.0-bullseye
|
||||||
|
|
||||||
# build assets & compile TypeScript
|
# build assets & compile TypeScript
|
||||||
|
|
||||||
|
8
locales/index.d.ts
vendored
8
locales/index.d.ts
vendored
@@ -139,10 +139,8 @@ export interface Locale {
|
|||||||
"suspendConfirm": string;
|
"suspendConfirm": string;
|
||||||
"unsuspendConfirm": string;
|
"unsuspendConfirm": string;
|
||||||
"selectList": string;
|
"selectList": string;
|
||||||
"editList": string;
|
|
||||||
"selectChannel": string;
|
"selectChannel": string;
|
||||||
"selectAntenna": string;
|
"selectAntenna": string;
|
||||||
"editAntenna": string;
|
|
||||||
"selectWidget": string;
|
"selectWidget": string;
|
||||||
"editWidgets": string;
|
"editWidgets": string;
|
||||||
"editWidgetsExit": string;
|
"editWidgetsExit": string;
|
||||||
@@ -316,7 +314,7 @@ export interface Locale {
|
|||||||
"rename": string;
|
"rename": string;
|
||||||
"avatar": string;
|
"avatar": string;
|
||||||
"banner": string;
|
"banner": string;
|
||||||
"displayOfSensitiveMedia": string;
|
"nsfw": string;
|
||||||
"whenServerDisconnected": string;
|
"whenServerDisconnected": string;
|
||||||
"disconnectedFromServer": string;
|
"disconnectedFromServer": string;
|
||||||
"reload": string;
|
"reload": string;
|
||||||
@@ -1070,7 +1068,6 @@ export interface Locale {
|
|||||||
"branding": string;
|
"branding": string;
|
||||||
"enableServerMachineStats": string;
|
"enableServerMachineStats": string;
|
||||||
"enableIdenticonGeneration": string;
|
"enableIdenticonGeneration": string;
|
||||||
"turnOffToImprovePerformance": string;
|
|
||||||
"_initialAccountSetting": {
|
"_initialAccountSetting": {
|
||||||
"accountCreated": string;
|
"accountCreated": string;
|
||||||
"letsStartAccountSetup": string;
|
"letsStartAccountSetup": string;
|
||||||
@@ -1531,7 +1528,6 @@ export interface Locale {
|
|||||||
"back": string;
|
"back": string;
|
||||||
"reduceFrequencyOfThisAd": string;
|
"reduceFrequencyOfThisAd": string;
|
||||||
"hide": string;
|
"hide": string;
|
||||||
"timezoneinfo": string;
|
|
||||||
};
|
};
|
||||||
"_forgotPassword": {
|
"_forgotPassword": {
|
||||||
"enterEmail": string;
|
"enterEmail": string;
|
||||||
@@ -1593,7 +1589,7 @@ export interface Locale {
|
|||||||
"morePatrons": string;
|
"morePatrons": string;
|
||||||
"patrons": string;
|
"patrons": string;
|
||||||
};
|
};
|
||||||
"_displayOfSensitiveMedia": {
|
"_nsfw": {
|
||||||
"respect": string;
|
"respect": string;
|
||||||
"ignore": string;
|
"ignore": string;
|
||||||
"force": string;
|
"force": string;
|
||||||
|
@@ -112,7 +112,7 @@ pinnedNote: "ピン留めされたノート"
|
|||||||
pinned: "ピン留め"
|
pinned: "ピン留め"
|
||||||
you: "あなた"
|
you: "あなた"
|
||||||
clickToShow: "クリックして表示"
|
clickToShow: "クリックして表示"
|
||||||
sensitive: "センシティブ"
|
sensitive: "閲覧注意"
|
||||||
add: "追加"
|
add: "追加"
|
||||||
reaction: "リアクション"
|
reaction: "リアクション"
|
||||||
reactions: "リアクション"
|
reactions: "リアクション"
|
||||||
@@ -120,8 +120,8 @@ reactionSetting: "ピッカーに表示するリアクション"
|
|||||||
reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
|
reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
|
||||||
rememberNoteVisibility: "公開範囲を記憶する"
|
rememberNoteVisibility: "公開範囲を記憶する"
|
||||||
attachCancel: "添付取り消し"
|
attachCancel: "添付取り消し"
|
||||||
markAsSensitive: "センシティブとして設定"
|
markAsSensitive: "閲覧注意にする"
|
||||||
unmarkAsSensitive: "センシティブを解除する"
|
unmarkAsSensitive: "閲覧注意を解除する"
|
||||||
enterFileName: "ファイル名を入力"
|
enterFileName: "ファイル名を入力"
|
||||||
mute: "ミュート"
|
mute: "ミュート"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
@@ -136,10 +136,8 @@ unblockConfirm: "ブロック解除しますか?"
|
|||||||
suspendConfirm: "凍結しますか?"
|
suspendConfirm: "凍結しますか?"
|
||||||
unsuspendConfirm: "解凍しますか?"
|
unsuspendConfirm: "解凍しますか?"
|
||||||
selectList: "リストを選択"
|
selectList: "リストを選択"
|
||||||
editList: "リストを編集"
|
|
||||||
selectChannel: "チャンネルを選択"
|
selectChannel: "チャンネルを選択"
|
||||||
selectAntenna: "アンテナを選択"
|
selectAntenna: "アンテナを選択"
|
||||||
editAntenna: "アンテナを編集"
|
|
||||||
selectWidget: "ウィジェットを選択"
|
selectWidget: "ウィジェットを選択"
|
||||||
editWidgets: "ウィジェットを編集"
|
editWidgets: "ウィジェットを編集"
|
||||||
editWidgetsExit: "編集を終了"
|
editWidgetsExit: "編集を終了"
|
||||||
@@ -313,7 +311,7 @@ copyUrl: "URLをコピー"
|
|||||||
rename: "名前を変更"
|
rename: "名前を変更"
|
||||||
avatar: "アイコン"
|
avatar: "アイコン"
|
||||||
banner: "バナー"
|
banner: "バナー"
|
||||||
displayOfSensitiveMedia: "センシティブなメディアの表示"
|
nsfw: "閲覧注意"
|
||||||
whenServerDisconnected: "サーバーとの接続が失われたとき"
|
whenServerDisconnected: "サーバーとの接続が失われたとき"
|
||||||
disconnectedFromServer: "サーバーから切断されました"
|
disconnectedFromServer: "サーバーから切断されました"
|
||||||
reload: "リロード"
|
reload: "リロード"
|
||||||
@@ -695,7 +693,7 @@ driveUsage: "ドライブ使用量"
|
|||||||
noCrawle: "クローラーによるインデックスを拒否"
|
noCrawle: "クローラーによるインデックスを拒否"
|
||||||
noCrawleDescription: "外部の検索エンジンにあなたのユーザーページ、ノート、Pagesなどのコンテンツを登録(インデックス)しないよう要求します。"
|
noCrawleDescription: "外部の検索エンジンにあなたのユーザーページ、ノート、Pagesなどのコンテンツを登録(インデックス)しないよう要求します。"
|
||||||
lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。"
|
lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。"
|
||||||
alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする"
|
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にする"
|
||||||
loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
|
loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
|
||||||
disableShowingAnimatedImages: "アニメーション画像を再生しない"
|
disableShowingAnimatedImages: "アニメーション画像を再生しない"
|
||||||
verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
|
verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
|
||||||
@@ -922,8 +920,8 @@ cannotUploadBecauseInappropriate: "不適切な内容を含む可能性がある
|
|||||||
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いためアップロードできません。"
|
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いためアップロードできません。"
|
||||||
cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズの制限を超えているためアップロードできません。"
|
cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズの制限を超えているためアップロードできません。"
|
||||||
beta: "ベータ"
|
beta: "ベータ"
|
||||||
enableAutoSensitive: "自動センシティブ判定"
|
enableAutoSensitive: "自動NSFW判定"
|
||||||
enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにセンシティブフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。"
|
enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにNSFWフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。"
|
||||||
activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかなどを判定しより積極的に行います。オフにすると単に文字列として正しいかどうかのみチェックされます。"
|
activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかなどを判定しより積極的に行います。オフにすると単に文字列として正しいかどうかのみチェックされます。"
|
||||||
navbar: "ナビゲーションバー"
|
navbar: "ナビゲーションバー"
|
||||||
shuffle: "シャッフル"
|
shuffle: "シャッフル"
|
||||||
@@ -1067,7 +1065,6 @@ installed: "インストール済み"
|
|||||||
branding: "ブランディング"
|
branding: "ブランディング"
|
||||||
enableServerMachineStats: "サーバーのマシン情報を公開する"
|
enableServerMachineStats: "サーバーのマシン情報を公開する"
|
||||||
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
|
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
|
||||||
turnOffToImprovePerformance: "オフにするとパフォーマンスが向上します。"
|
|
||||||
|
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "アカウントの作成が完了しました!"
|
accountCreated: "アカウントの作成が完了しました!"
|
||||||
@@ -1417,7 +1414,7 @@ _sensitiveMediaDetection:
|
|||||||
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
|
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
|
||||||
sensitivity: "検出感度"
|
sensitivity: "検出感度"
|
||||||
sensitivityDescription: "感度を低くすると、誤検知(偽陽性)が減ります。感度を高くすると、検知漏れ(偽陰性)が減ります。"
|
sensitivityDescription: "感度を低くすると、誤検知(偽陽性)が減ります。感度を高くすると、検知漏れ(偽陰性)が減ります。"
|
||||||
setSensitiveFlagAutomatically: "センシティブフラグを設定する"
|
setSensitiveFlagAutomatically: "NSFWフラグを設定する"
|
||||||
setSensitiveFlagAutomaticallyDescription: "この設定をオフにしても内部的に判定結果は保持されます。"
|
setSensitiveFlagAutomaticallyDescription: "この設定をオフにしても内部的に判定結果は保持されます。"
|
||||||
analyzeVideos: "動画の解析を有効化"
|
analyzeVideos: "動画の解析を有効化"
|
||||||
analyzeVideosDescription: "静止画に加えて動画も解析するようにします。サーバーの負荷が少し増えます。"
|
analyzeVideosDescription: "静止画に加えて動画も解析するようにします。サーバーの負荷が少し増えます。"
|
||||||
@@ -1451,7 +1448,6 @@ _ad:
|
|||||||
back: "戻る"
|
back: "戻る"
|
||||||
reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"
|
reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"
|
||||||
hide: "表示しない"
|
hide: "表示しない"
|
||||||
timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されます。"
|
|
||||||
|
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
|
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
|
||||||
@@ -1511,9 +1507,9 @@ _aboutMisskey:
|
|||||||
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
|
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
|
||||||
patrons: "支援者"
|
patrons: "支援者"
|
||||||
|
|
||||||
_displayOfSensitiveMedia:
|
_nsfw:
|
||||||
respect: "センシティブ設定されたメディアを隠す"
|
respect: "閲覧注意のメディアは隠す"
|
||||||
ignore: "センシティブ設定されたメディアを隠さない"
|
ignore: "閲覧注意のメディアを隠さない"
|
||||||
force: "常にメディアを隠す"
|
force: "常にメディアを隠す"
|
||||||
|
|
||||||
_instanceTicker:
|
_instanceTicker:
|
||||||
|
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "13.14.0-beta.1",
|
"version": "13.13.2",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"migrateandstart": "pnpm migrate && pnpm start",
|
"migrateandstart": "pnpm migrate && pnpm start",
|
||||||
"gulp": "pnpm exec gulp build",
|
"gulp": "pnpm exec gulp build",
|
||||||
"watch": "pnpm dev",
|
"watch": "pnpm dev",
|
||||||
"dev": "node ./scripts/dev.mjs",
|
"dev": "node ./scripts/dev.js",
|
||||||
"lint": "pnpm -r lint",
|
"lint": "pnpm -r lint",
|
||||||
"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
|
"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
|
||||||
"cy:run": "pnpm cypress run",
|
"cy:run": "pnpm cypress run",
|
||||||
@@ -44,23 +44,23 @@
|
|||||||
"lodash": "4.17.21"
|
"lodash": "4.17.21"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"execa": "7.1.1",
|
"execa": "5.1.1",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-replace": "1.1.4",
|
"gulp-replace": "1.1.4",
|
||||||
"gulp-terser": "2.1.0",
|
"gulp-terser": "2.1.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"typescript": "5.1.6"
|
"typescript": "5.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/gulp": "4.0.10",
|
"@types/gulp": "4.0.10",
|
||||||
"@types/gulp-rename": "2.0.1",
|
"@types/gulp-rename": "2.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "5.61.0",
|
"@typescript-eslint/eslint-plugin": "5.60.0",
|
||||||
"@typescript-eslint/parser": "5.61.0",
|
"@typescript-eslint/parser": "5.60.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "12.17.0",
|
"cypress": "12.15.0",
|
||||||
"eslint": "8.44.0",
|
"eslint": "8.43.0",
|
||||||
"start-server-and-test": "2.0.0"
|
"start-server-and-test": "2.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
export class ad1677054292210 {
|
|
||||||
name = 'ad1677054292210';
|
|
||||||
async up(queryRunner) {
|
|
||||||
await queryRunner.query(`ALTER TABLE "ad" ADD "dayOfWeek" integer NOT NULL Default 0`);
|
|
||||||
}
|
|
||||||
async down(queryRunner) {
|
|
||||||
await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "dayOfWeek"`);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,9 +3,6 @@
|
|||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
|
||||||
"node": ">=18.16.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./built/index.js",
|
"start": "node ./built/index.js",
|
||||||
"start:test": "NODE_ENV=test node ./built/index.js",
|
"start:test": "NODE_ENV=test node ./built/index.js",
|
||||||
@@ -54,12 +51,12 @@
|
|||||||
"utf-8-validate": "^6.0.3"
|
"utf-8-validate": "^6.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.367.0",
|
"@aws-sdk/client-s3": "3.321.1",
|
||||||
"@aws-sdk/lib-storage": "3.367.0",
|
"@aws-sdk/lib-storage": "3.321.1",
|
||||||
"@aws-sdk/node-http-handler": "3.360.0",
|
"@aws-sdk/node-http-handler": "3.321.1",
|
||||||
"@bull-board/api": "5.6.0",
|
"@bull-board/api": "5.5.3",
|
||||||
"@bull-board/fastify": "5.6.0",
|
"@bull-board/fastify": "5.5.3",
|
||||||
"@bull-board/ui": "5.6.0",
|
"@bull-board/ui": "5.5.3",
|
||||||
"@discordapp/twemoji": "14.1.2",
|
"@discordapp/twemoji": "14.1.2",
|
||||||
"@fastify/accepts": "4.2.0",
|
"@fastify/accepts": "4.2.0",
|
||||||
"@fastify/cookie": "8.3.0",
|
"@fastify/cookie": "8.3.0",
|
||||||
@@ -67,22 +64,21 @@
|
|||||||
"@fastify/http-proxy": "9.2.1",
|
"@fastify/http-proxy": "9.2.1",
|
||||||
"@fastify/multipart": "7.7.0",
|
"@fastify/multipart": "7.7.0",
|
||||||
"@fastify/static": "6.10.2",
|
"@fastify/static": "6.10.2",
|
||||||
"@fastify/view": "8.0.0",
|
"@fastify/view": "7.4.1",
|
||||||
"@nestjs/common": "10.0.5",
|
"@nestjs/common": "10.0.3",
|
||||||
"@nestjs/core": "10.0.5",
|
"@nestjs/core": "10.0.3",
|
||||||
"@nestjs/testing": "10.0.5",
|
"@nestjs/testing": "10.0.3",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@sinonjs/fake-timers": "10.3.0",
|
"@sinonjs/fake-timers": "10.3.0",
|
||||||
"@swc/cli": "0.1.62",
|
"@swc/cli": "0.1.62",
|
||||||
"@swc/core": "1.3.68",
|
"@swc/core": "1.3.66",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "5.3.1",
|
"archiver": "5.3.1",
|
||||||
"async-mutex": "^0.4.0",
|
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"bullmq": "4.2.0",
|
"bullmq": "4.1.0",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.0",
|
"cbor": "9.0.0",
|
||||||
"chalk": "5.2.0",
|
"chalk": "5.2.0",
|
||||||
@@ -94,18 +90,18 @@
|
|||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"fastify": "4.19.2",
|
"fastify": "4.18.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "18.5.0",
|
"file-type": "18.5.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"got": "13.0.0",
|
"got": "13.0.0",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "9.20.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"ip-cidr": "3.1.0",
|
"ip-cidr": "3.1.0",
|
||||||
"ipaddr.js": "2.1.0",
|
"ipaddr.js": "2.1.0",
|
||||||
"is-svg": "5.0.0",
|
"is-svg": "4.3.2",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "22.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
@@ -122,9 +118,9 @@
|
|||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "2.4.2",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.1.3",
|
"otpauth": "9.1.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.1",
|
"pg": "8.11.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
"pug": "3.0.2",
|
"pug": "3.0.2",
|
||||||
@@ -148,14 +144,14 @@
|
|||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"summaly": "github:misskey-dev/summaly",
|
"summaly": "github:misskey-dev/summaly",
|
||||||
"systeminformation": "5.18.6",
|
"systeminformation": "5.18.4",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"tsc-alias": "1.8.7",
|
"tsc-alias": "1.8.6",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"twemoji-parser": "14.0.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"typeorm": "0.3.17",
|
"typeorm": "0.3.17",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.1.3",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"unzipper": "0.10.14",
|
"unzipper": "0.10.14",
|
||||||
"uuid": "9.0.0",
|
"uuid": "9.0.0",
|
||||||
@@ -165,7 +161,7 @@
|
|||||||
"xev": "3.0.2"
|
"xev": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.6.1",
|
"@jest/globals": "29.5.0",
|
||||||
"@swc/jest": "0.2.26",
|
"@swc/jest": "0.2.26",
|
||||||
"@types/accepts": "1.3.5",
|
"@types/accepts": "1.3.5",
|
||||||
"@types/archiver": "5.3.2",
|
"@types/archiver": "5.3.2",
|
||||||
@@ -182,14 +178,14 @@
|
|||||||
"@types/jsrsasign": "10.5.8",
|
"@types/jsrsasign": "10.5.8",
|
||||||
"@types/mime-types": "2.1.1",
|
"@types/mime-types": "2.1.1",
|
||||||
"@types/ms": "^0.7.31",
|
"@types/ms": "^0.7.31",
|
||||||
"@types/node": "20.4.0",
|
"@types/node": "20.3.1",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.8",
|
"@types/nodemailer": "6.4.8",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
"@types/pg": "8.10.2",
|
"@types/pg": "8.10.2",
|
||||||
"@types/pug": "2.0.6",
|
"@types/pug": "2.0.6",
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
"@types/qrcode": "1.5.1",
|
"@types/qrcode": "1.5.0",
|
||||||
"@types/random-seed": "0.3.3",
|
"@types/random-seed": "0.3.3",
|
||||||
"@types/ratelimiter": "3.4.4",
|
"@types/ratelimiter": "3.4.4",
|
||||||
"@types/redis": "4.0.11",
|
"@types/redis": "4.0.11",
|
||||||
@@ -206,14 +202,15 @@
|
|||||||
"@types/web-push": "3.3.2",
|
"@types/web-push": "3.3.2",
|
||||||
"@types/websocket": "1.0.5",
|
"@types/websocket": "1.0.5",
|
||||||
"@types/ws": "8.5.5",
|
"@types/ws": "8.5.5",
|
||||||
"@typescript-eslint/eslint-plugin": "5.61.0",
|
"@typescript-eslint/eslint-plugin": "5.60.0",
|
||||||
"@typescript-eslint/parser": "5.61.0",
|
"@typescript-eslint/parser": "5.60.0",
|
||||||
"aws-sdk-client-mock": "3.0.0",
|
"aws-sdk-client-mock": "2.1.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.44.0",
|
"eslint": "8.43.0",
|
||||||
"eslint-plugin-import": "2.27.5",
|
"eslint-plugin-import": "2.27.5",
|
||||||
"execa": "7.1.1",
|
"execa": "6.1.0",
|
||||||
"jest": "29.6.1",
|
"jest": "29.5.0",
|
||||||
"jest-mock": "29.6.1"
|
"jest-mock": "29.5.0",
|
||||||
|
"schema-type": "github:misskey-dev/schema-type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -96,6 +96,12 @@ function showNodejsVersion(): void {
|
|||||||
const nodejsLogger = bootLogger.createSubLogger('nodejs');
|
const nodejsLogger = bootLogger.createSubLogger('nodejs');
|
||||||
|
|
||||||
nodejsLogger.info(`Version ${process.version} detected.`);
|
nodejsLogger.info(`Version ${process.version} detected.`);
|
||||||
|
|
||||||
|
const minVersion = fs.readFileSync(`${_dirname}/../../../../.node-version`, 'utf-8').trim();
|
||||||
|
if (semver.lt(process.version, minVersion)) {
|
||||||
|
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadConfigBoot(): Config {
|
function loadConfigBoot(): Config {
|
||||||
|
@@ -4,83 +4,7 @@ import type { User } from '@/models/entities/User.js';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { NotificationService } from '@/core/NotificationService.js';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
|
import { ACHIEVEMENT_TYPES } from 'misskey-js';
|
||||||
export const ACHIEVEMENT_TYPES = [
|
|
||||||
'notes1',
|
|
||||||
'notes10',
|
|
||||||
'notes100',
|
|
||||||
'notes500',
|
|
||||||
'notes1000',
|
|
||||||
'notes5000',
|
|
||||||
'notes10000',
|
|
||||||
'notes20000',
|
|
||||||
'notes30000',
|
|
||||||
'notes40000',
|
|
||||||
'notes50000',
|
|
||||||
'notes60000',
|
|
||||||
'notes70000',
|
|
||||||
'notes80000',
|
|
||||||
'notes90000',
|
|
||||||
'notes100000',
|
|
||||||
'login3',
|
|
||||||
'login7',
|
|
||||||
'login15',
|
|
||||||
'login30',
|
|
||||||
'login60',
|
|
||||||
'login100',
|
|
||||||
'login200',
|
|
||||||
'login300',
|
|
||||||
'login400',
|
|
||||||
'login500',
|
|
||||||
'login600',
|
|
||||||
'login700',
|
|
||||||
'login800',
|
|
||||||
'login900',
|
|
||||||
'login1000',
|
|
||||||
'passedSinceAccountCreated1',
|
|
||||||
'passedSinceAccountCreated2',
|
|
||||||
'passedSinceAccountCreated3',
|
|
||||||
'loggedInOnBirthday',
|
|
||||||
'loggedInOnNewYearsDay',
|
|
||||||
'noteClipped1',
|
|
||||||
'noteFavorited1',
|
|
||||||
'myNoteFavorited1',
|
|
||||||
'profileFilled',
|
|
||||||
'markedAsCat',
|
|
||||||
'following1',
|
|
||||||
'following10',
|
|
||||||
'following50',
|
|
||||||
'following100',
|
|
||||||
'following300',
|
|
||||||
'followers1',
|
|
||||||
'followers10',
|
|
||||||
'followers50',
|
|
||||||
'followers100',
|
|
||||||
'followers300',
|
|
||||||
'followers500',
|
|
||||||
'followers1000',
|
|
||||||
'collectAchievements30',
|
|
||||||
'viewAchievements3min',
|
|
||||||
'iLoveMisskey',
|
|
||||||
'foundTreasure',
|
|
||||||
'client30min',
|
|
||||||
'client60min',
|
|
||||||
'noteDeletedWithin1min',
|
|
||||||
'postedAtLateNight',
|
|
||||||
'postedAt0min0sec',
|
|
||||||
'selfQuote',
|
|
||||||
'htl20npm',
|
|
||||||
'viewInstanceChart',
|
|
||||||
'outputHelloWorldOnScratchpad',
|
|
||||||
'open3windows',
|
|
||||||
'driveFolderCircularReference',
|
|
||||||
'reactWithoutRead',
|
|
||||||
'clickedClickHere',
|
|
||||||
'justPlainLucky',
|
|
||||||
'setNameToSyuilo',
|
|
||||||
'cookieClicked',
|
|
||||||
'brainDiver',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AchievementService {
|
export class AchievementService {
|
||||||
|
@@ -4,7 +4,6 @@ import { dirname } from 'node:path';
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as nsfw from 'nsfwjs';
|
import * as nsfw from 'nsfwjs';
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { Mutex } from 'async-mutex';
|
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@@ -18,7 +17,6 @@ let isSupportedCpu: undefined | boolean = undefined;
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AiService {
|
export class AiService {
|
||||||
private model: nsfw.NSFWJS;
|
private model: nsfw.NSFWJS;
|
||||||
private modelLoadMutex: Mutex = new Mutex();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
@@ -41,13 +39,7 @@ export class AiService {
|
|||||||
|
|
||||||
const tf = await import('@tensorflow/tfjs-node');
|
const tf = await import('@tensorflow/tfjs-node');
|
||||||
|
|
||||||
if (this.model == null) {
|
if (this.model == null) this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
|
||||||
await this.modelLoadMutex.runExclusive(async () => {
|
|
||||||
if (this.model == null) {
|
|
||||||
this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = await fs.promises.readFile(path);
|
const buffer = await fs.promises.readFile(path);
|
||||||
const image = await tf.node.decodeImage(buffer, 3) as any;
|
const image = await tf.node.decodeImage(buffer, 3) as any;
|
||||||
|
@@ -10,7 +10,7 @@ import { isUserRelated } from '@/misc/is-user-related.js';
|
|||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
||||||
import * as Acct from '@/misc/acct.js';
|
import * as Acct from '@/misc/acct.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, NotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js';
|
import type { MutingsRepository, NotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
|
@@ -32,6 +32,11 @@ export class AppLockService {
|
|||||||
return this.lock(`ap-object:${uri}`, timeout);
|
return this.lock(`ap-object:${uri}`, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> {
|
||||||
|
return this.lock(`instance:${host}`, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
|
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
|
||||||
return this.lock(`chart-insert:${lockKey}`, timeout);
|
return this.lock(`chart-insert:${lockKey}`, timeout);
|
||||||
|
@@ -3,6 +3,8 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { Instance } from '@/models/entities/Instance.js';
|
||||||
|
import type { InstancesRepository } from '@/models/index.js';
|
||||||
|
import { AppLockService } from '@/core/AppLockService.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
@@ -10,7 +12,6 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import type { DOMWindow } from 'jsdom';
|
import type { DOMWindow } from 'jsdom';
|
||||||
import * as Redis from 'ioredis';
|
|
||||||
|
|
||||||
type NodeInfo = {
|
type NodeInfo = {
|
||||||
openRegistrations?: unknown;
|
openRegistrations?: unknown;
|
||||||
@@ -36,43 +37,33 @@ export class FetchInstanceMetadataService {
|
|||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(DI.instancesRepository)
|
||||||
|
private instancesRepository: InstancesRepository,
|
||||||
|
|
||||||
|
private appLockService: AppLockService,
|
||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
private loggerService: LoggerService,
|
private loggerService: LoggerService,
|
||||||
private federatedInstanceService: FederatedInstanceService,
|
private federatedInstanceService: FederatedInstanceService,
|
||||||
@Inject(DI.redis)
|
|
||||||
private redisClient: Redis.Redis,
|
|
||||||
) {
|
) {
|
||||||
this.logger = this.loggerService.getLogger('metadata', 'cyan');
|
this.logger = this.loggerService.getLogger('metadata', 'cyan');
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async tryLock(host: string): Promise<boolean> {
|
|
||||||
const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET');
|
|
||||||
return mutex !== '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public unlock(host: string): Promise<'OK'> {
|
|
||||||
return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> {
|
public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> {
|
||||||
const host = instance.host;
|
const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host);
|
||||||
// Acquire mutex to ensure no parallel runs
|
|
||||||
if (!await this.tryLock(host)) return;
|
if (!force) {
|
||||||
try {
|
const _instance = await this.instancesRepository.findOneBy({ host: instance.host });
|
||||||
if (!force) {
|
const now = Date.now();
|
||||||
const _instance = await this.federatedInstanceService.fetch(host);
|
if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
|
||||||
const now = Date.now();
|
unlock();
|
||||||
if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
|
return;
|
||||||
// unlock at the finally caluse
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.info(`Fetching metadata of ${instance.host} ...`);
|
this.logger.info(`Fetching metadata of ${instance.host} ...`);
|
||||||
|
|
||||||
|
try {
|
||||||
const [info, dom, manifest] = await Promise.all([
|
const [info, dom, manifest] = await Promise.all([
|
||||||
this.fetchNodeinfo(instance).catch(() => null),
|
this.fetchNodeinfo(instance).catch(() => null),
|
||||||
this.fetchDom(instance).catch(() => null),
|
this.fetchDom(instance).catch(() => null),
|
||||||
@@ -113,7 +104,7 @@ export class FetchInstanceMetadataService {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
|
this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
|
||||||
} finally {
|
} finally {
|
||||||
await this.unlock(host);
|
unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -304,11 +304,11 @@ export class FileInfoService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public fixMime(mime: string | fileType.MimeType): string {
|
public fixMime(mime: string | fileType.MimeType): string {
|
||||||
// see https://github.com/misskey-dev/misskey/pull/10686
|
// see https://github.com/misskey-dev/misskey/pull/10686
|
||||||
if (mime === 'audio/x-flac') {
|
if (mime === "audio/x-flac") {
|
||||||
return 'audio/flac';
|
return "audio/flac";
|
||||||
}
|
}
|
||||||
if (mime === 'audio/vnd.wave') {
|
if (mime === "audio/vnd.wave") {
|
||||||
return 'audio/wav';
|
return "audio/wav";
|
||||||
}
|
}
|
||||||
|
|
||||||
return mime;
|
return mime;
|
||||||
@@ -355,12 +355,11 @@ export class FileInfoService {
|
|||||||
* Check the file is SVG or not
|
* Check the file is SVG or not
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async checkSvg(path: string): Promise<boolean> {
|
public async checkSvg(path: string) {
|
||||||
try {
|
try {
|
||||||
const size = await this.getFileSize(path);
|
const size = await this.getFileSize(path);
|
||||||
if (size > 1 * 1024 * 1024) return false;
|
if (size > 1 * 1024 * 1024) return false;
|
||||||
const buffer = await fs.promises.readFile(path);
|
return isSvg(fs.readFileSync(path));
|
||||||
return isSvg(buffer.toString());
|
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ import type {
|
|||||||
UserListStreamTypes,
|
UserListStreamTypes,
|
||||||
RoleTimelineStreamTypes,
|
RoleTimelineStreamTypes,
|
||||||
} from '@/server/api/stream/types.js';
|
} from '@/server/api/stream/types.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -121,8 +121,10 @@ export class NoteDeleteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async findCascadingNotes(note: Note): Promise<Note[]> {
|
private async findCascadingNotes(note: Note) {
|
||||||
const recursive = async (noteId: string): Promise<Note[]> => {
|
const cascadingNotes: Note[] = [];
|
||||||
|
|
||||||
|
const recursive = async (noteId: string) => {
|
||||||
const query = this.notesRepository.createQueryBuilder('note')
|
const query = this.notesRepository.createQueryBuilder('note')
|
||||||
.where('note.replyId = :noteId', { noteId })
|
.where('note.replyId = :noteId', { noteId })
|
||||||
.orWhere(new Brackets(q => {
|
.orWhere(new Brackets(q => {
|
||||||
@@ -131,14 +133,12 @@ export class NoteDeleteService {
|
|||||||
}))
|
}))
|
||||||
.leftJoinAndSelect('note.user', 'user');
|
.leftJoinAndSelect('note.user', 'user');
|
||||||
const replies = await query.getMany();
|
const replies = await query.getMany();
|
||||||
|
for (const reply of replies) {
|
||||||
return [
|
cascadingNotes.push(reply);
|
||||||
replies,
|
await recursive(reply.id);
|
||||||
...await Promise.all(replies.map(reply => recursive(reply.id))),
|
}
|
||||||
].flat();
|
|
||||||
};
|
};
|
||||||
|
await recursive(note.id);
|
||||||
const cascadingNotes: Note[] = await recursive(note.id);
|
|
||||||
|
|
||||||
return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users
|
return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
|||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
@@ -3,7 +3,7 @@ import push from 'web-push';
|
|||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { getNoteSummary } from '@/misc/get-note-summary.js';
|
import { getNoteSummary } from '@/misc/get-note-summary.js';
|
||||||
import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
|
import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
@@ -8,7 +8,7 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
|
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
|
||||||
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
|
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
|
||||||
import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
|
import type { DbJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
|
||||||
import type httpSignature from '@peertube/http-signature';
|
import type httpSignature from '@peertube/http-signature';
|
||||||
import type * as Bull from 'bullmq';
|
import type * as Bull from 'bullmq';
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export class QueueService {
|
|||||||
if (content == null) return null;
|
if (content == null) return null;
|
||||||
if (to == null) return null;
|
if (to == null) return null;
|
||||||
|
|
||||||
const data: DeliverJobData = {
|
const data = {
|
||||||
user: {
|
user: {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
},
|
},
|
||||||
@@ -88,40 +88,6 @@ export class QueueService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ApDeliverManager-DeliverManager.execute()からinboxesを突っ込んでaddBulkしたい
|
|
||||||
* @param user `{ id: string; }` この関数ではThinUserに変換しないので前もって変換してください
|
|
||||||
* @param content IActivity | null
|
|
||||||
* @param inboxes `Map<string, boolean>` / key: to (inbox url), value: isSharedInbox (whether it is sharedInbox)
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
@bindThis
|
|
||||||
public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>) {
|
|
||||||
if (content == null) return null;
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
attempts: this.config.deliverJobMaxAttempts ?? 12,
|
|
||||||
backoff: {
|
|
||||||
type: 'custom',
|
|
||||||
},
|
|
||||||
removeOnComplete: true,
|
|
||||||
removeOnFail: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.deliverQueue.addBulk(Array.from(inboxes.entries()).map(d => ({
|
|
||||||
name: d[0],
|
|
||||||
data: {
|
|
||||||
user,
|
|
||||||
content,
|
|
||||||
to: d[0],
|
|
||||||
isSharedInbox: d[1],
|
|
||||||
} as DeliverJobData,
|
|
||||||
opts,
|
|
||||||
})));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {
|
public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {
|
||||||
const data = {
|
const data = {
|
||||||
|
@@ -8,12 +8,12 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
import type { RoleCondFormulaValue } from '@/models/entities/Role.js';
|
import type { RoleCondFormulaValue } from 'misskey-js/built/schemas/role.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
export type RolePolicies = {
|
export type RolePolicies = {
|
||||||
|
@@ -174,7 +174,7 @@ export class SearchService {
|
|||||||
if (me) this.queryService.generateMutedUserQuery(query, me);
|
if (me) this.queryService.generateMutedUserQuery(query, me);
|
||||||
if (me) this.queryService.generateBlockedUserQuery(query, me);
|
if (me) this.queryService.generateBlockedUserQuery(query, me);
|
||||||
|
|
||||||
return await query.limit(pagination.limit).getMany();
|
return await query.take(pagination.limit).getMany();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -107,7 +107,10 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
if (this.userEntityService.isLocalUser(followee)) {
|
if (this.userEntityService.isLocalUser(followee)) {
|
||||||
this.userEntityService.pack(followee, followee, {
|
this.userEntityService.pack(followee, followee, {
|
||||||
detail: true,
|
detail: true,
|
||||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
}).then(packed => {
|
||||||
|
this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed);
|
||||||
|
return packed; // somehow this is needed by typescript
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
||||||
@@ -122,6 +125,8 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return packed; // somehow this is needed by typescript
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
|||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import InstanceChart from '@/core/chart/charts/instance.js';
|
import InstanceChart from '@/core/chart/charts/instance.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import { WebhookService } from '@/core/WebhookService.js';
|
import { WebhookService } from '@/core/WebhookService.js';
|
||||||
@@ -267,7 +267,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
this.userEntityService.pack(followee.id, follower, {
|
this.userEntityService.pack(followee.id, follower, {
|
||||||
detail: true,
|
detail: true,
|
||||||
}).then(async packed => {
|
}).then(async packed => {
|
||||||
this.globalEventService.publishMainStream(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>);
|
this.globalEventService.publishMainStream(follower.id, 'follow', packed);
|
||||||
|
|
||||||
const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
import escapeRegexp from 'escape-regexp';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
@@ -55,18 +56,25 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public parseUri(value: string | IObject): UriParseResult {
|
public parseUri(value: string | IObject): UriParseResult {
|
||||||
const separator = '/';
|
const uri = getApId(value);
|
||||||
|
|
||||||
const uri = new URL(getApId(value));
|
// the host part of a URL is case insensitive, so use the 'i' flag.
|
||||||
if (uri.origin !== this.config.url) return { local: false, uri: uri.href };
|
const localRegex = new RegExp('^' + escapeRegexp(this.config.url) + '/(\\w+)/(\\w+)(?:\/(.+))?', 'i');
|
||||||
|
const matchLocal = uri.match(localRegex);
|
||||||
|
|
||||||
const [, type, id, ...rest] = uri.pathname.split(separator);
|
if (matchLocal) {
|
||||||
return {
|
return {
|
||||||
local: true,
|
local: true,
|
||||||
type,
|
type: matchLocal[1],
|
||||||
id,
|
id: matchLocal[2],
|
||||||
rest: rest.length === 0 ? undefined : rest.join(separator),
|
rest: matchLocal[3],
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
local: false,
|
||||||
|
uri,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -7,8 +7,6 @@ import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
|||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { IActivity } from '@/core/activitypub/type.js';
|
|
||||||
import { ThinUser } from '@/queue/types.js';
|
|
||||||
|
|
||||||
interface IRecipe {
|
interface IRecipe {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -23,10 +21,10 @@ interface IDirectRecipe extends IRecipe {
|
|||||||
to: RemoteUser;
|
to: RemoteUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
|
const isFollowers = (recipe: any): recipe is IFollowersRecipe =>
|
||||||
recipe.type === 'Followers';
|
recipe.type === 'Followers';
|
||||||
|
|
||||||
const isDirect = (recipe: IRecipe): recipe is IDirectRecipe =>
|
const isDirect = (recipe: any): recipe is IDirectRecipe =>
|
||||||
recipe.type === 'Direct';
|
recipe.type === 'Direct';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -48,11 +46,11 @@ export class ApDeliverManagerService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deliver activity to followers
|
* Deliver activity to followers
|
||||||
* @param actor
|
|
||||||
* @param activity Activity
|
* @param activity Activity
|
||||||
|
* @param from Followee
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: IActivity) {
|
public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: any) {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@@ -66,12 +64,11 @@ export class ApDeliverManagerService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deliver activity to user
|
* Deliver activity to user
|
||||||
* @param actor
|
|
||||||
* @param activity Activity
|
* @param activity Activity
|
||||||
* @param to Target user
|
* @param to Target user
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: IActivity, to: RemoteUser) {
|
public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: any, to: RemoteUser) {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@@ -84,7 +81,7 @@ export class ApDeliverManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createDeliverManager(actor: { id: User['id']; host: null; }, activity: IActivity | null) {
|
public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) {
|
||||||
return new DeliverManager(
|
return new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@@ -97,15 +94,12 @@ export class ApDeliverManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DeliverManager {
|
class DeliverManager {
|
||||||
private actor: ThinUser;
|
private actor: { id: User['id']; host: null; };
|
||||||
private activity: IActivity | null;
|
private activity: any;
|
||||||
private recipes: IRecipe[] = [];
|
private recipes: IRecipe[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param userEntityService
|
|
||||||
* @param followingsRepository
|
|
||||||
* @param queueService
|
|
||||||
* @param actor Actor
|
* @param actor Actor
|
||||||
* @param activity Activity to deliver
|
* @param activity Activity to deliver
|
||||||
*/
|
*/
|
||||||
@@ -115,15 +109,9 @@ class DeliverManager {
|
|||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
|
|
||||||
actor: { id: User['id']; host: null; },
|
actor: { id: User['id']; host: null; },
|
||||||
activity: IActivity | null,
|
activity: any,
|
||||||
) {
|
) {
|
||||||
// 型で弾いてはいるが一応ローカルユーザーかチェック
|
this.actor = actor;
|
||||||
if (actor.host != null) throw new Error('actor.host must be null');
|
|
||||||
|
|
||||||
// パフォーマンス向上のためキューに突っ込むのはidのみに絞る
|
|
||||||
this.actor = {
|
|
||||||
id: actor.id,
|
|
||||||
};
|
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +155,9 @@ class DeliverManager {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async execute() {
|
public async execute() {
|
||||||
|
if (!this.userEntityService.isLocalUser(this.actor)) return;
|
||||||
|
|
||||||
// The value flags whether it is shared or not.
|
// The value flags whether it is shared or not.
|
||||||
// key: inbox URL, value: whether it is sharedInbox
|
|
||||||
const inboxes = new Map<string, boolean>();
|
const inboxes = new Map<string, boolean>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -212,6 +201,9 @@ class DeliverManager {
|
|||||||
.forEach(recipe => inboxes.set(recipe.to.inbox!, false));
|
.forEach(recipe => inboxes.set(recipe.to.inbox!, false));
|
||||||
|
|
||||||
// deliver
|
// deliver
|
||||||
this.queueService.deliverMany(this.actor, this.activity, inboxes);
|
for (const inbox of inboxes) {
|
||||||
|
// inbox[0]: inbox, inbox[1]: whether it is sharedInbox
|
||||||
|
this.queueService.deliver(this.actor, this.activity, inbox[0], inbox[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,10 +10,9 @@ import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js';
|
|||||||
import { DriveService } from '@/core/DriveService.js';
|
import { DriveService } from '@/core/DriveService.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { checkHttps } from '@/misc/check-https.js';
|
|
||||||
import { ApResolverService } from '../ApResolverService.js';
|
import { ApResolverService } from '../ApResolverService.js';
|
||||||
import { ApLoggerService } from '../ApLoggerService.js';
|
import { ApLoggerService } from '../ApLoggerService.js';
|
||||||
import type { IObject } from '../type.js';
|
import { checkHttps } from '@/misc/check-https.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApImageService {
|
export class ApImageService {
|
||||||
@@ -38,22 +37,18 @@ export class ApImageService {
|
|||||||
* Imageを作成します。
|
* Imageを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> {
|
public async createImage(actor: RemoteUser, value: any): Promise<DriveFile> {
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
throw new Error('actor has been suspended');
|
throw new Error('actor has been suspended');
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = await this.apResolverService.createResolver().resolve(value);
|
const image = await this.apResolverService.createResolver().resolve(value) as any;
|
||||||
|
|
||||||
if (image.url == null) {
|
if (image.url == null) {
|
||||||
throw new Error('invalid image: url not privided');
|
throw new Error('invalid image: url not privided');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof image.url !== 'string') {
|
|
||||||
throw new Error('invalid image: unexpected type of url: ' + JSON.stringify(image.url, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkHttps(image.url)) {
|
if (!checkHttps(image.url)) {
|
||||||
throw new Error('invalid image: unexpected schema of url: ' + image.url);
|
throw new Error('invalid image: unexpected schema of url: ' + image.url);
|
||||||
}
|
}
|
||||||
@@ -62,19 +57,29 @@ export class ApImageService {
|
|||||||
|
|
||||||
const instance = await this.metaService.fetch();
|
const instance = await this.metaService.fetch();
|
||||||
|
|
||||||
const file = await this.driveService.uploadFromUrl({
|
let file = await this.driveService.uploadFromUrl({
|
||||||
url: image.url,
|
url: image.url,
|
||||||
user: actor,
|
user: actor,
|
||||||
uri: image.url,
|
uri: image.url,
|
||||||
sensitive: image.sensitive,
|
sensitive: image.sensitive,
|
||||||
isLink: !instance.cacheRemoteFiles,
|
isLink: !instance.cacheRemoteFiles,
|
||||||
comment: truncate(image.name ?? undefined, DB_MAX_IMAGE_COMMENT_LENGTH),
|
comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH),
|
||||||
});
|
});
|
||||||
if (!file.isLink || file.url === image.url) return file;
|
|
||||||
|
|
||||||
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、URLを更新する
|
if (file.isLink) {
|
||||||
await this.driveFilesRepository.update({ id: file.id }, { url: image.url, uri: image.url });
|
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
|
||||||
return await this.driveFilesRepository.findOneByOrFail({ id: file.id });
|
// URLを更新する
|
||||||
|
if (file.url !== image.url) {
|
||||||
|
await this.driveFilesRepository.update({ id: file.id }, {
|
||||||
|
url: image.url,
|
||||||
|
uri: image.url,
|
||||||
|
});
|
||||||
|
|
||||||
|
file = await this.driveFilesRepository.findOneByOrFail({ id: file.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +89,7 @@ export class ApImageService {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> {
|
public async resolveImage(actor: RemoteUser, value: any): Promise<DriveFile> {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// リモートサーバーからフェッチしてきて登録
|
||||||
|
@@ -22,8 +22,8 @@ export class ApMentionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<User[]> {
|
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) {
|
||||||
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href));
|
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string));
|
||||||
|
|
||||||
const limit = promiseLimit<User | null>(2);
|
const limit = promiseLimit<User | null>(2);
|
||||||
const mentionedUsers = (await Promise.all(
|
const mentionedUsers = (await Promise.all(
|
||||||
|
@@ -20,6 +20,7 @@ import { UtilityService } from '@/core/UtilityService.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { checkHttps } from '@/misc/check-https.js';
|
import { checkHttps } from '@/misc/check-https.js';
|
||||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||||
import { ApLoggerService } from '../ApLoggerService.js';
|
import { ApLoggerService } from '../ApLoggerService.js';
|
||||||
import { ApMfmService } from '../ApMfmService.js';
|
import { ApMfmService } from '../ApMfmService.js';
|
||||||
import { ApDbResolverService } from '../ApDbResolverService.js';
|
import { ApDbResolverService } from '../ApDbResolverService.js';
|
||||||
@@ -71,9 +72,13 @@ export class ApNoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public validateNote(object: IObject, uri: string): Error | null {
|
public validateNote(object: IObject, uri: string) {
|
||||||
const expectHost = this.utilityService.extractDbHost(uri);
|
const expectHost = this.utilityService.extractDbHost(uri);
|
||||||
|
|
||||||
|
if (object == null) {
|
||||||
|
return new Error('invalid Note: object is null');
|
||||||
|
}
|
||||||
|
|
||||||
if (!validPost.includes(getApType(object))) {
|
if (!validPost.includes(getApType(object))) {
|
||||||
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
|
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
|
||||||
}
|
}
|
||||||
@@ -105,7 +110,6 @@ export class ApNoteService {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
const object = await resolver.resolve(value);
|
const object = await resolver.resolve(value);
|
||||||
@@ -113,10 +117,12 @@ export class ApNoteService {
|
|||||||
const entryUri = getApId(value);
|
const entryUri = getApId(value);
|
||||||
const err = this.validateNote(object, entryUri);
|
const err = this.validateNote(object, entryUri);
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.error(err.message, {
|
this.logger.error(`${err.message}`, {
|
||||||
resolver: { history: resolver.getHistory() },
|
resolver: {
|
||||||
value,
|
history: resolver.getHistory(),
|
||||||
object,
|
},
|
||||||
|
value: value,
|
||||||
|
object: object,
|
||||||
});
|
});
|
||||||
throw new Error('invalid note');
|
throw new Error('invalid note');
|
||||||
}
|
}
|
||||||
@@ -138,11 +144,7 @@ export class ApNoteService {
|
|||||||
this.logger.info(`Creating the Note: ${note.id}`);
|
this.logger.info(`Creating the Note: ${note.id}`);
|
||||||
|
|
||||||
// 投稿者をフェッチ
|
// 投稿者をフェッチ
|
||||||
if (note.attributedTo == null) {
|
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo!), resolver) as RemoteUser;
|
||||||
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser;
|
|
||||||
|
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
@@ -162,49 +164,59 @@ export class ApNoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
||||||
const apHashtags = extractApHashtags(note.tag);
|
const apHashtags = await extractApHashtags(note.tag);
|
||||||
|
|
||||||
// 添付ファイル
|
// 添付ファイル
|
||||||
// TODO: attachmentは必ずしもImageではない
|
// TODO: attachmentは必ずしもImageではない
|
||||||
// TODO: attachmentは必ずしも配列ではない
|
// TODO: attachmentは必ずしも配列ではない
|
||||||
const limit = promiseLimit<DriveFile>(2);
|
// Noteがsensitiveなら添付もsensitiveにする
|
||||||
const files = (await Promise.all(toArray(note.attachment).map(attach => (
|
const limit = promiseLimit(2);
|
||||||
limit(() => this.apImageService.resolveImage(actor, {
|
|
||||||
...attach,
|
note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : [];
|
||||||
sensitive: note.sensitive, // Noteがsensitiveなら添付もsensitiveにする
|
const files = note.attachment
|
||||||
}))
|
.map(attach => attach.sensitive = note.sensitive)
|
||||||
))));
|
? (await Promise.all(note.attachment.map(x => limit(() => this.apImageService.resolveImage(actor, x)) as Promise<DriveFile>)))
|
||||||
|
.filter(image => image != null)
|
||||||
|
: [];
|
||||||
|
|
||||||
// リプライ
|
// リプライ
|
||||||
const reply: Note | null = note.inReplyTo
|
const reply: Note | null = note.inReplyTo
|
||||||
? await this.resolveNote(note.inReplyTo, resolver)
|
? await this.resolveNote(note.inReplyTo, resolver).then(x => {
|
||||||
.then(x => {
|
if (x == null) {
|
||||||
if (x == null) {
|
this.logger.warn('Specified inReplyTo, but not found');
|
||||||
this.logger.warn('Specified inReplyTo, but not found');
|
throw new Error('inReplyTo not found');
|
||||||
throw new Error('inReplyTo not found');
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
})
|
}
|
||||||
.catch(async err => {
|
}).catch(async err => {
|
||||||
this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`);
|
this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`);
|
||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// 引用
|
// 引用
|
||||||
let quote: Note | undefined | null = null;
|
let quote: Note | undefined | null;
|
||||||
|
|
||||||
if (note._misskey_quote || note.quoteUrl) {
|
if (note._misskey_quote || note.quoteUrl) {
|
||||||
const tryResolveNote = async (uri: string): Promise<
|
const tryResolveNote = async (uri: string): Promise<{
|
||||||
| { status: 'ok'; res: Note }
|
status: 'ok';
|
||||||
| { status: 'permerror' | 'temperror' }
|
res: Note | null;
|
||||||
> => {
|
} | {
|
||||||
if (!uri.match(/^https?:/)) return { status: 'permerror' };
|
status: 'permerror' | 'temperror';
|
||||||
|
}> => {
|
||||||
|
if (typeof uri !== 'string' || !uri.match(/^https?:/)) return { status: 'permerror' };
|
||||||
try {
|
try {
|
||||||
const res = await this.resolveNote(uri);
|
const res = await this.resolveNote(uri);
|
||||||
if (res == null) return { status: 'permerror' };
|
if (res) {
|
||||||
return { status: 'ok', res };
|
return {
|
||||||
|
status: 'ok',
|
||||||
|
res,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 'permerror',
|
||||||
|
};
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror',
|
status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror',
|
||||||
@@ -213,9 +225,9 @@ export class ApNoteService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
|
const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
|
||||||
const results = await Promise.all(uris.map(tryResolveNote));
|
const results = await Promise.all(uris.map(uri => tryResolveNote(uri)));
|
||||||
|
|
||||||
quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0);
|
quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x);
|
||||||
if (!quote) {
|
if (!quote) {
|
||||||
if (results.some(x => x.status === 'temperror')) {
|
if (results.some(x => x.status === 'temperror')) {
|
||||||
throw new Error('quote resolve failed');
|
throw new Error('quote resolve failed');
|
||||||
@@ -259,7 +271,7 @@ export class ApNoteService {
|
|||||||
|
|
||||||
const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => {
|
const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => {
|
||||||
this.logger.info(`extractEmojis: ${e}`);
|
this.logger.info(`extractEmojis: ${e}`);
|
||||||
return [];
|
return [] as Emoji[];
|
||||||
});
|
});
|
||||||
|
|
||||||
const apEmojis = emojis.map(emoji => emoji.name);
|
const apEmojis = emojis.map(emoji => emoji.name);
|
||||||
@@ -297,18 +309,19 @@ export class ApNoteService {
|
|||||||
const uri = typeof value === 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
if (uri == null) throw new Error('missing uri');
|
if (uri == null) throw new Error('missing uri');
|
||||||
|
|
||||||
// ブロックしていたら中断
|
// ブロックしてたら中断
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) {
|
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) throw new StatusError('blocked host', 451);
|
||||||
throw new StatusError('blocked host', 451);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unlock = await this.appLockService.getApLock(uri);
|
const unlock = await this.appLockService.getApLock(uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await this.fetchNote(uri);
|
const exist = await this.fetchNote(uri);
|
||||||
if (exist) return exist;
|
|
||||||
|
if (exist) {
|
||||||
|
return exist;
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (uri.startsWith(this.config.url)) {
|
if (uri.startsWith(this.config.url)) {
|
||||||
@@ -326,41 +339,43 @@ export class ApNoteService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> {
|
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> {
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
host = this.utilityService.toPuny(host);
|
host = this.utilityService.toPuny(host);
|
||||||
|
|
||||||
|
if (!tags) return [];
|
||||||
|
|
||||||
const eomjiTags = toArray(tags).filter(isEmoji);
|
const eomjiTags = toArray(tags).filter(isEmoji);
|
||||||
|
|
||||||
const existingEmojis = await this.emojisRepository.findBy({
|
const existingEmojis = await this.emojisRepository.findBy({
|
||||||
host,
|
host,
|
||||||
name: In(eomjiTags.map(tag => tag.name.replaceAll(':', ''))),
|
name: In(eomjiTags.map(tag => tag.name!.replaceAll(':', ''))),
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Promise.all(eomjiTags.map(async tag => {
|
return await Promise.all(eomjiTags.map(async tag => {
|
||||||
const name = tag.name.replaceAll(':', '');
|
const name = tag.name!.replaceAll(':', '');
|
||||||
tag.icon = toSingle(tag.icon);
|
tag.icon = toSingle(tag.icon);
|
||||||
|
|
||||||
const exists = existingEmojis.find(x => x.name === name);
|
const exists = existingEmojis.find(x => x.name === name);
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
if ((exists.updatedAt == null)
|
if ((tag.updated != null && exists.updatedAt == null)
|
||||||
|| (tag.id != null && exists.uri == null)
|
|| (tag.id != null && exists.uri == null)
|
||||||
|| (new Date(tag.updated) > exists.updatedAt)
|
|| (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt)
|
||||||
|| (tag.icon.url !== exists.originalUrl)
|
|| (tag.icon!.url !== exists.originalUrl)
|
||||||
) {
|
) {
|
||||||
await this.emojisRepository.update({
|
await this.emojisRepository.update({
|
||||||
host,
|
host,
|
||||||
name,
|
name,
|
||||||
}, {
|
}, {
|
||||||
uri: tag.id,
|
uri: tag.id,
|
||||||
originalUrl: tag.icon.url,
|
originalUrl: tag.icon!.url,
|
||||||
publicUrl: tag.icon.url,
|
publicUrl: tag.icon!.url,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const emoji = await this.emojisRepository.findOneBy({ host, name });
|
return await this.emojisRepository.findOneBy({
|
||||||
if (emoji == null) throw new Error('emoji update failed');
|
host,
|
||||||
return emoji;
|
name,
|
||||||
|
}) as Emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
return exists;
|
return exists;
|
||||||
@@ -373,11 +388,11 @@ export class ApNoteService {
|
|||||||
host,
|
host,
|
||||||
name,
|
name,
|
||||||
uri: tag.id,
|
uri: tag.id,
|
||||||
originalUrl: tag.icon.url,
|
originalUrl: tag.icon!.url,
|
||||||
publicUrl: tag.icon.url,
|
publicUrl: tag.icon!.url,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
aliases: [],
|
aliases: [],
|
||||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
} as Partial<Emoji>).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import promiseLimit from 'promise-limit';
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
import type { BlockingsRepository, MutingsRepository, FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
@@ -15,6 +15,7 @@ import type Logger from '@/logger.js';
|
|||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import type { IdService } from '@/core/IdService.js';
|
import type { IdService } from '@/core/IdService.js';
|
||||||
import type { MfmService } from '@/core/MfmService.js';
|
import type { MfmService } from '@/core/MfmService.js';
|
||||||
|
import type { Emoji } from '@/models/entities/Emoji.js';
|
||||||
import { toArray } from '@/misc/prelude/array.js';
|
import { toArray } from '@/misc/prelude/array.js';
|
||||||
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
@@ -47,8 +48,6 @@ import type { IActor, IObject } from '../type.js';
|
|||||||
const nameLength = 128;
|
const nameLength = 128;
|
||||||
const summaryLength = 2048;
|
const summaryLength = 2048;
|
||||||
|
|
||||||
type Field = Record<'name' | 'value', string>;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApPersonService implements OnModuleInit {
|
export class ApPersonService implements OnModuleInit {
|
||||||
private utilityService: UtilityService;
|
private utilityService: UtilityService;
|
||||||
@@ -95,10 +94,28 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
|
|
||||||
@Inject(DI.followingsRepository)
|
@Inject(DI.followingsRepository)
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
|
|
||||||
|
//private utilityService: UtilityService,
|
||||||
|
//private userEntityService: UserEntityService,
|
||||||
|
//private idService: IdService,
|
||||||
|
//private globalEventService: GlobalEventService,
|
||||||
|
//private metaService: MetaService,
|
||||||
|
//private federatedInstanceService: FederatedInstanceService,
|
||||||
|
//private fetchInstanceMetadataService: FetchInstanceMetadataService,
|
||||||
|
//private cacheService: CacheService,
|
||||||
|
//private apResolverService: ApResolverService,
|
||||||
|
//private apNoteService: ApNoteService,
|
||||||
|
//private apImageService: ApImageService,
|
||||||
|
//private apMfmService: ApMfmService,
|
||||||
|
//private mfmService: MfmService,
|
||||||
|
//private hashtagService: HashtagService,
|
||||||
|
//private usersChart: UsersChart,
|
||||||
|
//private instanceChart: InstanceChart,
|
||||||
|
//private apLoggerService: ApLoggerService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onModuleInit(): void {
|
onModuleInit() {
|
||||||
this.utilityService = this.moduleRef.get('UtilityService');
|
this.utilityService = this.moduleRef.get('UtilityService');
|
||||||
this.userEntityService = this.moduleRef.get('UserEntityService');
|
this.userEntityService = this.moduleRef.get('UserEntityService');
|
||||||
this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
|
this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
|
||||||
@@ -136,6 +153,10 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
private validateActor(x: IObject, uri: string): IActor {
|
private validateActor(x: IObject, uri: string): IActor {
|
||||||
const expectHost = this.punyHost(uri);
|
const expectHost = this.punyHost(uri);
|
||||||
|
|
||||||
|
if (x == null) {
|
||||||
|
throw new Error('invalid Actor: object is null');
|
||||||
|
}
|
||||||
|
|
||||||
if (!isActor(x)) {
|
if (!isActor(x)) {
|
||||||
throw new Error(`invalid Actor type '${x.type}'`);
|
throw new Error(`invalid Actor type '${x.type}'`);
|
||||||
}
|
}
|
||||||
@@ -197,19 +218,21 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> {
|
public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> {
|
||||||
const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null | undefined;
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
|
const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null;
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならデータベースからフェッチ
|
// URIがこのサーバーを指しているならデータベースからフェッチ
|
||||||
if (uri.startsWith(`${this.config.url}/`)) {
|
if (uri.startsWith(`${this.config.url}/`)) {
|
||||||
const id = uri.split('/').pop();
|
const id = uri.split('/').pop();
|
||||||
const u = await this.usersRepository.findOneBy({ id }) as LocalUser | null;
|
const u = await this.usersRepository.findOneBy({ id }) as LocalUser;
|
||||||
if (u) this.cacheService.uriPersonCache.set(uri, u);
|
if (u) this.cacheService.uriPersonCache.set(uri, u);
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser | null;
|
const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser;
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
this.cacheService.uriPersonCache.set(uri, exist);
|
this.cacheService.uriPersonCache.set(uri, exist);
|
||||||
@@ -231,11 +254,9 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user');
|
throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user');
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
const object = await resolver.resolve(uri);
|
const object = await resolver.resolve(uri) as any;
|
||||||
if (object.id == null) throw new Error('invalid object.id: ' + object.id);
|
|
||||||
|
|
||||||
const person = this.validateActor(object, uri);
|
const person = this.validateActor(object, uri);
|
||||||
|
|
||||||
@@ -243,9 +264,9 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
|
|
||||||
const host = this.punyHost(object.id);
|
const host = this.punyHost(object.id);
|
||||||
|
|
||||||
const fields = this.analyzeAttachments(person.attachment ?? []);
|
const { fields } = this.analyzeAttachments(person.attachment ?? []);
|
||||||
|
|
||||||
const tags = extractApHashtags(person.tag).map(normalizeForSearch).splice(0, 32);
|
const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
|
||||||
|
|
||||||
const isBot = getApType(object) === 'Service';
|
const isBot = getApType(object) === 'Service';
|
||||||
|
|
||||||
@@ -258,7 +279,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user: RemoteUser | null = null;
|
let user: RemoteUser;
|
||||||
try {
|
try {
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
@@ -269,16 +290,16 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
lastFetchedAt: new Date(),
|
lastFetchedAt: new Date(),
|
||||||
name: truncate(person.name, nameLength),
|
name: truncate(person.name, nameLength),
|
||||||
isLocked: person.manuallyApprovesFollowers,
|
isLocked: !!person.manuallyApprovesFollowers,
|
||||||
movedToUri: person.movedTo,
|
movedToUri: person.movedTo,
|
||||||
movedAt: person.movedTo ? new Date() : null,
|
movedAt: person.movedTo ? new Date() : null,
|
||||||
alsoKnownAs: person.alsoKnownAs,
|
alsoKnownAs: person.alsoKnownAs,
|
||||||
isExplorable: person.discoverable,
|
isExplorable: !!person.discoverable,
|
||||||
username: person.preferredUsername,
|
username: person.preferredUsername,
|
||||||
usernameLower: person.preferredUsername?.toLowerCase(),
|
usernameLower: person.preferredUsername!.toLowerCase(),
|
||||||
host,
|
host,
|
||||||
inbox: person.inbox,
|
inbox: person.inbox,
|
||||||
sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox,
|
sharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
followersUri: person.followers ? getApId(person.followers) : undefined,
|
followersUri: person.followers ? getApId(person.followers) : undefined,
|
||||||
featured: person.featured ? getApId(person.featured) : undefined,
|
featured: person.featured ? getApId(person.featured) : undefined,
|
||||||
uri: person.id,
|
uri: person.id,
|
||||||
@@ -290,9 +311,9 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new UserProfile({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
||||||
url,
|
url: url,
|
||||||
fields,
|
fields,
|
||||||
birthday: bday?.[0] ?? null,
|
birthday: bday ? bday[0] : null,
|
||||||
location: person['vcard:Address'] ?? null,
|
location: person['vcard:Address'] ?? null,
|
||||||
userHost: host,
|
userHost: host,
|
||||||
}));
|
}));
|
||||||
@@ -309,18 +330,21 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
// duplicate key error
|
// duplicate key error
|
||||||
if (isDuplicateKeyValueError(e)) {
|
if (isDuplicateKeyValueError(e)) {
|
||||||
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
||||||
const u = await this.usersRepository.findOneBy({ uri: person.id });
|
const u = await this.usersRepository.findOneBy({
|
||||||
if (u == null) throw new Error('already registered');
|
uri: person.id,
|
||||||
|
});
|
||||||
|
|
||||||
user = u as RemoteUser;
|
if (u) {
|
||||||
|
user = u as RemoteUser;
|
||||||
|
} else {
|
||||||
|
throw new Error('already registered');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user == null) throw new Error('failed to create user: user is null');
|
|
||||||
|
|
||||||
// Register host
|
// Register host
|
||||||
this.federatedInstanceService.fetch(host).then(async i => {
|
this.federatedInstanceService.fetch(host).then(async i => {
|
||||||
this.instancesRepository.increment({ id: i.id }, 'usersCount', 1);
|
this.instancesRepository.increment({ id: i.id }, 'usersCount', 1);
|
||||||
@@ -330,26 +354,29 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.usersChart.update(user, true);
|
this.usersChart.update(user!, true);
|
||||||
|
|
||||||
// ハッシュタグ更新
|
// ハッシュタグ更新
|
||||||
this.hashtagService.updateUsertags(user, tags);
|
this.hashtagService.updateUsertags(user!, tags);
|
||||||
|
|
||||||
//#region アバターとヘッダー画像をフェッチ
|
//#region アバターとヘッダー画像をフェッチ
|
||||||
const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => {
|
const [avatar, banner] = await Promise.all([
|
||||||
if (img == null) return null;
|
person.icon,
|
||||||
if (user == null) throw new Error('failed to create user: user is null');
|
person.image,
|
||||||
return this.apImageService.resolveImage(user, img).catch(() => null);
|
].map(img =>
|
||||||
}));
|
img == null
|
||||||
|
? Promise.resolve(null)
|
||||||
|
: this.apImageService.resolveImage(user!, img).catch(() => null),
|
||||||
|
));
|
||||||
|
|
||||||
const avatarId = avatar?.id ?? null;
|
const avatarId = avatar ? avatar.id : null;
|
||||||
const bannerId = banner?.id ?? null;
|
const bannerId = banner ? banner.id : null;
|
||||||
const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null;
|
const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null;
|
||||||
const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null;
|
const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null;
|
||||||
const avatarBlurhash = avatar?.blurhash ?? null;
|
const avatarBlurhash = avatar ? avatar.blurhash : null;
|
||||||
const bannerBlurhash = banner?.blurhash ?? null;
|
const bannerBlurhash = banner ? banner.blurhash : null;
|
||||||
|
|
||||||
await this.usersRepository.update(user.id, {
|
await this.usersRepository.update(user!.id, {
|
||||||
avatarId,
|
avatarId,
|
||||||
bannerId,
|
bannerId,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
@@ -358,28 +385,30 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
bannerBlurhash,
|
bannerBlurhash,
|
||||||
});
|
});
|
||||||
|
|
||||||
user.avatarId = avatarId;
|
user!.avatarId = avatarId;
|
||||||
user.bannerId = bannerId;
|
user!.bannerId = bannerId;
|
||||||
user.avatarUrl = avatarUrl;
|
user!.avatarUrl = avatarUrl;
|
||||||
user.bannerUrl = bannerUrl;
|
user!.bannerUrl = bannerUrl;
|
||||||
user.avatarBlurhash = avatarBlurhash;
|
user!.avatarBlurhash = avatarBlurhash;
|
||||||
user.bannerBlurhash = bannerBlurhash;
|
user!.bannerBlurhash = bannerBlurhash;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region カスタム絵文字取得
|
//#region カスタム絵文字取得
|
||||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => {
|
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => {
|
||||||
this.logger.info(`extractEmojis: ${err}`);
|
this.logger.info(`extractEmojis: ${err}`);
|
||||||
return [];
|
return [] as Emoji[];
|
||||||
});
|
});
|
||||||
|
|
||||||
const emojiNames = emojis.map(emoji => emoji.name);
|
const emojiNames = emojis.map(emoji => emoji.name);
|
||||||
|
|
||||||
await this.usersRepository.update(user.id, { emojis: emojiNames });
|
await this.usersRepository.update(user!.id, {
|
||||||
|
emojis: emojiNames,
|
||||||
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
await this.updateFeatured(user.id, resolver).catch(err => this.logger.error(err));
|
await this.updateFeatured(user!.id, resolver).catch(err => this.logger.error(err));
|
||||||
|
|
||||||
return user;
|
return user!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,14 +426,18 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならスキップ
|
// URIがこのサーバーを指しているならスキップ
|
||||||
if (uri.startsWith(`${this.config.url}/`)) return;
|
if (uri.startsWith(`${this.config.url}/`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//#region このサーバーに既に登録されているか
|
//#region このサーバーに既に登録されているか
|
||||||
const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null;
|
const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null;
|
||||||
if (exist === null) return;
|
|
||||||
|
if (exist === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
const object = hint ?? await resolver.resolve(uri);
|
const object = hint ?? await resolver.resolve(uri);
|
||||||
@@ -414,22 +447,26 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
this.logger.info(`Updating the Person: ${person.id}`);
|
this.logger.info(`Updating the Person: ${person.id}`);
|
||||||
|
|
||||||
// アバターとヘッダー画像をフェッチ
|
// アバターとヘッダー画像をフェッチ
|
||||||
const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => {
|
const [avatar, banner] = await Promise.all([
|
||||||
if (img == null) return null;
|
person.icon,
|
||||||
return this.apImageService.resolveImage(exist, img).catch(() => null);
|
person.image,
|
||||||
}));
|
].map(img =>
|
||||||
|
img == null
|
||||||
|
? Promise.resolve(null)
|
||||||
|
: this.apImageService.resolveImage(exist, img).catch(() => null),
|
||||||
|
));
|
||||||
|
|
||||||
// カスタム絵文字取得
|
// カスタム絵文字取得
|
||||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => {
|
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => {
|
||||||
this.logger.info(`extractEmojis: ${e}`);
|
this.logger.info(`extractEmojis: ${e}`);
|
||||||
return [];
|
return [] as Emoji[];
|
||||||
});
|
});
|
||||||
|
|
||||||
const emojiNames = emojis.map(emoji => emoji.name);
|
const emojiNames = emojis.map(emoji => emoji.name);
|
||||||
|
|
||||||
const fields = this.analyzeAttachments(person.attachment ?? []);
|
const { fields } = this.analyzeAttachments(person.attachment ?? []);
|
||||||
|
|
||||||
const tags = extractApHashtags(person.tag).map(normalizeForSearch).splice(0, 32);
|
const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
|
||||||
|
|
||||||
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
|
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
|
||||||
|
|
||||||
@@ -442,7 +479,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
const updates = {
|
const updates = {
|
||||||
lastFetchedAt: new Date(),
|
lastFetchedAt: new Date(),
|
||||||
inbox: person.inbox,
|
inbox: person.inbox,
|
||||||
sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox,
|
sharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
followersUri: person.followers ? getApId(person.followers) : undefined,
|
followersUri: person.followers ? getApId(person.followers) : undefined,
|
||||||
featured: person.featured,
|
featured: person.featured,
|
||||||
emojis: emojiNames,
|
emojis: emojiNames,
|
||||||
@@ -450,29 +487,18 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
tags,
|
tags,
|
||||||
isBot: getApType(object) === 'Service',
|
isBot: getApType(object) === 'Service',
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
isLocked: person.manuallyApprovesFollowers,
|
isLocked: !!person.manuallyApprovesFollowers,
|
||||||
movedToUri: person.movedTo ?? null,
|
movedToUri: person.movedTo ?? null,
|
||||||
alsoKnownAs: person.alsoKnownAs ?? null,
|
alsoKnownAs: person.alsoKnownAs ?? null,
|
||||||
isExplorable: person.discoverable,
|
isExplorable: !!person.discoverable,
|
||||||
} as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
} as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
||||||
|
|
||||||
const moving = ((): boolean => {
|
const moving =
|
||||||
// 移行先がない→ある
|
// 移行先がない→ある
|
||||||
if (
|
(!exist.movedToUri && updates.movedToUri) ||
|
||||||
exist.movedToUri === null &&
|
|
||||||
updates.movedToUri
|
|
||||||
) return true;
|
|
||||||
|
|
||||||
// 移行先がある→別のもの
|
// 移行先がある→別のもの
|
||||||
if (
|
(exist.movedToUri !== updates.movedToUri && exist.movedToUri && updates.movedToUri);
|
||||||
exist.movedToUri !== null &&
|
|
||||||
updates.movedToUri !== null &&
|
|
||||||
exist.movedToUri !== updates.movedToUri
|
|
||||||
) return true;
|
|
||||||
|
|
||||||
// 移行先がある→ない、ない→ないは無視
|
// 移行先がある→ない、ない→ないは無視
|
||||||
return false;
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (moving) updates.movedAt = new Date();
|
if (moving) updates.movedAt = new Date();
|
||||||
|
|
||||||
@@ -499,10 +525,10 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.userProfilesRepository.update({ userId: exist.id }, {
|
await this.userProfilesRepository.update({ userId: exist.id }, {
|
||||||
url,
|
url: url,
|
||||||
fields,
|
fields,
|
||||||
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
||||||
birthday: bday?.[0] ?? null,
|
birthday: bday ? bday[0] : null,
|
||||||
location: person['vcard:Address'] ?? null,
|
location: person['vcard:Address'] ?? null,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -512,10 +538,11 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
this.hashtagService.updateUsertags(exist, tags);
|
this.hashtagService.updateUsertags(exist, tags);
|
||||||
|
|
||||||
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
||||||
await this.followingsRepository.update(
|
await this.followingsRepository.update({
|
||||||
{ followerId: exist.id },
|
followerId: exist.id,
|
||||||
{ followerSharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox },
|
}, {
|
||||||
);
|
followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
|
});
|
||||||
|
|
||||||
await this.updateFeatured(exist.id, resolver).catch(err => this.logger.error(err));
|
await this.updateFeatured(exist.id, resolver).catch(err => this.logger.error(err));
|
||||||
|
|
||||||
@@ -553,22 +580,27 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> {
|
public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> {
|
||||||
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await this.fetchPerson(uri);
|
const exist = await this.fetchPerson(uri);
|
||||||
if (exist) return exist;
|
|
||||||
|
if (exist) {
|
||||||
|
return exist;
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// リモートサーバーからフェッチしてきて登録
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
return await this.createPerson(uri, resolver);
|
return await this.createPerson(uri, resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
// TODO: `attachments`が`IObject`だった場合、返り値が`[]`になるようだが構わないのか?
|
public analyzeAttachments(attachments: IObject | IObject[] | undefined) {
|
||||||
public analyzeAttachments(attachments: IObject | IObject[] | undefined): Field[] {
|
const fields: {
|
||||||
const fields: Field[] = [];
|
name: string,
|
||||||
|
value: string
|
||||||
|
}[] = [];
|
||||||
if (Array.isArray(attachments)) {
|
if (Array.isArray(attachments)) {
|
||||||
for (const attachment of attachments.filter(isPropertyValue)) {
|
for (const attachment of attachments.filter(isPropertyValue)) {
|
||||||
fields.push({
|
fields.push({
|
||||||
@@ -578,11 +610,11 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields;
|
return { fields };
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateFeatured(userId: User['id'], resolver?: Resolver): Promise<void> {
|
public async updateFeatured(userId: User['id'], resolver?: Resolver) {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
||||||
if (!this.userEntityService.isRemoteUser(user)) return;
|
if (!this.userEntityService.isRemoteUser(user)) return;
|
||||||
if (!user.featured) return;
|
if (!user.featured) return;
|
||||||
@@ -611,13 +643,13 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
|
|
||||||
// とりあえずidを別の時間で生成して順番を維持
|
// とりあえずidを別の時間で生成して順番を維持
|
||||||
let td = 0;
|
let td = 0;
|
||||||
for (const note of featuredNotes.filter((note): note is Note => note != null)) {
|
for (const note of featuredNotes.filter(note => note != null)) {
|
||||||
td -= 1000;
|
td -= 1000;
|
||||||
transactionalEntityManager.insert(UserNotePining, {
|
transactionalEntityManager.insert(UserNotePining, {
|
||||||
id: this.idService.genId(new Date(Date.now() + td)),
|
id: this.idService.genId(new Date(Date.now() + td)),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
noteId: note.id,
|
noteId: note!.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -4,12 +4,12 @@ import type { NotesRepository, PollsRepository } from '@/models/index.js';
|
|||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { IPoll } from '@/models/entities/Poll.js';
|
import type { IPoll } from '@/models/entities/Poll.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
import { isQuestion } from '../type.js';
|
import { isQuestion } from '../type.js';
|
||||||
import { ApLoggerService } from '../ApLoggerService.js';
|
import { ApLoggerService } from '../ApLoggerService.js';
|
||||||
import { ApResolverService } from '../ApResolverService.js';
|
import { ApResolverService } from '../ApResolverService.js';
|
||||||
import type { Resolver } from '../ApResolverService.js';
|
import type { Resolver } from '../ApResolverService.js';
|
||||||
import type { IObject, IQuestion } from '../type.js';
|
import type { IObject, IQuestion } from '../type.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApQuestionService {
|
export class ApQuestionService {
|
||||||
@@ -33,25 +33,33 @@ export class ApQuestionService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
|
public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
const question = await resolver.resolve(source);
|
const question = await resolver.resolve(source);
|
||||||
if (!isQuestion(question)) throw new Error('invalid type');
|
|
||||||
|
|
||||||
const multiple = question.oneOf === undefined;
|
if (!isQuestion(question)) {
|
||||||
if (multiple && question.anyOf === undefined) throw new Error('invalid question');
|
throw new Error('invalid type');
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiple = !question.oneOf;
|
||||||
const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null;
|
const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null;
|
||||||
|
|
||||||
const choices = question[multiple ? 'anyOf' : 'oneOf']
|
if (multiple && !question.anyOf) {
|
||||||
?.map((x) => x.name)
|
throw new Error('invalid question');
|
||||||
.filter((x): x is string => typeof x === 'string')
|
}
|
||||||
?? [];
|
|
||||||
|
|
||||||
const votes = question[multiple ? 'anyOf' : 'oneOf']?.map((x) => x.replies?.totalItems ?? x._misskey_votes ?? 0);
|
const choices = question[multiple ? 'anyOf' : 'oneOf']!
|
||||||
|
.map((x, i) => x.name!);
|
||||||
|
|
||||||
return { choices, votes, multiple, expiresAt };
|
const votes = question[multiple ? 'anyOf' : 'oneOf']!
|
||||||
|
.map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
choices,
|
||||||
|
votes,
|
||||||
|
multiple,
|
||||||
|
expiresAt,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,9 +68,8 @@ export class ApQuestionService {
|
|||||||
* @returns true if updated
|
* @returns true if updated
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateQuestion(value: string | IObject, resolver?: Resolver): Promise<boolean> {
|
public async updateQuestion(value: any, resolver?: Resolver) {
|
||||||
const uri = typeof value === 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
if (uri == null) throw new Error('uri is null');
|
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならスキップ
|
// URIがこのサーバーを指しているならスキップ
|
||||||
if (uri.startsWith(this.config.url + '/')) throw new Error('uri points local');
|
if (uri.startsWith(this.config.url + '/')) throw new Error('uri points local');
|
||||||
@@ -76,7 +83,6 @@ export class ApQuestionService {
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// resolve new Question object
|
// resolve new Question object
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
const question = await resolver.resolve(value) as IQuestion;
|
const question = await resolver.resolve(value) as IQuestion;
|
||||||
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
|
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
|
||||||
@@ -84,14 +90,12 @@ export class ApQuestionService {
|
|||||||
if (question.type !== 'Question') throw new Error('object is not a Question');
|
if (question.type !== 'Question') throw new Error('object is not a Question');
|
||||||
|
|
||||||
const apChoices = question.oneOf ?? question.anyOf;
|
const apChoices = question.oneOf ?? question.anyOf;
|
||||||
if (apChoices == null) throw new Error('invalid apChoices: ' + apChoices);
|
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
for (const choice of poll.choices) {
|
for (const choice of poll.choices) {
|
||||||
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
||||||
const newCount = apChoices.filter(ap => ap.name === choice).at(0)?.replies?.totalItems;
|
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
||||||
if (newCount == null) throw new Error('invalid newCount: ' + newCount);
|
|
||||||
|
|
||||||
if (oldCount !== newCount) {
|
if (oldCount !== newCount) {
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -99,7 +103,9 @@ export class ApQuestionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.pollsRepository.update({ noteId: note.id }, { votes: poll.votes });
|
await this.pollsRepository.update({ noteId: note.id }, {
|
||||||
|
votes: poll.votes,
|
||||||
|
});
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { toArray } from '@/misc/prelude/array.js';
|
|||||||
import { isHashtag } from '../type.js';
|
import { isHashtag } from '../type.js';
|
||||||
import type { IObject, IApHashtag } from '../type.js';
|
import type { IObject, IApHashtag } from '../type.js';
|
||||||
|
|
||||||
export function extractApHashtags(tags: IObject | IObject[] | null | undefined): string[] {
|
export function extractApHashtags(tags: IObject | IObject[] | null | undefined) {
|
||||||
if (tags == null) return [];
|
if (tags == null) return [];
|
||||||
|
|
||||||
const hashtags = extractApHashtagObjects(tags);
|
const hashtags = extractApHashtagObjects(tags);
|
||||||
|
@@ -1,17 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'activeUsers';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'activeUsers' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'readWrite': { intersection: ['read', 'write'] },
|
|
||||||
'read': { uniqueIncrement: true },
|
|
||||||
'write': { uniqueIncrement: true },
|
|
||||||
'registeredWithinWeek': { uniqueIncrement: true },
|
|
||||||
'registeredWithinMonth': { uniqueIncrement: true },
|
|
||||||
'registeredWithinYear': { uniqueIncrement: true },
|
|
||||||
'registeredOutsideWeek': { uniqueIncrement: true },
|
|
||||||
'registeredOutsideMonth': { uniqueIncrement: true },
|
|
||||||
'registeredOutsideYear': { uniqueIncrement: true },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'apRequest';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'apRequest' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'deliverFailed': { },
|
|
||||||
'deliverSucceeded': { },
|
|
||||||
'inboxReceived': { },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'drive';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'drive' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'local.incCount': {},
|
|
||||||
'local.incSize': {}, // in kilobyte
|
|
||||||
'local.decCount': {},
|
|
||||||
'local.decSize': {}, // in kilobyte
|
|
||||||
'remote.incCount': {},
|
|
||||||
'remote.incSize': {}, // in kilobyte
|
|
||||||
'remote.decCount': {},
|
|
||||||
'remote.decSize': {}, // in kilobyte
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'federation';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'federation' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'deliveredInstances': { uniqueIncrement: true, range: 'small' },
|
|
||||||
'inboxInstances': { uniqueIncrement: true, range: 'small' },
|
|
||||||
'stalled': { uniqueIncrement: true, range: 'small' },
|
|
||||||
'sub': { accumulate: true, range: 'small' },
|
|
||||||
'pub': { accumulate: true, range: 'small' },
|
|
||||||
'pubsub': { accumulate: true, range: 'small' },
|
|
||||||
'subActive': { accumulate: true, range: 'small' },
|
|
||||||
'pubActive': { accumulate: true, range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,32 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'instance';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'instance' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'requests.failed': { range: 'small' },
|
|
||||||
'requests.succeeded': { range: 'small' },
|
|
||||||
'requests.received': { range: 'small' },
|
|
||||||
'notes.total': { accumulate: true },
|
|
||||||
'notes.inc': {},
|
|
||||||
'notes.dec': {},
|
|
||||||
'notes.diffs.normal': {},
|
|
||||||
'notes.diffs.reply': {},
|
|
||||||
'notes.diffs.renote': {},
|
|
||||||
'notes.diffs.withFile': {},
|
|
||||||
'users.total': { accumulate: true },
|
|
||||||
'users.inc': { range: 'small' },
|
|
||||||
'users.dec': { range: 'small' },
|
|
||||||
'following.total': { accumulate: true },
|
|
||||||
'following.inc': { range: 'small' },
|
|
||||||
'following.dec': { range: 'small' },
|
|
||||||
'followers.total': { accumulate: true },
|
|
||||||
'followers.inc': { range: 'small' },
|
|
||||||
'followers.dec': { range: 'small' },
|
|
||||||
'drive.totalFiles': { accumulate: true },
|
|
||||||
'drive.incFiles': {},
|
|
||||||
'drive.decFiles': {},
|
|
||||||
'drive.incUsage': {}, // in kilobyte
|
|
||||||
'drive.decUsage': {}, // in kilobyte
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,22 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'notes';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'notes' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'local.total': { accumulate: true },
|
|
||||||
'local.inc': {},
|
|
||||||
'local.dec': {},
|
|
||||||
'local.diffs.normal': {},
|
|
||||||
'local.diffs.reply': {},
|
|
||||||
'local.diffs.renote': {},
|
|
||||||
'local.diffs.withFile': {},
|
|
||||||
'remote.total': { accumulate: true },
|
|
||||||
'remote.inc': {},
|
|
||||||
'remote.dec': {},
|
|
||||||
'remote.diffs.normal': {},
|
|
||||||
'remote.diffs.reply': {},
|
|
||||||
'remote.diffs.renote': {},
|
|
||||||
'remote.diffs.withFile': {},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,14 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'perUserDrive';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'perUserDrive' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'totalCount': { accumulate: true },
|
|
||||||
'totalSize': { accumulate: true }, // in kilobyte
|
|
||||||
'incCount': { range: 'small' },
|
|
||||||
'incSize': {}, // in kilobyte
|
|
||||||
'decCount': { range: 'small' },
|
|
||||||
'decSize': {}, // in kilobyte
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,20 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'perUserFollowing';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'perUserFollowing' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'local.followings.total': { accumulate: true },
|
|
||||||
'local.followings.inc': { range: 'small' },
|
|
||||||
'local.followings.dec': { range: 'small' },
|
|
||||||
'local.followers.total': { accumulate: true },
|
|
||||||
'local.followers.inc': { range: 'small' },
|
|
||||||
'local.followers.dec': { range: 'small' },
|
|
||||||
'remote.followings.total': { accumulate: true },
|
|
||||||
'remote.followings.inc': { range: 'small' },
|
|
||||||
'remote.followings.dec': { range: 'small' },
|
|
||||||
'remote.followers.total': { accumulate: true },
|
|
||||||
'remote.followers.inc': { range: 'small' },
|
|
||||||
'remote.followers.dec': { range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,15 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'perUserNotes';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'perUserNotes' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'total': { accumulate: true },
|
|
||||||
'inc': { range: 'small' },
|
|
||||||
'dec': { range: 'small' },
|
|
||||||
'diffs.normal': { range: 'small' },
|
|
||||||
'diffs.reply': { range: 'small' },
|
|
||||||
'diffs.renote': { range: 'small' },
|
|
||||||
'diffs.withFile': { range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,12 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'perUserPv';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'perUserPv' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'upv.user': { uniqueIncrement: true, range: 'small' },
|
|
||||||
'pv.user': { range: 'small' },
|
|
||||||
'upv.visitor': { uniqueIncrement: true, range: 'small' },
|
|
||||||
'pv.visitor': { range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
export const name = 'perUserReaction';
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
|
export const name = 'perUserReactions' as const;
|
||||||
export const schema = {
|
export const schema = chartsSchemas[name];
|
||||||
'local.count': { range: 'small' },
|
|
||||||
'remote.count': { range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
export const name = 'testGrouped';
|
export const name = 'testGrouped';
|
||||||
|
export const schema = chartsSchemas[name];
|
||||||
export const schema = {
|
|
||||||
'foo.total': { accumulate: true },
|
|
||||||
'foo.inc': {},
|
|
||||||
'foo.dec': {},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema, true);
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
export const name = 'testIntersection';
|
export const name = 'testIntersection';
|
||||||
|
export const schema = chartsSchemas[name];
|
||||||
export const schema = {
|
|
||||||
'a': { uniqueIncrement: true },
|
|
||||||
'b': { uniqueIncrement: true },
|
|
||||||
'aAndB': { intersection: ['a', 'b'] },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
export const name = 'testUnique';
|
export const name = 'testUnique';
|
||||||
|
export const schema = chartsSchemas[name];
|
||||||
export const schema = {
|
|
||||||
'foo': { uniqueIncrement: true },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
export const name = 'test';
|
export const name = 'test';
|
||||||
|
export const schema = chartsSchemas[name];
|
||||||
export const schema = {
|
|
||||||
'foo.total': { accumulate: true },
|
|
||||||
'foo.inc': {},
|
|
||||||
'foo.dec': {},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -1,14 +1,7 @@
|
|||||||
import Chart from '../../core.js';
|
import Chart from '../../core.js';
|
||||||
|
|
||||||
|
import { chartsSchemas } from 'misskey-js/built/schemas/charts.js';
|
||||||
export const name = 'users';
|
export const name = 'users';
|
||||||
|
export const schema = chartsSchemas[name];
|
||||||
export const schema = {
|
|
||||||
'local.total': { accumulate: true },
|
|
||||||
'local.inc': { range: 'small' },
|
|
||||||
'local.dec': { range: 'small' },
|
|
||||||
'remote.total': { accumulate: true },
|
|
||||||
'remote.inc': { range: 'small' },
|
|
||||||
'remote.dec': { range: 'small' },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const entity = Chart.schemaToEntity(name, schema);
|
export const entity = Chart.schemaToEntity(name, schema);
|
||||||
|
@@ -10,22 +10,12 @@ import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc
|
|||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { Repository, DataSource } from 'typeorm';
|
import type { Repository, DataSource } from 'typeorm';
|
||||||
|
import type { ChartSchema as Schema, ChartResult, Unflatten } from 'misskey-js/built/schemas';
|
||||||
|
|
||||||
const COLUMN_PREFIX = '___' as const;
|
const COLUMN_PREFIX = '___' as const;
|
||||||
const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const;
|
const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const;
|
||||||
const COLUMN_DELIMITER = '_' as const;
|
const COLUMN_DELIMITER = '_' as const;
|
||||||
|
|
||||||
type Schema = Record<string, {
|
|
||||||
uniqueIncrement?: boolean;
|
|
||||||
|
|
||||||
intersection?: string[] | ReadonlyArray<string>;
|
|
||||||
|
|
||||||
range?: 'big' | 'small' | 'medium';
|
|
||||||
|
|
||||||
// previousな値を引き継ぐかどうか
|
|
||||||
accumulate?: boolean;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type KeyToColumnName<T extends string> = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof COLUMN_DELIMITER}${KeyToColumnName<R2>}` : T;
|
type KeyToColumnName<T extends string> = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof COLUMN_DELIMITER}${KeyToColumnName<R2>}` : T;
|
||||||
|
|
||||||
type Columns<S extends Schema> = {
|
type Columns<S extends Schema> = {
|
||||||
@@ -64,47 +54,6 @@ export type KVs<S extends Schema> = {
|
|||||||
[K in keyof S]: number;
|
[K in keyof S]: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChartResult<T extends Schema> = {
|
|
||||||
[P in keyof T]: number[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
|
|
||||||
|
|
||||||
type UnflattenSingleton<K extends string, V> = K extends `${infer A}.${infer B}`
|
|
||||||
? { [_ in A]: UnflattenSingleton<B, V>; }
|
|
||||||
: { [_ in K]: V; };
|
|
||||||
|
|
||||||
type Unflatten<T extends Record<string, any>> = UnionToIntersection<
|
|
||||||
{
|
|
||||||
[K in Extract<keyof T, string>]: UnflattenSingleton<K, T[K]>;
|
|
||||||
}[Extract<keyof T, string>]
|
|
||||||
>;
|
|
||||||
|
|
||||||
type ToJsonSchema<S> = {
|
|
||||||
type: 'object';
|
|
||||||
properties: {
|
|
||||||
[K in keyof S]: S[K] extends number[] ? { type: 'array'; items: { type: 'number'; }; } : ToJsonSchema<S[K]>;
|
|
||||||
},
|
|
||||||
required: (keyof S)[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> {
|
|
||||||
const jsonSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {} as Record<string, unknown>,
|
|
||||||
required: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const k in schema) {
|
|
||||||
jsonSchema.properties[k] = {
|
|
||||||
type: 'array',
|
|
||||||
items: { type: 'number' },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 様々なチャートの管理を司るクラス
|
* 様々なチャートの管理を司るクラス
|
||||||
*/
|
*/
|
||||||
|
@@ -5,6 +5,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
|
|||||||
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
|
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AbuseUserReportEntityService {
|
export class AbuseUserReportEntityService {
|
||||||
@@ -19,7 +20,7 @@ export class AbuseUserReportEntityService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async pack(
|
public async pack(
|
||||||
src: AbuseUserReport['id'] | AbuseUserReport,
|
src: AbuseUserReport['id'] | AbuseUserReport,
|
||||||
) {
|
): Promise<Packed<'AbuseUserReport'>> {
|
||||||
const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
|
const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
@@ -46,7 +47,7 @@ export class AbuseUserReportEntityService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public packMany(
|
public packMany(
|
||||||
reports: any[],
|
reports: any[],
|
||||||
) {
|
): Promise<Packed<'AbuseUserReport'>[]> {
|
||||||
return Promise.all(reports.map(x => this.pack(x)));
|
return Promise.all(reports.map(x => this.pack(x)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { AntennasRepository } from '@/models/index.js';
|
import type { AntennasRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { Antenna } from '@/models/entities/Antenna.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { AccessTokensRepository, AppsRepository } from '@/models/index.js';
|
import type { AccessTokensRepository, AppsRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { App } from '@/models/entities/App.js';
|
import type { App } from '@/models/entities/App.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -6,6 +6,7 @@ import type { AuthSession } from '@/models/entities/AuthSession.js';
|
|||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import { AppEntityService } from './AppEntityService.js';
|
import { AppEntityService } from './AppEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthSessionEntityService {
|
export class AuthSessionEntityService {
|
||||||
@@ -21,7 +22,7 @@ export class AuthSessionEntityService {
|
|||||||
public async pack(
|
public async pack(
|
||||||
src: AuthSession['id'] | AuthSession,
|
src: AuthSession['id'] | AuthSession,
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
) {
|
): Promise<Packed<'AuthSession'>> {
|
||||||
const session = typeof src === 'object' ? src : await this.authSessionsRepository.findOneByOrFail({ id: src });
|
const session = typeof src === 'object' ? src : await this.authSessionsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
|
@@ -2,11 +2,12 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { BlockingsRepository } from '@/models/index.js';
|
import type { BlockingsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { Blocking } from '@/models/entities/Blocking.js';
|
import type { Blocking } from '@/models/entities/Blocking.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
|
import type { Serialized } from 'schema-type';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BlockingEntityService {
|
export class BlockingEntityService {
|
||||||
@@ -39,7 +40,7 @@ export class BlockingEntityService {
|
|||||||
public packMany(
|
public packMany(
|
||||||
blockings: any[],
|
blockings: any[],
|
||||||
me: { id: User['id'] },
|
me: { id: User['id'] },
|
||||||
) {
|
): Promise<Packed<'Blocking'>[]> {
|
||||||
return Promise.all(blockings.map(x => this.pack(x, me)));
|
return Promise.all(blockings.map(x => this.pack(x, me)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { ChannelFavoritesRepository, ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository, NotesRepository } from '@/models/index.js';
|
import type { ChannelFavoritesRepository, ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository, NotesRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Channel } from '@/models/entities/Channel.js';
|
import type { Channel } from '@/models/entities/Channel.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { ClipFavoritesRepository, ClipsRepository, User } from '@/models/index.js';
|
import type { ClipFavoritesRepository, ClipsRepository, User } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { Clip } from '@/models/entities/Clip.js';
|
import type { Clip } from '@/models/entities/Clip.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
@@ -47,7 +46,7 @@ export class ClipEntityService {
|
|||||||
public packMany(
|
public packMany(
|
||||||
clips: Clip[],
|
clips: Clip[],
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
) {
|
): Promise<Packed<'Clip'>[]> {
|
||||||
return Promise.all(clips.map(x => this.pack(x, me)));
|
return Promise.all(clips.map(x => this.pack(x, me)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import { DataSource, In } from 'typeorm';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NotesRepository, DriveFilesRepository } from '@/models/index.js';
|
import type { NotesRepository, DriveFilesRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
||||||
@@ -265,11 +265,11 @@ export class DriveFileEntityService {
|
|||||||
public async packManyByIdsMap(
|
public async packManyByIdsMap(
|
||||||
fileIds: DriveFile['id'][],
|
fileIds: DriveFile['id'][],
|
||||||
options?: PackOptions,
|
options?: PackOptions,
|
||||||
): Promise<Map<Packed<'DriveFile'>['id'], Packed<'DriveFile'> | null>> {
|
): Promise<Map<Packed<'DriveFile'>>['id'], Serialized<Packed<'DriveFile'> | null>> {
|
||||||
if (fileIds.length === 0) return new Map();
|
if (fileIds.length === 0) return new Map();
|
||||||
const files = await this.driveFilesRepository.findBy({ id: In(fileIds) });
|
const files = await this.driveFilesRepository.findBy({ id: In(fileIds) });
|
||||||
const packedFiles = await this.packMany(files, options);
|
const packedFiles = await this.packMany(files, options);
|
||||||
const map = new Map<Packed<'DriveFile'>['id'], Packed<'DriveFile'> | null>(packedFiles.map(f => [f.id, f]));
|
const map = new Map<Packed<'DriveFile'>>['id'], Serialized<Packed<'DriveFile'> | null>(packedFiles.map(f => [f.id, f]));
|
||||||
for (const id of fileIds) {
|
for (const id of fileIds) {
|
||||||
if (!map.has(id)) map.set(id, null);
|
if (!map.has(id)) map.set(id, null);
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js';
|
import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
|
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { EmojisRepository } from '@/models/index.js';
|
import type { EmojisRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { Emoji } from '@/models/entities/Emoji.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js';
|
import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Flash } from '@/models/entities/Flash.js';
|
import type { Flash } from '@/models/entities/Flash.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FlashLikesRepository } from '@/models/index.js';
|
import type { FlashLikesRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { FlashLike } from '@/models/entities/FlashLike.js';
|
import type { FlashLike } from '@/models/entities/FlashLike.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowRequestsRepository } from '@/models/index.js';
|
import type { FollowRequestsRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { FollowRequest } from '@/models/entities/FollowRequest.js';
|
import type { FollowRequest } from '@/models/entities/FollowRequest.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { Packed } from 'misskey-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FollowRequestEntityService {
|
export class FollowRequestEntityService {
|
||||||
@@ -21,7 +21,7 @@ export class FollowRequestEntityService {
|
|||||||
public async pack(
|
public async pack(
|
||||||
src: FollowRequest['id'] | FollowRequest,
|
src: FollowRequest['id'] | FollowRequest,
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
) {
|
): Promise<Packed<'FollowRequest'>> {
|
||||||
const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
|
const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowingsRepository } from '@/models/index.js';
|
import type { FollowingsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Following } from '@/models/entities/Following.js';
|
import type { Following } from '@/models/entities/Following.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { GalleryLikesRepository } from '@/models/index.js';
|
import type { GalleryLikesRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { GalleryLike } from '@/models/entities/GalleryLike.js';
|
import type { GalleryLike } from '@/models/entities/GalleryLike.js';
|
||||||
import { GalleryPostEntityService } from './GalleryPostEntityService.js';
|
import { GalleryPostEntityService } from './GalleryPostEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js';
|
import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { GalleryPost } from '@/models/entities/GalleryPost.js';
|
import type { GalleryPost } from '@/models/entities/GalleryPost.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { HashtagsRepository } from '@/models/index.js';
|
import type { HashtagsRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { Hashtag } from '@/models/entities/Hashtag.js';
|
import type { Hashtag } from '@/models/entities/Hashtag.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { InstancesRepository } from '@/models/index.js';
|
import type { InstancesRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { Instance } from '@/models/entities/Instance.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -2,10 +2,10 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { ModerationLogsRepository } from '@/models/index.js';
|
import type { ModerationLogsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { ModerationLog } from '@/models/entities/ModerationLog.js';
|
import type { ModerationLog } from '@/models/entities/ModerationLog.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ModerationLogEntityService {
|
export class ModerationLogEntityService {
|
||||||
@@ -20,7 +20,7 @@ export class ModerationLogEntityService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async pack(
|
public async pack(
|
||||||
src: ModerationLog['id'] | ModerationLog,
|
src: ModerationLog['id'] | ModerationLog,
|
||||||
) {
|
): Promise<Packed<'ModerationLog'>> {
|
||||||
const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
|
const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
@@ -38,8 +38,7 @@ export class ModerationLogEntityService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public packMany(
|
public packMany(
|
||||||
reports: any[],
|
reports: any[],
|
||||||
) {
|
): Promise<Packed<'ModerationLog'>[]> {
|
||||||
return Promise.all(reports.map(x => this.pack(x)));
|
return Promise.all(reports.map(x => this.pack(x)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository } from '@/models/index.js';
|
import type { MutingsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Muting } from '@/models/entities/Muting.js';
|
import type { Muting } from '@/models/entities/Muting.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -3,7 +3,7 @@ import { DataSource, In } from 'typeorm';
|
|||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { nyaize } from '@/misc/nyaize.js';
|
import { nyaize } from '@/misc/nyaize.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NoteFavoritesRepository } from '@/models/index.js';
|
import type { NoteFavoritesRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { NoteFavorite } from '@/models/entities/NoteFavorite.js';
|
import type { NoteFavorite } from '@/models/entities/NoteFavorite.js';
|
||||||
import { NoteEntityService } from './NoteEntityService.js';
|
import { NoteEntityService } from './NoteEntityService.js';
|
||||||
@@ -21,7 +21,7 @@ export class NoteFavoriteEntityService {
|
|||||||
public async pack(
|
public async pack(
|
||||||
src: NoteFavorite['id'] | NoteFavorite,
|
src: NoteFavorite['id'] | NoteFavorite,
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
) {
|
): Promise<Packed<'NoteFavorite'>> {
|
||||||
const favorite = typeof src === 'object' ? src : await this.noteFavoritesRepository.findOneByOrFail({ id: src });
|
const favorite = typeof src === 'object' ? src : await this.noteFavoritesRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NoteReactionsRepository } from '@/models/index.js';
|
import type { NoteReactionsRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
||||||
import type { ReactionService } from '../ReactionService.js';
|
import type { ReactionService } from '../ReactionService.js';
|
||||||
|
@@ -6,10 +6,10 @@ import type { AccessTokensRepository, FollowRequestsRepository, NoteReactionsRep
|
|||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Notification } from '@/models/entities/Notification.js';
|
import type { Notification } from '@/models/entities/Notification.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
import { notificationTypes } from '@/types.js';
|
import { notificationTypes } from 'misskey-js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js';
|
import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Page } from '@/models/entities/Page.js';
|
import type { Page } from '@/models/entities/Page.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { PageLikesRepository } from '@/models/index.js';
|
import type { PageLikesRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { PageLike } from '@/models/entities/PageLike.js';
|
import type { PageLike } from '@/models/entities/PageLike.js';
|
||||||
import { PageEntityService } from './PageEntityService.js';
|
import { PageEntityService } from './PageEntityService.js';
|
||||||
|
@@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { RenoteMutingsRepository } from '@/models/index.js';
|
import type { RenoteMutingsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { RenoteMuting } from '@/models/entities/RenoteMuting.js';
|
import type { RenoteMuting } from '@/models/entities/RenoteMuting.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@@ -8,6 +8,7 @@ import type { Role } from '@/models/entities/Role.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RoleEntityService {
|
export class RoleEntityService {
|
||||||
@@ -26,7 +27,7 @@ export class RoleEntityService {
|
|||||||
public async pack(
|
public async pack(
|
||||||
src: Role['id'] | Role,
|
src: Role['id'] | Role,
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
) {
|
): Promise<Packed<'Role'>> {
|
||||||
const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src });
|
const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign')
|
const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign')
|
||||||
@@ -37,7 +38,7 @@ export class RoleEntityService {
|
|||||||
}))
|
}))
|
||||||
.getCount();
|
.getCount();
|
||||||
|
|
||||||
const policies = { ...role.policies };
|
const policies: { [x: string]: Packed<'RolePolicy'> } = { ...role.policies };
|
||||||
for (const [k, v] of Object.entries(DEFAULT_POLICIES)) {
|
for (const [k, v] of Object.entries(DEFAULT_POLICIES)) {
|
||||||
if (policies[k] == null) policies[k] = {
|
if (policies[k] == null) policies[k] = {
|
||||||
useDefault: true,
|
useDefault: true,
|
||||||
@@ -72,8 +73,7 @@ export class RoleEntityService {
|
|||||||
public packMany(
|
public packMany(
|
||||||
roles: any[],
|
roles: any[],
|
||||||
me: { id: User['id'] },
|
me: { id: User['id'] },
|
||||||
) {
|
): Promise<Packed<'Role'>[]> {
|
||||||
return Promise.all(roles.map(x => this.pack(x, me)));
|
return Promise.all(roles.map(x => this.pack(x, me)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { SigninsRepository } from '@/models/index.js';
|
import type { SigninsRepository } from '@/models/index.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { Signin } from '@/models/entities/Signin.js';
|
import type { Signin } from '@/models/entities/Signin.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@@ -19,8 +19,10 @@ export class SigninEntityService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async pack(
|
public async pack(
|
||||||
src: Signin,
|
src: Signin,
|
||||||
) {
|
): Promise<Packed<'SignIn'>> {
|
||||||
return src;
|
return {
|
||||||
|
...src,
|
||||||
|
createdAt: src.createdAt.toISOString(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ import _Ajv from 'ajv';
|
|||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { Promiseable } from '@/misc/prelude/await-all.js';
|
import type { Promiseable } from '@/misc/prelude/await-all.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
||||||
@@ -28,7 +28,7 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo
|
|||||||
Detailed extends true ?
|
Detailed extends true ?
|
||||||
ExpectsMe extends true ? Packed<'MeDetailed'> :
|
ExpectsMe extends true ? Packed<'MeDetailed'> :
|
||||||
ExpectsMe extends false ? Packed<'UserDetailedNotMe'> :
|
ExpectsMe extends false ? Packed<'UserDetailedNotMe'> :
|
||||||
Packed<'UserDetailed'> :
|
(Packed<'MeDetailed'> | Packed<'UserDetailedNotMe'>) :
|
||||||
Packed<'UserLite'>;
|
Packed<'UserLite'>;
|
||||||
|
|
||||||
const Ajv = _Ajv.default;
|
const Ajv = _Ajv.default;
|
||||||
@@ -291,7 +291,7 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
return `${this.config.url}/users/${userId}`;
|
return `${this.config.url}/users/${userId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false, R = IsMeAndIsUserDetailed<ExpectsMe, D>>(
|
||||||
src: User['id'] | User,
|
src: User['id'] | User,
|
||||||
me?: { id: User['id']; } | null | undefined,
|
me?: { id: User['id']; } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -299,7 +299,7 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
userProfile?: UserProfile,
|
userProfile?: UserProfile,
|
||||||
},
|
},
|
||||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
): Promise<R> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
detail: false,
|
detail: false,
|
||||||
includeSecrets: false,
|
includeSecrets: false,
|
||||||
@@ -499,19 +499,19 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
isMuted: relation.isMuted,
|
isMuted: relation.isMuted,
|
||||||
isRenoteMuted: relation.isRenoteMuted,
|
isRenoteMuted: relation.isRenoteMuted,
|
||||||
} : {}),
|
} : {}),
|
||||||
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
|
} as Promiseable<R>;
|
||||||
|
|
||||||
return await awaitAll(packed);
|
return await awaitAll(packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public packMany<D extends boolean = false>(
|
public packMany<D extends boolean = false, R = IsUserDetailed<D>>(
|
||||||
users: (User['id'] | User)[],
|
users: (User['id'] | User)[],
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
detail?: D,
|
detail?: D,
|
||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
},
|
},
|
||||||
): Promise<IsUserDetailed<D>[]> {
|
): Promise<R[]> {
|
||||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
return Promise.all(users.map(u => this.pack<null, D, R>(u, me, options)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js';
|
import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { UserList } from '@/models/entities/UserList.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
@@ -39,4 +38,3 @@ export class UserListEntityService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
export function checkHttps(url: string): boolean {
|
export function checkHttps(url: string) {
|
||||||
return url.startsWith('https://') ||
|
return url.startsWith('https://') ||
|
||||||
(url.startsWith('http://') && process.env.NODE_ENV !== 'production');
|
(url.startsWith('http://') && process.env.NODE_ENV !== 'production');
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { Packed } from './json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 投稿を表す文字列を取得します。
|
* 投稿を表す文字列を取得します。
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { Packed } from './json-schema.js';
|
import type { Packed } from 'misskey-js';
|
||||||
|
|
||||||
export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean {
|
export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean {
|
||||||
if (mutedInstances.has(note.user.host ?? '')) return true;
|
if (mutedInstances.has(note.user.host ?? '')) return true;
|
||||||
|
@@ -1,185 +0,0 @@
|
|||||||
import {
|
|
||||||
packedUserLiteSchema,
|
|
||||||
packedUserDetailedNotMeOnlySchema,
|
|
||||||
packedMeDetailedOnlySchema,
|
|
||||||
packedUserDetailedNotMeSchema,
|
|
||||||
packedMeDetailedSchema,
|
|
||||||
packedUserDetailedSchema,
|
|
||||||
packedUserSchema,
|
|
||||||
} from '@/models/json-schema/user.js';
|
|
||||||
import { packedNoteSchema } from '@/models/json-schema/note.js';
|
|
||||||
import { packedUserListSchema } from '@/models/json-schema/user-list.js';
|
|
||||||
import { packedAppSchema } from '@/models/json-schema/app.js';
|
|
||||||
import { packedNotificationSchema } from '@/models/json-schema/notification.js';
|
|
||||||
import { packedDriveFileSchema } from '@/models/json-schema/drive-file.js';
|
|
||||||
import { packedDriveFolderSchema } from '@/models/json-schema/drive-folder.js';
|
|
||||||
import { packedFollowingSchema } from '@/models/json-schema/following.js';
|
|
||||||
import { packedMutingSchema } from '@/models/json-schema/muting.js';
|
|
||||||
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
|
|
||||||
import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
|
|
||||||
import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
|
|
||||||
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
|
|
||||||
import { packedPageSchema } from '@/models/json-schema/page.js';
|
|
||||||
import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js';
|
|
||||||
import { packedChannelSchema } from '@/models/json-schema/channel.js';
|
|
||||||
import { packedAntennaSchema } from '@/models/json-schema/antenna.js';
|
|
||||||
import { packedClipSchema } from '@/models/json-schema/clip.js';
|
|
||||||
import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js';
|
|
||||||
import { packedQueueCountSchema } from '@/models/json-schema/queue.js';
|
|
||||||
import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js';
|
|
||||||
import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js';
|
|
||||||
import { packedFlashSchema } from '@/models/json-schema/flash.js';
|
|
||||||
|
|
||||||
export const refs = {
|
|
||||||
UserLite: packedUserLiteSchema,
|
|
||||||
UserDetailedNotMeOnly: packedUserDetailedNotMeOnlySchema,
|
|
||||||
MeDetailedOnly: packedMeDetailedOnlySchema,
|
|
||||||
UserDetailedNotMe: packedUserDetailedNotMeSchema,
|
|
||||||
MeDetailed: packedMeDetailedSchema,
|
|
||||||
UserDetailed: packedUserDetailedSchema,
|
|
||||||
User: packedUserSchema,
|
|
||||||
|
|
||||||
UserList: packedUserListSchema,
|
|
||||||
App: packedAppSchema,
|
|
||||||
Note: packedNoteSchema,
|
|
||||||
NoteReaction: packedNoteReactionSchema,
|
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
|
||||||
Notification: packedNotificationSchema,
|
|
||||||
DriveFile: packedDriveFileSchema,
|
|
||||||
DriveFolder: packedDriveFolderSchema,
|
|
||||||
Following: packedFollowingSchema,
|
|
||||||
Muting: packedMutingSchema,
|
|
||||||
RenoteMuting: packedRenoteMutingSchema,
|
|
||||||
Blocking: packedBlockingSchema,
|
|
||||||
Hashtag: packedHashtagSchema,
|
|
||||||
Page: packedPageSchema,
|
|
||||||
Channel: packedChannelSchema,
|
|
||||||
QueueCount: packedQueueCountSchema,
|
|
||||||
Antenna: packedAntennaSchema,
|
|
||||||
Clip: packedClipSchema,
|
|
||||||
FederationInstance: packedFederationInstanceSchema,
|
|
||||||
GalleryPost: packedGalleryPostSchema,
|
|
||||||
EmojiSimple: packedEmojiSimpleSchema,
|
|
||||||
EmojiDetailed: packedEmojiDetailedSchema,
|
|
||||||
Flash: packedFlashSchema,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
|
|
||||||
|
|
||||||
type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any';
|
|
||||||
type StringDefToType<T extends TypeStringef> =
|
|
||||||
T extends 'null' ? null :
|
|
||||||
T extends 'boolean' ? boolean :
|
|
||||||
T extends 'integer' ? number :
|
|
||||||
T extends 'number' ? number :
|
|
||||||
T extends 'string' ? string | Date :
|
|
||||||
T extends 'array' ? ReadonlyArray<any> :
|
|
||||||
T extends 'object' ? Record<string, any> :
|
|
||||||
any;
|
|
||||||
|
|
||||||
// https://swagger.io/specification/?sbsearch=optional#schema-object
|
|
||||||
type OfSchema = {
|
|
||||||
readonly anyOf?: ReadonlyArray<Schema>;
|
|
||||||
readonly oneOf?: ReadonlyArray<Schema>;
|
|
||||||
readonly allOf?: ReadonlyArray<Schema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Schema extends OfSchema {
|
|
||||||
readonly type?: TypeStringef;
|
|
||||||
readonly nullable?: boolean;
|
|
||||||
readonly optional?: boolean;
|
|
||||||
readonly items?: Schema;
|
|
||||||
readonly properties?: Obj;
|
|
||||||
readonly required?: ReadonlyArray<Extract<keyof NonNullable<this['properties']>, string>>;
|
|
||||||
readonly description?: string;
|
|
||||||
readonly example?: any;
|
|
||||||
readonly format?: string;
|
|
||||||
readonly ref?: keyof typeof refs;
|
|
||||||
readonly enum?: ReadonlyArray<string | null>;
|
|
||||||
readonly default?: (this['type'] extends TypeStringef ? StringDefToType<this['type']> : any) | null;
|
|
||||||
readonly maxLength?: number;
|
|
||||||
readonly minLength?: number;
|
|
||||||
readonly maximum?: number;
|
|
||||||
readonly minimum?: number;
|
|
||||||
readonly pattern?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequiredPropertyNames<s extends Obj> = {
|
|
||||||
[K in keyof s]:
|
|
||||||
// K is not optional
|
|
||||||
s[K]['optional'] extends false ? K :
|
|
||||||
// K has default value
|
|
||||||
s[K]['default'] extends null | string | number | boolean | Record<string, unknown> ? K :
|
|
||||||
never
|
|
||||||
}[keyof s];
|
|
||||||
|
|
||||||
export type Obj = Record<string, Schema>;
|
|
||||||
|
|
||||||
// https://github.com/misskey-dev/misskey/issues/8535
|
|
||||||
// To avoid excessive stack depth error,
|
|
||||||
// deceive TypeScript with UnionToIntersection (or more precisely, `infer` expression within it).
|
|
||||||
export type ObjType<s extends Obj, RequiredProps extends ReadonlyArray<keyof s>> =
|
|
||||||
UnionToIntersection<
|
|
||||||
{ -readonly [R in RequiredPropertyNames<s>]-?: SchemaType<s[R]> } &
|
|
||||||
{ -readonly [R in RequiredProps[number]]-?: SchemaType<s[R]> } &
|
|
||||||
{ -readonly [P in keyof s]?: SchemaType<s[P]> }
|
|
||||||
>;
|
|
||||||
|
|
||||||
type NullOrUndefined<p extends Schema, T> =
|
|
||||||
| (p['nullable'] extends true ? null : never)
|
|
||||||
| (p['optional'] extends true ? undefined : never)
|
|
||||||
| T;
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
|
|
||||||
// Get intersection from union
|
|
||||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
||||||
type PartialIntersection<T> = Partial<UnionToIntersection<T>>;
|
|
||||||
|
|
||||||
// https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552
|
|
||||||
// To get union, we use `Foo extends any ? Hoge<Foo> : never`
|
|
||||||
type UnionSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? SchemaType<X> : never;
|
|
||||||
//type UnionObjectSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? ObjectSchemaType<X> : never;
|
|
||||||
type UnionObjType<s extends Obj, a extends readonly any[], X extends ReadonlyArray<keyof s> = a[number]> = X extends any ? ObjType<s, X> : never;
|
|
||||||
type ArrayUnion<T> = T extends any ? Array<T> : never;
|
|
||||||
|
|
||||||
type ObjectSchemaTypeDef<p extends Schema> =
|
|
||||||
p['ref'] extends keyof typeof refs ? Packed<p['ref']> :
|
|
||||||
p['properties'] extends NonNullable<Obj> ?
|
|
||||||
p['anyOf'] extends ReadonlyArray<Schema> ? p['anyOf'][number]['required'] extends ReadonlyArray<keyof p['properties']> ?
|
|
||||||
UnionObjType<p['properties'], NonNullable<p['anyOf'][number]['required']>> & ObjType<p['properties'], NonNullable<p['required']>>
|
|
||||||
: never
|
|
||||||
: ObjType<p['properties'], NonNullable<p['required']>>
|
|
||||||
:
|
|
||||||
p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md
|
|
||||||
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
|
|
||||||
any
|
|
||||||
|
|
||||||
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
|
|
||||||
|
|
||||||
export type SchemaTypeDef<p extends Schema> =
|
|
||||||
p['type'] extends 'null' ? null :
|
|
||||||
p['type'] extends 'integer' ? number :
|
|
||||||
p['type'] extends 'number' ? number :
|
|
||||||
p['type'] extends 'string' ? (
|
|
||||||
p['enum'] extends readonly (string | null)[] ?
|
|
||||||
p['enum'][number] :
|
|
||||||
p['format'] extends 'date-time' ? string : // Dateにする??
|
|
||||||
string
|
|
||||||
) :
|
|
||||||
p['type'] extends 'boolean' ? boolean :
|
|
||||||
p['type'] extends 'object' ? ObjectSchemaTypeDef<p> :
|
|
||||||
p['type'] extends 'array' ? (
|
|
||||||
p['items'] extends OfSchema ? (
|
|
||||||
p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] :
|
|
||||||
p['items']['oneOf'] extends ReadonlyArray<Schema> ? ArrayUnion<UnionSchemaType<NonNullable<p['items']['oneOf']>>> :
|
|
||||||
p['items']['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<NonNullable<p['items']['allOf']>>>[] :
|
|
||||||
never
|
|
||||||
) :
|
|
||||||
p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] :
|
|
||||||
any[]
|
|
||||||
) :
|
|
||||||
p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> :
|
|
||||||
p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> :
|
|
||||||
any;
|
|
||||||
|
|
||||||
export type SchemaType<p extends Schema> = NullOrUndefined<p, SchemaTypeDef<p>>;
|
|
@@ -55,10 +55,7 @@ export class Ad {
|
|||||||
length: 8192, nullable: false,
|
length: 8192, nullable: false,
|
||||||
})
|
})
|
||||||
public memo: string;
|
public memo: string;
|
||||||
@Column('integer', {
|
|
||||||
default: 0, nullable: false,
|
|
||||||
})
|
|
||||||
public dayOfWeek: number;
|
|
||||||
constructor(data: Partial<Ad>) {
|
constructor(data: Partial<Ad>) {
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
|
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { mutedNoteReasons } from '../../types.js';
|
import { mutedNoteReasons } from 'misskey-js';
|
||||||
import { Note } from './Note.js';
|
import { Note } from './Note.js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { noteVisibilities } from '../../types.js';
|
import { noteVisibilities } from 'misskey-js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { Channel } from './Channel.js';
|
import { Channel } from './Channel.js';
|
||||||
import type { DriveFile } from './DriveFile.js';
|
import type { DriveFile } from './DriveFile.js';
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { notificationTypes } from '@/types.js';
|
import { ACHIEVEMENT_TYPES, notificationTypes } from 'misskey-js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { Note } from './Note.js';
|
import { Note } from './Note.js';
|
||||||
import { FollowRequest } from './FollowRequest.js';
|
import { FollowRequest } from './FollowRequest.js';
|
||||||
@@ -39,7 +39,7 @@ export type Notification = {
|
|||||||
|
|
||||||
choice: number | null;
|
choice: number | null;
|
||||||
|
|
||||||
achievement: string | null;
|
achievement: typeof ACHIEVEMENT_TYPES[number] | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* アプリ通知のbody
|
* アプリ通知のbody
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { noteVisibilities } from '../../types.js';
|
import { noteVisibilities } from 'misskey-js';
|
||||||
import { Note } from './Note.js';
|
import { Note } from './Note.js';
|
||||||
import type { User } from './User.js';
|
import type { User } from './User.js';
|
||||||
|
|
||||||
|
@@ -1,83 +1,6 @@
|
|||||||
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
|
import type { RoleCondFormulaValue } from 'misskey-js/built/schemas/role.js';
|
||||||
type CondFormulaValueAnd = {
|
|
||||||
type: 'and';
|
|
||||||
values: RoleCondFormulaValue[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueOr = {
|
|
||||||
type: 'or';
|
|
||||||
values: RoleCondFormulaValue[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueNot = {
|
|
||||||
type: 'not';
|
|
||||||
value: RoleCondFormulaValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueIsLocal = {
|
|
||||||
type: 'isLocal';
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueIsRemote = {
|
|
||||||
type: 'isRemote';
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueCreatedLessThan = {
|
|
||||||
type: 'createdLessThan';
|
|
||||||
sec: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueCreatedMoreThan = {
|
|
||||||
type: 'createdMoreThan';
|
|
||||||
sec: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueFollowersLessThanOrEq = {
|
|
||||||
type: 'followersLessThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueFollowersMoreThanOrEq = {
|
|
||||||
type: 'followersMoreThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueFollowingLessThanOrEq = {
|
|
||||||
type: 'followingLessThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueFollowingMoreThanOrEq = {
|
|
||||||
type: 'followingMoreThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueNotesLessThanOrEq = {
|
|
||||||
type: 'notesLessThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CondFormulaValueNotesMoreThanOrEq = {
|
|
||||||
type: 'notesMoreThanOrEq';
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RoleCondFormulaValue =
|
|
||||||
CondFormulaValueAnd |
|
|
||||||
CondFormulaValueOr |
|
|
||||||
CondFormulaValueNot |
|
|
||||||
CondFormulaValueIsLocal |
|
|
||||||
CondFormulaValueIsRemote |
|
|
||||||
CondFormulaValueCreatedLessThan |
|
|
||||||
CondFormulaValueCreatedMoreThan |
|
|
||||||
CondFormulaValueFollowersLessThanOrEq |
|
|
||||||
CondFormulaValueFollowersMoreThanOrEq |
|
|
||||||
CondFormulaValueFollowingLessThanOrEq |
|
|
||||||
CondFormulaValueFollowingMoreThanOrEq |
|
|
||||||
CondFormulaValueNotesLessThanOrEq |
|
|
||||||
CondFormulaValueNotesMoreThanOrEq;
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Role {
|
export class Role {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
||||||
import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/types.js';
|
import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from 'misskey-js';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { Page } from './Page.js';
|
import { Page } from './Page.js';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user