1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-05 13:35:52 +02:00

Compare commits

...

106 Commits

Author SHA1 Message Date
renovate[bot]
98d8c42b17 fix(deps): update [frontend] update dependencies 2026-01-11 06:11:04 +00:00
かっこかり
4bc0026900 deps(frontend): update vue-tsc to v3.2.1 (#17091) 2026-01-11 15:00:00 +09:00
syuilo
faf2399e31 enhance(frontend): 連合なしが指定されているときに公開範囲を指名にできるように
Resolve #14760
2026-01-11 13:58:58 +09:00
おさむのひと
106fffdcfe chore(backend): FileServerServiceのunit-testを追加 (#17086)
* add test

* fix

* fix type error
2026-01-11 11:34:29 +09:00
github-actions[bot]
141964e57c Bump version to 2026.1.0-alpha.4 2026-01-09 14:19:30 +00:00
syuilo
41592eafb3 refactor: make noImplicitAny true (#17083)
* wip

* Update emojis.emoji.vue

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update manager.ts

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update analytics.ts
2026-01-09 22:06:40 +09:00
かっこかり
2a14025c29 fix(frontend): popupのemit型が正しく利用できるように修正 (#16826)
* fix(frontend): popupのemit型が正しく利用できるように修正

* fix: revert unnecessary code (for testing purpose)

* fix lint

* fix type errors

* fix types

* add comment

* fix

* fix

* fix: OverloadToUnionの仕組みを変更

* add comments, clean up

* fix lint

* fix types

* clean up [ci skip]

* fix

* add comments [ci skip]
2026-01-09 12:21:08 +09:00
github-actions[bot]
75b5dc1cd8 Bump version to 2026.1.0-alpha.3 2026-01-08 12:35:31 +00:00
syuilo
ee0eeb052f 🎨 2026-01-08 21:31:44 +09:00
かっこかり
ece4efcefe fix(frontend): mfmFunctionPickerを使用して挿入する際のハンドリングを改善 (#17018)
* fix(frontend): mfmFunctionPickerを使用して絵文字を挿入する際のハンドリングを改善

* fix

* Update MkPostForm.vue

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2026-01-08 21:08:27 +09:00
かっこかり
cd973b252a fix(frontend): 2月29日を誕生日に設定している場合、平年は3月1日を誕生日として扱うように (#17072)
* fix(frontend): 2月29日を誕生日に設定している場合、平年は3月1日を誕生日として扱うように

* Update Changelog

* add tests

* spdx
2026-01-08 12:16:33 +09:00
anatawa12
666f78e676 enable and fix no-unused-vars and no-async-promise-executor (#17070)
* dev: set --no-bail for lint task

* lint: enable no-async-promise-executor lint and fix them

* lint: enable no-unused-vars with allowing _ prefix

* lint: fix semi
2026-01-08 11:49:12 +09:00
renovate[bot]
cf89c4e363 chore(deps): update pnpm to v10.27.0 [security] (#17082)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-08 09:00:30 +09:00
github-actions[bot]
bf41e9edd1 Bump version to 2026.1.0-alpha.2 2026-01-07 13:02:33 +00:00
syuilo
f92c187e2b make __VUE_OPTIONS_API__ false (#17077) 2026-01-07 21:59:22 +09:00
syuilo
8c5572dd3b enhance(frontend): remove vuedraggable (#17073)
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update page-editor.blocks.vue

* Update MkDraggable.vue

* refactor

* refactor

* ✌️

* refactor

* Update MkDraggable.vue

* ios

* 🎨

* 🎨
2026-01-07 21:46:03 +09:00
syuilo
e18b92823f Update README.md 2026-01-07 21:43:31 +09:00
anatawa12
2d709ceeb4 fix: typo in import specifier (#17076) 2026-01-07 20:40:14 +09:00
anatawa12
38b3eecc8c migrate build scripts to esmodules (#17071)
* chore: migrate build scripts to esmodules

* chore: do not use export default in build script
2026-01-06 19:23:59 +09:00
おさむのひと
f6fc78f578 refactor: DriveFileEntityServiceとDriveFolderEntityServiceの複数件取得をリファクタ (#17064)
* refactor: DriveFileEntityServiceとDriveFolderEntityServiceの複数件取得をリファクタ

* add test

* fix

* Update packages/backend/src/core/entities/DriveFolderEntityService.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/backend/test/unit/entities/DriveFolderEntityService.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/backend/src/core/entities/DriveFileEntityService.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Revert "Update packages/backend/src/core/entities/DriveFileEntityService.ts"

This reverts commit 83bb9564cf.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-06 13:13:06 +09:00
syuilo
6e99acf7a7 update clean scripts 2026-01-05 21:49:45 +09:00
github-actions[bot]
553a147396 Bump version to 2026.1.0-alpha.1 2026-01-05 12:03:42 +00:00
syuilo
7bcfeba7e5 Minify backend (#17054)
* wip

* Update build.js

* Update build.js

* [minify-backend用] フィジビリティ検証 (#16878)

* fix: minify-backend

* 間違えて入れちゃったのを戻す

* 追従

* fix

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

* test

* use node 24

* Revert "use node 24"

This reverts commit 7ae2debf23.

* Revert "test"

This reverts commit d919879091.

* Update package.json

* wip

* Update compile_config.js

* Revert "Update compile_config.js"

This reverts commit 0ee286f02b.

* Update config.ts

* wip

* Update .swcrc

* Update ClientServerService.ts

* [ci skip] update CHANGELOG

---------

Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com>
2026-01-05 20:56:52 +09:00
renovate[bot]
4f65c1529b chore(deps): update [misskey-js] update dependencies [ci skip] (#17025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-03 18:35:10 +09:00
かっこかり
589ae8d4c6 fix(deps): update [frontend] update dependencies (#17062)
* fix(deps): update [frontend] update dependencies

* rollback tsgo to fix type error

* Revert "rollback tsgo to fix type error"

This reverts commit 3a0b94e5b2.

* rollback vue-tsc to fix type errors (test)

* update vue-tsc to 3.2.0

* update vue-tsc stack to v3.2.1

* rollback vue-tsc to v3.1.8
2026-01-03 18:15:34 +09:00
かっこかり
0be4405a79 fix(deps): run pnpm dedupe (#17063) 2026-01-03 15:30:04 +09:00
renovate[bot]
2fba2e7049 fix(deps): update [root] update dependencies [ci skip] (#17023)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-03 12:28:57 +09:00
github-actions[bot]
96b03a7179 Bump version to 2026.1.0-alpha.0 2026-01-02 12:51:50 +00:00
かっこかり
cdb958cdf0 fix(frontend): 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正 (#17059)
* fix(frontend): 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2026-01-02 21:50:25 +09:00
syuilo
245775ea87 [skip ci] Update CHANGELOG.md 2026-01-02 21:48:39 +09:00
renovate[bot]
40d55fc6a3 fix(deps): update [backend] update dependencies (#17026)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-02 21:47:23 +09:00
かっこかり
9c22538454 fix(frontend): ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正 (#17019)
* fix(frontend): ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正

* Update Changelog

* refactor

* Update Changelog
2026-01-02 21:41:32 +09:00
かっこかり
a1ba403f9a fix(frontend): ログインダイアログが表示されたあとの処理がおかしくなる問題を修正 (#17038)
* fix(frontend): ログインダイアログが表示されたあとの処理がおかしくなる問題を修正

* Update Changelog
2026-01-02 21:38:53 +09:00
かっこかり
443e1ed29e refactor(frontend): prefer.model, store.modelではcustomRefを使用するように (#17058)
* refactor(frontend): prefer.model, store.modelではcustomRefを使用するように

* fix: watchの解除に失敗してもエラーで落ちないように

* Update packages/frontend/src/lib/pizzax.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-02 21:34:43 +09:00
かっこかり
b5454cb2c4 fix(frontend): 登録日によるソートの場合はpaginator側のソートを使用するように (#17048)
* fix(frontend): 登録日によるソートの場合はpaginator側のソートを使用するように

* Update Changelog

* fix lint

* refactor
2026-01-01 10:32:38 +09:00
かっこかり
8577f10456 2026 (#17052) 2026-01-01 00:00:00 +09:00
かっこかり
16ffd88ecc enhance: 誕生日のユーザーウィジェットで、今日だけに限らず、直近の誕生日ユーザーを表示できるように (#13637)
* enhance(frontend): 「今日誕生日のフォロー中ユーザー」ウィジェットをリファクタリング

(cherry picked from commit 24652b9364)

* fix(backend): 年越しの時期で誕生日検索クエリーが誤動作する問題を修正 (MisskeyIO#577)

(cherry picked from commit 38581006be)

* fix

* spdx

* delete birthday param on users/following api

* 名称を一本化

* Update Changelog

* Update Changelog

* fix(frontend/WidgetBirthdayFollowings): ユーザーの名前が長いと投稿ボタンがはみ出てしまう問題を修正 (MisskeyIO#582)

(cherry picked from commit fa47a545b1)

* use module css

* default 3day

* Revert "delete birthday param on users/following api"

This reverts commit a47456c1c4.

* Update Changelog

* 日付が1ヶ月ズレている問題を修正?

* fix: 日付関連のバグを修正

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>

* build misskey-js types

* add comment

* Update CHANGELOG.md

* migrate

* change migration

* UPdate Changelog

* fix: revert unnecessary changes

* 🎨

* i18n

* fix

* update changelog

* 🎨

* fix lint

* refactor: remove unnecessary classes

* fix

* fix

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
2025-12-31 22:33:26 +09:00
かっこかり
866e675134 fix(frontend): ウィジェットの設定画面のプレビューが利用できない問題を修正 (#17056) 2025-12-31 18:06:22 +09:00
かっこかり
01aa56c602 enhance(backend/oauth): Support client information discovery in the IndieAuth 11 July 2024 spec (#17030)
* enhance(backend): Support client information discovery in the IndieAuth 11 July 2024 spec

* add tests

* Update Changelog

* Update Changelog

* fix tests

* fix test describe to align with the other describe format
2025-12-31 14:50:01 +09:00
かっこかり
ff7d2c1083 refactor(frontend): remove undefined css rules (#17051) 2025-12-31 13:42:59 +09:00
かっこかり
404fca6c2d fix(frontend): fix build error (#17050) 2025-12-30 17:55:52 +09:00
かっこかり
3fe0477cac fix(frontend): ディレクティブの型が当たらない問題を修正 (#17049) 2025-12-30 16:39:07 +09:00
かっこかり
97d485bdd2 enhance(frontend): ウィジェットの設定項目の多言語対応 (#17032)
* enhance(frontend): ウィジェットの設定項目の多言語対応

* Update Changelog

* refactor: move options locale key to root for optimizing artifacts for locale inlining

* fix

* fix

* ✌️

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-30 15:59:18 +09:00
かっこかり
4285303c81 fix(frontend): follow-up of #17033 (#17047)
* wip

* fix

* ref -> reactive

* tweak throttle threshold

* tweak throttle threshold

* rss設定にはmanualSaveを使用するように

* Update MkWidgetSettingsDialog.vue

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-30 14:32:40 +09:00
かっこかり
14f58255ee enhance(frontend): ウィジェットの設定画面を改良 (#17033)
* enhance(frontend): ウィジェットの設定画面を改良

* Update Changelog

* fix lint
2025-12-28 20:50:11 +09:00
おさむのひと
b69b0acf59 chore: SearchServiceのunit-test追加 (#17035)
* add serach service test

* add meili test

* CIの修正が足りなかった

* テストの追加

* fix
2025-12-28 19:57:18 +09:00
かっこかり
7a5430199f enhance(frontend): MkDriveで自動でもっと見るを有効化 (#17037)
* enhance(frontend): MkDriveで自動でもっと見るを有効化

* Update Changelog
2025-12-28 19:53:08 +09:00
syuilo
c32307dca4 Update README.md 2025-12-27 14:30:36 +09:00
kami8
bc78bb9b8e Fix(frontend): ドライブクリーナーから画像を削除した際、リロードしなくても画面に反映されるよう修正 (#16888)
* ドライブクリーナーでファイル削除後、リロードなしで画面に反映されるように修正

* CHANGELOG.mdを修正

* CHANGELOGがおかしかったので修正
2025-12-26 09:19:32 +09:00
renovate[bot]
a33b003282 chore(deps): update [tools] update dependencies [ci skip] (#17024)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 19:32:38 +09:00
anatawa12
74e847a04d refactor: use TRANSIENT scope to avoid service bucket relay (#16985)
* refactor: use TRANSIENT scope to avoid service bucket relay

* lint: fix lints

* refactor: use transient for apResolver

* Update packages/backend/src/core/activitypub/models/ApImageService.ts

* fix
2025-12-22 17:01:10 +09:00
anatawa12
06657c81d3 feat: use tsgo where capable (#16984) 2025-12-22 16:52:05 +09:00
おさむのひと
5c5e965151 fix(ci): dockleのciをより安定して動かせるようにする (#16987) 2025-12-22 16:51:38 +09:00
github-actions[bot]
b07a1e692f [skip ci] Update CHANGELOG.md (prepend template) 2025-12-22 05:30:46 +00:00
github-actions[bot]
78348007ed Release: 2025.12.2 2025-12-22 05:30:41 +00:00
かっこかり
92f1e599db Update CHANGELOG.md [ci skip] 2025-12-22 12:05:25 +09:00
github-actions[bot]
26b5979c76 Bump version to 2025.12.2-beta.4 2025-12-20 12:30:29 +00:00
かっこかり
b1048525d2 fix(frontend): 一部のUnicode絵文字がリアクションボタンにならない問題を修正 (#17017)
* fix(frontend): 一部のUnicode絵文字がリアクションボタンにならない問題を修正

* Update Changelog

* fix
2025-12-20 21:23:39 +09:00
かっこかり
4c31eb409c fix(frontend): ストレージが消去される事がある問題を軽減 (#16704)
* fix(frontend): ストレージが消去される事がある問題を軽減

* add comment

* add catch to continue request permissions

* Update Changelog

* update changelog

* fix

通知権限の許可取得はボタン押下時に移動

* fix

* wip

* Update main-boot.ts

* wip

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-20 21:22:31 +09:00
おさむのひと
f739cb6270 fix: admin/queue/deliver-delayedとadmin/queue/inbox-delayedの応答速度を改善 (#17009) 2025-12-20 19:15:05 +09:00
github-actions[bot]
81bacb6203 Bump version to 2025.12.2-beta.3 2025-12-20 10:07:41 +00:00
かっこかり
ee8dccea2f fix(backend): fix #16994 by approach 6 (#17005)
* fix(backend): narrow down trustproxy default value and enhance documentation on how to configure it

* Update Changelog

* indent [ci skip]

* Update CHANGELOG.md [ci skip]

* add cloudflare specific example

* Update .config/example.yml

Co-authored-by: anatawa12 <anatawa12@icloud.com>

* fix: productionでIPレートリミットされる際にlocalhostからリクエストが来たらログを残すように

* fix: wrong condition

* fix: use own logger for signin api

* flip configuration

* fix

* fix [ci skip]

* fix: wrong message [ci skip]

* fix: どこがおかしいか明記 [ci skip]

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
2025-12-20 19:07:05 +09:00
syuilo
6d00645bc7 fix(frontend): iPadOSのPWAでアプリを切り替えた際にウィジェット表示ボタンが消滅する問題を修正 2025-12-18 20:27:12 +09:00
syuilo
baeed4bc80 perf(backend): lazy load systeminformation
systeminformationを必要とする機能を有効にしていないサーバーで無駄に読み込まれることが無いように
2025-12-18 20:05:20 +09:00
syuilo
dba44daf9c 🎨 and refactor 2025-12-18 15:40:40 +09:00
syuilo
46e6dd99d1 chore: remove beta label from some features 2025-12-18 15:15:07 +09:00
syuilo
f48af7f73b 🎨 2025-12-18 14:55:19 +09:00
syuilo
834e8b4c24 fix(frontend): デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正 2025-12-18 14:55:16 +09:00
github-actions[bot]
7ef0c96758 Bump version to 2025.12.2-beta.2 2025-12-17 03:31:55 +00:00
syuilo
b10074e939 enhance(frontend): add deck tour 2025-12-17 12:27:55 +09:00
renovate[bot]
260dbd150b fix(deps): update dependency systeminformation to v5.27.14 [security] [ci skip] (#17003)
* fix(deps): update dependency systeminformation to v5.27.14 [security]

* update whitelist to force update systeminformation package

* bump other dependencies to fix dep error

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-17 09:36:15 +09:00
syuilo
79cbbcfe0f Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-12-17 09:08:25 +09:00
syuilo
c893f85864 Update example.yml 2025-12-17 09:08:22 +09:00
syuilo
24d4ffa2ec Update CHANGELOG.md
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-17 09:07:51 +09:00
github-actions[bot]
0b931daefd Bump version to 2025.12.2-beta.1 2025-12-16 14:00:25 +00:00
かっこかり
cc05d93194 fix(frontend): まれに設定変更のタブ間同期に失敗する問題を修正 (#16991) 2025-12-16 22:56:57 +09:00
かっこかり
90345591bb fix(frontend): 無限スクロールできる箇所の調整 (#17000) 2025-12-16 22:50:26 +09:00
github-actions[bot]
730227f353 Bump version to 2025.12.2-beta.0 2025-12-16 12:31:04 +00:00
syuilo
4acb37ee9d Update CHANGELOG.md 2025-12-16 21:23:51 +09:00
syuilo
7025769c69 fix(frontend): バージョン表記のないPlayが正しく動作しない問題を修正
Fix #16996
2025-12-16 21:23:23 +09:00
syuilo
1a4ef8769f Update CHANGELOG.md 2025-12-16 20:08:01 +09:00
github-actions[bot]
055cd0c250 Bump version to 2025.12.2-alpha.0 2025-12-16 11:00:42 +00:00
syuilo
d35ddc77d2 enhance(backend): request ip が localhost だった場合、レートリミットをスキップ & 警告を出すように 2025-12-16 19:56:44 +09:00
syuilo
8d871a58e3 Update CHANGELOG.md 2025-12-16 19:55:31 +09:00
syuilo
99b0b436e0 Update example.yml 2025-12-16 19:55:16 +09:00
syuilo
e3d5b95672 Revert "Merge commit from fork"
This reverts commit 5512898463.

see https://github.com/misskey-dev/misskey/issues/16994
2025-12-16 14:21:39 +09:00
かっこかり
0d52145b2b deps: update deps [ci skip] (#16997)
* update deps

* update chokidar to v5

* fix type error

* rollback serviceworker types to r74

* fix [ci skip]
2025-12-16 11:42:06 +09:00
かっこかり
467404d5bb fix(gh): thollander/actions-comment-pull-request@v3 の breaking change 対応漏れ 2025-12-16 09:45:26 +09:00
renovate[bot]
99e25784ad chore(deps): update [github actions] update dependencies (major) (#16869)
* chore(deps): update [github actions] update dependencies

* migrate

* bump download-artifact to v7

* bump upload-artifact to v6

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-16 09:26:10 +09:00
github-actions[bot]
9e1e40d35a [skip ci] Update CHANGELOG.md (prepend template) 2025-12-14 07:27:11 +00:00
github-actions[bot]
8eb6e29d2c Release: 2025.12.1 2025-12-14 07:27:06 +00:00
Copilot
2d198a711b Update PostgreSQL volume mount paths for PostgreSQL 18 (#16986)
* Initial plan

* Update PostgreSQL volume mount paths for PostgreSQL 18 compatibility

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-14 11:32:42 +09:00
syuilo
e0b872dc09 Update CHANGELOG for version 2025.12.1
Removed empty section under General and retained fixes under Client.
2025-12-13 21:16:12 +09:00
github-actions[bot]
711b86ab7d Bump version to 2025.12.1-alpha.1 2025-12-13 10:14:03 +00:00
syuilo
e8b4dae553 New Crowdin updates (#16939)
* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)
2025-12-13 19:08:25 +09:00
かっこかり
36d404818d fix(frontend/aiscript): nullを返すnote_view_intrruptorが動作しない問題を修正 (#16977)
* fix(frontend/aiscript): nullを返すnote_view_intrruptorが動作しない問題を修正

* Update Changelog
2025-12-13 19:08:02 +09:00
かっこかり
cb03f3f013 fix(frontend): follow-up of 16970 (#16975) 2025-12-13 18:22:40 +09:00
かっこかり
c109bec013 fix(frontend): ドライブファイルを日付以外で並び替える場合は月でグループ化して表示しないように (#16978)
* fix(frontend): ドライブファイルを日付以外で並び替える場合は月でグループ化して表示しないように

* Update Changelog
2025-12-13 18:17:09 +09:00
かっこかり
6fa4eb8c4f fix(frontend): 無限スクロールできる箇所の調整 (#16979) 2025-12-13 18:16:15 +09:00
github-actions[bot]
5fb4caa14b Bump version to 2025.12.1-alpha.0 2025-12-12 08:09:12 +00:00
まっちゃてぃー。
bc1f83664f fix(sw): Service Worker Auto Preloadをオプトアウトするように (#16971)
* Service Worker Auto Preloadをオプトアウトするように

* TypeErrorを修正

* コメントを追記
2025-12-12 17:08:15 +09:00
anatawa12
78435dc8d4 Fix: deckのタイムラインセレクタのデフォルトの値が現在のタイムラインではない問題 (#16008)
* fix: os.selectの型定義の上で default が文字列である問題を修正

* fix: deckのタイムライン選択画面で今指定されているタイムラインがデフォルトで選択されない問題

* fix lint

* fix: type error
2025-12-12 12:53:59 +09:00
かっこかり
d74aded35f fix: remove unused dependencies (#16969)
* fix(frontend): remove unused dependencies

* fix(backend): remove unused dependencies
2025-12-12 10:45:45 +09:00
FINEARCHS
d605680524 fix(frontend): オーナー不在のチャンネルをミュートできないのを修正 (#16906)
* オーナー不在のチャンネルをミュートできないのを修正

* update changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-11 23:15:57 +09:00
かっこかり
1096ce8e4a fix(frontend): 削除されたノートのリノートが正しく表示されない問題を修正 (#16970)
* fix(frontend): 削除されたノートのリノートが正しく表示されない問題を修正

* Update Changelog

* 🎨

* fix
2025-12-11 23:10:23 +09:00
syuilo
8e6fffee68 enhance: use native glob (#16965)
* enhance: use native glob

* remove tiny-glob

* remove fast-glob

* refactor

* fix: use async glob if possible

---------

Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-11 23:08:26 +09:00
603 changed files with 11522 additions and 7534 deletions

View File

@@ -107,13 +107,51 @@ port: 3000
# Proxy trust settings # Proxy trust settings
# #
# Changes how the server interpret the origin IP of the request. # Specifies the IP addresses that Misskey will use as trusted
# reverse proxies (e.g., nginx, Cloudflare). This affects how
# Misskey determines the source IP for each request and is used
# for important rate limiting and security features. If the value
# is not set correctly, Misskey may use the IP address of the
# reverse proxy instead of the actual source IP, which may lead to
# unintended rate limiting or security vulnerabilities.
# By default, the loopback network and private network address
# ranges shown below are trusted.
# If you are using a single reverse proxy and it is on the same
# machine or the same private network as Misskey, it is unlikely you
# need to change this setting, and the default setting is fine.
# Also, if you are using multiple reverse proxy servers and they are
# all on the same private network as Misskey, the default setting
# is fine.
# However, if you are using a reverse proxy server that accesses
# Misskey web servers and streaming servers via public IP addresses
# (for example, Cloudflare), you must set this variable.
# When changing this setting, you can use one of the following values:
# #
# Any format supported by Fastify is accepted. # - true: Trust all proxies
# Default: do not trust any proxies (i.e. trustProxy: false) # - false: Do not trust any proxies
# See: https://fastify.dev/docs/latest/reference/server/#trustproxy # - IP address, IP address range, or array of them: Trust hops that
# match the specified criteria.
# - Integer: Trust the nth hop from the front-facing proxy server as
# the client.
# For more information on how to configure this setting, please refer
# to the Fastify documentation:
# https://fastify.dev/docs/latest/Reference/Server/#trustproxy
# #
# trustProxy: false # Note that if this variable is set, it overrides the default range,
# so if you have both an external reverse proxy and a proxy on the
# local host, you must include both IPs (or IP ranges).
#
#trustProxy:
# - '10.0.0.0/8'
# - '172.16.0.0/12'
# - '192.168.0.0/16'
# - '127.0.0.1/32'
# - '::1/128'
# - 'fc00::/7'
# # Example: If you are using some external reverse proxies like CDNs,
# # you may need to add the CDN IP ranges here.
# # If you're using Cloudflare, you can find IP Ranges at:
# # https://www.cloudflare.com/ips/
# ┌──────────────────────────┐ # ┌──────────────────────────┐
#───┘ PostgreSQL configuration └──────────────────────────────── #───┘ PostgreSQL configuration └────────────────────────────────
@@ -283,6 +321,10 @@ id: 'aidx'
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Enable internal IP-based rate limiting (default: true)
# To configure them in reverse proxy instead, set this to false.
#enableIpRateLimit: true
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View File

@@ -6,6 +6,7 @@
Dockerfile Dockerfile
build/ build/
built/ built/
src-js/
db/ db/
.devcontainer/compose.yml .devcontainer/compose.yml
node_modules/ node_modules/

View File

@@ -54,7 +54,7 @@ body:
* Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4 * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
* Browser: Chrome 113.0.5672.126 * Browser: Chrome 113.0.5672.126
* Server URL: misskey.example.com * Server URL: misskey.example.com
* Misskey: 2025.x.x * Misskey: 2026.x.x
value: | value: |
* Model and OS of the device(s): * Model and OS of the device(s):
* Browser: * Browser:
@@ -74,7 +74,7 @@ body:
Examples: Examples:
* Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
* Misskey: 2025.x.x * Misskey: 2026.x.x
* Node: 20.x.x * Node: 20.x.x
* PostgreSQL: 18.x.x * PostgreSQL: 18.x.x
* Redis: 7.x.x * Redis: 7.x.x

View File

@@ -16,13 +16,13 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View File

@@ -12,9 +12,9 @@ jobs:
steps: steps:
- name: Checkout head - name: Checkout head
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'

View File

@@ -18,7 +18,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }} if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
persist-credentials: false persist-credentials: false
@@ -29,7 +29,7 @@ jobs:
- name: setup node - name: setup node
id: setup-node id: setup-node
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: pnpm cache: pnpm
@@ -53,7 +53,7 @@ jobs:
# packages/misskey-js/generator/built/autogen # packages/misskey-js/generator/built/autogen
- name: Upload Generated - name: Upload Generated
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: generated-misskey-js name: generated-misskey-js
path: packages/misskey-js/generator/built/autogen path: packages/misskey-js/generator/built/autogen
@@ -66,14 +66,14 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }} if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
persist-credentials: false persist-credentials: false
ref: refs/pull/${{ github.event.pull_request.number }}/merge ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Upload From Merged - name: Upload From Merged
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: actual-misskey-js name: actual-misskey-js
path: packages/misskey-js/src/autogen path: packages/misskey-js/src/autogen
@@ -86,13 +86,13 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: download generated-misskey-js - name: download generated-misskey-js
uses: actions/download-artifact@v4 uses: actions/download-artifact@v7
with: with:
name: generated-misskey-js name: generated-misskey-js
path: misskey-js-generated path: misskey-js-generated
- name: download actual-misskey-js - name: download actual-misskey-js
uses: actions/download-artifact@v4 uses: actions/download-artifact@v7
with: with:
name: actual-misskey-js name: actual-misskey-js
path: misskey-js-actual path: misskey-js-actual
@@ -113,9 +113,9 @@ jobs:
- name: send message - name: send message
if: steps.check-changes.outputs.changes == 'true' if: steps.check-changes.outputs.changes == 'true'
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@v3
with: with:
comment_tag: check-misskey-js-autogen comment-tag: check-misskey-js-autogen
message: |- message: |-
Thank you for sending us a great Pull Request! 👍 Thank you for sending us a great Pull Request! 👍
Please regenerate misskey-js type definitions! 🙏 Please regenerate misskey-js type definitions! 🙏
@@ -127,9 +127,9 @@ jobs:
- name: send message - name: send message
if: steps.check-changes.outputs.changes == 'false' if: steps.check-changes.outputs.changes == 'false'
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@v3
with: with:
comment_tag: check-misskey-js-autogen comment-tag: check-misskey-js-autogen
mode: delete mode: delete
message: "Thank you!" message: "Thank you!"
create_if_not_exists: false create_if_not_exists: false

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Check version - name: Check version
run: | run: |
if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Check - name: Check
run: | run: |
counter=0 counter=0

View File

@@ -10,7 +10,7 @@ jobs:
check_copyright_year: check_copyright_year:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
- run: | - run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!" echo "Please change copyright year!"

View File

@@ -28,7 +28,7 @@ jobs:
wait_time: ${{ steps.get-wait-time.outputs.wait_time }} wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Check allowed users - name: Check allowed users
id: check-allowed-users id: check-allowed-users

View File

@@ -27,7 +27,7 @@ jobs:
platform=${{ matrix.platform }} platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub - name: Log in to Docker Hub
@@ -53,7 +53,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}" digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}" touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest - name: Upload digest
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: digests-${{ env.PLATFORM_PAIR }} name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/* path: /tmp/digests/*
@@ -66,7 +66,7 @@ jobs:
- build - build
steps: steps:
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v7
with: with:
path: /tmp/digests path: /tmp/digests
pattern: digests-* pattern: digests-*

View File

@@ -32,7 +32,7 @@ jobs:
platform=${{ matrix.platform }} platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Docker meta - name: Docker meta
@@ -64,7 +64,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}" digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}" touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest - name: Upload digest
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: digests-${{ env.PLATFORM_PAIR }} name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/* path: /tmp/digests/*
@@ -77,7 +77,7 @@ jobs:
- build - build
steps: steps:
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v7
with: with:
path: /tmp/digests path: /tmp/digests
pattern: digests-* pattern: digests-*

View File

@@ -11,38 +11,43 @@ on:
jobs: jobs:
dockle: dockle:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
DOCKER_CONTENT_TRUST: 1 DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.15 DOCKLE_VERSION: 0.4.15
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
- name: Download and install dockle v${{ env.DOCKLE_VERSION }} - name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: | run: |
set -eux
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb" curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
sudo dpkg -i dockle.deb sudo dpkg -i dockle.deb
- run: | - name: Build web image (docker build)
cp .config/docker_example.env .config/docker.env
cp ./compose_example.yml ./compose.yml
- run: |
docker compose up -d web
IMAGE_ID=$(docker compose images --format json web | jq -r '.[0].ID')
docker tag "${IMAGE_ID}" misskey-web:latest
- name: Prune docker junk (optional but recommended)
run: | run: |
docker system prune -af set -eux
docker volume prune -f docker build -t "misskey-web:ci" .
docker image ls
- name: Save image for Dockle - name: Mount tmpfs for Dockle tar
env:
TMPFS_SIZE: 8G
run: | run: |
docker save misskey-web:latest -o ./misskey-web.tar set -eux
ls -lh ./misskey-web.tar sudo mkdir -p /mnt/dockle-tmp
sudo mount -t tmpfs -o size=${{ env.TMPFS_SIZE }} tmpfs /mnt/dockle-tmp
free -h
df -h
- name: Run Dockle with tar input - name: Save image tar into tmpfs
run: | run: |
dockle --exit-code 1 --input ./misskey-web.tar set -eux
docker save misskey-web:ci -o /mnt/dockle-tmp/misskey-web.tar
ls -lh /mnt/dockle-tmp/misskey-web.tar
- name: Run Dockle Scan (tar input)
run: |
set -eux
dockle --exit-code 1 --input /mnt/dockle-tmp/misskey-web.tar

View File

@@ -25,14 +25,14 @@ jobs:
ref: refs/pull/${{ github.event.number }}/merge ref: refs/pull/${{ github.event.number }}/merge
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
ref: ${{ matrix.ref }} ref: ${{ matrix.ref }}
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -48,7 +48,7 @@ jobs:
- name: Copy API.json - name: Copy API.json
run: cp packages/backend/built/api.json ${{ matrix.api-json-name }} run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: api-artifact-${{ matrix.api-json-name }} name: api-artifact-${{ matrix.api-json-name }}
path: ${{ matrix.api-json-name }} path: ${{ matrix.api-json-name }}
@@ -61,7 +61,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }} PR_NUMBER: ${{ github.event.number }}
run: | run: |
echo "$PR_NUMBER" > ./pr_number echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v6
with: with:
name: api-artifact-pr-number name: api-artifact-pr-number
path: pr_number path: pr_number

View File

@@ -40,14 +40,14 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
ref: ${{ matrix.ref }} ref: ${{ matrix.ref }}
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -67,7 +67,7 @@ jobs:
# Start the server and measure memory usage # Start the server and measure memory usage
node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }} node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }}
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: memory-artifact-${{ matrix.memory-json-name }} name: memory-artifact-${{ matrix.memory-json-name }}
path: ${{ matrix.memory-json-name }} path: ${{ matrix.memory-json-name }}
@@ -81,7 +81,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }} PR_NUMBER: ${{ github.event.number }}
run: | run: |
echo "$PR_NUMBER" > ./pr_number echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v6
with: with:
name: memory-artifact-pr-number name: memory-artifact-pr-number
path: pr_number path: pr_number

View File

@@ -11,6 +11,6 @@ jobs:
pull-requests: write pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/labeler@v5 - uses: actions/labeler@v6
with: with:
repo-token: "${{ secrets.GITHUB_TOKEN }}" repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -36,13 +36,13 @@ jobs:
pnpm_install: pnpm_install:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0 - uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -69,13 +69,13 @@ jobs:
eslint-cache-version: v1 eslint-cache-version: v1
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }} eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0 - uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -100,13 +100,13 @@ jobs:
- sw - sw
- misskey-js - misskey-js
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0 - uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View File

@@ -16,13 +16,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0 - uses: actions/setup-node@v6.1.0
with: with:
node-version-file: ".node-version" node-version-file: ".node-version"
cache: "pnpm" cache: "pnpm"

View File

@@ -16,13 +16,13 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View File

@@ -19,7 +19,7 @@ jobs:
edit: edit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得 # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PR - name: Get PR
run: | run: |

View File

@@ -36,7 +36,7 @@ jobs:
outputs: outputs:
pr_number: ${{ steps.get_pr.outputs.pr_number }} pr_number: ${{ steps.get_pr.outputs.pr_number }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得 # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PRs - name: Get PRs
run: | run: |

View File

@@ -16,7 +16,7 @@ jobs:
# api-artifact # api-artifact
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/github-script@v7.1.0 uses: actions/github-script@v8.0.0
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
@@ -60,7 +60,7 @@ jobs:
- name: Echo full diff - name: Echo full diff
run: cat ./api-full.json.diff run: cat ./api-full.json.diff
- name: Upload full diff to Artifact - name: Upload full diff to Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: api-artifact name: api-artifact
path: | path: |
@@ -73,9 +73,9 @@ jobs:
HEADER="このPRによるapi.jsonの差分" HEADER="このPRによるapi.jsonの差分"
FOOTER="[Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})" FOOTER="[Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
DIFF_BYTES="$(stat ./api.json.diff -c '%s' | tr -d '\n')" DIFF_BYTES="$(stat ./api.json.diff -c '%s' | tr -d '\n')"
echo "$HEADER" > ./output.md echo "$HEADER" > ./output.md
if (( "$DIFF_BYTES" <= 1 )); then if (( "$DIFF_BYTES" <= 1 )); then
echo '差分はありません。' >> ./output.md echo '差分はありません。' >> ./output.md
else else
@@ -87,18 +87,18 @@ jobs:
echo '```' >> ./output.md echo '```' >> ./output.md
echo '</details>' >> .output.md echo '</details>' >> .output.md
fi fi
echo "$FOOTER" >> ./output.md echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v2 - uses: thollander/actions-comment-pull-request@v3
with: with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }} pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_diff comment-tag: show_diff
filePath: ./output.md file-path: ./output.md
- name: Tell error to PR - name: Tell error to PR
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number if: failure() && steps.load-pr-num.outputs.pr-number
with: with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }} pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_diff_error comment-tag: show_diff_error
message: | message: |
api.jsonの差分作成中にエラーが発生しました。詳細は[Workflowのログ](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})を確認してください。 api.jsonの差分作成中にエラーが発生しました。詳細は[Workflowのログ](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})を確認してください。

View File

@@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/github-script@v7.1.0 uses: actions/github-script@v8.0.0
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
@@ -107,16 +107,16 @@ jobs:
fi fi
echo "$FOOTER" >> ./output.md echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v2 - uses: thollander/actions-comment-pull-request@v3
with: with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }} pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_memory_diff comment-tag: show_memory_diff
filePath: ./output.md file-path: ./output.md
- name: Tell error to PR - name: Tell error to PR
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number if: failure() && steps.load-pr-num.outputs.pr-number
with: with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }} pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_memory_diff_error comment-tag: show_memory_diff_error
message: | message: |
An error occurred while comparing backend memory usage. See [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. An error occurred while comparing backend memory usage. See [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Reply - name: Reply
uses: actions/github-script@v6 uses: actions/github-script@v8
with: with:
script: | script: |
const body = `To dev team (@misskey-dev/dev): const body = `To dev team (@misskey-dev/dev):

View File

@@ -22,12 +22,12 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=7168" NODE_OPTIONS: "--max_old_space_size=7168"
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
if: github.event_name != 'pull_request_target' if: github.event_name != 'pull_request_target'
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -39,7 +39,7 @@ jobs:
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -90,7 +90,7 @@ jobs:
env: env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes - name: Notify that Chromatic detects changes
uses: actions/github-script@v7.1.0 uses: actions/github-script@v8.0.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false' if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -102,7 +102,7 @@ jobs:
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).' body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
}) })
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: storybook name: storybook
path: packages/frontend/storybook-static path: packages/frontend/storybook-static

View File

@@ -48,9 +48,16 @@ jobs:
image: redis:7 image: redis:7
ports: ports:
- 56312:6379 - 56312:6379
meilisearch:
image: getmeili/meilisearch:v1.3.4
ports:
- 57712:7700
env:
MEILI_NO_ANALYTICS: true
MEILI_ENV: development
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@@ -86,7 +93,7 @@ jobs:
fi fi
done done
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: ${{ matrix.node-version-file }} node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm' cache: 'pnpm'
@@ -129,13 +136,13 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: ${{ matrix.node-version-file }} node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm' cache: 'pnpm'
@@ -173,7 +180,7 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@@ -182,7 +189,7 @@ jobs:
id: current-date id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: ${{ matrix.node-version-file }} node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm' cache: 'pnpm'

View File

@@ -32,7 +32,7 @@ jobs:
- .node-version - .node-version
- .github/min.node-version - .github/min.node-version
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@@ -68,7 +68,7 @@ jobs:
fi fi
done done
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: ${{ matrix.node-version-file }} node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm' cache: 'pnpm'

View File

@@ -28,13 +28,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -76,7 +76,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150 # https://github.com/cypress-io/cypress-docker-images/issues/150
@@ -88,7 +88,7 @@ jobs:
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@@ -113,12 +113,12 @@ jobs:
wait-on: 'http://localhost:61812' wait-on: 'http://localhost:61812'
headed: true headed: true
browser: ${{ matrix.browser }} browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v6
if: failure() if: failure()
with: with:
name: ${{ matrix.browser }}-cypress-screenshots name: ${{ matrix.browser }}-cypress-screenshots
path: cypress/screenshots path: cypress/screenshots
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v6
if: always() if: always()
with: with:
name: ${{ matrix.browser }}-cypress-videos name: ${{ matrix.browser }}-cypress-videos

View File

@@ -22,13 +22,13 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.3.0 uses: actions/checkout@v6.0.1
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View File

@@ -16,13 +16,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View File

@@ -17,13 +17,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.3.0 - uses: actions/checkout@v6.0.1
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.2.0 uses: pnpm/action-setup@v4.2.0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v6.1.0
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

1
.gitignore vendored
View File

@@ -46,6 +46,7 @@ docker-compose.yml
built built
built-test built-test
js-built js-built
src-js
/data /data
/.cache-loader /.cache-loader
/db /db

View File

@@ -1,11 +1,70 @@
## Unreleased ## 2026.1.0
### Note
- `users/following``birthday` プロパティは非推奨になりました。代わりに `users/get-following-birthday-users` をご利用ください。
### General ### General
- - Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey)
- 「今日誕生日のユーザー」は「もうすぐ誕生日のユーザー」に名称変更されました
- 依存関係の更新
### Client
- Enhance: ドライブのファイル一覧で自動でもっと見るを利用可能に
- Enhance: ウィジェットの表示設定をプレビューを見ながら行えるように
- Enhance: ウィジェットの設定項目のラベルの多言語対応
- Enhance: パフォーマンスの向上
- Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061
- Fix: 非ログイン時にログインを求めるダイアログが表示された後にダイアログのぼかしが解除されず操作不能になることがある問題を修正
- Fix: ドライブのソートが「登録日(昇順)」の場合に正しく動作しない問題を修正
- Fix: 高度なMFMのピッカーを使用する際の挙動を改善
- Fix: 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正
- Fix: ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正
- Fix: 2月29日を誕生日に設定している場合、閏年以外は3月1日を誕生日として扱うように修正
### Server
- Enhance: OAuthのクライアント情報取得Client Information Discoveryにおいて、IndieWeb Living Standard 11 July 2024で定義されているJSONドキュメント形式に対応しました
- JSONによるClient Information Discoveryを行うには、レスポンスの`Content-Type`ヘッダーが`application/json`である必要があります
- 従来の実装12 February 2022版・HTML Microformat形式も引き続きサポートされます
- Enhance: メモリ使用量を削減
## 2025.12.2
### Note
v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`に変更」について、正しく環境に応じた設定を行わないとサインインが困難になるといった状態を緩和するために、以下の対応を行いました。
**正しく設定しないと、上記のような不具合の原因となったり、セキュリティリスクが高まったりする可能性があります。必ず現在のconfigをご確認の上、必要に応じて値を変更してください。**
- `trustProxy`について、デフォルトconfigに値が設定されていない状態ではループバックアドレスとローカルIPアドレス空間を信頼するようにしました。
- `trustProxy`の設定方法について、より詳細に記述しました。
- リバースプロキシやCDNなどのより上流のレイヤでレートリミットを設定したい場合や、緊急時の一時的な緩和策として、Misskey内部でのIPアドレスペースでのレートリミットを無効化できるようにしました。
### General
- 依存関係の更新
### Client
- Enhance: デッキのUI説明を追加
- Enhance: 設定がブラウザによって消去されないようにするオプションを追加
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
- Fix: 一部のUnicode絵文字のリアクションがボタンにならない問題を修正
### Server
- Enhance: Misskey内部でのIPアドレスペースでのレートリミットを無効化できるように
- リバースプロキシやCDNなど別のレイヤで別途レートリミットを設定する場合や、ローカルでのテスト用途等として利用することを想定しています。
- デフォルトは `enableIpRateLimit: true`Misskey内部でのIPアドレスペースでのレートリミットは有効です。
- Fix: コントロールパネルのジョブキューページで使用される一部APIの応答速度を改善
## 2025.12.1
### Client ### Client
- Fix: 特定の条件下でMisskeyが起動せず空白のページが表示されることがある問題を軽減 - Fix: 特定の条件下でMisskeyが起動せず空白のページが表示されることがある問題を軽減
- Fix: 初回読み込み時などに、言語設定で不整合が発生することがある問題を修正 - Fix: 初回読み込み時などに、言語設定で不整合が発生することがある問題を修正
- Fix: 削除されたノートのリノートが正しく動作されない問題を修正
- Fix: チャンネルオーナーが削除済みの時にチャンネルのヘッダーメニューが表示されない不具合を修正
- Fix: ドライブで登録日以外でソートする場合は月でグループ化して表示しないように
- Fix: `null` を返す note_view_intrruptor プラグインが動作しない問題を修正
### Server ### Server
- Fix: ジョブキューでSentryが有効にならない問題を修正 - Fix: ジョブキューでSentryが有効にならない問題を修正

View File

@@ -1,5 +1,5 @@
Unless otherwise stated this repository is Unless otherwise stated this repository is
Copyright © 2014-2025 syuilo and contributors Copyright © 2014-2026 syuilo and contributors
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE. And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.

View File

@@ -102,6 +102,7 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/src-js ./packages/backend/src-js
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./ COPY --chown=misskey:misskey . ./

View File

@@ -26,6 +26,8 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/misskey-dev/misskey) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/misskey-dev/misskey)
<a href="https://flatt.tech/oss/gmo/trampoline" target="_blank"><img src="https://flatt.tech/assets/images/badges/gmo-oss.svg" height="24px"/></a>
</div> </div>
## Thanks ## Thanks
@@ -49,3 +51,13 @@ Thanks to [Crowdin](https://crowdin.com/) for providing the localization platfor
<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a> <a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a>
Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production. Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production.
---
<div align="center">
Support us with a ⭐ !
[![Star History Chart](https://api.star-history.com/svg?repos=misskey-dev/misskey&type=Date)](https://star-history.com/#misskey-dev/misskey&Date)
</div>

View File

@@ -21,7 +21,7 @@ services:
env_file: env_file:
- .config/docker.env - .config/docker.env
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql
healthcheck: healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s interval: 5s

View File

@@ -43,7 +43,7 @@ services:
env_file: env_file:
- .config/docker.env - .config/docker.env
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql
healthcheck: healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s interval: 5s

View File

@@ -83,6 +83,7 @@ files: "Dateien"
download: "Herunterladen" download: "Herunterladen"
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Einige Inhalte, die diese Datei verwenden, werden auch verschwinden." driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Einige Inhalte, die diese Datei verwenden, werden auch verschwinden."
unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?" unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?"
rejectFollowRequestConfirm: "Möchtest du die Follow-Anfrage von {name} ablehnen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt." exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen." importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
lists: "Listen" lists: "Listen"
@@ -1018,6 +1019,7 @@ pushNotificationAlreadySubscribed: "Push-Benachrichtigungen sind bereits aktivie
pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterstützt Push-Benachrichtigungen nicht" pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterstützt Push-Benachrichtigungen nicht"
sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald sie gelesen wurden" sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald sie gelesen wurden"
sendPushNotificationReadMessageCaption: "Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen." sendPushNotificationReadMessageCaption: "Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
pleaseAllowPushNotification: "Bitte erlauben Sie Benachrichtigungen in Ihrem Browser."
windowMaximize: "Maximieren" windowMaximize: "Maximieren"
windowMinimize: "Minimieren" windowMinimize: "Minimieren"
windowRestore: "Wiederherstellen" windowRestore: "Wiederherstellen"
@@ -1054,6 +1056,7 @@ permissionDeniedError: "Aktion verweigert"
permissionDeniedErrorDescription: "Dieses Benutzerkonto besitzt nicht die Berechtigung, um diese Aktion auszuführen." permissionDeniedErrorDescription: "Dieses Benutzerkonto besitzt nicht die Berechtigung, um diese Aktion auszuführen."
preset: "Vorlage" preset: "Vorlage"
selectFromPresets: "Aus Vorlagen wählen" selectFromPresets: "Aus Vorlagen wählen"
custom: "Benutzerdefiniert"
achievements: "Errungenschaften" achievements: "Errungenschaften"
gotInvalidResponseError: "Ungültige Antwort des Servers" gotInvalidResponseError: "Ungültige Antwort des Servers"
gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal." gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal."
@@ -1243,6 +1246,7 @@ releaseToRefresh: "Zum Aktualisieren loslassen"
refreshing: "Wird aktualisiert..." refreshing: "Wird aktualisiert..."
pullDownToRefresh: "Zum Aktualisieren ziehen" pullDownToRefresh: "Zum Aktualisieren ziehen"
useGroupedNotifications: "Benachrichtigungen gruppieren" useGroupedNotifications: "Benachrichtigungen gruppieren"
emailVerificationFailedError: "Es gab ein Problem bei der Überprüfung Ihrer E-Mail-Adresse. Der Link ist möglicherweise abgelaufen."
cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden." cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden."
doReaction: "Reagieren" doReaction: "Reagieren"
code: "Code" code: "Code"
@@ -1370,7 +1374,12 @@ defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe"
defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität." defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
inMinutes: "Minute(n)" inMinutes: "Minute(n)"
inDays: "Tag(en)" inDays: "Tag(en)"
safeModeEnabled: "Der abgesicherte Modus ist aktiviert."
schedule: "Planen"
scheduled: "Geplant"
widgets: "Widgets" widgets: "Widgets"
deviceInfo: "Geräteinformation"
youAreAdmin: "Sie sind ein Administrator"
presets: "Vorlage" presets: "Vorlage"
_imageEditing: _imageEditing:
_vars: _vars:

View File

@@ -83,6 +83,8 @@ files: "Files"
download: "Download" download: "Download"
driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted." driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted."
unfollowConfirm: "Are you sure you want to unfollow {name}?" unfollowConfirm: "Are you sure you want to unfollow {name}?"
cancelFollowRequestConfirm: "Are you sure that you want to cancel your follow request to {name}?"
rejectFollowRequestConfirm: "Are you sure that you want to reject the follow request from {name}?"
exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed." exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed."
importRequested: "You've requested an import. This may take a while." importRequested: "You've requested an import. This may take a while."
lists: "Lists" lists: "Lists"

View File

@@ -1252,7 +1252,7 @@ detachAll: "Quitar todo"
angle: "Ángulo" angle: "Ángulo"
flip: "Echar de un capirotazo" flip: "Echar de un capirotazo"
showAvatarDecorations: "Mostrar decoraciones de avatar" showAvatarDecorations: "Mostrar decoraciones de avatar"
releaseToRefresh: "Soltar para recargar" releaseToRefresh: "Suelta para recargar"
refreshing: "Recargando..." refreshing: "Recargando..."
pullDownToRefresh: "Tira hacia abajo para recargar" pullDownToRefresh: "Tira hacia abajo para recargar"
useGroupedNotifications: "Mostrar notificaciones agrupadas" useGroupedNotifications: "Mostrar notificaciones agrupadas"

View File

@@ -1406,6 +1406,7 @@ youAreAdmin: "あなたは管理者です"
frame: "フレーム" frame: "フレーム"
presets: "プリセット" presets: "プリセット"
zeroPadding: "ゼロ埋め" zeroPadding: "ゼロ埋め"
nothingToConfigure: "設定項目はありません"
_imageEditing: _imageEditing:
_vars: _vars:
@@ -1557,6 +1558,9 @@ _settings:
showPageTabBarBottom: "ページのタブバーを下部に表示" showPageTabBarBottom: "ページのタブバーを下部に表示"
emojiPaletteBanner: "絵文字ピッカーに固定表示するプリセットをパレットとして登録したり、ピッカーの表示方法をカスタマイズしたりできます。" emojiPaletteBanner: "絵文字ピッカーに固定表示するプリセットをパレットとして登録したり、ピッカーの表示方法をカスタマイズしたりできます。"
enableAnimatedImages: "アニメーション画像を有効にする" enableAnimatedImages: "アニメーション画像を有効にする"
settingsPersistence_title: "設定の永続化"
settingsPersistence_description1: "設定の永続化を有効にすると、設定情報が失われるのを防止できます。"
settingsPersistence_description2: "環境によっては有効化できない場合があります。"
_chat: _chat:
showSenderName: "送信者の名前を表示" showSenderName: "送信者の名前を表示"
@@ -2596,9 +2600,48 @@ _widgets:
_userList: _userList:
chooseList: "リストを選択" chooseList: "リストを選択"
clicker: "クリッカー" clicker: "クリッカー"
birthdayFollowings: "今日誕生日のユーザー" birthdayFollowings: "もうすぐ誕生日のユーザー"
chat: "ダイレクトメッセージ" chat: "ダイレクトメッセージ"
_widgetOptions:
showHeader: "ヘッダーを表示"
transparent: "背景を透明にする"
height: "高さ"
_button:
colored: "色付き"
_clock:
size: "サイズ"
thickness: "針の太さ"
thicknessThin: "細い"
thicknessMedium: "普通"
thicknessThick: "太い"
graduations: "文字盤の目盛り"
graduationDots: "ドット"
graduationArabic: "アラビア数字"
fadeGraduations: "目盛りをフェード"
sAnimation: "秒針のアニメーション"
sAnimationElastic: "リアル"
sAnimationEaseOut: "滑らか"
twentyFour: "24時間表示"
labelTime: "時刻"
labelTz: "タイムゾーン"
labelTimeAndTz: "時刻とタイムゾーン"
timezone: "タイムゾーン"
showMs: "ミリ秒を表示"
showLabel: "ラベルを表示"
_jobQueue:
sound: "音を鳴らす"
_rss:
url: "RSSフィードのURL"
refreshIntervalSec: "更新間隔(秒)"
maxEntries: "最大表示件数"
_rssTicker:
shuffle: "表示順をシャッフル"
duration: "ティッカーのスクロール速度(秒)"
reverse: "逆方向にスクロール"
_birthdayFollowings:
period: "期間"
_cw: _cw:
hide: "隠す" hide: "隠す"
show: "もっと見る" show: "もっと見る"
@@ -2890,6 +2933,15 @@ _deck:
usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります" usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります"
flexible: "幅を自動調整" flexible: "幅を自動調整"
enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする" enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする"
showHowToUse: "UIの説明を見る"
_howToUse:
addColumn_title: "カラム追加"
addColumn_description: "カラムの種類を選んで追加できます。"
settings_title: "UI設定"
settings_description: "デッキUIの詳細設定を行えます。"
switchProfile_title: "プロファイル切り替え"
switchProfile_description: "UIのレイアウトをプロファイルとして保存し、いつでも切り替えられるようにできます。"
_columns: _columns:
main: "メイン" main: "メイン"
@@ -3406,7 +3458,6 @@ _imageEffector:
title: "エフェクト" title: "エフェクト"
addEffect: "エフェクトを追加" addEffect: "エフェクトを追加"
discardChangesConfirm: "変更を破棄して終了しますか?" discardChangesConfirm: "変更を破棄して終了しますか?"
nothingToConfigure: "設定項目はありません"
failedToLoadImage: "画像の読み込みに失敗しました" failedToLoadImage: "画像の読み込みに失敗しました"
_fxs: _fxs:

View File

@@ -53,7 +53,7 @@ copyRemoteLink: "复制远程链接"
copyLinkRenote: "复制转帖链接" copyLinkRenote: "复制转帖链接"
delete: "删除" delete: "删除"
deleteAndEdit: "删除并编辑" deleteAndEdit: "删除并编辑"
deleteAndEditConfirm: "要删除此帖并再次编辑吗?此帖所有回应、转发和回复也将被删除。" deleteAndEditConfirm: "要删除此帖并再次编辑吗?此帖所有回应、转发和回复也将被删除。"
addToList: "添加至列表" addToList: "添加至列表"
addToAntenna: "添加到天线" addToAntenna: "添加到天线"
sendMessage: "发送消息" sendMessage: "发送消息"
@@ -136,7 +136,7 @@ emojiPicker: "表情符号选择器"
pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显示的表情符号" pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显示的表情符号"
pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号" pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号"
emojiPickerDisplay: "选择器显示设置" emojiPickerDisplay: "选择器显示设置"
overwriteFromPinnedEmojisForReaction: "「置顶(回应)」设置覆盖" overwriteFromPinnedEmojisForReaction: "使用「置顶(回应)」设置覆盖"
overwriteFromPinnedEmojis: "从全局设置覆盖" overwriteFromPinnedEmojis: "从全局设置覆盖"
reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。" reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
rememberNoteVisibility: "保存上次设置的可见性" rememberNoteVisibility: "保存上次设置的可见性"
@@ -153,8 +153,8 @@ block: "拉黑"
unblock: "取消拉黑" unblock: "取消拉黑"
suspend: "冻结" suspend: "冻结"
unsuspend: "解除冻结" unsuspend: "解除冻结"
blockConfirm: "确定要拉黑吗?" blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要取消拉黑吗?" unblockConfirm: "确定要取消屏蔽吗?"
suspendConfirm: "要冻结吗?" suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?" unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表" selectList: "选择列表"
@@ -174,7 +174,7 @@ emojiUrl: "emoji 地址"
addEmoji: "添加表情符号" addEmoji: "添加表情符号"
settingGuide: "推荐配置" settingGuide: "推荐配置"
cacheRemoteFiles: "缓存远程文件" cacheRemoteFiles: "缓存远程文件"
cacheRemoteFilesDescription: "启用此设定时,将在此服务器上缓存远程文件。虽然可以加快图片显示的速度,但是相对的会消耗大量的服务器存储空间。用户角色内的网盘容量决定了这个远程用户能在服务器上保留多少缓存。当超出了这个限制时,旧的文件将从缓存中被删除,成为链接。当禁用此设定时,则是从一开始就将远程文件保留为链接。此时推荐将 default.yml 的 proxyRemoteFiles 设置为 true 以优化缩略图生成及保护用户隐私。" cacheRemoteFilesDescription: "启用此设定时,将在此服务器上缓存远程文件。虽然可以加快图片显示的速度,但是相对的会消耗大量的服务器存储空间。用户角色内的网盘容量决定了这个远程用户能在服务器上保留多少缓存。当超出了这个限制时,旧的文件将从缓存中被删除,成为链接。当禁用此设定时,则是从一开始就将远程文件保留为链接。此时推荐将 的 proxyRemoteFiles 设置为 true 以优化缩略图生成及保护用户隐私。"
youCanCleanRemoteFilesCache: "可以使用文件管理的🗑️按钮来删除所有的缓存。" youCanCleanRemoteFilesCache: "可以使用文件管理的🗑️按钮来删除所有的缓存。"
cacheRemoteSensitiveFiles: "缓存远程敏感媒体文件" cacheRemoteSensitiveFiles: "缓存远程敏感媒体文件"
cacheRemoteSensitiveFilesDescription: "如果禁用这项设定,远程服务器的敏感媒体将不会被缓存,而是直接链接。" cacheRemoteSensitiveFilesDescription: "如果禁用这项设定,远程服务器的敏感媒体将不会被缓存,而是直接链接。"
@@ -184,7 +184,7 @@ flagAsCat: "喵!!!!!!!!!!!!"
flagAsCatDescription: "喵喵喵??" flagAsCatDescription: "喵喵喵??"
flagShowTimelineReplies: "在时间线上显示帖子的回复" flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。" flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许来自我关注的用户对我的关注请求" autoAcceptFollowed: "自动允许回关请求"
addAccount: "添加账户" addAccount: "添加账户"
reloadAccountsList: "更新账户列表" reloadAccountsList: "更新账户列表"
loginFailed: "登录失败" loginFailed: "登录失败"
@@ -247,8 +247,8 @@ mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,
federationAllowedHosts: "允许联合的服务器" federationAllowedHosts: "允许联合的服务器"
federationAllowedHostsDescription: "设定允许联合的服务器,以换行分隔。" federationAllowedHostsDescription: "设定允许联合的服务器,以换行分隔。"
muteAndBlock: "屏蔽/拉黑" muteAndBlock: "屏蔽/拉黑"
mutedUsers: "已屏蔽用户" mutedUsers: "已静音的用户"
blockedUsers: "已拉黑的用户" blockedUsers: "已屏蔽的用户"
noUsers: "无用户" noUsers: "无用户"
editProfile: "编辑资料" editProfile: "编辑资料"
noteDeleteConfirm: "确定要删除该帖子吗?" noteDeleteConfirm: "确定要删除该帖子吗?"
@@ -1344,7 +1344,7 @@ skip: "跳过"
restore: "恢复" restore: "恢复"
syncBetweenDevices: "设备间同步" syncBetweenDevices: "设备间同步"
preferenceSyncConflictTitle: "服务器上已存在设定值" preferenceSyncConflictTitle: "服务器上已存在设定值"
preferenceSyncConflictText: "服务器上已有此设置的设定值。要覆盖哪个设定值?" preferenceSyncConflictText: "即将保存设定值到服务器,但检测到服务器上已有此设置的设定值。要使用哪个设定值?"
preferenceSyncConflictChoiceMerge: "合并" preferenceSyncConflictChoiceMerge: "合并"
preferenceSyncConflictChoiceServer: "服务器上的设定值" preferenceSyncConflictChoiceServer: "服务器上的设定值"
preferenceSyncConflictChoiceDevice: "设备上的设定值" preferenceSyncConflictChoiceDevice: "设备上的设定值"
@@ -3270,7 +3270,7 @@ _watermarkEditor:
driveFileTypeWarn: "不支持此文件" driveFileTypeWarn: "不支持此文件"
driveFileTypeWarnDescription: "请选择图像文件" driveFileTypeWarnDescription: "请选择图像文件"
title: "编辑水印" title: "编辑水印"
cover: "覆盖全体" cover: "覆盖所有"
repeat: "平铺" repeat: "平铺"
preserveBoundingRect: "调整为旋转时不超出范围" preserveBoundingRect: "调整为旋转时不超出范围"
opacity: "不透明度" opacity: "不透明度"

View File

@@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.12.0", "version": "2026.1.0-alpha.4",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@10.24.0", "packageManager": "pnpm@10.27.0",
"workspaces": [ "workspaces": [
"packages/misskey-js", "packages/misskey-js",
"packages/i18n", "packages/i18n",
@@ -23,12 +23,12 @@
"private": true, "private": true,
"scripts": { "scripts": {
"compile-config": "cd packages/backend && pnpm compile-config", "compile-config": "cd packages/backend && pnpm compile-config",
"build-pre": "node ./scripts/build-pre.js", "build-pre": "node scripts/build-pre.mjs",
"build-assets": "node ./scripts/build-assets.mjs", "build-assets": "node ./scripts/build-assets.mjs",
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook", "build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api", "build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "pnpm check:connect && cd packages/backend && pnpm compile-config && node ./built/boot/entry.js", "start": "cd packages/backend && pnpm compile-config && node ./built/boot/entry.js",
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/boot/entry.js", "start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js", "start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"cli": "cd packages/backend && pnpm cli", "cli": "cd packages/backend && pnpm cli",
@@ -39,7 +39,7 @@
"migrateandstart": "pnpm migrate && pnpm start", "migrateandstart": "pnpm migrate && pnpm start",
"watch": "pnpm dev", "watch": "pnpm dev",
"dev": "node scripts/dev.mjs", "dev": "node scripts/dev.mjs",
"lint": "pnpm -r lint", "lint": "pnpm --no-bail -r lint",
"cy:open": "pnpm cypress open --config-file=cypress.config.ts", "cy:open": "pnpm cypress open --config-file=cypress.config.ts",
"cy:run": "pnpm cypress run", "cy:run": "pnpm cypress run",
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run", "e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
@@ -48,40 +48,39 @@
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage", "jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
"test": "pnpm -r test", "test": "pnpm -r test",
"test-and-coverage": "pnpm -r test-and-coverage", "test-and-coverage": "pnpm -r test-and-coverage",
"clean": "node ./scripts/clean.js", "clean": "node scripts/clean.mjs",
"clean-all": "node ./scripts/clean-all.js", "clean-all": "node scripts/clean-all.mjs",
"cleanall": "pnpm clean-all" "cleanall": "pnpm clean-all"
}, },
"resolutions": { "resolutions": {
"chokidar": "4.0.3", "chokidar": "5.0.0",
"lodash": "4.17.21" "lodash": "4.17.21"
}, },
"dependencies": { "dependencies": {
"cssnano": "7.1.2", "cssnano": "7.1.2",
"esbuild": "0.27.0", "esbuild": "0.27.2",
"execa": "9.6.0", "execa": "9.6.1",
"fast-glob": "3.3.3",
"glob": "13.0.0",
"ignore-walk": "8.0.0", "ignore-walk": "8.0.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"postcss": "8.5.6", "postcss": "8.5.6",
"tar": "7.5.2", "tar": "7.5.2",
"terser": "5.44.1", "terser": "5.44.1"
"typescript": "5.9.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.39.1", "@eslint/js": "9.39.2",
"@misskey-dev/eslint-plugin": "2.2.0", "@misskey-dev/eslint-plugin": "2.2.0",
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/node": "24.10.1", "@types/node": "24.10.4",
"@typescript-eslint/eslint-plugin": "8.47.0", "@typescript-eslint/eslint-plugin": "8.50.1",
"@typescript-eslint/parser": "8.47.0", "@typescript-eslint/parser": "8.50.1",
"@typescript/native-preview": "7.0.0-dev.20251226.1",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"cypress": "15.7.0", "cypress": "15.8.1",
"eslint": "9.39.1", "eslint": "9.39.2",
"globals": "16.5.0", "globals": "16.5.0",
"ncp": "2.0.0", "ncp": "2.0.0",
"pnpm": "10.24.0", "pnpm": "10.27.0",
"typescript": "5.9.3",
"start-server-and-test": "2.1.3" "start-server-and-test": "2.1.3"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@@ -9,7 +9,7 @@ window.onload = async () => {
const account = JSON.parse(localStorage.getItem('account')); const account = JSON.parse(localStorage.getItem('account'));
const i = account.token; const i = account.token;
const api = (endpoint, data = {}) => { const _api = (endpoint, data = {}) => {
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
// Append a credential // Append a credential
if (i) data.i = i; if (i) data.i = i;

121
packages/backend/build.js Normal file
View File

@@ -0,0 +1,121 @@
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { build } from 'esbuild';
import { swcPlugin } from 'esbuild-plugin-swc';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const resolveTsPathsPlugin = {
name: 'resolve-ts-paths',
setup(build) {
build.onResolve({ filter: /^\.{1,2}\/.*\.js$/ }, (args) => {
if (args.importer) {
const absPath = join(args.resolveDir, args.path);
const tsPath = absPath.slice(0, -3) + '.ts';
if (fs.existsSync(tsPath)) return { path: tsPath };
const tsxPath = absPath.slice(0, -3) + '.tsx';
if (fs.existsSync(tsxPath)) return { path: tsxPath };
}
});
},
};
const externalIpaddrPlugin = {
name: 'external-ipaddr',
setup(build) {
build.onResolve({ filter: /^ipaddr\.js$/ }, (args) => {
return { path: args.path, external: true };
});
},
};
/** @type {import('esbuild').BuildOptions} */
const options = {
entryPoints: ['./src/boot/entry.ts'],
minify: true,
keepNames: true,
bundle: true,
outdir: './built/boot',
target: 'node22',
platform: 'node',
format: 'esm',
sourcemap: 'linked',
packages: 'external',
banner: {
js: 'import { createRequire as topLevelCreateRequire } from "module";' +
'import ___url___ from "url";' +
'const require = topLevelCreateRequire(import.meta.url);' +
'const __filename = ___url___.fileURLToPath(import.meta.url);' +
'const __dirname = ___url___.fileURLToPath(new URL(".", import.meta.url));',
},
plugins: [
externalIpaddrPlugin,
resolveTsPathsPlugin,
swcPlugin({
jsc: {
parser: {
syntax: 'typescript',
decorators: true,
dynamicImport: true,
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
},
experimental: {
keepImportAssertions: true,
},
baseUrl: join(_dirname, 'src'),
paths: {
'@/*': ['*'],
},
target: 'esnext',
keepClassNames: true,
},
}),
externalIpaddrPlugin,
],
// external: [
// 'slacc-*',
// 'class-transformer',
// 'class-validator',
// '@sentry/*',
// '@nestjs/websockets/socket-module',
// '@nestjs/microservices/microservices-module',
// '@nestjs/microservices',
// '@napi-rs/canvas-win32-x64-msvc',
// 'mock-aws-s3',
// 'aws-sdk',
// 'nock',
// 'sharp',
// 'jsdom',
// 're2',
// '@napi-rs/canvas',
// ],
};
const args = process.argv.slice(2).map(arg => arg.toLowerCase());
if (!args.includes('--no-clean')) {
fs.rmSync('./built', { recursive: true, force: true });
}
await buildSrc();
async function buildSrc() {
console.log(`[${_package.name}] start building...`);
await build(options)
.then(() => {
console.log(`[${_package.name}] build succeeded.`);
})
.catch((err) => {
process.stderr.write(err.stderr || err.message || err);
process.exit(1);
});
console.log(`[${_package.name}] finish building.`);
}

View File

@@ -25,7 +25,6 @@ export default [
}, },
}, },
rules: { rules: {
'@typescript-eslint/no-unused-vars': 'off',
'import/order': ['warn', { 'import/order': ['warn', {
groups: [ groups: [
'builtin', 'builtin',

View File

@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class BirthdayIndex1767169026317 {
name = 'BirthdayIndex1767169026317'
async up(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_de22cd2b445eee31ae51cdbe99"`);
await queryRunner.query(`CREATE OR REPLACE FUNCTION get_birthday_date(birthday TEXT) RETURNS SMALLINT AS $$ BEGIN RETURN CAST((SUBSTR(birthday, 6, 2) || SUBSTR(birthday, 9, 2)) AS SMALLINT); END; $$ LANGUAGE plpgsql IMMUTABLE;`);
await queryRunner.query(`CREATE INDEX "IDX_USERPROFILE_BIRTHDAY_DATE" ON "user_profile" (get_birthday_date("birthday"))`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE INDEX "IDX_de22cd2b445eee31ae51cdbe99" ON "user_profile" (substr("birthday", 6, 5))`);
await queryRunner.query(`DROP INDEX "public"."IDX_USERPROFILE_BIRTHDAY_DATE"`);
await queryRunner.query(`DROP FUNCTION IF EXISTS get_birthday_date(birthday TEXT)`);
}
}

View File

@@ -1,6 +1,6 @@
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { loadConfig } from './built/config.js'; import { loadConfig } from './src-js/config.js';
import { entities } from './built/postgres.js'; import { entities } from './src-js/postgres.js';
const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1'; const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';

View File

@@ -12,17 +12,17 @@
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js", "start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"migrate": "pnpm compile-config && pnpm typeorm migration:run -d ormconfig.js", "migrate": "pnpm compile-config && pnpm typeorm migration:run -d ormconfig.js",
"revert": "pnpm compile-config && pnpm typeorm migration:revert -d ormconfig.js", "revert": "pnpm compile-config && pnpm typeorm migration:revert -d ormconfig.js",
"cli": "pnpm compile-config && node ./built/boot/cli.js", "cli": "pnpm compile-config && node ./src-js/boot/cli.js",
"check:connect": "pnpm compile-config && node ./scripts/check_connect.js", "check:connect": "pnpm compile-config && node ./scripts/check_connect.js",
"compile-config": "node ./scripts/compile_config.js", "compile-config": "node ./scripts/compile_config.js",
"build": "swc src -d built -D --strip-leading-paths", "build": "swc src -d src-js -D --strip-leading-paths && node ./build.js",
"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths", "build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
"watch:swc": "swc src -d built -D -w --strip-leading-paths", "watch:swc": "swc src -d built -D -w --strip-leading-paths",
"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
"watch": "pnpm compile-config && node ./scripts/watch.mjs", "watch": "pnpm compile-config && node ./scripts/watch.mjs",
"restart": "pnpm build && pnpm start", "restart": "pnpm build && pnpm start",
"dev": "pnpm compile-config && node ./scripts/dev.mjs", "dev": "pnpm compile-config && node ./scripts/dev.mjs",
"typecheck": "tsc --noEmit && tsc -p test --noEmit && tsc -p test-federation --noEmit", "typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"", "eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
"lint": "pnpm typecheck && pnpm eslint", "lint": "pnpm typecheck && pnpm eslint",
"jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs", "jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs",
@@ -41,20 +41,20 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.15.3", "@swc/core-darwin-arm64": "1.15.7",
"@swc/core-darwin-x64": "1.15.3", "@swc/core-darwin-x64": "1.15.7",
"@swc/core-freebsd-x64": "1.3.11", "@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.15.3", "@swc/core-linux-arm-gnueabihf": "1.15.7",
"@swc/core-linux-arm64-gnu": "1.15.3", "@swc/core-linux-arm64-gnu": "1.15.7",
"@swc/core-linux-arm64-musl": "1.15.3", "@swc/core-linux-arm64-musl": "1.15.7",
"@swc/core-linux-x64-gnu": "1.15.3", "@swc/core-linux-x64-gnu": "1.15.7",
"@swc/core-linux-x64-musl": "1.15.3", "@swc/core-linux-x64-musl": "1.15.7",
"@swc/core-win32-arm64-msvc": "1.15.3", "@swc/core-win32-arm64-msvc": "1.15.7",
"@swc/core-win32-ia32-msvc": "1.15.3", "@swc/core-win32-ia32-msvc": "1.15.7",
"@swc/core-win32-x64-msvc": "1.15.3", "@swc/core-win32-x64-msvc": "1.15.7",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9", "bufferutil": "4.1.0",
"slacc-android-arm-eabi": "0.0.10", "slacc-android-arm-eabi": "0.0.10",
"slacc-android-arm64": "0.0.10", "slacc-android-arm64": "0.0.10",
"slacc-darwin-arm64": "0.0.10", "slacc-darwin-arm64": "0.0.10",
@@ -68,34 +68,33 @@
"slacc-linux-x64-musl": "0.0.10", "slacc-linux-x64-musl": "0.0.10",
"slacc-win32-arm64-msvc": "0.0.10", "slacc-win32-arm64-msvc": "0.0.10",
"slacc-win32-x64-msvc": "0.0.10", "slacc-win32-x64-msvc": "0.0.10",
"utf-8-validate": "6.0.5" "utf-8-validate": "6.0.6"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.940.0", "@aws-sdk/client-s3": "3.958.0",
"@aws-sdk/lib-storage": "3.940.0", "@aws-sdk/lib-storage": "3.958.0",
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3", "@fastify/accepts": "5.0.4",
"@fastify/cookie": "11.0.2", "@fastify/cors": "11.2.0",
"@fastify/cors": "11.1.0",
"@fastify/express": "4.0.2", "@fastify/express": "4.0.2",
"@fastify/http-proxy": "11.3.0", "@fastify/http-proxy": "11.4.1",
"@fastify/multipart": "9.3.0", "@fastify/multipart": "9.3.0",
"@fastify/static": "8.3.0", "@fastify/static": "8.3.0",
"@kitajs/html": "4.2.11", "@kitajs/html": "4.2.11",
"@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5", "@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.83", "@napi-rs/canvas": "0.1.87",
"@nestjs/common": "11.1.9", "@nestjs/common": "11.1.10",
"@nestjs/core": "11.1.9", "@nestjs/core": "11.1.10",
"@nestjs/testing": "11.1.9", "@nestjs/testing": "11.1.10",
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@sentry/node": "10.27.0", "@sentry/node": "10.32.1",
"@sentry/profiling-node": "10.27.0", "@sentry/profiling-node": "10.32.1",
"@simplewebauthn/server": "13.2.2", "@simplewebauthn/server": "13.2.2",
"@sinonjs/fake-timers": "15.0.0", "@sinonjs/fake-timers": "15.1.0",
"@smithy/node-http-handler": "4.4.5", "@smithy/node-http-handler": "4.4.7",
"@swc/cli": "0.7.9", "@swc/cli": "0.7.9",
"@swc/core": "1.15.3", "@swc/core": "1.15.7",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3", "@types/redis-info": "3.0.3",
"accepts": "1.3.8", "accepts": "1.3.8",
@@ -105,12 +104,11 @@
"bcryptjs": "3.0.3", "bcryptjs": "3.0.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "2.2.1", "body-parser": "2.2.1",
"bullmq": "5.65.0", "bullmq": "5.66.3",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "10.0.11",
"chalk": "5.6.2", "chalk": "5.6.2",
"chalk-template": "1.1.2", "chalk-template": "1.1.2",
"chokidar": "4.0.3", "chokidar": "5.0.0",
"color-convert": "3.1.3", "color-convert": "3.1.3",
"content-disposition": "1.0.1", "content-disposition": "1.0.1",
"date-fns": "4.1.0", "date-fns": "4.1.0",
@@ -118,7 +116,7 @@
"fastify": "5.6.2", "fastify": "5.6.2",
"fastify-raw-body": "5.0.0", "fastify-raw-body": "5.0.0",
"feed": "5.1.0", "feed": "5.1.0",
"file-type": "21.1.1", "file-type": "21.2.0",
"fluent-ffmpeg": "2.1.3", "fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5", "form-data": "4.0.5",
"got": "14.6.5", "got": "14.6.5",
@@ -131,7 +129,6 @@
"is-svg": "6.1.0", "is-svg": "6.1.0",
"json5": "2.2.3", "json5": "2.2.3",
"jsonld": "9.0.0", "jsonld": "9.0.0",
"jsrsasign": "11.1.0",
"juice": "11.0.3", "juice": "11.0.3",
"meilisearch": "0.54.0", "meilisearch": "0.54.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
@@ -143,9 +140,8 @@
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.3.2", "node-fetch": "3.3.2",
"node-html-parser": "7.0.1", "node-html-parser": "7.0.1",
"nodemailer": "7.0.11", "nodemailer": "7.0.12",
"nsfwjs": "4.2.0", "nsfwjs": "4.2.0",
"oauth": "0.10.2",
"oauth2orize": "1.12.0", "oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2", "oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14", "os-utils": "0.0.14",
@@ -157,7 +153,7 @@
"qrcode": "1.5.4", "qrcode": "1.5.4",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"ratelimiter": "3.4.1", "ratelimiter": "3.4.1",
"re2": "1.22.3", "re2": "1.23.0",
"redis-info": "3.1.0", "redis-info": "3.1.0",
"reflect-metadata": "0.2.2", "reflect-metadata": "0.2.2",
"rename": "1.0.4", "rename": "1.0.4",
@@ -170,14 +166,12 @@
"slacc": "0.0.10", "slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"systeminformation": "5.27.11", "systeminformation": "5.28.1",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.5", "tmp": "0.2.5",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "typeorm": "0.3.28",
"typeorm": "0.3.27", "ulid": "3.0.2",
"typescript": "5.9.3",
"ulid": "3.0.1",
"vary": "1.1.2", "vary": "1.1.2",
"web-push": "3.6.7", "web-push": "3.6.7",
"ws": "8.18.3", "ws": "8.18.3",
@@ -186,8 +180,8 @@
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@kitajs/ts-html-plugin": "4.1.3", "@kitajs/ts-html-plugin": "4.1.3",
"@nestjs/platform-express": "11.1.9", "@nestjs/platform-express": "11.1.10",
"@sentry/vue": "10.27.0", "@sentry/vue": "10.32.1",
"@simplewebauthn/types": "12.0.0", "@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39", "@swc/jest": "0.2.39",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",
@@ -199,22 +193,20 @@
"@types/http-link-header": "1.0.7", "@types/http-link-header": "1.0.7",
"@types/jest": "29.5.14", "@types/jest": "29.5.14",
"@types/jsonld": "1.5.15", "@types/jsonld": "1.5.15",
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "3.0.1", "@types/mime-types": "3.0.1",
"@types/ms": "2.1.0", "@types/ms": "2.1.0",
"@types/node": "24.10.1", "@types/node": "24.10.4",
"@types/nodemailer": "7.0.4", "@types/nodemailer": "7.0.4",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5", "@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2", "@types/oauth2orize-pkce": "0.1.2",
"@types/pg": "8.15.6", "@types/pg": "8.16.0",
"@types/qrcode": "1.5.6", "@types/qrcode": "1.5.6",
"@types/random-seed": "0.3.5", "@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6", "@types/ratelimiter": "3.4.6",
"@types/rename": "1.0.7", "@types/rename": "1.0.7",
"@types/sanitize-html": "2.16.0", "@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1", "@types/semver": "7.7.1",
"@types/simple-oauth2": "5.0.7", "@types/simple-oauth2": "5.0.8",
"@types/sinonjs__fake-timers": "15.0.1", "@types/sinonjs__fake-timers": "15.0.1",
"@types/supertest": "6.0.3", "@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
@@ -222,21 +214,22 @@
"@types/vary": "1.1.3", "@types/vary": "1.1.3",
"@types/web-push": "3.6.4", "@types/web-push": "3.6.4",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.48.0", "@typescript-eslint/eslint-plugin": "8.50.1",
"@typescript-eslint/parser": "8.48.0", "@typescript-eslint/parser": "8.50.1",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.11",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"esbuild-plugin-swc": "1.0.1",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"execa": "9.6.0", "execa": "9.6.1",
"fkill": "10.0.1", "fkill": "10.0.1",
"jest": "29.7.0", "jest": "29.7.0",
"jest-mock": "29.7.0", "jest-mock": "29.7.0",
"jest-util": "29.7.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"nodemon": "3.1.11", "nodemon": "3.1.11",
"pid-port": "2.0.0", "pid-port": "2.0.0",
"simple-oauth2": "5.1.0", "simple-oauth2": "5.1.0",
"supertest": "7.1.4", "supertest": "7.1.4",
"vite": "7.2.4" "vite": "7.3.0"
} }
} }

View File

@@ -4,8 +4,8 @@
*/ */
import Redis from 'ioredis'; import Redis from 'ioredis';
import { loadConfig } from '../built/config.js'; import { loadConfig } from '../src-js/config.js';
import { createPostgresDataSource } from '../built/postgres.js'; import { createPostgresDataSource } from '../src-js/postgres.js';
const config = loadConfig(); const config = loadConfig();
@@ -16,26 +16,22 @@ async function connectToPostgres() {
} }
async function connectToRedis(redisOptions) { async function connectToRedis(redisOptions) {
return await new Promise(async (resolve, reject) => { let redis;
const redis = new Redis({ try {
redis = new Redis({
...redisOptions, ...redisOptions,
lazyConnect: true, lazyConnect: true,
reconnectOnError: false, reconnectOnError: false,
showFriendlyErrorStack: true, showFriendlyErrorStack: true,
}); });
redis.on('error', e => reject(e));
try { await Promise.race([
await redis.connect(); new Promise((_, reject) => redis.on('error', e => reject(e))),
resolve(); redis.connect(),
]);
} catch (e) { } finally {
reject(e); redis.disconnect(false);
}
} finally {
redis.disconnect(false);
}
});
} }
// If not all of these are defined, the default one gets reused. // If not all of these are defined, the default one gets reused.
@@ -50,7 +46,7 @@ const promises = Array
])) ]))
.map(connectToRedis) .map(connectToRedis)
.concat([ .concat([
connectToPostgres() connectToPostgres(),
]); ]);
await Promise.all(promises); await Promise.all(promises);

View File

@@ -3,8 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { writeFileSync, existsSync } from 'node:fs';
import { execa } from 'execa'; import { execa } from 'execa';
import { writeFileSync, existsSync } from "node:fs";
async function main() { async function main() {
if (!process.argv.includes('--no-build')) { if (!process.argv.includes('--no-build')) {
@@ -19,10 +19,10 @@ async function main() {
} }
/** @type {import('../src/config.js')} */ /** @type {import('../src/config.js')} */
const { loadConfig } = await import('../built/config.js'); const { loadConfig } = await import('../src-js/config.js');
/** @type {import('../src/server/api/openapi/gen-spec.js')} */ /** @type {import('../src/server/api/openapi/gen-spec.js')} */
const { genOpenapiSpec } = await import('../built/server/api/openapi/gen-spec.js'); const { genOpenapiSpec } = await import('../src-js/server/api/openapi/gen-spec.js');
const config = loadConfig(); const config = loadConfig();
const spec = genOpenapiSpec(config, true); const spec = genOpenapiSpec(config, true);

View File

@@ -21,7 +21,7 @@ import { execa } from 'execa';
}); });
}, 3000); }, 3000);
execa('tsc', ['-w', '-p', 'tsconfig.json'], { execa('tsgo', ['-w', '-p', 'tsconfig.json'], {
stdout: process.stdout, stdout: process.stdout,
stderr: process.stderr, stderr: process.stderr,
}); });

View File

@@ -4,8 +4,6 @@
*/ */
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as os from 'node:os'; import * as os from 'node:os';
import cluster from 'node:cluster'; import cluster from 'node:cluster';
import chalk from 'chalk'; import chalk from 'chalk';
@@ -17,20 +15,15 @@ import { showMachineInfo } from '@/misc/show-machine-info.js';
import { envOption } from '@/env.js'; import { envOption } from '@/env.js';
import { jobQueue, server } from './common.js'; import { jobQueue, server } from './common.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
const logger = new Logger('core', 'cyan'); const logger = new Logger('core', 'cyan');
const bootLogger = logger.createSubLogger('boot', 'magenta'); const bootLogger = logger.createSubLogger('boot', 'magenta');
const themeColor = chalk.hex('#86b300'); const themeColor = chalk.hex('#86b300');
function greet() { function greet(props: { version: string }) {
if (!envOption.quiet) { if (!envOption.quiet) {
//#region Misskey logo //#region Misskey logo
const v = `v${meta.version}`; const v = `v${props.version}`;
console.log(themeColor(' _____ _ _ ')); console.log(themeColor(' _____ _ _ '));
console.log(themeColor(' | |_|___ ___| |_ ___ _ _ ')); console.log(themeColor(' | |_|___ ___| |_ ___ _ _ '));
console.log(themeColor(' | | | | |_ -|_ -| \'_| -_| | |')); console.log(themeColor(' | | | | |_ -|_ -| \'_| -_| | |'));
@@ -46,7 +39,7 @@ function greet() {
} }
bootLogger.info('Welcome to Misskey!'); bootLogger.info('Welcome to Misskey!');
bootLogger.info(`Misskey v${meta.version}`, null, true); bootLogger.info(`Misskey v${props.version}`, null, true);
} }
/** /**
@@ -57,15 +50,15 @@ export async function masterMain() {
// initialize app // initialize app
try { try {
greet(); config = loadConfigBoot();
greet({ version: config.version });
showEnvironment(); showEnvironment();
await showMachineInfo(bootLogger); await showMachineInfo(bootLogger);
showNodejsVersion(); showNodejsVersion();
config = loadConfigBoot();
//await connectDb(); //await connectDb();
if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString()); if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString());
} catch (e) { } catch (e) {
bootLogger.error('Fatal error occurred during initialization', null, true); bootLogger.error('Fatal error occurred during initialization: ' + e, null, true);
process.exit(1); process.exit(1);
} }

View File

@@ -30,6 +30,7 @@ type Source = {
socket?: string; socket?: string;
trustProxy?: FastifyServerOptions['trustProxy']; trustProxy?: FastifyServerOptions['trustProxy'];
chmodSocket?: string; chmodSocket?: string;
enableIpRateLimit?: boolean;
disableHsts?: boolean; disableHsts?: boolean;
db: { db: {
host: string; host: string;
@@ -120,8 +121,9 @@ export type Config = {
url: string; url: string;
port: number; port: number;
socket: string | undefined; socket: string | undefined;
trustProxy: FastifyServerOptions['trustProxy']; trustProxy: NonNullable<FastifyServerOptions['trustProxy']>;
chmodSocket: string | undefined; chmodSocket: string | undefined;
enableIpRateLimit: boolean;
disableHsts: boolean | undefined; disableHsts: boolean | undefined;
db: { db: {
host: string; host: string;
@@ -217,24 +219,42 @@ export type FulltextSearchProvider = 'sqlLike' | 'sqlPgroonga' | 'meilisearch';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
const compiledConfigFilePathForTest = resolve(_dirname, '../../../built/._config_.json'); /** Path of repository root directory */
let rootDir = _dirname;
// 見つかるまで上に遡る
while (!fs.existsSync(resolve(rootDir, 'packages'))) {
const parentDir = dirname(rootDir);
if (parentDir === rootDir) {
throw new Error('Cannot find root directory');
}
rootDir = parentDir;
}
export const compiledConfigFilePath = fs.existsSync(compiledConfigFilePathForTest) ? compiledConfigFilePathForTest : resolve(_dirname, '../../../built/.config.json'); /** Path of configuration directory */
const configDir = resolve(rootDir, '.config');
/** Path of built directory */
const projectBuiltDir = resolve(rootDir, 'built');
const compiledConfigFilePathForTest = resolve(projectBuiltDir, '._config_.json');
export const compiledConfigFilePath = fs.existsSync(compiledConfigFilePathForTest)
? compiledConfigFilePathForTest
: resolve(projectBuiltDir, '.config.json');
export function loadConfig(): Config { export function loadConfig(): Config {
if (!fs.existsSync(compiledConfigFilePath)) { if (!fs.existsSync(compiledConfigFilePath)) {
throw new Error('Compiled configuration file not found. Try running \'pnpm compile-config\'.'); throw new Error('Compiled configuration file not found. Try running \'pnpm compile-config\'.');
} }
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); const meta = JSON.parse(fs.readFileSync(resolve(projectBuiltDir, 'meta.json'), 'utf-8'));
const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json'); const frontendManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'));
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json'); const frontendEmbedManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'));
const frontendManifest = frontendManifestExists ? const frontendManifest = frontendManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8')) JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'), 'utf-8'))
: { 'src/_boot_.ts': { file: null } }; : { 'src/_boot_.ts': { file: null } };
const frontendEmbedManifest = frontendEmbedManifestExists ? const frontendEmbedManifest = frontendEmbedManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8')) JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'), 'utf-8'))
: { 'src/boot.ts': { file: null } }; : { 'src/boot.ts': { file: null } };
const config = JSON.parse(fs.readFileSync(compiledConfigFilePath, 'utf-8')) as Source; const config = JSON.parse(fs.readFileSync(compiledConfigFilePath, 'utf-8')) as Source;
@@ -263,9 +283,17 @@ export function loadConfig(): Config {
url: url.origin, url: url.origin,
port: config.port ?? parseInt(process.env.PORT ?? '', 10), port: config.port ?? parseInt(process.env.PORT ?? '', 10),
socket: config.socket, socket: config.socket,
trustProxy: config.trustProxy, trustProxy: config.trustProxy ?? [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'127.0.0.1/32',
'::1/128',
'fc00::/7',
],
chmodSocket: config.chmodSocket, chmodSocket: config.chmodSocket,
disableHsts: config.disableHsts, disableHsts: config.disableHsts,
enableIpRateLimit: config.enableIpRateLimit ?? true,
host, host,
hostname, hostname,
scheme, scheme,
@@ -324,7 +352,7 @@ export function loadConfig(): Config {
function tryCreateUrl(url: string) { function tryCreateUrl(url: string) {
try { try {
return new URL(url); return new URL(url);
} catch (e) { } catch (_) {
throw new Error(`url="${url}" is not a valid URL.`); throw new Error(`url="${url}" is not a valid URL.`);
} }
} }

View File

@@ -75,7 +75,7 @@ export class AccountMoveService {
*/ */
@bindThis @bindThis
public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> { public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> {
const srcUri = this.userEntityService.getUserUri(src); const _srcUri = this.userEntityService.getUserUri(src);
const dstUri = this.userEntityService.getUserUri(dst); const dstUri = this.userEntityService.getUserUri(dst);
// add movedToUri to indicate that the user has moved // add movedToUri to indicate that the user has moved

View File

@@ -7,7 +7,6 @@ import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import si from 'systeminformation';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@@ -84,6 +83,7 @@ export class AiService {
@bindThis @bindThis
private async getCpuFlags(): Promise<string[]> { private async getCpuFlags(): Promise<string[]> {
const si = await import('systeminformation');
const str = await si.cpuFlags(); const str = await si.cpuFlags();
return str.split(/\s+/); return str.split(/\s+/);
} }

View File

@@ -205,7 +205,7 @@ export class AnnouncementService {
announcementId: announcementId, announcementId: announcementId,
userId: user.id, userId: user.id,
}); });
} catch (e) { } catch (_) {
return; return;
} }

View File

@@ -39,7 +39,7 @@ export class AvatarDecorationService implements OnApplicationShutdown {
const obj = JSON.parse(data); const obj = JSON.parse(data);
if (obj.channel === 'internal') { if (obj.channel === 'internal') {
const { type, body } = obj.message as GlobalEvents['internal']['payload']; const { type, body: _ } = obj.message as GlobalEvents['internal']['payload'];
switch (type) { switch (type) {
case 'avatarDecorationCreated': case 'avatarDecorationCreated':
case 'avatarDecorationUpdated': case 'avatarDecorationUpdated':

View File

@@ -141,7 +141,7 @@ import { ApLoggerService } from './activitypub/ApLoggerService.js';
import { ApMfmService } from './activitypub/ApMfmService.js'; import { ApMfmService } from './activitypub/ApMfmService.js';
import { ApRendererService } from './activitypub/ApRendererService.js'; import { ApRendererService } from './activitypub/ApRendererService.js';
import { ApRequestService } from './activitypub/ApRequestService.js'; import { ApRequestService } from './activitypub/ApRequestService.js';
import { ApResolverService } from './activitypub/ApResolverService.js'; import { ApResolverService, Resolver } from './activitypub/ApResolverService.js';
import { JsonLdService } from './activitypub/JsonLdService.js'; import { JsonLdService } from './activitypub/JsonLdService.js';
import { RemoteLoggerService } from './RemoteLoggerService.js'; import { RemoteLoggerService } from './RemoteLoggerService.js';
import { RemoteUserResolveService } from './RemoteUserResolveService.js'; import { RemoteUserResolveService } from './RemoteUserResolveService.js';
@@ -447,6 +447,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ApRendererService, ApRendererService,
ApRequestService, ApRequestService,
ApResolverService, ApResolverService,
Resolver,
JsonLdService, JsonLdService,
RemoteLoggerService, RemoteLoggerService,
RemoteUserResolveService, RemoteUserResolveService,
@@ -745,6 +746,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ApRendererService, ApRendererService,
ApRequestService, ApRequestService,
ApResolverService, ApResolverService,
Resolver,
JsonLdService, JsonLdService,
RemoteLoggerService, RemoteLoggerService,
RemoteUserResolveService, RemoteUserResolveService,

View File

@@ -366,7 +366,7 @@ export class EmailService {
valid: true, valid: true,
reason: null, reason: null,
}; };
} catch (error) { } catch (_) {
return { return {
valid: false, valid: false,
reason: 'network', reason: 'network',

View File

@@ -484,25 +484,13 @@ export class FileInfoService {
* Calculate blurhash string of image * Calculate blurhash string of image
*/ */
@bindThis @bindThis
private getBlurhash(path: string, type: string): Promise<string> { private async getBlurhash(path: string, type: string): Promise<string> {
return new Promise(async (resolve, reject) => { const sharp = await sharpBmp(path, type);
(await sharpBmp(path, type)) const { data: buffer, info } = await sharp
.raw() .raw()
.ensureAlpha() .ensureAlpha()
.resize(64, 64, { fit: 'inside' }) .resize(64, 64, { fit: 'inside' })
.toBuffer((err, buffer, info) => { .toBuffer({ resolveWithObject: true });
if (err) return reject(err); return blurhash.encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
let hash;
try {
hash = blurhash.encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
} catch (e) {
return reject(e);
}
resolve(hash);
});
});
} }
} }

View File

@@ -38,11 +38,7 @@ export interface BroadcastTypes {
emojis: Packed<'EmojiDetailed'>[]; emojis: Packed<'EmojiDetailed'>[];
}; };
emojiDeleted: { emojiDeleted: {
emojis: { emojis: Packed<'EmojiDetailed'>[];
id?: string;
name: string;
[other: string]: any;
}[];
}; };
announcementCreated: { announcementCreated: {
announcement: Packed<'Announcement'>; announcement: Packed<'Announcement'>;

View File

@@ -308,7 +308,7 @@ export class MfmService {
try { try {
const date = new Date(parseInt(text, 10) * 1000); const date = new Date(parseInt(text, 10) * 1000);
return `<time datetime="${escapeHtml(date.toISOString())}">${escapeHtml(date.toISOString())}</time>`; return `<time datetime="${escapeHtml(date.toISOString())}">${escapeHtml(date.toISOString())}</time>`;
} catch (err) { } catch (_) {
return fnDefault(node); return fnDefault(node);
} }
} }
@@ -376,7 +376,7 @@ export class MfmService {
try { try {
const url = new URL(node.props.url); const url = new URL(node.props.url);
return `<a href="${escapeHtml(url.href)}">${toHtml(node.children)}</a>`; return `<a href="${escapeHtml(url.href)}">${toHtml(node.children)}</a>`;
} catch (err) { } catch (_) {
return `[${toHtml(node.children)}](${escapeHtml(node.props.url)})`; return `[${toHtml(node.children)}](${escapeHtml(node.props.url)})`;
} }
}, },
@@ -390,7 +390,7 @@ export class MfmService {
try { try {
const url = new URL(href); const url = new URL(href);
return `<a href="${escapeHtml(url.href)}" class="u-url mention">${escapeHtml(acct)}</a>`; return `<a href="${escapeHtml(url.href)}" class="u-url mention">${escapeHtml(acct)}</a>`;
} catch (err) { } catch (_) {
return escapeHtml(acct); return escapeHtml(acct);
} }
}, },
@@ -419,7 +419,7 @@ export class MfmService {
try { try {
const url = new URL(node.props.url); const url = new URL(node.props.url);
return `<a href="${escapeHtml(url.href)}">${escapeHtml(node.props.url)}</a>`; return `<a href="${escapeHtml(url.href)}">${escapeHtml(node.props.url)}</a>`;
} catch (err) { } catch (_) {
return escapeHtml(node.props.url); return escapeHtml(node.props.url);
} }
}, },

View File

@@ -187,9 +187,9 @@ export class NoteDraftService {
} }
//#region visibleUsers //#region visibleUsers
let visibleUsers: MiUser[] = []; let _visibleUsers: MiUser[] = [];
if (data.visibleUserIds != null && data.visibleUserIds.length > 0) { if (data.visibleUserIds != null && data.visibleUserIds.length > 0) {
visibleUsers = await this.usersRepository.findBy({ _visibleUsers = await this.usersRepository.findBy({
id: In(data.visibleUserIds), id: In(data.visibleUserIds),
}); });
} }

View File

@@ -314,7 +314,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
default: default:
return false; return false;
} }
} catch (err) { } catch (_) {
// TODO: log error // TODO: log error
return false; return false;
} }

View File

@@ -190,8 +190,7 @@ export class SearchService {
return this.searchNoteByMeiliSearch(q, me, opts, pagination); return this.searchNoteByMeiliSearch(q, me, opts, pagination);
} }
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const _: never = this.provider;
const typeCheck: never = this.provider;
return []; return [];
} }
} }

View File

@@ -49,8 +49,8 @@ export class UserSuspendService {
}); });
(async () => { (async () => {
await this.postSuspend(user).catch(e => {}); await this.postSuspend(user).catch(_ => {});
await this.unFollowAll(user).catch(e => {}); await this.unFollowAll(user).catch(_ => {});
})(); })();
} }
@@ -67,7 +67,7 @@ export class UserSuspendService {
}); });
(async () => { (async () => {
await this.postUnsuspend(user).catch(e => {}); await this.postUnsuspend(user).catch(_ => {});
})(); })();
} }

View File

@@ -98,7 +98,7 @@ export class UtilityService {
try { try {
// TODO: RE2インスタンスをキャッシュ // TODO: RE2インスタンスをキャッシュ
return new RE2(regexp[1], regexp[2]).test(text); return new RE2(regexp[1], regexp[2]).test(text);
} catch (err) { } catch (_) {
// This should never happen due to input sanitisation. // This should never happen due to input sanitisation.
return false; return false;
} }

View File

@@ -95,7 +95,7 @@ export class ApInboxService {
if (isCollectionOrOrderedCollection(activity)) { if (isCollectionOrOrderedCollection(activity)) {
const results = [] as [string, string | void][]; const results = [] as [string, string | void][];
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems); const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems);
if (items.length >= resolver.getRecursionLimit()) { if (items.length >= resolver.getRecursionLimit()) {
@@ -221,7 +221,7 @@ export class ApInboxService {
this.logger.info(`Accept: ${uri}`); this.logger.info(`Accept: ${uri}`);
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(err => { const object = await resolver.resolve(activity.object).catch(err => {
this.logger.error(`Resolution failed: ${err}`); this.logger.error(`Resolution failed: ${err}`);
@@ -284,7 +284,7 @@ export class ApInboxService {
this.logger.info(`Announce: ${uri}`); this.logger.info(`Announce: ${uri}`);
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
if (!activity.object) return 'skip: activity has no object property'; if (!activity.object) return 'skip: activity has no object property';
const targetUri = getApId(activity.object); const targetUri = getApId(activity.object);
@@ -406,7 +406,7 @@ export class ApInboxService {
} }
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => { const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`); this.logger.error(`Resolution failed: ${e}`);
@@ -575,7 +575,7 @@ export class ApInboxService {
this.logger.info(`Reject: ${uri}`); this.logger.info(`Reject: ${uri}`);
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => { const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`); this.logger.error(`Resolution failed: ${e}`);
@@ -642,7 +642,7 @@ export class ApInboxService {
this.logger.info(`Undo: ${uri}`); this.logger.info(`Undo: ${uri}`);
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => { const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`); this.logger.error(`Resolution failed: ${e}`);
@@ -774,7 +774,7 @@ export class ApInboxService {
this.logger.debug('Update'); this.logger.debug('Update');
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
resolver ??= this.apResolverService.createResolver(); resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => { const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`); this.logger.error(`Resolution failed: ${e}`);

View File

@@ -515,7 +515,7 @@ export class ApRendererService {
const restPart = maybeUrl.slice(match[0].length); const restPart = maybeUrl.slice(match[0].length);
return `<a href="${urlPartParsed.href}" rel="me nofollow noopener" target="_blank">${urlPart}</a>${restPart}`; return `<a href="${urlPartParsed.href}" rel="me nofollow noopener" target="_blank">${urlPart}</a>${restPart}`;
} catch (e) { } catch (_) {
return maybeUrl; return maybeUrl;
} }
}; };

View File

@@ -226,7 +226,7 @@ export class ApRequestService {
return await this.signedGet(href, user, allowSoftfail, false); return await this.signedGet(href, user, allowSoftfail, false);
} }
} }
} catch (e) { } catch (_) {
// something went wrong parsing the HTML, ignore the whole thing // something went wrong parsing the HTML, ignore the whole thing
} }
} }

View File

@@ -3,10 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable, Scope } from '@nestjs/common';
import { IsNull, Not } from 'typeorm'; import { IsNull, Not } from 'typeorm';
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js'; import type {
FollowRequestsRepository,
MiMeta,
NoteReactionsRepository,
NotesRepository,
PollsRepository,
UsersRepository
} from '@/models/_.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@@ -16,26 +23,43 @@ import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { SystemAccountService } from '@/core/SystemAccountService.js'; import { SystemAccountService } from '@/core/SystemAccountService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { ICollection, IObject, IOrderedCollection } from './type.js';
import { isCollectionOrOrderedCollection } from './type.js'; import { isCollectionOrOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js'; import { ApDbResolverService } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js'; import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js'; import { ApRequestService } from './ApRequestService.js';
import { FetchAllowSoftFailMask } from './misc/check-against-url.js'; import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
import type { IObject, ICollection, IOrderedCollection } from './type.js'; import { ModuleRef } from '@nestjs/core';
@Injectable({ scope: Scope.TRANSIENT })
export class Resolver { export class Resolver {
private history: Set<string>; private history: Set<string>;
private user?: MiLocalUser; private user?: MiLocalUser;
private logger: Logger; private logger: Logger;
private recursionLimit = 256;
constructor( constructor(
@Inject(DI.config)
private config: Config, private config: Config,
@Inject(DI.meta)
private meta: MiMeta, private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository, private notesRepository: NotesRepository,
@Inject(DI.pollsRepository)
private pollsRepository: PollsRepository, private pollsRepository: PollsRepository,
@Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository, private noteReactionsRepository: NoteReactionsRepository,
@Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository, private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService, private utilityService: UtilityService,
private systemAccountService: SystemAccountService, private systemAccountService: SystemAccountService,
private apRequestService: ApRequestService, private apRequestService: ApRequestService,
@@ -43,7 +67,6 @@ export class Resolver {
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService, private apDbResolverService: ApDbResolverService,
private loggerService: LoggerService, private loggerService: LoggerService,
private recursionLimit = 256,
) { ) {
this.history = new Set(); this.history = new Set();
this.logger = this.loggerService.getLogger('ap-resolve'); this.logger = this.loggerService.getLogger('ap-resolve');
@@ -180,54 +203,12 @@ export class Resolver {
@Injectable() @Injectable()
export class ApResolverService { export class ApResolverService {
constructor( constructor(
@Inject(DI.config) private moduleRef: ModuleRef,
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@Inject(DI.pollsRepository)
private pollsRepository: PollsRepository,
@Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository,
@Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService,
private systemAccountService: SystemAccountService,
private apRequestService: ApRequestService,
private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService,
private loggerService: LoggerService,
) { ) {
} }
@bindThis @bindThis
public createResolver(): Resolver { public async createResolver(): Promise<Resolver> {
return new Resolver( return await this.moduleRef.create(Resolver);
this.config,
this.meta,
this.usersRepository,
this.notesRepository,
this.pollsRepository,
this.noteReactionsRepository,
this.followRequestsRepository,
this.utilityService,
this.systemAccountService,
this.apRequestService,
this.httpRequestService,
this.apRendererService,
this.apDbResolverService,
this.loggerService,
);
} }
} }

View File

@@ -46,7 +46,7 @@ export class ApImageService {
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 (await this.apResolverService.createResolver()).resolve(value);
if (!isDocument(image)) return null; if (!isDocument(image)) return null;

View File

@@ -128,7 +128,7 @@ export class ApNoteService {
@bindThis @bindThis
public async createNote(value: string | IObject, actor?: MiRemoteUser, resolver?: Resolver, silent = false): Promise<MiNote | null> { public async createNote(value: string | IObject, actor?: MiRemoteUser, resolver?: Resolver, silent = false): Promise<MiNote | null> {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = await resolver.resolve(value); const object = await resolver.resolve(value);

View File

@@ -310,7 +310,7 @@ export class ApPersonService implements OnModuleInit {
} }
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = await resolver.resolve(uri); const object = await resolver.resolve(uri);
if (object.id == null) throw new Error('invalid object.id: ' + object.id); if (object.id == null) throw new Error('invalid object.id: ' + object.id);
@@ -500,7 +500,7 @@ export class ApPersonService implements OnModuleInit {
//#endregion //#endregion
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = hint ?? await resolver.resolve(uri); const object = hint ?? await resolver.resolve(uri);
@@ -678,7 +678,7 @@ export class ApPersonService implements OnModuleInit {
// リモートサーバーからフェッチしてきて登録 // リモートサーバーからフェッチしてきて登録
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
return await this.createPerson(uri, resolver); return await this.createPerson(uri, resolver);
} }
@@ -707,7 +707,7 @@ export class ApPersonService implements OnModuleInit {
this.logger.info(`Updating the featured: ${user.uri}`); this.logger.info(`Updating the featured: ${user.uri}`);
const _resolver = resolver ?? this.apResolverService.createResolver(); const _resolver = resolver ?? await this.apResolverService.createResolver();
// Resolve to (Ordered)Collection Object // Resolve to (Ordered)Collection Object
const collection = await _resolver.resolveCollection(user.featured); const collection = await _resolver.resolveCollection(user.featured);

View File

@@ -45,7 +45,7 @@ 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 // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
const question = await resolver.resolve(source); const question = await resolver.resolve(source);
if (!isQuestion(question)) throw new Error('invalid type'); if (!isQuestion(question)) throw new Error('invalid type');
@@ -91,7 +91,7 @@ export class ApQuestionService {
// resolve new Question object // resolve new Question object
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = await this.apResolverService.createResolver();
const question = await resolver.resolve(value); const question = await resolver.resolve(value);
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);

View File

@@ -138,7 +138,7 @@ export class ChatEntityService {
const reactions: { reaction: string; }[] = []; const reactions: { reaction: string; }[] = [];
for (const record of message.reactions) { for (const record of message.reactions) {
const [userId, reaction] = record.split('/'); const [, reaction] = record.split('/');
reactions.push({ reactions.push({
reaction, reaction,
}); });

View File

@@ -17,6 +17,7 @@ import { deepClone } from '@/misc/clone.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { isMimeImage } from '@/misc/is-mime-image.js'; import { isMimeImage } from '@/misc/is-mime-image.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { uniqueByKey } from '@/misc/unique-by-key.js';
import { UtilityService } from '../UtilityService.js'; import { UtilityService } from '../UtilityService.js';
import { VideoProcessingService } from '../VideoProcessingService.js'; import { VideoProcessingService } from '../VideoProcessingService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@@ -226,6 +227,7 @@ export class DriveFileEntityService {
options?: PackOptions, options?: PackOptions,
hint?: { hint?: {
packedUser?: Packed<'UserLite'> packedUser?: Packed<'UserLite'>
packedFolder?: Packed<'DriveFolder'>
}, },
): Promise<Packed<'DriveFile'> | null> { ): Promise<Packed<'DriveFile'> | null> {
const opts = Object.assign({ const opts = Object.assign({
@@ -250,9 +252,9 @@ export class DriveFileEntityService {
thumbnailUrl: this.getThumbnailUrl(file), thumbnailUrl: this.getThumbnailUrl(file),
comment: file.comment, comment: file.comment,
folderId: file.folderId, folderId: file.folderId,
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, { folder: opts.detail && file.folderId ? (hint?.packedFolder ?? this.driveFolderEntityService.pack(file.folderId, {
detail: true, detail: true,
}) : null, })) : null,
userId: file.userId, userId: file.userId,
user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null, user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
}); });
@@ -263,10 +265,41 @@ export class DriveFileEntityService {
files: MiDriveFile[], files: MiDriveFile[],
options?: PackOptions, options?: PackOptions,
): Promise<Packed<'DriveFile'>[]> { ): Promise<Packed<'DriveFile'>[]> {
const _user = files.map(({ user, userId }) => user ?? userId).filter(x => x != null); // -- ユーザ情報の事前取得 --
const _userMap = await this.userEntityService.packMany(_user)
.then(users => new Map(users.map(user => [user.id, user]))); let userMap: Map<string, Packed<'UserLite'>> | null = null;
const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {}))); if (options?.withUser) {
const users = files
.map(({ user, userId }) => user ?? userId)
.filter(x => x != null);
const uniqueUsers = uniqueByKey(users, (user) => typeof user === 'string' ? user : user.id);
const packedUsers = await this.userEntityService.packMany(uniqueUsers);
userMap = new Map(packedUsers.map(user => [user.id, user]));
}
// -- フォルダ情報の事前取得 --
let folderMap: Map<string, Packed<'DriveFolder'>> | null = null;
if (options?.detail) {
const folders = files
.map(({ folder, folderId }) => folder ?? folderId)
.filter(x => x != null);
const uniqueFolders = uniqueByKey(folders, (folder) => typeof folder === 'string' ? folder : folder.id);
const packedFolders = await this.driveFolderEntityService.packMany(uniqueFolders, { detail: true });
folderMap = new Map(packedFolders.map(folder => [folder.id, folder]));
}
const items = await Promise.all(files.map(f => this.packNullable(
f,
options,
{
packedUser: f.userId ? userMap?.get(f.userId) : undefined,
packedFolder: f.folderId ? folderMap?.get(f.folderId) : undefined,
},
)));
return items.filter(x => x != null); return items.filter(x => x != null);
} }

View File

@@ -12,6 +12,9 @@ import type { } from '@/models/Blocking.js';
import type { MiDriveFolder } from '@/models/DriveFolder.js'; import type { MiDriveFolder } from '@/models/DriveFolder.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { In } from 'typeorm';
import { uniqueByKey } from '@/misc/unique-by-key.js';
import { splitIdAndObjects } from '@/misc/split-id-and-objects.js';
@Injectable() @Injectable()
export class DriveFolderEntityService { export class DriveFolderEntityService {
@@ -32,12 +35,20 @@ export class DriveFolderEntityService {
options?: { options?: {
detail: boolean detail: boolean
}, },
hint?: {
folderMap?: Map<string, MiDriveFolder>;
foldersCountMap?: Map<string, number> | null;
filesCountMap?: Map<string, number> | null;
parentPacker?: (id: string) => Promise<Packed<'DriveFolder'>>;
},
): Promise<Packed<'DriveFolder'>> { ): Promise<Packed<'DriveFolder'>> {
const opts = Object.assign({ const opts = Object.assign({
detail: false, detail: false,
}, options); }, options);
const folder = typeof src === 'object' ? src : await this.driveFoldersRepository.findOneByOrFail({ id: src }); const folder = typeof src === 'object'
? src
: hint?.folderMap?.get(src) ?? await this.driveFoldersRepository.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: folder.id, id: folder.id,
@@ -46,20 +57,141 @@ export class DriveFolderEntityService {
parentId: folder.parentId, parentId: folder.parentId,
...(opts.detail ? { ...(opts.detail ? {
foldersCount: this.driveFoldersRepository.countBy({ foldersCount: hint?.foldersCountMap?.get(folder.id)
parentId: folder.id, ?? this.driveFoldersRepository.countBy({
}), parentId: folder.id,
filesCount: this.driveFilesRepository.countBy({ }),
folderId: folder.id, filesCount: hint?.filesCountMap?.get(folder.id)
}), ?? this.driveFilesRepository.countBy({
folderId: folder.id,
}),
...(folder.parentId ? { ...(folder.parentId ? {
parent: this.pack(folder.parentId, { parent: hint?.parentPacker
detail: true, ? hint.parentPacker(folder.parentId)
}), : this.pack(folder.parentId, { detail: true }, hint),
} : {}), } : {}),
} : {}), } : {}),
}); });
} }
}
public async packMany(
src: Array<MiDriveFolder['id'] | MiDriveFolder>,
options?: {
detail: boolean
},
): Promise<Array<Packed<'DriveFolder'>>> {
/**
* 重複を除去しつつ、必要なDriveFolderオブジェクトをすべて取得する
*/
const collectUniqueObjects = async (src: Array<MiDriveFolder['id'] | MiDriveFolder>) => {
const uniqueSrc = uniqueByKey(
src,
(s) => typeof s === 'string' ? s : s.id,
);
const { ids, objects } = splitIdAndObjects(uniqueSrc);
const uniqueObjects = new Map<string, MiDriveFolder>(objects.map(s => [s.id, s]));
const needsFetchIds = ids.filter(id => !uniqueObjects.has(id));
if (needsFetchIds.length > 0) {
const fetchedObjects = await this.driveFoldersRepository.find({
where: {
id: In(needsFetchIds),
},
});
for (const obj of fetchedObjects) {
uniqueObjects.set(obj.id, obj);
}
}
return uniqueObjects;
};
/**
* 親フォルダーを再帰的に収集する
*/
const collectAncestors = async (folderMap: Map<string, MiDriveFolder>) => {
for (;;) {
const parentIds = new Set<string>();
for (const folder of folderMap.values()) {
if (folder.parentId != null && !folderMap.has(folder.parentId)) {
parentIds.add(folder.parentId);
}
}
if (parentIds.size === 0) break;
const fetchedParents = await this.driveFoldersRepository.find({
where: {
id: In([...parentIds]),
},
});
if (fetchedParents.length === 0) break;
for (const parent of fetchedParents) {
folderMap.set(parent.id, parent);
}
}
};
const opts = Object.assign({
detail: false,
}, options);
const folderMap = await collectUniqueObjects(src);
let foldersCountMap: Map<string, number> | null = null;
let filesCountMap: Map<string, number> | null = null;
if (opts.detail) {
await collectAncestors(folderMap);
const ids = [...folderMap.keys()];
if (ids.length > 0) {
const folderCounts = await this.driveFoldersRepository.createQueryBuilder('folder')
.select('folder.parentId', 'parentId')
.addSelect('COUNT(*)', 'count')
.where('folder.parentId IN (:...ids)', { ids })
.groupBy('folder.parentId')
.getRawMany<{ parentId: string; count: string }>();
const fileCounts = await this.driveFilesRepository.createQueryBuilder('file')
.select('file.folderId', 'folderId')
.addSelect('COUNT(*)', 'count')
.where('file.folderId IN (:...ids)', { ids })
.groupBy('file.folderId')
.getRawMany<{ folderId: string; count: string }>();
foldersCountMap = new Map(folderCounts.map(row => [row.parentId, Number(row.count)]));
filesCountMap = new Map(fileCounts.map(row => [row.folderId, Number(row.count)]));
} else {
foldersCountMap = new Map();
filesCountMap = new Map();
}
}
const packedMap = new Map<string, Promise<Packed<'DriveFolder'>>>();
const packFromId = (id: string): Promise<Packed<'DriveFolder'>> => {
const cached = packedMap.get(id);
if (cached) return cached;
const folder = folderMap.get(id);
if (!folder) {
throw new Error(`DriveFolder not found: ${id}`);
}
const packedPromise = this.pack(folder, options, {
folderMap,
foldersCountMap,
filesCountMap,
parentPacker: packFromId,
});
packedMap.set(id, packedPromise);
return packedPromise;
};
return Promise.all(src.map(s => packFromId(typeof s === 'string' ? s : s.id)));
}
}

View File

@@ -41,7 +41,7 @@ export class EmojiEntityService {
@bindThis @bindThis
public packSimpleMany( public packSimpleMany(
emojis: any[], emojis: (MiEmoji['id'] | MiEmoji)[],
) { ) {
return Promise.all(emojis.map(x => this.packSimple(x))); return Promise.all(emojis.map(x => this.packSimple(x)));
} }
@@ -69,7 +69,7 @@ export class EmojiEntityService {
@bindThis @bindThis
public packDetailedMany( public packDetailedMany(
emojis: any[], emojis: (MiEmoji['id'] | MiEmoji)[],
): Promise<Packed<'EmojiDetailed'>[]> { ): Promise<Packed<'EmojiDetailed'>[]> {
return Promise.all(emojis.map(x => this.packDetailed(x))); return Promise.all(emojis.map(x => this.packDetailed(x)));
} }

View File

@@ -55,13 +55,13 @@ export class MetaEntityService {
if (instance.defaultLightTheme) { if (instance.defaultLightTheme) {
try { try {
defaultLightTheme = JSON.stringify(JSON5.parse(instance.defaultLightTheme)); defaultLightTheme = JSON.stringify(JSON5.parse(instance.defaultLightTheme));
} catch (e) { } catch (_) {
} }
} }
if (instance.defaultDarkTheme) { if (instance.defaultDarkTheme) {
try { try {
defaultDarkTheme = JSON.stringify(JSON5.parse(instance.defaultDarkTheme)); defaultDarkTheme = JSON.stringify(JSON5.parse(instance.defaultDarkTheme));
} catch (e) { } catch (_) {
} }
} }

View File

@@ -54,7 +54,7 @@ export class NoteReactionEntityService implements OnModuleInit {
packedUser?: Packed<'UserLite'> packedUser?: Packed<'UserLite'>
}, },
): Promise<Packed<'NoteReaction'>> { ): Promise<Packed<'NoteReaction'>> {
const opts = Object.assign({ const _opts = Object.assign({
}, options); }, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
@@ -90,7 +90,7 @@ export class NoteReactionEntityService implements OnModuleInit {
packedUser?: Packed<'UserLite'> packedUser?: Packed<'UserLite'>
}, },
): Promise<Packed<'NoteReactionWithNote'>> { ): Promise<Packed<'NoteReactionWithNote'>> {
const opts = Object.assign({ const _opts = Object.assign({
}, options); }, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });

View File

@@ -720,7 +720,7 @@ export class UserEntityService implements OnModuleInit {
me, me,
{ {
...options, ...options,
userProfile: profilesMap.get(u.id), userProfile: profilesMap?.get(u.id),
userRelations: userRelations, userRelations: userRelations,
userMemos: userMemos, userMemos: userMemos,
pinNotes: pinNotes, pinNotes: pinNotes,

View File

@@ -4,13 +4,12 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import si from 'systeminformation';
import Xev from 'xev'; import Xev from 'xev';
import * as osUtils from 'os-utils'; import * as osUtils from 'os-utils';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { MiMeta } from '@/models/_.js'; import { MiMeta } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { OnApplicationShutdown } from '@nestjs/common';
const ev = new Xev(); const ev = new Xev();
@@ -97,12 +96,14 @@ function cpuUsage(): Promise<number> {
// MEMORY STAT // MEMORY STAT
async function mem() { async function mem() {
const si = await import('systeminformation');
const data = await si.mem(); const data = await si.mem();
return data; return data;
} }
// NETWORK STAT // NETWORK STAT
async function net() { async function net() {
const si = await import('systeminformation');
const iface = await si.networkInterfaceDefault(); const iface = await si.networkInterfaceDefault();
const data = await si.networkStats(iface); const data = await si.networkStats(iface);
return data[0]; return data[0];
@@ -110,5 +111,6 @@ async function net() {
// FS STAT // FS STAT
async function fs() { async function fs() {
const si = await import('systeminformation');
return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
} }

View File

@@ -56,7 +56,7 @@ export async function checkWordMute(note: NoteLike, me: UserLike | null | undefi
try { try {
return new RE2(regexp[1], regexp[2]).test(text); return new RE2(regexp[1], regexp[2]).test(text);
} catch (err) { } catch (_) {
// This should never happen due to input sanitisation. // This should never happen due to input sanitisation.
return false; return false;
} }

View File

@@ -12,7 +12,7 @@ export function getIpHash(ip: string): string {
// (this means for IPv4 the entire address is used) // (this means for IPv4 the entire address is used)
const prefix = IPCIDR.createAddress(ip).mask(64); const prefix = IPCIDR.createAddress(ip).mask(64);
return 'ip-' + BigInt('0b' + prefix).toString(36); return 'ip-' + BigInt('0b' + prefix).toString(36);
} catch (e) { } catch (_) {
const prefix = IPCIDR.createAddress(ip.replace(/:[0-9]+$/, '')).mask(64); const prefix = IPCIDR.createAddress(ip.replace(/:[0-9]+$/, '')).mask(64);
return 'ip-' + BigInt('0b' + prefix).toString(36); return 'ip-' + BigInt('0b' + prefix).toString(36);
} }

View File

@@ -26,7 +26,7 @@ export class I18n<T extends Record<string, any>> {
} }
} }
return str; return str;
} catch (e) { } catch (_) {
console.warn(`missing localization '${key}'`); console.warn(`missing localization '${key}'`);
return key; return key;
} }

View File

@@ -262,8 +262,6 @@ type ObjectSchemaTypeDef<p extends Schema> =
never : never :
any; any;
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
export type SchemaTypeDef<p extends Schema> = export type SchemaTypeDef<p extends Schema> =
p['type'] extends 'null' ? null : p['type'] extends 'null' ? null :
p['type'] extends 'integer' ? number : p['type'] extends 'integer' ? number :

View File

@@ -4,15 +4,11 @@
*/ */
import * as os from 'node:os'; import * as os from 'node:os';
import sysUtils from 'systeminformation';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
export async function showMachineInfo(parentLogger: Logger) { export async function showMachineInfo(parentLogger: Logger) {
const logger = parentLogger.createSubLogger('machine'); const logger = parentLogger.createSubLogger('machine');
logger.debug(`Hostname: ${os.hostname()}`); logger.debug(`Hostname: ${os.hostname()}`);
logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`); logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`);
const mem = await sysUtils.mem(); logger.debug(`CPU: ${os.cpus().length} core MEM: ${(os.totalmem() / 1024 / 1024 / 1024).toFixed(1)}GB (available: ${(os.freemem() / 1024 / 1024 / 1024).toFixed(1)}GB)`);
const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
logger.debug(`CPU: ${os.cpus().length} core MEM: ${totalmem}GB (available: ${availmem}GB)`);
} }

View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
/**
* idとオブジェクトを分離する
* @param input idまたはオブジェクトの配列
* @returns idの配列とオブジェクトの配列
*/
export function splitIdAndObjects<T extends { id: string }>(input: (T | string)[]): { ids: string[]; objects: T[] } {
const ids: string[] = [];
const objects : T[] = [];
for (const item of input) {
if (typeof item === 'string') {
ids.push(item);
} else {
objects.push(item);
}
}
return {
ids,
objects,
};
}

View File

@@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
/**
* itemsの中でkey関数が返す値が重複しないようにした配列を返す
* @param items 重複を除去したい配列
* @param key 重複判定に使うキーを返す関数
* @returns 重複を除去した配列
*/
export function uniqueByKey<TItem, TKey = string>(items: Iterable<TItem>, key: (item: TItem) => TKey): TItem[] {
const map = new Map<TKey, TItem>();
for (const item of items) {
const k = key(item);
if (!map.has(k)) {
map.set(k, item);
}
}
return [...map.values()];
}

View File

@@ -67,7 +67,7 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* 通知先のユーザ. * 通知先のユーザ.
*/ */
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId1' }) @JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId1' })
@@ -76,7 +76,7 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* 通知先のユーザプロフィール. * 通知先のユーザプロフィール.
*/ */
@ManyToOne(type => MiUserProfile, { @ManyToOne(() => MiUserProfile, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' }) @JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
@@ -96,7 +96,7 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* 通知先のシステムWebhook. * 通知先のシステムWebhook.
*/ */
@ManyToOne(type => MiSystemWebhook, { @ManyToOne(() => MiSystemWebhook, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' }) @JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' })

View File

@@ -18,7 +18,7 @@ export class MiAbuseUserReport {
@Column(id()) @Column(id())
public targetUserId: MiUser['id']; public targetUserId: MiUser['id'];
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()
@@ -28,7 +28,7 @@ export class MiAbuseUserReport {
@Column(id()) @Column(id())
public reporterId: MiUser['id']; public reporterId: MiUser['id'];
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()
@@ -40,7 +40,7 @@ export class MiAbuseUserReport {
}) })
public assigneeId: MiUser['id'] | null; public assigneeId: MiUser['id'] | null;
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'SET NULL', onDelete: 'SET NULL',
}) })
@JoinColumn() @JoinColumn()

View File

@@ -41,7 +41,7 @@ export class MiAccessToken {
@Column(id()) @Column(id())
public userId: MiUser['id']; public userId: MiUser['id'];
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()
@@ -53,7 +53,7 @@ export class MiAccessToken {
}) })
public appId: MiApp['id'] | null; public appId: MiApp['id'] | null;
@ManyToOne(type => MiApp, { @ManyToOne(() => MiApp, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()

View File

@@ -79,7 +79,7 @@ export class MiAnnouncement {
}) })
public userId: MiUser['id'] | null; public userId: MiUser['id'] | null;
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()

View File

@@ -18,7 +18,7 @@ export class MiAnnouncementRead {
@Column(id()) @Column(id())
public userId: MiUser['id']; public userId: MiUser['id'];
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()
@@ -28,7 +28,7 @@ export class MiAnnouncementRead {
@Column(id()) @Column(id())
public announcementId: MiAnnouncement['id']; public announcementId: MiAnnouncement['id'];
@ManyToOne(type => MiAnnouncement, { @ManyToOne(() => MiAnnouncement, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()

View File

@@ -24,7 +24,7 @@ export class MiAntenna {
}) })
public userId: MiUser['id']; public userId: MiUser['id'];
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()
@@ -45,7 +45,7 @@ export class MiAntenna {
}) })
public userListId: MiUserList['id'] | null; public userListId: MiUserList['id'] | null;
@ManyToOne(type => MiUserList, { @ManyToOne(() => MiUserList, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()

View File

@@ -20,7 +20,7 @@ export class MiApp {
}) })
public userId: MiUser['id'] | null; public userId: MiUser['id'] | null;
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'SET NULL', onDelete: 'SET NULL',
nullable: true, nullable: true,
}) })

View File

@@ -25,7 +25,7 @@ export class MiAuthSession {
}) })
public userId: MiUser['id'] | null; public userId: MiUser['id'] | null;
@ManyToOne(type => MiUser, { @ManyToOne(() => MiUser, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
nullable: true, nullable: true,
}) })
@@ -35,7 +35,7 @@ export class MiAuthSession {
@Column(id()) @Column(id())
public appId: MiApp['id']; public appId: MiApp['id'];
@ManyToOne(type => MiApp, { @ManyToOne(() => MiApp, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn()

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