Compare commits

...

261 Commits

Author SHA1 Message Date
syuilo
d56fc41865 Merge branch 'develop' 2023-01-16 15:21:43 +09:00
syuilo
9d64ac6d6f 13.0.0 2023-01-16 15:18:37 +09:00
syuilo
e13434c2f0 update codename 2023-01-16 15:13:49 +09:00
syuilo
5416a295c1 fix spinner style 2023-01-16 14:24:24 +09:00
syuilo
119c650406 13.0.0-rc.11 2023-01-16 14:21:40 +09:00
syuilo
57386f46d2 New Crowdin updates (#9607)
* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Japanese, Kansai)
2023-01-16 14:21:08 +09:00
syuilo
77e491f52c refactor 2023-01-16 14:18:11 +09:00
syuilo
6f1243f722 Update vite.config.ts 2023-01-16 13:59:14 +09:00
syuilo
fe0bb21b37 enhance(client): make possible to hide ads
Resolve #9590
Resolve #8996
2023-01-16 11:21:04 +09:00
syuilo
60d9bb0218 enhance(client): add timeline flash preset 2023-01-16 10:31:30 +09:00
syuilo
956375e2e8 enhance(client): AiScriptからカスタム絵文字一覧を参照できるように 2023-01-16 10:13:14 +09:00
syuilo
0fa602a184 tweak flash omikuji preset 2023-01-16 10:07:46 +09:00
syuilo
d600296360 enhance(client): flash作成時のプリセットを追加 2023-01-16 10:04:10 +09:00
syuilo
c9f5e60f43 Update CHANGELOG.md 2023-01-16 06:40:41 +09:00
syuilo
d513848f65 13.0.0-rc.10 2023-01-16 06:38:00 +09:00
Masaya Suzuki
ae6af6aefd Copyrightの年が今年になっているかをチェックするCI追加 (#9580)
* Copyrightの年が今年になっているかをチェックするCI追加

* PRでは動作しないようにする
2023-01-16 06:36:53 +09:00
syuilo
a0ae9f7593 New Crowdin updates (#9589)
* 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 Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Japanese, Kansai)
2023-01-16 06:36:44 +09:00
syuilo
dace5b6940 🎨 2023-01-16 06:35:16 +09:00
syuilo
2d8b97287e Update CHANGELOG.md 2023-01-16 06:13:32 +09:00
syuilo
ec63a50de2 fix css module 2023-01-16 06:12:43 +09:00
CyberRex
6e2d7e9792 Use pnpm for package manager (#9531)
* Use pnpm for package manager

* Fix

* Change github workflow

* use pnpm in workflow

* fix

* Fix test (e2e/jest)

* Update Dockerfile for pnpm

* Exclude node_modules from file search on VSCode

* Update pnpm-lock.yaml

* Update pnpm-lock.yaml

* Move typescript from devDependencies to dependencies

* Fix Dockerfile

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-01-16 06:08:42 +09:00
syuilo
39349dcba5 enhance(client): プロフィールが長い場合は折りたたむ 2023-01-16 06:06:28 +09:00
syuilo
a5b1fe5d16 🎨 2023-01-16 06:06:05 +09:00
syuilo
91bbb67e4a プロフィールの文字数制限を緩和 2023-01-16 06:05:58 +09:00
syuilo
f368bce9d5 enhance(client): カスタム絵文字一覧ページにカスタム絵文字管理ページへのリンクを追加 2023-01-16 05:35:01 +09:00
syuilo
fbfe42d6f0 enhance(client): improve usability of user select 2023-01-16 05:29:44 +09:00
syuilo
f3c5ca6cf4 refactor(client): use css modules 2023-01-16 05:26:29 +09:00
syuilo
0022267072 enhance(client): ロールのポリシーを絞り込めるように 2023-01-16 05:19:26 +09:00
syuilo
30fced38c4 refactor: rename instance.caughtAt to instance.firstRetrievedAt 2023-01-16 05:02:38 +09:00
tamaina
7e5f3dbf11 set -webkit-touch-callout (#9447) 2023-01-16 03:38:16 +09:00
syuilo
9f0dfb5517 typo 2023-01-16 03:16:44 +09:00
tamaina
678c7d9502 Fix style of messaging form, use css module (#9602)
* Fix style of messaging form, use css module

* clean up
2023-01-16 02:37:45 +09:00
CyberRex
91a3c3943d Dockerfile: copy fluent-emoji assets (#9605) 2023-01-16 02:24:37 +09:00
tamaina
c46b45a467 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-15 16:32:37 +00:00
tamaina
9385767b12 fix(client): add :disable-link="true" to some <MkAvatar /> Fix #9606 2023-01-15 16:32:24 +00:00
futchitwo
7795ff0c95 fix: note embedding in Pages editor (#9604) 2023-01-15 23:47:04 +09:00
tamaina
a9acd72eb7 fix(client): Fix #9600 chat paging issue
Previous chat history not shown
2023-01-15 14:28:02 +00:00
Masaya Suzuki
67d366c3ca fix(client): エラー画像表示崩れ修正 (#9599) 2023-01-15 21:58:09 +09:00
syuilo
1f8f051ee2 13.0.0-rc.9 2023-01-15 20:56:30 +09:00
Masaya Suzuki
94004b7a3f Fix widget tests (#9591) 2023-01-15 20:55:09 +09:00
A.Yamamoto
3e9f88506e dbヘルスチェックでFATALの表示が出ないように (#9593) 2023-01-15 20:54:20 +09:00
syuilo
81f11d8f86 refactor: rename role.options -> role.policies 2023-01-15 20:52:53 +09:00
syuilo
518b3e2f73 ロールの各オプションに優先度を設定できるように 2023-01-15 19:10:39 +09:00
syuilo
d0157b3bfd 13.0.0-rc.8 2023-01-15 16:55:08 +09:00
syuilo
7fc8d2e6d5 ロールでレートリミットを調整できるように
Resolve #9584
2023-01-15 16:52:12 +09:00
Masaya Suzuki
fb0f9711ba Update actions/github-script (#9588) 2023-01-15 16:14:06 +09:00
syuilo
92136272b0 enhance(client): show readable error when rate limit exceeded 2023-01-15 16:13:57 +09:00
Masaya Suzuki
e1159e9ef2 Update actions/checkout (#9587) 2023-01-15 16:03:18 +09:00
Masaya Suzuki
a2e61c6708 CI Publish Docker image (develop) をforkしたリポジトリでは実行しない (#9585) 2023-01-15 15:59:01 +09:00
Masaya Suzuki
726959911c Update actions/setup-node (#9586) 2023-01-15 15:58:10 +09:00
syuilo
d59914b959 tweak style 2023-01-15 14:18:45 +09:00
syuilo
07025caee9 refactor(client): use css modules 2023-01-15 14:03:28 +09:00
syuilo
1c0289e490 Fix #9582 2023-01-15 13:46:09 +09:00
syuilo
275fcd8bbc tweak style 2023-01-15 13:39:06 +09:00
Masaya Suzuki
0c0aa93668 GitHub Actionsとpackages/swをDependabotによるアップデート対象にする (#9572) 2023-01-15 12:12:28 +09:00
Masaya Suzuki
bfcd5ea440 Dockerで構築する場合のconfigファイルの雛形追加 (#9577) 2023-01-15 12:11:38 +09:00
Masaya Suzuki
3ff43cca02 frontendに@type/nodeをインストールする (#9571) 2023-01-15 11:55:12 +09:00
Nya Candy
6bd536c526 feat: update year in COPYING file (#9578)
Happy new year :D
2023-01-15 11:53:22 +09:00
syuilo
7738a36014 refactor(client): use css modules 2023-01-15 11:30:40 +09:00
syuilo
daddec8362 refactor(client): use css modules 2023-01-15 11:22:58 +09:00
syuilo
a3832d73fd 13.0.0-rc.7 2023-01-15 09:19:11 +09:00
syuilo
cedb4267ba update deps 2023-01-15 09:19:02 +09:00
syuilo
9c6629d582 refactor(client): use css modules 2023-01-15 09:14:17 +09:00
syuilo
4ee4e70ee0 fix(client): Custom emojis in Inline emoji autocomplete(MkAutoComplete) is way too big
Fix #9570
2023-01-15 09:09:44 +09:00
syuilo
bb7867351c typo 2023-01-15 09:03:07 +09:00
syuilo
fea7460930 13.0.0-rc.6 2023-01-15 08:36:51 +09:00
syuilo
1bf2bf1773 New Crowdin updates (#9562)
* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)
2023-01-15 08:36:30 +09:00
syuilo
3d668ad10d Update CHANGELOG.md 2023-01-15 08:36:21 +09:00
Takuya Yoshida
2801338a3c Fix MODULE_NOT_FOUND (#9563) 2023-01-15 08:35:36 +09:00
syuilo
b66f4ebba1 chore: use computed 2023-01-15 08:35:03 +09:00
Masaya Suzuki
9ee1b5f30a Fix widget test (#9565) 2023-01-15 08:34:26 +09:00
Masaya Suzuki
0f31a0548c Fix import (#9566) 2023-01-15 08:33:07 +09:00
syuilo
ffc29aa6f5 refactor(client): use css modules 2023-01-15 08:32:20 +09:00
syuilo
d23aa94b41 refactor(client): use css modules 2023-01-15 08:30:29 +09:00
syuilo
c1b6378951 refactor(client): use css modules 2023-01-15 08:07:11 +09:00
syuilo
bb5d2bda51 refactor(client): use css modules 2023-01-15 08:01:46 +09:00
syuilo
d075471b2d fix(client): fix custom emoji rendering 2023-01-15 07:53:15 +09:00
Masaya Suzuki
199d98bf79 DB・Redis -> Misskeyの順に起動する (#9569) 2023-01-15 06:24:56 +09:00
syuilo
3ae798d526 13.0.0-rc.5 2023-01-14 21:09:38 +09:00
Takuya Yoshida
e1bd61c70e Change docker user to non-root (#9560) 2023-01-14 21:09:11 +09:00
syuilo
0296f841c3 New Crowdin updates (#9552)
* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)
2023-01-14 21:09:00 +09:00
syuilo
bd1f4b8d98 refactor 2023-01-14 21:04:30 +09:00
syuilo
dc19f20153 refactor 2023-01-14 21:04:20 +09:00
syuilo
f5cd809f62 refactor(client): use css modules 2023-01-14 20:51:07 +09:00
syuilo
09d5a7806a Update style.scss 2023-01-14 20:39:47 +09:00
syuilo
4606f23ed8 refactor(client): use css modules 2023-01-14 20:31:48 +09:00
syuilo
8451e08aaa Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-14 20:21:06 +09:00
syuilo
2047449294 enhance(server): add rate limits for some endpoints 2023-01-14 20:21:03 +09:00
Masaya Suzuki
d61eee695f forkしたリポジトリからのPRではCI Destroy preview environment が動作しないようにする (#9559) 2023-01-14 20:10:09 +09:00
Masaya Suzuki
73b62797cd CI Deploy preview environmentの不要なトリガー削除 (#9558)
* CI Deploy preview environmentの不要なトリガー削除

* 不要な条件削除
2023-01-14 19:51:34 +09:00
Masaya Suzuki
170cfc6a0e E2Eテスト "first widget should be removed" 修正 (#9556)
* Fix e2e test "first widget should be removed"

* E2Eテスト用クラス追加

* empty commit
2023-01-14 19:25:20 +09:00
syuilo
6bf1d7e398 13.0.0-rc.4 2023-01-14 18:50:25 +09:00
syuilo
e46e7f5252 ノートをピン留めできる数を設定可能に
Resolve #9555
2023-01-14 18:04:56 +09:00
syuilo
5952f1ac24 Update roles.editor.vue 2023-01-14 17:49:02 +09:00
syuilo
a08369fe36 enhance(client): 分かりやすいエラーメッセージを表示するように 2023-01-14 17:46:45 +09:00
syuilo
6cb9612943 fix import 2023-01-14 17:40:51 +09:00
syuilo
76c049522e enhance: ユーザーリストおよびユーザーリスト内のユーザーの作成可能数を設定可能に 2023-01-14 17:38:16 +09:00
syuilo
c41879c542 refactor(client): use css modules 2023-01-14 17:23:49 +09:00
syuilo
99bdb11d24 Update CHANGELOG.md 2023-01-14 17:02:49 +09:00
syuilo
c2009acb2d enhance: クリップおよびクリップ内のノートの作成可能数を設定可能に 2023-01-14 16:14:24 +09:00
syuilo
46d2a8726e fix missing import 2023-01-14 16:04:13 +09:00
syuilo
7df3ca7388 enhance(server): add rate limits for some endpoints 2023-01-14 15:59:15 +09:00
syuilo
51b8d4ae3e 13.0.0-rc.3 2023-01-14 15:08:30 +09:00
syuilo
ab1124abba refactor(client): use css modules 2023-01-14 15:05:23 +09:00
syuilo
3db84a2e8f refactor(client): use css modules 2023-01-14 15:02:14 +09:00
syuilo
9a78bbf0f1 refactor(client): use css modules 2023-01-14 15:01:28 +09:00
syuilo
efbec444e8 refactor(client): use css modules 2023-01-14 14:57:48 +09:00
syuilo
2f06f2a6da New Crowdin updates (#9544)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

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

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

* New translations ja-JP.yml (Chinese Traditional)
2023-01-14 14:20:09 +09:00
tamaina
b8da51e08c fix css module syntax error 2023-01-14 04:52:42 +00:00
syuilo
af6a578fa6 13.0.0-rc.2 2023-01-14 13:49:13 +09:00
tamaina
73d735a1f7 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-14 04:48:17 +00:00
tamaina
b8b1899a9f chore: fix ref name (pages/timeline.vue) 2023-01-14 04:47:59 +00:00
syuilo
d52f0617a1 fix(server): ドライブ容量超過時のエラーが適切にレスポンスされない問題を修正
Fix #9550
2023-01-14 13:41:53 +09:00
syuilo
c730973294 多分 fix #9551 2023-01-14 13:36:18 +09:00
syuilo
2c2e064871 refactor(client): use css modules 2023-01-14 13:29:41 +09:00
syuilo
e3c39d4b52 refactor(client): use css modules 2023-01-14 12:45:20 +09:00
syuilo
5da74897ae refactor(client): use css modules 2023-01-14 12:43:54 +09:00
syuilo
4b1009b34e refactor(client): use css modules 2023-01-14 12:30:32 +09:00
syuilo
203a7ad073 refactor(client): use css modules 2023-01-14 12:15:02 +09:00
syuilo
34a7b52105 13.0.0-rc.1 2023-01-14 12:00:07 +09:00
syuilo
30fc166c08 refactor(client): use css modules 2023-01-14 11:59:08 +09:00
syuilo
c84d86b368 refactor(client): use css modules 2023-01-14 11:48:30 +09:00
syuilo
1e5d4db0a1 refactor(client): use css modules 2023-01-14 11:46:22 +09:00
syuilo
5e02f0d325 refactor(client): use css modules 2023-01-14 11:39:35 +09:00
syuilo
ce5506f331 refactor(client): use css modules 2023-01-14 11:23:02 +09:00
syuilo
91105845d8 refactor(client): use css modules 2023-01-14 11:18:12 +09:00
syuilo
2bedc084a3 tweak MkRolePreview 2023-01-14 11:14:14 +09:00
syuilo
027ef1ea4a Update vite.config.ts 2023-01-14 11:10:41 +09:00
syuilo
668aa17eef refactor(client): use css modules 2023-01-14 10:57:34 +09:00
syuilo
ebf8ef22e4 🎨 2023-01-14 10:49:53 +09:00
syuilo
bcb5182e86 Webhookの作成可能数を設定可能に 2023-01-14 10:48:11 +09:00
syuilo
f45059b7b1 fix 2023-01-14 10:46:40 +09:00
syuilo
d0aee58599 13.0.0-beta.43 2023-01-14 09:02:48 +09:00
syuilo
68e65ed5df update mfm-js 2023-01-14 09:02:33 +09:00
syuilo
367ccb9971 enhance(client): add new mfm function (position, fg, bg)
Resolve #9527
2023-01-14 08:52:32 +09:00
syuilo
4151087d3c フォロワー数、フォロー数もConditional roleで利用できるように 2023-01-14 08:27:23 +09:00
syuilo
39c058a4bb Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-14 08:04:41 +09:00
syuilo
d1807ee5dc enhance: ハードワードミュートの最大文字数を設定可能に
Resolve #7574
2023-01-14 08:04:38 +09:00
syuilo
e6a76b31be Update CHANGELOG.md 2023-01-14 06:26:06 +09:00
こけっち
98469117bf fix: follow request list api pagination (#9548)
* fix: follow request list api pagination

* fix: improve alias

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-01-14 06:18:58 +09:00
syuilo
a5becfc042 Update CHANGELOG.md 2023-01-14 06:00:58 +09:00
tamaina
d2204fd5c8 refactor: pagination/date-separated-list系処理を良い感じに? (#8209)
* pages/messaging/messaging-room.vue

* wip

* wip

* wip???

* wip?

* ✌️

* messaaging-room.form.vue rewrite to compositon api

* refactor

* 関心事でないのでとりあえず置いておく

* 🎨

* 🎨

* i18n.ts

* fix scroll container find function

* fix

* FIX

* ✌️

* Fix scroll bottom detect

* wip

* aaaaaaaaaaa

* rename

* fix

* fix?

* ✌️

* ✌️

* clean up

* clena up

* refactor

* scroll event once or not

* fix

* fix once

* add safe-area-inset-bottom to spacer

* fix

* ✌️

* 🎨

* fix

* fix

* wip

* ✌️

* clean up

* fix lint

* Update packages/client/src/components/global/sticky-container.vue

Co-authored-by: Johann150 <johann.galle@protonmail.com>

* Update packages/client/src/components/ui/pagination.vue

Co-authored-by: Johann150 <johann.galle@protonmail.com>

* Update packages/client/src/pages/messaging/messaging-room.form.vue

Co-authored-by: Johann150 <johann.galle@protonmail.com>

* clean up: single line comment

* https://github.com/misskey-dev/misskey/pull/8209#discussion_r867386077

* fix

* asobi → tolerance

* pick form

* pick message

* pick room

* fix lint

* fix scroll?

* fix scroll.ts

* fix directives/sticky-container

* update global/sticky-container.vue

* fix, 🎨

* revert merge

* ✌️

* fix lint errors

* 🎨

* Update packages/client/src/types/date-separated-list.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* https://github.com/misskey-dev/misskey/pull/8209#discussion_r917225080

* use '

* Update packages/client/src/scripts/scroll.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* use Number.EPSILON

Co-authored-by: acid-chicken <root@acid-chicken.com>

* revert

* fix

* fix

* Use % instead of vh

* 🎨

* 🎨

* 🎨

* wip

* wip

* css modules

Co-authored-by: Johann150 <johann.galle@protonmail.com>
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
2023-01-13 18:25:40 +09:00
syuilo
519a08f8b5 Update CHANGELOG.md 2023-01-13 18:21:47 +09:00
tamaina
303519a1bd enhance: Judge instance block by endsWith (#9263)
* TypeScriptでendsWith

* fix

* SQL?

* バ〜カアホ

* Update packages/backend/src/core/UtilityService.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* add comment

* add description

* Update packages/backend/src/core/UtilityService.ts

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* Update packages/backend/src/core/chart/charts/federation.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* remove comment

* fix

* fix?

* add changelog

* ILIKE, ARRAY

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-01-13 18:21:07 +09:00
syuilo
161da24841 fix(server): signup-complete Broken
Fix #9538
2023-01-13 17:59:40 +09:00
syuilo
6e40024660 typo 2023-01-13 17:49:05 +09:00
syuilo
73c78d4c38 fix(client): 数式を含む投稿が表示されないのを修正
Fix #9535
2023-01-13 17:45:20 +09:00
syuilo
2654936c17 fix 2023-01-13 17:42:12 +09:00
syuilo
23810e3e1e Update CHANGELOG.md 2023-01-13 15:29:55 +09:00
syuilo
d6c89bf003 13.0.0-beta.42 2023-01-13 15:17:28 +09:00
syuilo
49ab2a5f93 New Crowdin updates (#9520)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Greek)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

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

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)
2023-01-13 15:17:04 +09:00
syuilo
bc0b8afb1f fix ads/update api 2023-01-13 15:15:31 +09:00
syuilo
b250456814 カスタム絵文字の管理を権限を持つユーザーが行えるように
Resolve #9542
2023-01-13 14:58:27 +09:00
syuilo
0a6e237d09 refactor 2023-01-13 14:46:56 +09:00
syuilo
54ff4e53cb Update CHANGELOG.md 2023-01-13 14:32:37 +09:00
syuilo
002ccbb5f0 fix(client): 特定の場所でカスタム絵文字を含むテキストが表示されないのを修正
Fix #9537
2023-01-13 14:29:01 +09:00
syuilo
7b7faf1e84 招待コード発行を権限を持つユーザーが行えるように
Resolve #9541
2023-01-13 14:22:53 +09:00
あずき⪥™
9936088200 fix navButtonIcon not being aligned to center (#9543)
* fix `navButtonIcon` not being aligned to center

* fix `navButtonIcon` on deck not being aligned
2023-01-13 13:24:38 +09:00
syuilo
990f4b52bd Update CHANGELOG.md 2023-01-13 12:20:29 +09:00
syuilo
4c21d83639 プロフィールにロールを表示するように 2023-01-13 12:07:24 +09:00
syuilo
d43a4a2d46 fix(client): ロールの条件を削除する手段が無かったのを修正 2023-01-13 11:51:36 +09:00
syuilo
8d2c3bb18d 13.0.0-beta.41 2023-01-13 11:16:50 +09:00
syuilo
4e39e690b6 Revert "リモートユーザーはcanPublicNoteがfalseでもhomeにしないように"
This reverts commit b2117ba3a1.
2023-01-13 11:14:23 +09:00
syuilo
6458239a7c remoteDriveCapacityMbをロールに統合 2023-01-13 11:14:07 +09:00
syuilo
a5aaa032ca 🎨 2023-01-13 11:07:51 +09:00
syuilo
71bbef69c7 ✌️ 2023-01-13 11:04:58 +09:00
syuilo
c5c40a73b7 feat: conditional role
Resolve #9539
2023-01-13 11:03:54 +09:00
syuilo
74910f8d70 Revert "perf: build backend with swc (#9463)"
This reverts commit 4579d02296.
2023-01-13 09:32:56 +09:00
syuilo
e00003edff refactor 2023-01-13 08:56:06 +09:00
syuilo
bedb98185e 🎨 2023-01-13 08:23:52 +09:00
syuilo
da6f955d58 fix typo
Fix #9536
2023-01-13 08:22:23 +09:00
syuilo
6bdccea26b 🎨 2023-01-13 08:16:45 +09:00
tamaina
b2117ba3a1 リモートユーザーはcanPublicNoteがfalseでもhomeにしないように
https://github.com/misskey-dev/misskey/issues/9534#issuecomment-1380645073
2023-01-12 16:54:11 +00:00
tamaina
ba349fc62f Fix #9534 2023-01-12 16:33:00 +00:00
syuilo
b2c79a5f2c Update CHANGELOG.md 2023-01-12 21:41:31 +09:00
syuilo
3e415e733d Update CHANGELOG.md 2023-01-12 21:22:20 +09:00
syuilo
a5e84e5de9 Update CHANGELOG.md 2023-01-12 21:15:56 +09:00
syuilo
8673353029 13.0.0-beta.40 2023-01-12 21:07:13 +09:00
파링
4579d02296 perf: build backend with swc (#9463)
* feat: build backend with swc

* fix: swc build target

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-01-12 21:05:45 +09:00
tamaina
978a9bbb3b perf(backend): Use undici instead of node-fetch and got (#9459)
* Implement? HttpFetchService

* ✌️

* remove node-fetch

* fix

* refactor

* fix

* gateway timeout

* UndiciFetcherクラスを追加 (仮コミット, ビルドもstartもさせていない)

* fix

* add logger and fix url preview

* fix ip check

* enhance logger and error handling

* fix

* fix

* clean up

* Use custom fetcher for ApRequest / ApResolver

* bypassProxyはproxyBypassHostsに判断を委譲するように

* set maxRedirections (default 3, ApRequest/ApResolver: 0)

* fix comment

* handle error s3 upload

* add debug message

* no return await

* Revert "no return await"

This reverts commit b5b0dc58a3.

* reduce maxSockets

* apResolverのUndiciFetcherを廃止しapRequestのものを使う、 add ap logger

* Revert "apResolverのUndiciFetcherを廃止しapRequestのものを使う、 add ap logger"

This reverts commit 997243915c.

* add logger

* fix

* change logger name

* safe

* デフォルトでUser-Agentを設定
2023-01-12 21:03:02 +09:00
syuilo
2470afaa2e Role (#9437)
* wip

* Update CHANGELOG.md

* wip

* wip

* wip

* Update create.ts

* wip

* wip

* Update CHANGELOG.md

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update CHANGELOG.md

* wip

* wip

* Update delete.ts

* Update delete.ts

* wip

* wip

* wip

* Update account-info.vue

* wip

* wip

* Update settings.vue

* Update user-info.vue

* wip

* Update show-file.ts

* Update show-user.ts

* wip

* wip

* Update delete.ts

* wip

* wip

* Update overview.moderators.vue

* Create 1673500412259-Role.js

* wip

* wip

* Update roles.vue

* 色

* Update roles.vue

* integrate silence

* wip

* wip
2023-01-12 21:02:26 +09:00
syuilo
60e545b2fd Update CHANGELOG.md 2023-01-12 09:34:22 +09:00
Masaya Suzuki
6555644b88 ログイン画面のユーザー名フォームへのautocomplete設定 (#9515)
* Add username class to username form

* Fix add class process

* Set id

* Set autocomplete
2023-01-12 09:33:06 +09:00
syuilo
df56bd6d57 Update CHANGELOG.md 2023-01-11 19:01:17 +09:00
syuilo
e51432a461 remove katex 2023-01-11 08:58:13 +09:00
syuilo
90e2186872 13.0.0-beta.39 2023-01-10 20:22:22 +09:00
syuilo
3043b2f619 fix(client): 画面の幅が狭いとウィジェットドロワーを閉じる手段がなくなるのを修正
Fix #7191
2023-01-10 20:21:39 +09:00
syuilo
d2fc5a248b refactor(client): use css modules 2023-01-10 20:17:08 +09:00
syuilo
e6d666e1ee fix style 2023-01-10 20:12:38 +09:00
syuilo
c5cfbd99d0 perf(server): improve nodeinfo performance
Resolve #9505
2023-01-10 20:08:55 +09:00
syuilo
33b22a323c perf(server): improve stats api performance 2023-01-10 20:06:25 +09:00
syuilo
f032fb628a Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-10 19:53:53 +09:00
あずき⪥™
7761eb8897 prefer using composition (#9521) 2023-01-10 19:53:33 +09:00
syuilo
58fa8c4a01 🎨 2023-01-10 19:52:44 +09:00
syuilo
789d61d175 enhance(server): アンケート選択肢の文字数制限を緩和
Resolve #9511
2023-01-10 16:41:38 +09:00
syuilo
b52fd72727 13.0.0-beta.38 2023-01-10 16:29:22 +09:00
syuilo
d79905e141 New Crowdin updates (#9502)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Greek)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Swedish)

* New translations ja-JP.yml (Turkish)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kabyle)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Greek)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Swedish)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Simplified)
2023-01-10 16:28:45 +09:00
syuilo
cd6b1290cb fix deck style 2023-01-10 16:27:38 +09:00
syuilo
c382497167 fix style 2023-01-10 16:26:51 +09:00
syuilo
a8fb578854 Update .node-version 2023-01-10 16:13:24 +09:00
syuilo
ff00c90a88 refactor(client): use css modules 2023-01-10 14:45:02 +09:00
syuilo
d0755b5ce8 refactor(client): use css modules 2023-01-10 14:37:32 +09:00
syuilo
17fa5667b8 refactor(client): use css modules 2023-01-10 14:20:58 +09:00
syuilo
01d5e385ec refactor(client): use css modules 2023-01-10 14:16:59 +09:00
syuilo
af80fee899 lint 2023-01-10 13:53:41 +09:00
tamaina
6b37c09274 refactor: api, apiGet関数をosから@/scripts/api.tsに分離する (#9510)
* split api?

* fix

* ✌️

* no vue split?

* Revert "no vue split?"

This reverts commit 27ccec971e.
2023-01-10 13:53:06 +09:00
あずき⪥™
1453a0f5cf 画面下部に必要なスペース関連の改善 (#9509)
* enhance: apply same safe area processing to the tab bar

* fix: remove unnecessary bottom space on messaging room

* enhance bottom spacing

* fix size of `minBottomSpacing`
2023-01-10 13:50:34 +09:00
syuilo
1688083e9a 13.0.0-beta.37 2023-01-10 11:40:31 +09:00
syuilo
616594d3cd 🎨 2023-01-10 11:34:06 +09:00
syuilo
6783178dc3 tweak css 2023-01-10 11:19:56 +09:00
syuilo
3f033d6ab7 refactor(client): use css modules 2023-01-10 11:15:29 +09:00
syuilo
d10e000883 refactor(client): use css modules 2023-01-10 10:35:02 +09:00
syuilo
ce528ff22e refactor(client): use css modules 2023-01-10 10:30:38 +09:00
syuilo
5e4e02235a 🎨 2023-01-10 09:57:41 +09:00
syuilo
e4179336e4 refactor(client): use css modules 2023-01-10 09:53:01 +09:00
syuilo
7823ba494f refactor(client): use css modules 2023-01-10 09:41:53 +09:00
syuilo
7bdff90415 update node 2023-01-10 09:32:56 +09:00
syuilo
f3c0af7e23 refactor(client): use css modules 2023-01-10 08:48:37 +09:00
syuilo
72dfbfcf35 Update vite.config.ts 2023-01-10 08:39:19 +09:00
syuilo
9cbe878d0b fix import 2023-01-10 08:28:09 +09:00
syuilo
618405c4d3 refactor(client): rename widget filename 2023-01-10 06:08:40 +09:00
syuilo
0b08fcac4a refactor(client): use css modules 2023-01-10 06:02:46 +09:00
syuilo
eac6ebb239 refactor(client): use css modules 2023-01-10 05:48:00 +09:00
syuilo
194fb14e07 refactor(client): use css modules 2023-01-10 05:45:33 +09:00
syuilo
c2d05b507a refactor(client): use css modules 2023-01-10 05:40:53 +09:00
syuilo
4df43a9107 fix style 2023-01-10 05:40:51 +09:00
syuilo
0da7fcdbed refactor 2023-01-10 05:31:11 +09:00
syuilo
1e50b2688a refactor 2023-01-10 05:31:02 +09:00
syuilo
c1cd018626 tweak style 2023-01-10 05:22:47 +09:00
syuilo
b588e8b60b Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-01-10 05:18:05 +09:00
syuilo
06f55ffb37 refactor(client): use css modules 2023-01-10 05:17:54 +09:00
tamaina
02df6a28cd fix: 続rss-ticker.vue修正 2023-01-09 15:45:22 +00:00
tamaina
d64abedf9f fix: fix rss-ticker TypeError 2023-01-09 15:41:06 +00:00
syuilo
4d39d1caf6 🎨 2023-01-09 20:41:52 +09:00
syuilo
d06f61f23f feat(client): add instance info widget 2023-01-09 20:35:36 +09:00
syuilo
c179d6f735 feat(client): add profile widget
Resolve #7722
2023-01-09 20:23:06 +09:00
syuilo
3bc0cdbfb7 typo 2023-01-09 17:22:21 +09:00
syuilo
b04155e7ba 🎨 2023-01-09 17:18:45 +09:00
syuilo
014c97fa85 13.0.0-beta.36 2023-01-09 17:13:38 +09:00
syuilo
96ccf550b1 New Crowdin updates (#9490)
* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Swedish)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Swedish)

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

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Chinese Traditional)
2023-01-09 17:13:25 +09:00
syuilo
8f28ff63f1 絵文字ピッカーにフォーカスされないのを修正 2023-01-09 17:08:58 +09:00
syuilo
b7dec6e87d refactor 2023-01-09 17:04:52 +09:00
syuilo
1bb2c22493 Update vite.config.ts 2023-01-09 16:45:18 +09:00
syuilo
39c3995c74 refactor 2023-01-09 16:45:05 +09:00
rinsuki
fcabc99303 masterブランチをmaster_securityとマージ (#9260)
* Fix: forkbomb 2

* 12.119.2

Co-authored-by: mei23 <m@m544.net>
2022-12-04 05:34:51 +09:00
syuilo
fccd9c32e8 12.119.1 2022-12-03 18:42:19 +09:00
syuilo
58a3a0b7d4 forkbomb DOS mitigation 2022-12-03 18:42:06 +09:00
syuilo
a2a1636c10 Merge branch 'develop' 2022-09-10 19:26:43 +09:00
syuilo
46ec0303b7 Merge branch 'develop' 2022-08-08 11:39:04 +09:00
syuilo
3b1669fb6b Merge branch 'develop' 2022-08-07 00:39:21 +09:00
syuilo
09591fa4ae Merge branch 'develop' 2022-07-19 17:22:32 +09:00
syuilo
85ce00adc0 Merge branch 'develop' 2022-07-18 05:07:12 +09:00
syuilo
f25518af91 Merge branch 'develop' 2022-07-17 05:35:36 +09:00
syuilo
b796aacf7f Merge branch 'develop' 2022-07-16 23:53:24 +09:00
syuilo
ff24811676 Merge branch 'develop' 2022-07-16 18:21:44 +09:00
syuilo
4c8a1867f0 Merge branch 'develop' 2022-07-15 22:45:13 +09:00
syuilo
bce48dfee9 Merge branch 'develop' 2022-07-13 21:59:47 +09:00
syuilo
c20311b8a7 Merge branch 'develop' 2022-07-09 18:32:55 +09:00
syuilo
fb14ac50b8 Merge branch 'develop' 2022-07-08 17:34:53 +09:00
syuilo
84d984bd31 Merge branch 'develop' 2022-07-07 21:23:03 +09:00
syuilo
1bc856c451 Merge pull request #8821 from misskey-dev/develop
Release: 12.111.1
2022-06-13 00:41:09 +09:00
395 changed files with 23573 additions and 25089 deletions

151
.config/docker_example.yml Normal file
View File

@@ -0,0 +1,151 @@
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Misskey configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user.
url: https://example.tld/
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# URL SETTINGS AFTER THAT!
# ┌───────────────────────┐
#───┘ Port and TLS settings └───────────────────────────────────
#
# Misskey requires a reverse proxy to support HTTPS connections.
#
# +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
# You need to set up a reverse proxy. (e.g. nginx)
# An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# The port that your Misskey server should listen on.
port: 3000
# ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
db:
host: db
port: 5432
# Database name
db: misskey
# Auth
user: example-misskey-user
pass: example-misskey-pass
# Whether disable Caching queries
#disableCache: true
# Extra Connection options
#extra:
# ssl: true
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: redis
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
# Whether disable HSTS
#disableHsts: true
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Syslog option
#syslog:
# host: localhost
# port: 514
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
proxyBypassHosts:
- api.deepl.com
- api-free.deepl.com
- www.recaptcha.net
- hcaptcha.com
- challenges.cloudflare.com
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000

View File

@@ -122,10 +122,12 @@ id: 'aid'
# Proxy for HTTP/HTTPS # Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128 #proxy: http://127.0.0.1:3128
#proxyBypassHosts: [ proxyBypassHosts:
# 'example.com', - api.deepl.com
# '192.0.2.8' - api-free.deepl.com
#] - www.recaptcha.net
- hcaptcha.com
- challenges.cloudflare.com
# Proxy for SMTP/SMTPS # Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT #proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT

View File

@@ -5,6 +5,11 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 0
- package-ecosystem: npm - package-ecosystem: npm
directory: "/" directory: "/"
schedule: schedule:
@@ -20,3 +25,8 @@ updates:
schedule: schedule:
interval: daily interval: daily
open-pull-requests-limit: 0 open-pull-requests-limit: 0
- package-ecosystem: npm
directory: "/packages/sw"
schedule:
interval: daily
open-pull-requests-limit: 0

View File

@@ -0,0 +1,18 @@
name: Check copyright year
on:
push:
branches:
- master
- develop
jobs:
check_copyright_year:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.2.0
- run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!"
exit 1
fi

View File

@@ -10,10 +10,10 @@ jobs:
push_to_registry: push_to_registry:
name: Push Docker image to Docker Hub name: Push Docker image to Docker Hub
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'misskey-dev/misskey'
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v2 uses: actions/checkout@v3.3.0
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v3

View File

@@ -12,7 +12,7 @@ jobs:
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v2 uses: actions/checkout@v3.3.0
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v3

View File

@@ -8,22 +8,26 @@ on:
pull_request: pull_request:
jobs: jobs:
yarn_install: pnpm_install:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- uses: actions/setup-node@v3.2.0 - uses: pnpm/action-setup@v2
with:
version: 7
run_install: false
- uses: actions/setup-node@v3.6.0
with: with:
node-version: 18.x node-version: 18.x
cache: 'yarn' cache: 'pnpm'
- run: corepack enable - run: corepack enable
- run: yarn install --immutable - run: pnpm i --frozen-lockfile
lint: lint:
needs: [yarn_install] needs: [pnpm_install]
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
strategy: strategy:
@@ -33,14 +37,18 @@ jobs:
- frontend - frontend
- sw - sw
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- uses: actions/setup-node@v3.2.0 - uses: pnpm/action-setup@v2
with:
version: 7
run_install: false
- uses: actions/setup-node@v3.6.0
with: with:
node-version: 18.x node-version: 18.x
cache: 'yarn' cache: 'pnpm'
- run: corepack enable - run: corepack enable
- run: yarn install --immutable - run: pnpm i --frozen-lockfile
- run: yarn workspace ${{ matrix.workspace }} run lint - run: pnpm --filter ${{ matrix.workspace }} run lint

View File

@@ -1,7 +1,5 @@
# Run secret-dependent integration tests only after /deploy approval # Run secret-dependent integration tests only after /deploy approval
on: on:
pull_request:
types: [opened, reopened, synchronize]
repository_dispatch: repository_dispatch:
types: [deploy-command] types: [deploy-command]
@@ -12,11 +10,10 @@ jobs:
deploy-preview-environment: deploy-preview-environment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: if:
github.event_name == 'repository_dispatch' &&
github.event.client_payload.slash_command.sha != '' && github.event.client_payload.slash_command.sha != '' &&
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha) contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
steps: steps:
- uses: actions/github-script@v5 - uses: actions/github-script@v6.3.3
id: check-id id: check-id
env: env:
number: ${{ github.event.client_payload.pull_request.number }} number: ${{ github.event.client_payload.pull_request.number }}
@@ -40,7 +37,7 @@ jobs:
return check[0].id; return check[0].id;
- uses: actions/github-script@v5 - uses: actions/github-script@v6.3.3
env: env:
check_id: ${{ steps.check-id.outputs.result }} check_id: ${{ steps.check-id.outputs.result }}
details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }} details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
@@ -56,7 +53,7 @@ jobs:
# Check out merge commit # Check out merge commit
- name: Fork based /deploy checkout - name: Fork based /deploy checkout
uses: actions/checkout@v2 uses: actions/checkout@v3.3.0
with: with:
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
@@ -75,7 +72,7 @@ jobs:
timeout: 15m timeout: 15m
# Update check run called "integration-fork" # Update check run called "integration-fork"
- uses: actions/github-script@v5 - uses: actions/github-script@v6.3.3
id: update-check-run id: update-check-run
if: ${{ always() }} if: ${{ always() }}
env: env:

View File

@@ -9,6 +9,7 @@ name: Destroy preview environment
jobs: jobs:
destroy-preview-environment: destroy-preview-environment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == github.event.pull_request.head.repo.full_name
steps: steps:
- name: Context - name: Context
uses: okteto/context@latest uses: okteto/context@latest

View File

@@ -23,31 +23,35 @@ jobs:
env: env:
POSTGRES_DB: test-misskey POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
YARN_CHECKSUM_BEHAVIOR: update
redis: redis:
image: redis:6 image: redis:6
ports: ports:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3.3.0
with: with:
submodules: true submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7
run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.2.0 uses: actions/setup-node@v3.6.0
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'yarn' cache: 'pnpm'
- run: corepack enable - run: corepack enable
- run: yarn install --immutable - run: pnpm i --frozen-lockfile
- name: Check yarn.lock - name: Check pnpm-lock.yaml
run: git diff --exit-code yarn.lock run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure - name: Copy Configure
run: cp .github/misskey/test.yml .config run: cp .github/misskey/test.yml .config
- name: Build - name: Build
run: yarn build run: pnpm build
- name: Test - name: Test
run: yarn jest-and-coverage run: pnpm jest-and-coverage
- name: Upload Coverage - name: Upload Coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3
with: with:
@@ -77,7 +81,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3.3.0
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
@@ -86,19 +90,22 @@ jobs:
# if: ${{ matrix.browser == 'firefox' }} # if: ${{ matrix.browser == 'firefox' }}
#- uses: browser-actions/setup-firefox@latest #- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }} # if: ${{ matrix.browser == 'firefox' }}
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7
run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.2.0 uses: actions/setup-node@v3.6.0
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'yarn' cache: 'pnpm'
- run: corepack enable - run: corepack enable
- run: yarn install --immutable - run: pnpm i --frozen-lockfile
env:
YARN_CHECKSUM_BEHAVIOR: update
- name: Copy Configure - name: Copy Configure
run: cp .github/misskey/test.yml .config run: cp .github/misskey/test.yml .config
- name: Build - name: Build
run: yarn build run: pnpm build
# https://github.com/cypress-io/cypress/issues/4351#issuecomment-559489091 # https://github.com/cypress-io/cypress/issues/4351#issuecomment-559489091
- name: ALSA Env - name: ALSA Env
run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc
@@ -106,7 +113,7 @@ jobs:
uses: cypress-io/github-action@v4 uses: cypress-io/github-action@v4
with: with:
install: false install: false
start: yarn start:test start: pnpm start:test
wait-on: 'http://localhost:61812' wait-on: 'http://localhost:61812'
headless: false headless: false
browser: ${{ matrix.browser }} browser: ${{ matrix.browser }}

1
.gitignore vendored
View File

@@ -30,6 +30,7 @@ coverage
# config # config
/.config/* /.config/*
!/.config/example.yml !/.config/example.yml
!/.config/docker_example.yml
!/.config/docker_example.env !/.config/docker_example.env
# misskey # misskey

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"search.exclude": {
"**/node_modules": true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
network-timeout 600000

View File

@@ -1,42 +0,0 @@
httpTimeout: 600000
nmHoistingLimits: none
nodeLinker: pnpm
packageExtensions:
"@bull-board/api@*":
peerDependencies:
"@bull-board/ui": "*"
"@tensorflow/tfjs@*":
dependencies:
long: "*"
chartjs-adapter-date-fns@*:
peerDependencies:
date-fns: "*"
consolidate@*:
dependencies:
ejs: "*"
# these are needed to extend fastify types
"@fastify/accepts@*":
peerDependencies:
fastify: "*"
"@fastify/cookie@*":
peerDependencies:
fastify: "*"
"@fastify/static@*":
peerDependencies:
fastify: "*"
"@fastify/view@*":
peerDependencies:
fastify: "*"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
progressBarStyle: patrick
yarnPath: .yarn/releases/yarn-3.3.0.cjs

View File

@@ -9,15 +9,21 @@
You should also include the user name that made the change. You should also include the user name that made the change.
--> -->
## 13.0.0 (unreleased) ## 13.0.0 (2023/01/16)
### TL;DR ### TL;DR
- New features (Play, new widgets, new charts, 🍪👈, etc) - New features (Role system, Misskey Play, New widgets, New charts, 🍪👈, etc)
- Rewriten backend - Rewriten backend
- Better performance (backend and frontend) - Better performance (backend and frontend)
- Various usability improvements - Various usability improvements
- Various UI tweaks - Various UI tweaks
### Notable features
- ロール機能
- 従来より柔軟にユーザーのポリシーを管理できます。例えば、「インスタンスのパトロンはアンテナを30個まで作れる」「基本的にLTLは見れないが、許可した人だけ見れる」「招待制インスタンスだけどユーザーなら誰でも他者を招待できる」のような運用はもちろん、「ローカルユーザーかつアカウント作成から1日未満のユーザーはパブリックな投稿を行えない」のように複数条件を組み合わせて、自動でロールを付与する設定も可能です。
- Misskey Play
- 従来の動的なPagesに代わる、新しいプラットフォームです。動的なコンテンツ(アプリケーション)に特化していて、Pagesに比べてはるかに柔軟なアプリケーションを作成可能です。
### Changes ### Changes
#### For server admins #### For server admins
- Node.js 18.x or later is required - Node.js 18.x or later is required
@@ -25,19 +31,27 @@ You should also include the user name that made the change.
- Misskey not using 15 specific features at 13.0.0, but may do so in the future. - Misskey not using 15 specific features at 13.0.0, but may do so in the future.
- Elasticsearchのサポートが削除されました - Elasticsearchのサポートが削除されました
- 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます
- Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator - Yarnからpnpmに移行されました
- You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic - インスタンスブロックはサブドメインにも適用されるようになります
- ロールの導入に伴い、いくつかの機能がロールと統合されました
- モデレーターはロールに統合されました。今までのモデレーター情報は失われるため、予めモデレーター一覧を記録しておき、アップデート後にモデレーターロールを作りアサインし直してください。
- サイレンスはロールに統合されました。今までのユーザーは恩赦されるため、予めサイレンス一覧を記録しておくのをおすすめします。
- ユーザーごとのドライブ容量設定はロールに統合されました。
- インスタンスデフォルトのドライブ容量設定はロールに統合されました。アップデート後、ベースロールのドライブ容量を編集してください。
- LTL/GTLの解放状態はロールに統合されました。
#### For users #### For users
- ノートのウォッチ機能が削除されました - ノートのウォッチ機能が削除されました
- アンケートに投票された際に通知が作成されなくなりました - アンケートに投票された際に通知が作成されなくなりました
- ノートの数式埋め込みが削除されました
- 新たに動的なPagesを作ることはできなくなりました - 新たに動的なPagesを作ることはできなくなりました
- 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。 - 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。
- AiScriptが0.12.2にアップデートされました - AiScriptが0.12.2にアップデートされました
- 0.12.xの変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - 0.12.xの変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120
- 0.12.x未満のプラグインは読み込むことはできません - 0.12.x未満のプラグインは読み込むことはできません
- iOS15以下のデバイスはサポートされなくなりました - iOS15以下のデバイスはサポートされなくなりました
- Firefox109以下はサポートされなくなりました - Firefox110以下はサポートされなくなりました
- 109でもContainerQueriesのフラグを有効にする事で問題なく使用できます
#### For app developers #### For app developers
- API: metaのレスポンスに`emojis`プロパティが含まれなくなりました - API: metaのレスポンスに`emojis`プロパティが含まれなくなりました
@@ -49,8 +63,10 @@ You should also include the user name that made the change.
- API: `user`および`note`エンティティに`emojis`プロパティが含まれなくなりました - API: `user`および`note`エンティティに`emojis`プロパティが含まれなくなりました
- API: `user`エンティティに`avatarColor`および`bannerColor`プロパティが含まれなくなりました - API: `user`エンティティに`avatarColor`および`bannerColor`プロパティが含まれなくなりました
- API: `instance`エンティティに`latestStatus``lastCommunicatedAt``latestRequestSentAt`プロパティが含まれなくなりました - API: `instance`エンティティに`latestStatus``lastCommunicatedAt``latestRequestSentAt`プロパティが含まれなくなりました
- API: `instance`エンティティの`caughtAt``firstRetrievedAt`に名前が変わりました
### Improvements ### Improvements
- Role system @syuilo
- Misskey Play @syuilo - Misskey Play @syuilo
- Introduce retention-rate aggregation @syuilo - Introduce retention-rate aggregation @syuilo
- Make possible to export favorited notes @syuilo - Make possible to export favorited notes @syuilo
@@ -58,41 +74,62 @@ You should also include the user name that made the change.
- Push notification of Antenna note @tamaina - Push notification of Antenna note @tamaina
- AVIF support @tamaina - AVIF support @tamaina
- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Add Cloudflare Turnstile CAPTCHA support @CyberRex0
- レートリミットをユーザーごとに調整可能に @syuilo
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように @syuilo
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように @syuilo
- クリップおよびクリップ内のノートの作成可能数を設定可能に @syuilo
- ユーザーリストおよびユーザーリスト内のユーザーの作成可能数を設定可能に @syuilo
- ハードワードミュートの最大文字数を設定可能に @syuilo
- Webhookの作成可能数を設定可能に @syuilo
- ノートをピン留めできる数を設定可能に @syuilo
- Server: signToActivityPubGet is set to true by default @syuilo - Server: signToActivityPubGet is set to true by default @syuilo
- Server: improve syslog performance @syuilo - Server: improve syslog performance @syuilo
- Server: Use undici instead of node-fetch and got @tamaina
- Server: Judge instance block by endsWith @tamaina
- Server: improve note scoring for featured notes @CyberRex0 - Server: improve note scoring for featured notes @CyberRex0
- Server: アンケート選択肢の文字数制限を緩和 @syuilo
- Server: プロフィールの文字数制限を緩和 @syuilo
- Server: add rate limits for some endpoints @syuilo
- Server: improve stats api performance @syuilo
- Server: improve nodeinfo performance @syuilo
- Server: delete outdated notifications regularly to improve db performance @syuilo - Server: delete outdated notifications regularly to improve db performance @syuilo
- Server: delete outdated hard-mutes regularly to improve db performance @syuilo - Server: delete outdated hard-mutes regularly to improve db performance @syuilo
- Server: delete outdated notes of antenna regularly to improve db performance @syuilo - Server: delete outdated notes of antenna regularly to improve db performance @syuilo
- Server: improve activitypub deliver performance @syuilo - Server: improve activitypub deliver performance @syuilo
- Client: use tabler-icons instead of fontawesome to better design @syuilo - Client: use tabler-icons instead of fontawesome to better design @syuilo
- Client: Add AiScript App widget
- Client: Add new gabber kick sounds (thanks for noizenecio) - Client: Add new gabber kick sounds (thanks for noizenecio)
- Client: Add link to user RSS feed in profile menu @ssmucny - Client: Add link to user RSS feed in profile menu @ssmucny
- Client: Compress non-animated PNG files @saschanaz - Client: Compress non-animated PNG files @saschanaz
- Client: YouTube window player @sim1222 - Client: YouTube window player @sim1222
- Client: show readable error when rate limit exceeded @syuilo
- Client: enhance dashboard of control panel @syuilo - Client: enhance dashboard of control panel @syuilo
- Client: Vite is upgraded to v4 @syuilo, @tamaina - Client: Vite is upgraded to v4 @syuilo, @tamaina
- Client: HMR is available while yarn dev @tamaina - Client: HMR is available while yarn dev @tamaina
- Client: Make widgets of universal/classic sync between devices @tamaina
- Client: Implement the button to subscribe push notification @tamaina - Client: Implement the button to subscribe push notification @tamaina
- Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina - Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina
- Client: Improve RSS widget @tamaina
- Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz - Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz
- Client: OpenSearch support @SoniEx2 @chaoticryptidz - Client: OpenSearch support @SoniEx2 @chaoticryptidz
- Client: Support remote objects in search @SoniEx2 - Client: Support remote objects in search @SoniEx2
- Client: user activity page @syuilo - Client: user activity page @syuilo
- Client: Make widgets of universal/classic sync between devices @tamaina
- Client: add user list widget @syuilo - Client: add user list widget @syuilo
- Client: Add AiScript App widget
- Client: add profile widget @syuilo
- Client: add instance info widget @syuilo
- Client: Improve RSS widget @tamaina
- Client: add heatmap of daily active users to about page @syuilo - Client: add heatmap of daily active users to about page @syuilo
- Client: introduce fluent emoji @syuilo - Client: introduce fluent emoji @syuilo
- Client: add new theme @syuilo - Client: add new theme @syuilo
- Client: add new mfm function (position, fg, bg) @syuilo
- Client: show fireworks when visit user who today is birthday @syuilo - Client: show fireworks when visit user who today is birthday @syuilo
- Client: show bot warning on screen when logged in as bot account @syuilo - Client: show bot warning on screen when logged in as bot account @syuilo
- Client: AiScriptからカスタム絵文字一覧を参照できるように @syuilo
- Client: improve overall performance of client @syuilo - Client: improve overall performance of client @syuilo
- Client: ui tweaks @syuilo - Client: ui tweaks @syuilo
- Client: clicker game @syuilo - Client: clicker game @syuilo
### Bugfixes ### Bugfixes
- Server: Fix @tensorflow/tfjs-core's MODULE_NOT_FOUND error @ikuradon
- Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468 - Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468
- Server: Bug fix for Pinned Users lookup on instance @squidicuzz - Server: Bug fix for Pinned Users lookup on instance @squidicuzz
- Server: Fix peers API returning suspended instances @ineffyble - Server: Fix peers API returning suspended instances @ineffyble
@@ -105,8 +142,12 @@ You should also include the user name that made the change.
- Server: 特定のPNG画像のアップロードに失敗する問題を修正 @usbharu - Server: 特定のPNG画像のアップロードに失敗する問題を修正 @usbharu
- Server: 非公開のクリップのURLでOGPレンダリングされる問題を修正 @syuilo - Server: 非公開のクリップのURLでOGPレンダリングされる問題を修正 @syuilo
- Server: アンテナタイムライン(ストリーミング)が、フォローしていないユーザーの鍵投稿も拾ってしまう @syuilo - Server: アンテナタイムライン(ストリーミング)が、フォローしていないユーザーの鍵投稿も拾ってしまう @syuilo
- Server: follow request list api pagination @sim1222
- Server: ドライブ容量超過時のエラーが適切にレスポンスされない問題を修正 @syuilo
- Client: パスワードマネージャーなどでユーザー名がオートコンプリートされない問題を修正 @massongit
- Client: 日付形式の文字列などがカスタム絵文字として表示されるのを修正 @syuilo - Client: 日付形式の文字列などがカスタム絵文字として表示されるのを修正 @syuilo
- Client: case insensitive emoji search @saschanaz - Client: case insensitive emoji search @saschanaz
- Client: 画面の幅が狭いとウィジェットドロワーを閉じる手段がなくなるのを修正 @syuilo
- Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina - Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina
- Client: use proxied image for instance icon @syuilo - Client: use proxied image for instance icon @syuilo
- Client: Webhookの編集画面で、内容を保存することができない問題を修正 @m-hayabusa - Client: Webhookの編集画面で、内容を保存することができない問題を修正 @m-hayabusa
@@ -115,6 +156,11 @@ You should also include the user name that made the change.
- Client: チャートのツールチップが画面に残ることがあるのを修正 @syuilo - Client: チャートのツールチップが画面に残ることがあるのを修正 @syuilo
- Client: fix wrong link in tutorial @syuilo - Client: fix wrong link in tutorial @syuilo
### Special thanks
- All contributors
- All who have created instances for the beta test
- All who participated in the beta test
## 12.119.1 (2022/12/03) ## 12.119.1 (2022/12/03)
### Bugfixes ### Bugfixes
- Server: Mitigate AP reference chain DoS vector @skehmatics - Server: Mitigate AP reference chain DoS vector @skehmatics

View File

@@ -1,5 +1,5 @@
Unless otherwise stated this repository is Unless otherwise stated this repository is
Copyright © 2014-2022 syuilo and contributers Copyright © 2014-2023 syuilo and contributers
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

@@ -1,6 +1,6 @@
FROM node:18.12.1-bullseye AS builder ARG NODE_VERSION=18.13.0-bullseye
ARG NODE_ENV=production FROM node:${NODE_VERSION} AS builder
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
@@ -8,38 +8,47 @@ RUN apt-get update \
WORKDIR /misskey WORKDIR /misskey
COPY [".yarnrc.yml", "package.json", "yarn.lock", "./"] COPY ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY [".yarn", "./.yarn"]
COPY ["scripts", "./scripts"] COPY ["scripts", "./scripts"]
COPY ["packages/backend/package.json", "./packages/backend/"] COPY ["packages/backend/package.json", "./packages/backend/"]
COPY ["packages/frontend/package.json", "./packages/frontend/"] COPY ["packages/frontend/package.json", "./packages/frontend/"]
COPY ["packages/sw/package.json", "./packages/sw/"] COPY ["packages/sw/package.json", "./packages/sw/"]
RUN yarn install --immutable RUN npm i -g pnpm
RUN pnpm i --frozen-lockfile
COPY . ./ COPY . ./
ARG NODE_ENV=production
RUN git submodule update --init RUN git submodule update --init
RUN yarn build RUN pnpm build
FROM node:18.12.1-bullseye-slim AS runner FROM node:${NODE_VERSION}-slim AS runner
WORKDIR /misskey ARG UID="991"
ARG GID="991"
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
ffmpeg tini \ ffmpeg tini \
&& apt-get -y clean \ && apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/* \
&& groupadd -g "${GID}" misskey \
&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey
COPY --from=builder /misskey/.yarn/install-state.gz ./.yarn/install-state.gz RUN npm i -g pnpm
COPY --from=builder /misskey/node_modules ./node_modules USER misskey
COPY --from=builder /misskey/built ./built WORKDIR /misskey
COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --from=builder /misskey/packages/backend/built ./packages/backend/built COPY --chown=misskey:misskey --from=builder /misskey/node_modules ./node_modules
COPY --from=builder /misskey/packages/frontend/node_modules ./packages/frontend/node_modules COPY --chown=misskey:misskey --from=builder /misskey/built ./built
COPY . ./ COPY --chown=misskey:misskey --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --chown=misskey:misskey --from=builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=builder /misskey/packages/frontend/node_modules ./packages/frontend/node_modules
COPY --chown=misskey:misskey --from=builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./
ENV NODE_ENV=production ENV NODE_ENV=production
ENTRYPOINT ["/usr/bin/tini", "--"] ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["yarn", "run", "migrateandstart"] CMD ["pnpm", "run", "migrateandstart"]

View File

@@ -29,17 +29,17 @@ describe('After user signed in', () => {
it('first widget should be removed', () => { it('first widget should be removed', () => {
cy.get('.mk-widget-edit').click(); cy.get('.mk-widget-edit').click();
cy.get('.customize-container:first-child .remove._button').click(); cy.get('.data-cy-customize-container:first-child .data-cy-customize-container-remove._button').click();
cy.get('.customize-container').should('have.length', 2); cy.get('.data-cy-customize-container').should('have.length', 2);
}); });
function buildWidgetTest(widgetName) { function buildWidgetTest(widgetName) {
it(`${widgetName} widget should get added`, () => { it(`${widgetName} widget should get added`, () => {
cy.get('.mk-widget-edit').click(); cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select select').select(widgetName, { force: true }); cy.get('.mk-widget-select select').select(widgetName, { force: true });
cy.get('.bg._modalBg.transparent').click({ multiple: true, force: true }); cy.get('.data-cy-bg._modalBg.data-cy-transparent').click({ multiple: true, force: true });
cy.get('.mk-widget-add').click({ force: true }); cy.get('.mk-widget-add').click({ force: true });
cy.get(`.mkw-${widgetName}`).should('exist'); cy.get(`.data-cy-mkw-${widgetName}`).should('exist');
}); });
} }

View File

@@ -8,6 +8,11 @@ services:
- db - db
- redis - redis
# - es # - es
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports: ports:
- "3000:3000" - "3000:3000"
networks: networks:
@@ -24,6 +29,10 @@ services:
- internal_network - internal_network
volumes: volumes:
- ./redis:/data - ./redis:/data
healthcheck:
test: "redis-cli ping"
interval: 5s
retries: 20
db: db:
restart: always restart: always
@@ -34,6 +43,10 @@ services:
- .config/docker.env - .config/docker.env
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql/data
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s
retries: 20
# es: # es:
# restart: always # restart: always

View File

@@ -817,6 +817,13 @@ account: "الحسابات"
cannotLoad: "تعذر التحميل" cannotLoad: "تعذر التحميل"
like: "أعجبني" like: "أعجبني"
show: "المظهر" show: "المظهر"
color: "اللون"
_role:
priority: "الأولوية"
_priority:
low: "منخفضة"
middle: "متوسط"
high: "عالية"
_emailUnavailable: _emailUnavailable:
used: "هذا البريد الإلكتروني مستخدم" used: "هذا البريد الإلكتروني مستخدم"
format: "صيغة البريد الإلكتروني غير صالحة" format: "صيغة البريد الإلكتروني غير صالحة"
@@ -840,6 +847,7 @@ _accountDelete:
_ad: _ad:
back: "رجوع" back: "رجوع"
reduceFrequencyOfThisAd: "قلل عرض هذا الإعلان" reduceFrequencyOfThisAd: "قلل عرض هذا الإعلان"
hide: "لا تظهره بتاتًا"
_forgotPassword: _forgotPassword:
enterEmail: "أدخل البريد الإلكتروني المرتبط بحسابك لكي يرسل إليك رابط لإعادة تعيين كلمة المرور." enterEmail: "أدخل البريد الإلكتروني المرتبط بحسابك لكي يرسل إليك رابط لإعادة تعيين كلمة المرور."
ifNoEmail: "إذا لم تربط حسابك ببريد إلكتروني سيتوجب عليك التواصل مع مدير الموقع." ifNoEmail: "إذا لم تربط حسابك ببريد إلكتروني سيتوجب عليك التواصل مع مدير الموقع."
@@ -1117,6 +1125,8 @@ _weekday:
friday: "الجمعة" friday: "الجمعة"
saturday: "السبت" saturday: "السبت"
_widgets: _widgets:
profile: "الملف التعريفي"
instanceInfo: "معلومات مثيل الخادم"
memo: "ملاحظة لاصقة" memo: "ملاحظة لاصقة"
notifications: "الإشعارات" notifications: "الإشعارات"
timeline: "الخيط الزمني" timeline: "الخيط الزمني"
@@ -1294,7 +1304,6 @@ _notification:
youGotReply: "ردّ عليك {name}" youGotReply: "ردّ عليك {name}"
youGotQuote: "اقتبس منك {name}" youGotQuote: "اقتبس منك {name}"
youRenoted: "إعادت نشر من {name}" youRenoted: "إعادت نشر من {name}"
youGotPoll: "شارك {name} في استطلاع الرأي"
youGotMessagingMessageFromUser: "لقد تلقيت رسالة مِن {name}" youGotMessagingMessageFromUser: "لقد تلقيت رسالة مِن {name}"
youGotMessagingMessageFromGroup: "لقد أرسِلَت رسالة إلى الفريق {name}" youGotMessagingMessageFromGroup: "لقد أرسِلَت رسالة إلى الفريق {name}"
youWereFollowed: "يتابعك" youWereFollowed: "يتابعك"
@@ -1311,7 +1320,6 @@ _notification:
renote: "أعد النشر" renote: "أعد النشر"
quote: "الاقتباسات" quote: "الاقتباسات"
reaction: "التفاعلات" reaction: "التفاعلات"
pollVote: "مصوِت شارك في الاستطلاع"
receiveFollowRequest: "طلبات المتابعة المتلقاة" receiveFollowRequest: "طلبات المتابعة المتلقاة"
followRequestAccepted: "طلبات المتابعة المقبولة" followRequestAccepted: "طلبات المتابعة المقبولة"
groupInvited: "دعوات الفريق" groupInvited: "دعوات الفريق"

View File

@@ -853,6 +853,13 @@ localOnly: "শুধুমাত্র লোকাল"
account: "অ্যাকাউন্টগুলি" account: "অ্যাকাউন্টগুলি"
like: "পছন্দ করা" like: "পছন্দ করা"
show: "প্রদর্শন" show: "প্রদর্শন"
color: "রং"
_role:
priority: "অগ্রাধিকার"
_priority:
low: "নিম্ন"
middle: "মাঝারি"
high: "উচ্চ"
_emailUnavailable: _emailUnavailable:
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
@@ -877,6 +884,7 @@ _accountDelete:
_ad: _ad:
back: "পিছনে" back: "পিছনে"
reduceFrequencyOfThisAd: "এই বিজ্ঞাপনটি কম দেখান" reduceFrequencyOfThisAd: "এই বিজ্ঞাপনটি কম দেখান"
hide: "দেখাবেন না"
_forgotPassword: _forgotPassword:
enterEmail: "আপনি আপনার অ্যাকাউন্টের জন্য নিবন্ধিত ইমেল ঠিকানা লিখুন. সেই ঠিকানায় একটি পাসওয়ার্ড রিসেট লিঙ্ক পাঠানো হবে।" enterEmail: "আপনি আপনার অ্যাকাউন্টের জন্য নিবন্ধিত ইমেল ঠিকানা লিখুন. সেই ঠিকানায় একটি পাসওয়ার্ড রিসেট লিঙ্ক পাঠানো হবে।"
ifNoEmail: "আপনি যদি নিবন্ধনের সময় ই-মেইল ঠিকানা না দিয়ে থাকেন, তাহলে অনুগ্রহ করে প্রশাসকের সাথে যোগাযোগ করুন।" ifNoEmail: "আপনি যদি নিবন্ধনের সময় ই-মেইল ঠিকানা না দিয়ে থাকেন, তাহলে অনুগ্রহ করে প্রশাসকের সাথে যোগাযোগ করুন।"
@@ -1200,6 +1208,8 @@ _weekday:
friday: "শুক্রবার" friday: "শুক্রবার"
saturday: "শনিবার" saturday: "শনিবার"
_widgets: _widgets:
profile: "প্রোফাইল"
instanceInfo: "ইন্সট্যান্সের তথ্য"
memo: "স্টিকি নোট" memo: "স্টিকি নোট"
notifications: "বিজ্ঞপ্তি" notifications: "বিজ্ঞপ্তি"
timeline: "টাইমলাইন" timeline: "টাইমলাইন"
@@ -1386,7 +1396,6 @@ _notification:
youGotReply: "{name} আপনাকে জবাব দিয়েছে" youGotReply: "{name} আপনাকে জবাব দিয়েছে"
youGotQuote: "{name} আপনাকে উদ্ধৃত করেছে" youGotQuote: "{name} আপনাকে উদ্ধৃত করেছে"
youRenoted: "{name} এর Renote" youRenoted: "{name} এর Renote"
youGotPoll: "{name} আপনার পোলে ভোট দিয়েছে"
youGotMessagingMessageFromUser: "{name} আপনাকে মেসেজ করেছে" youGotMessagingMessageFromUser: "{name} আপনাকে মেসেজ করেছে"
youGotMessagingMessageFromGroup: "{name} গ্রুপে একটি নতুন মেসেজ আছে" youGotMessagingMessageFromGroup: "{name} গ্রুপে একটি নতুন মেসেজ আছে"
youWereFollowed: "আপনাকে অনুসরণ করছে" youWereFollowed: "আপনাকে অনুসরণ করছে"
@@ -1403,7 +1412,6 @@ _notification:
renote: "রিনোট" renote: "রিনোট"
quote: "উদ্ধৃতি" quote: "উদ্ধৃতি"
reaction: "প্রতিক্রিয়া" reaction: "প্রতিক্রিয়া"
pollVote: "পোলে ভোট আছে"
pollEnded: "পোল শেষ" pollEnded: "পোল শেষ"
receiveFollowRequest: "প্রাপ্ত অনুসরণের অনুরোধসমূহ" receiveFollowRequest: "প্রাপ্ত অনুসরণের অনুরোধসমূহ"
followRequestAccepted: "গৃহীত অনুসরণের অনুরোধসমূহ" followRequestAccepted: "গৃহীত অনুসরণের অনুরোধসমূহ"

View File

@@ -399,6 +399,8 @@ _antennaSources:
userList: "Publicacions d'una llista d'usuaris" userList: "Publicacions d'una llista d'usuaris"
userGroup: "Publicacions d'usuaris d'un grup" userGroup: "Publicacions d'usuaris d'un grup"
_widgets: _widgets:
profile: "Perfil"
instanceInfo: "Informació del fitxer d'instal·lació"
notifications: "Notificacions" notifications: "Notificacions"
timeline: "Línia de temps" timeline: "Línia de temps"
activity: "Activitat" activity: "Activitat"

View File

@@ -611,6 +611,13 @@ slow: "Pomalá"
fast: "Rychlá" fast: "Rychlá"
account: "Účty" account: "Účty"
show: "Zobrazit" show: "Zobrazit"
color: "Barva"
_role:
priority: "Priorita"
_priority:
low: "Nízká"
middle: "Střední"
high: "Vysoká"
_ad: _ad:
back: "Zpět" back: "Zpět"
_gallery: _gallery:
@@ -694,6 +701,8 @@ _weekday:
friday: "Pátek" friday: "Pátek"
saturday: "Sobota" saturday: "Sobota"
_widgets: _widgets:
profile: "Váš profil"
instanceInfo: "Informace o instanci"
notifications: "Oznámení" notifications: "Oznámení"
timeline: "Časová osa" timeline: "Časová osa"
calendar: "Kalendář" calendar: "Kalendář"

View File

@@ -924,6 +924,73 @@ neverShow: "Nicht wieder anzeigen"
remindMeLater: "Vielleicht später" remindMeLater: "Vielleicht später"
didYouLikeMisskey: "Gefällt dir Misskey?" didYouLikeMisskey: "Gefällt dir Misskey?"
pleaseDonate: "Misskey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!" pleaseDonate: "Misskey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!"
roles: "Rollen"
role: "Rolle"
normalUser: "Standardbenutzer"
undefined: "Undefiniert"
assign: "Zuweisen"
unassign: "Entfernen"
color: "Farbe"
manageCustomEmojis: "Benutzerdefinierte Emojis verwalten"
youCannotCreateAnymore: "Du hast das Erstellungslimit erreicht."
cannotPerformTemporary: "Vorübergehend nicht verfügbar"
cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes des Ausführungslimits temporär nicht verfügbar. Bitte versuche es nach einiger Zeit erneut."
_role:
new: "Rolle erstellen"
edit: "Rolle bearbeiten"
name: "Rollenname"
description: "Rollenbeschreibung"
permission: "Rollenberechtigungen"
descriptionOfPermission: "<b>Moderatoren</b> können grundlegende Verwaltungsaufgaben erledigen.\n<b>Administratoren</b> können alle Einstellungen der Instanz verwalten."
assignTarget: "Zuweisungsart"
descriptionOfAssignTarget: "<b>Manuell</b> bedeutet, dass die Liste der Benutzer einer Rolle manuell verwaltet wird.\n<b>Konditionell</b> bedeutet, dass die Liste der Benutzer einer Rolle durch eine Bedingung automatisch verwaltet wird."
manual: "Manuell"
conditional: "Konditional"
condition: "Bedingung"
isConditionalRole: "Dies ist eine konditionale Rolle."
isPublic: "Öffentliche Rolle"
descriptionOfIsPublic: "Ist dies aktiviert, so kann jeder die Liste der Benutzer, die dieser Rolle zugewiesen sind, einsehen. Zusätzlich wird diese Rolle im Profil zugewiesener Benutzer angezeigt."
options: "Optionen"
policies: "Richtlinien"
baseRole: "Rollenvorlage"
useBaseValue: "Wert der Rollenvorlage verwenden"
chooseRoleToAssign: "Zuzuweisende Rolle auswählen"
canEditMembersByModerator: "Moderatoren können Benutzern diese Rolle zuweisen"
descriptionOfCanEditMembersByModerator: "Wenn aktiviert, so können Moderatoren und Adminstratoren anderen Benutzern diese Rolle zuweisen bzw. diese Zuweisung aufheben. Wenn deaktiviert, so ist es nur Administratoren möglich, Zuweisungen dieser Rolle zu verwalten."
priority: "Priorität"
_priority:
low: "Niedrig"
middle: "Mittel"
high: "Hoch"
_options:
gtlAvailable: "Kann auf die globale Chronik zugreifen"
ltlAvailable: "Kann auf die lokale Chronik zugreifen"
canPublicNote: "Kann öffentliche Notizen erstellen"
canInvite: "Einladungscodes für diese Instanz erstellen"
canManageCustomEmojis: "Benutzerdefinierte Emojis verwalten"
driveCapacity: "Drive-Kapazität"
pinMax: "Maximale Anzahl an angehefteten Notizen"
antennaMax: "Maximale Anzahl an Antennen"
wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen"
webhookMax: "Maximale Anzahl an Webhooks"
clipMax: "Maximale Anzahl an Clips"
noteEachClipsMax: "Maximale Anzahl an Notizen innerhalb eines Clips"
userListMax: "Maximale Anzahl an Benutzern in einer Benutzerliste"
userEachUserListsMax: "Maximale Anzahl an Benutzerlisten"
rateLimitFactor: "Versuchsanzahl"
descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver."
_condition:
isLocal: "Lokaler Benutzer"
isRemote: "Benutzer fremder Instanz"
createdLessThan: "Kontoerstellung liegt weniger als X zurück"
createdMoreThan: "Kontoerstellung liegt mehr als X zurück"
followersLessThanOrEq: "Hat X oder weniger Follower"
followersMoreThanOrEq: "Hat X oder mehr Follower"
followingLessThanOrEq: "Folgt X oder weniger Benutzern"
followingMoreThanOrEq: "Folgt X oder mehr Benutzern"
and: "UND-Bedingung"
or: "ODER-Bedingung"
not: "NICHT-Bedingung"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht."
sensitivity: "Erkennungssensitivität" sensitivity: "Erkennungssensitivität"
@@ -956,6 +1023,7 @@ _accountDelete:
_ad: _ad:
back: "Zurück" back: "Zurück"
reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen"
hide: "Nie anzeigen"
_forgotPassword: _forgotPassword:
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst."
ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator."
@@ -1302,6 +1370,8 @@ _weekday:
friday: "Freitag" friday: "Freitag"
saturday: "Samstag" saturday: "Samstag"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Instanzinformationen"
memo: "Merkzettel" memo: "Merkzettel"
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
timeline: "Chronik" timeline: "Chronik"
@@ -1328,6 +1398,7 @@ _widgets:
userList: "Benutzerliste" userList: "Benutzerliste"
_userList: _userList:
chooseList: "Liste auswählen" chooseList: "Liste auswählen"
clicker: "Klickzähler"
_cw: _cw:
hide: "Inhalt verbergen" hide: "Inhalt verbergen"
show: "Inhalt anzeigen" show: "Inhalt anzeigen"
@@ -1503,7 +1574,6 @@ _notification:
youGotReply: "{name} hat dir geantwortet" youGotReply: "{name} hat dir geantwortet"
youGotQuote: "{name} hat dich zitiert" youGotQuote: "{name} hat dich zitiert"
youRenoted: "Renote deiner Notiz von {name}" youRenoted: "Renote deiner Notiz von {name}"
youGotPoll: "{name} hat in deiner Umfrage abgestimmt"
youGotMessagingMessageFromUser: "{name} hat dir eine Chatnachricht gesendet" youGotMessagingMessageFromUser: "{name} hat dir eine Chatnachricht gesendet"
youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht gesendet" youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht gesendet"
youWereFollowed: "ist dir gefolgt" youWereFollowed: "ist dir gefolgt"
@@ -1521,7 +1591,6 @@ _notification:
renote: "Renotes" renote: "Renotes"
quote: "Zitationen" quote: "Zitationen"
reaction: "Reaktionen" reaction: "Reaktionen"
pollVote: "Antworten auf Umfragen"
pollEnded: "Ende von Umfragen" pollEnded: "Ende von Umfragen"
receiveFollowRequest: "Erhaltene Follow-Anfragen" receiveFollowRequest: "Erhaltene Follow-Anfragen"
followRequestAccepted: "Akzeptierte Follow-Anfragen" followRequestAccepted: "Akzeptierte Follow-Anfragen"

View File

@@ -1,6 +1,6 @@
--- ---
_lang_: "Ελληνικά" _lang_: "Ελληνικά"
monthAndDay: "{μήνας}/{ημέρα}" monthAndDay: "{day}/{month}"
search: "Αναζήτηση" search: "Αναζήτηση"
notifications: "Ειδοποιήσεις" notifications: "Ειδοποιήσεις"
username: "Όνομα μέλους" username: "Όνομα μέλους"
@@ -343,6 +343,8 @@ _antennaSources:
userList: "Σημειώματα από καθορισμένη λίστα μελών" userList: "Σημειώματα από καθορισμένη λίστα μελών"
userGroup: "Σημειώματα από μέλη καθορισμένης ομάδας" userGroup: "Σημειώματα από μέλη καθορισμένης ομάδας"
_widgets: _widgets:
profile: "Προφίλ"
instanceInfo: "Πληροφορίες του instance"
notifications: "Ειδοποιήσεις" notifications: "Ειδοποιήσεις"
timeline: "Χρονολόγιο" timeline: "Χρονολόγιο"
calendar: "Ημερολόγιο" calendar: "Ημερολόγιο"

View File

@@ -924,6 +924,76 @@ neverShow: "Don't show again"
remindMeLater: "Maybe later" remindMeLater: "Maybe later"
didYouLikeMisskey: "Have you taken a liking to Misskey?" didYouLikeMisskey: "Have you taken a liking to Misskey?"
pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!" pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!"
roles: "Roles"
role: "Role"
normalUser: "Normal user"
undefined: "Undefined"
assign: "Assign"
unassign: "Unassign"
color: "Color"
manageCustomEmojis: "Manage Custom Emojis"
youCannotCreateAnymore: "You've hit the creation limit."
cannotPerformTemporary: "Temporarily unavailable"
cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again."
preset: "Presets"
selectFromPresets: "Choose from presets"
_role:
new: "New role"
edit: "Edit role"
name: "Role name"
description: "Role description"
permission: "Role permissions"
descriptionOfPermission: "<b>Moderators</b> can perform basic moderation operations.\n<b>Administrators</b> can change all settings of the instance."
assignTarget: "Assignment type"
descriptionOfAssignTarget: "<b>Manual</b> to manually change who is part of this role and who is not.\n<b>Conditional</b> to have users be automatically assigned and removed from this role based on a condition."
manual: "Manual"
conditional: "Conditional"
condition: "Condition"
isConditionalRole: "This is a conditional role."
isPublic: "Public role"
descriptionOfIsPublic: "Anyone will be able to view a list of users assigned to this role. In addition, this role will be displayed in the profiles of assigned users."
options: "Role options"
policies: "Policies"
baseRole: "Base role"
useBaseValue: "Use base role value"
chooseRoleToAssign: "Select the role to assign"
canEditMembersByModerator: "Allow moderators to edit the list members of this role"
descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users."
priority: "Priority"
_priority:
low: "Low"
middle: "Medium"
high: "High"
_options:
gtlAvailable: "Viewing the global timeline"
ltlAvailable: "Viewing the local timeline"
canPublicNote: "Can send public notes"
canInvite: "Create instance invite codes"
canManageCustomEmojis: "Manage Custom Emojis"
driveCapacity: "Drive capacity"
pinMax: "Maximum number of pinned notes"
antennaMax: "Maximum number of antennas"
wordMuteMax: "Maximum number of characters allowed in word mutes"
webhookMax: "Maximum number of Webhooks"
clipMax: "Maximum number of Clips"
noteEachClipsMax: "Maximum number of notes within a clip"
userListMax: "Maximum number of user lists"
userEachUserListsMax: "Maximum number of users within a user list"
rateLimitFactor: "Rate limit"
descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. "
canHideAds: "Remove ads"
_condition:
isLocal: "Local user"
isRemote: "Remote user"
createdLessThan: "Less than X has passed since account creation"
createdMoreThan: "More than X has passed since account creation"
followersLessThanOrEq: "Has X or fewer followers"
followersMoreThanOrEq: "Has X or more followers"
followingLessThanOrEq: "Follows X or fewer accounts"
followingMoreThanOrEq: "Follows X or more accounts"
and: "AND-Condition"
or: "OR-Condition"
not: "NOT-Condition"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server."
sensitivity: "Detection sensitivity" sensitivity: "Detection sensitivity"
@@ -956,6 +1026,7 @@ _accountDelete:
_ad: _ad:
back: "Back" back: "Back"
reduceFrequencyOfThisAd: "Show this ad less" reduceFrequencyOfThisAd: "Show this ad less"
hide: "Never show"
_forgotPassword: _forgotPassword:
enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it."
ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead." ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead."
@@ -1302,6 +1373,8 @@ _weekday:
friday: "Friday" friday: "Friday"
saturday: "Saturday" saturday: "Saturday"
_widgets: _widgets:
profile: "Profile"
instanceInfo: "Instance Information"
memo: "Sticky notes" memo: "Sticky notes"
notifications: "Notifications" notifications: "Notifications"
timeline: "Timeline" timeline: "Timeline"
@@ -1328,6 +1401,7 @@ _widgets:
userList: "User list" userList: "User list"
_userList: _userList:
chooseList: "Select a list" chooseList: "Select a list"
clicker: "Clicker"
_cw: _cw:
hide: "Hide" hide: "Hide"
show: "Show content" show: "Show content"
@@ -1503,7 +1577,6 @@ _notification:
youGotReply: "{name} replied to you" youGotReply: "{name} replied to you"
youGotQuote: "{name} quoted you" youGotQuote: "{name} quoted you"
youRenoted: "Renote from {name}" youRenoted: "Renote from {name}"
youGotPoll: "{name} voted on your poll"
youGotMessagingMessageFromUser: "{name} sent you a chat message" youGotMessagingMessageFromUser: "{name} sent you a chat message"
youGotMessagingMessageFromGroup: "A chat message was sent to the {name} group" youGotMessagingMessageFromGroup: "A chat message was sent to the {name} group"
youWereFollowed: "followed you" youWereFollowed: "followed you"
@@ -1521,7 +1594,6 @@ _notification:
renote: "Renotes" renote: "Renotes"
quote: "Quotes" quote: "Quotes"
reaction: "Reactions" reaction: "Reactions"
pollVote: "Votes on polls"
pollEnded: "Polls ending" pollEnded: "Polls ending"
receiveFollowRequest: "Received follow requests" receiveFollowRequest: "Received follow requests"
followRequestAccepted: "Accepted follow requests" followRequestAccepted: "Accepted follow requests"

View File

@@ -918,6 +918,13 @@ cannotLoad: "No se puede cargar."
numberOfProfileView: "Número de vistas de perfil" numberOfProfileView: "Número de vistas de perfil"
like: "¡Muy bien!" like: "¡Muy bien!"
show: "Apariencia" show: "Apariencia"
color: "Color"
_role:
priority: "Prioridad"
_priority:
low: "Baja"
middle: "Mediano"
high: "Alta"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor." description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor."
sensitivity: "Sensibilidad de detección" sensitivity: "Sensibilidad de detección"
@@ -950,6 +957,7 @@ _accountDelete:
_ad: _ad:
back: "Deseleccionar" back: "Deseleccionar"
reduceFrequencyOfThisAd: "Mostrar menos este anuncio." reduceFrequencyOfThisAd: "Mostrar menos este anuncio."
hide: "No mostrar"
_forgotPassword: _forgotPassword:
enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link para resetear la contraseña." enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link para resetear la contraseña."
ifNoEmail: "Si no utilizó un correo para crear la cuenta, contáctese con el administrador." ifNoEmail: "Si no utilizó un correo para crear la cuenta, contáctese con el administrador."
@@ -1296,6 +1304,8 @@ _weekday:
friday: "Viernes" friday: "Viernes"
saturday: "Sábado" saturday: "Sábado"
_widgets: _widgets:
profile: "Perfil"
instanceInfo: "información de la instancia"
memo: "Nota adhesiva" memo: "Nota adhesiva"
notifications: "Notificaciones" notifications: "Notificaciones"
timeline: "Linea de tiempo" timeline: "Linea de tiempo"
@@ -1487,7 +1497,6 @@ _notification:
youGotReply: "Respuesta de {name}" youGotReply: "Respuesta de {name}"
youGotQuote: "Citado por {name}" youGotQuote: "Citado por {name}"
youRenoted: "Renotado por {name}" youRenoted: "Renotado por {name}"
youGotPoll: "Encuestado por {name}"
youGotMessagingMessageFromUser: "{name} comenzó un chat contigo" youGotMessagingMessageFromUser: "{name} comenzó un chat contigo"
youGotMessagingMessageFromGroup: "Tienes un chat de {name}" youGotMessagingMessageFromGroup: "Tienes un chat de {name}"
youWereFollowed: "te ha seguido" youWereFollowed: "te ha seguido"
@@ -1505,7 +1514,6 @@ _notification:
renote: "Renotar" renote: "Renotar"
quote: "Citar" quote: "Citar"
reaction: "Reacción" reaction: "Reacción"
pollVote: "Votado en la encuesta"
pollEnded: "La encuesta terminó" pollEnded: "La encuesta terminó"
receiveFollowRequest: "Recibió una solicitud de seguimiento" receiveFollowRequest: "Recibió una solicitud de seguimiento"
followRequestAccepted: "El seguimiento fue aceptado" followRequestAccepted: "El seguimiento fue aceptado"

View File

@@ -911,7 +911,17 @@ loggedInAsBot: "Connecté actuellement en tant que bot"
tools: "Outils" tools: "Outils"
cannotLoad: "Chargement impossible" cannotLoad: "Chargement impossible"
like: "J'aime" like: "J'aime"
numberOfLikes: "Favoris"
show: "Affichage" show: "Affichage"
neverShow: "Ne plus afficher"
remindMeLater: "Peut-être plus tard"
color: "Couleur"
_role:
priority: "Priorité"
_priority:
low: "Basse"
middle: "Moyen"
high: "Haute"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement." description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
sensitivity: "Sensibilité de la détection" sensitivity: "Sensibilité de la détection"
@@ -944,6 +954,7 @@ _accountDelete:
_ad: _ad:
back: "Retour" back: "Retour"
reduceFrequencyOfThisAd: "Voir cette publicité moins souvent" reduceFrequencyOfThisAd: "Voir cette publicité moins souvent"
hide: "Cacher "
_forgotPassword: _forgotPassword:
enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette adresse." enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette adresse."
ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance." ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance."
@@ -1289,6 +1300,8 @@ _weekday:
friday: "Vendredi" friday: "Vendredi"
saturday: "Samedi" saturday: "Samedi"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Informations sur linstance"
memo: "Note collante" memo: "Note collante"
notifications: "Notifications" notifications: "Notifications"
timeline: "Fil" timeline: "Fil"
@@ -1478,7 +1491,6 @@ _notification:
youGotReply: "Réponse de {name}" youGotReply: "Réponse de {name}"
youGotQuote: "Cité·e par {name}" youGotQuote: "Cité·e par {name}"
youRenoted: "{name} vous a Renoté" youRenoted: "{name} vous a Renoté"
youGotPoll: "{name} a participé à votre sondage"
youGotMessagingMessageFromUser: "{name} vous envoyé un message" youGotMessagingMessageFromUser: "{name} vous envoyé un message"
youGotMessagingMessageFromGroup: "Un message a été envoyé au groupe {name}" youGotMessagingMessageFromGroup: "Un message a été envoyé au groupe {name}"
youWereFollowed: "Vous suit" youWereFollowed: "Vous suit"
@@ -1496,7 +1508,6 @@ _notification:
renote: "Renotes" renote: "Renotes"
quote: "Citations" quote: "Citations"
reaction: "Réactions" reaction: "Réactions"
pollVote: "Votes dans des sondages"
pollEnded: "Sondages se cloturant" pollEnded: "Sondages se cloturant"
receiveFollowRequest: "Demande d'abonnement reçue" receiveFollowRequest: "Demande d'abonnement reçue"
followRequestAccepted: "Demande d'abonnement acceptée" followRequestAccepted: "Demande d'abonnement acceptée"

View File

@@ -859,6 +859,13 @@ like: "Suka"
unlike: "Tidak Suka" unlike: "Tidak Suka"
numberOfLikes: "Jumlah yang disukai" numberOfLikes: "Jumlah yang disukai"
show: "Tampilkan" show: "Tampilkan"
color: "Warna"
_role:
priority: "Prioritas"
_priority:
low: "Rendah"
middle: "Sedang"
high: "Tinggi"
_emailUnavailable: _emailUnavailable:
used: "Alamat surel ini telah digunakan" used: "Alamat surel ini telah digunakan"
format: "Format tidak valid." format: "Format tidak valid."
@@ -883,6 +890,7 @@ _accountDelete:
_ad: _ad:
back: "Kembali" back: "Kembali"
reduceFrequencyOfThisAd: "Tampilkan iklan ini lebih sedikit" reduceFrequencyOfThisAd: "Tampilkan iklan ini lebih sedikit"
hide: "Jangan tampilkan"
_forgotPassword: _forgotPassword:
enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel tersebut." enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel tersebut."
ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi admin segera." ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi admin segera."
@@ -1206,6 +1214,8 @@ _weekday:
friday: "Jumat" friday: "Jumat"
saturday: "Sabtu" saturday: "Sabtu"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Informasi Instansi"
memo: "Catatan memo" memo: "Catatan memo"
notifications: "Pemberitahuan" notifications: "Pemberitahuan"
timeline: "Linimasa" timeline: "Linimasa"
@@ -1402,7 +1412,6 @@ _notification:
youGotReply: "{name} membalas kamu" youGotReply: "{name} membalas kamu"
youGotQuote: "{name} mengutip kamu" youGotQuote: "{name} mengutip kamu"
youRenoted: "{name} me-renote kamu" youRenoted: "{name} me-renote kamu"
youGotPoll: "{name} memilih di angket kamu"
youGotMessagingMessageFromUser: "{name} mengirimi kamu pesan" youGotMessagingMessageFromUser: "{name} mengirimi kamu pesan"
youGotMessagingMessageFromGroup: "Sebuah pesan telah dikirim ke grup {name}" youGotMessagingMessageFromGroup: "Sebuah pesan telah dikirim ke grup {name}"
youWereFollowed: "Mengikuti kamu" youWereFollowed: "Mengikuti kamu"
@@ -1419,7 +1428,6 @@ _notification:
renote: "Renote" renote: "Renote"
quote: "Kutip" quote: "Kutip"
reaction: "Reaksi" reaction: "Reaksi"
pollVote: "Memilih di angket"
pollEnded: "Jajak pendapat berakhir" pollEnded: "Jajak pendapat berakhir"
receiveFollowRequest: "Permintaan mengikuti diterima" receiveFollowRequest: "Permintaan mengikuti diterima"
followRequestAccepted: "Permintaan mengikuti disetujui" followRequestAccepted: "Permintaan mengikuti disetujui"

View File

@@ -8,7 +8,7 @@ search: "Cerca"
notifications: "Notifiche" notifications: "Notifiche"
username: "Nome utente" username: "Nome utente"
password: "Password" password: "Password"
forgotPassword: "Hai dimenticato la tua password?" forgotPassword: "Hai dimenticato la password?"
fetchingAsApObject: "Recuperando dal Fediverso..." fetchingAsApObject: "Recuperando dal Fediverso..."
ok: "OK" ok: "OK"
gotIt: "Ho capito" gotIt: "Ho capito"
@@ -109,7 +109,7 @@ you: "Tu"
clickToShow: "Clicca per visualizzare" clickToShow: "Clicca per visualizzare"
sensitive: "Contenuto sensibile" sensitive: "Contenuto sensibile"
add: "Aggiungi" add: "Aggiungi"
reaction: "Reazione" reaction: "Reazioni"
reactionSetting: "Reazioni visualizzate sul pannello" reactionSetting: "Reazioni visualizzate sul pannello"
reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
@@ -226,7 +226,7 @@ currentPassword: "Password attuale"
newPassword: "Nuova Password" newPassword: "Nuova Password"
newPasswordRetype: "Conferma password" newPasswordRetype: "Conferma password"
attachFile: "Allega file" attachFile: "Allega file"
more: "Altri!" more: "Di più!"
featured: "Tendenze" featured: "Tendenze"
usernameOrUserId: "Nome utente o ID utente" usernameOrUserId: "Nome utente o ID utente"
noSuchUser: "Nessun utente trovato" noSuchUser: "Nessun utente trovato"
@@ -325,7 +325,7 @@ connectService: "Connessione"
disconnectService: "Disconnessione " disconnectService: "Disconnessione "
enableLocalTimeline: "Abilita Timeline locale" enableLocalTimeline: "Abilita Timeline locale"
enableGlobalTimeline: "Abilita Timeline federata" enableGlobalTimeline: "Abilita Timeline federata"
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci." disablingTimelinesInfo: "Anche disabilitandole, gli Amministratori e i Moderatori potranno comunque accedervi."
registration: "Iscriviti" registration: "Iscriviti"
enableRegistration: "Permettere nuove registrazioni" enableRegistration: "Permettere nuove registrazioni"
invite: "Invita" invite: "Invita"
@@ -340,7 +340,7 @@ pinnedUsers: "Utenti in evidenza"
pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina \"Esplora\", un@ per riga." pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina \"Esplora\", un@ per riga."
pinnedPages: "Pagine in evidenza" pinnedPages: "Pagine in evidenza"
pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga."
pinnedClipId: "ID della nota in evidenza" pinnedClipId: "ID della Clip in evidenza"
pinnedNotes: "Nota fissata" pinnedNotes: "Nota fissata"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "Abilita hCaptcha" enableHcaptcha: "Abilita hCaptcha"
@@ -512,7 +512,7 @@ newNoteRecived: "Vedi le nuove note"
sounds: "Impostazioni suoni" sounds: "Impostazioni suoni"
sound: "Impostazioni suoni" sound: "Impostazioni suoni"
listen: "Ascolta" listen: "Ascolta"
none: "Niente" none: "Nessuno"
showInPage: "Visualizza in pagina" showInPage: "Visualizza in pagina"
popout: "Finestra pop-out" popout: "Finestra pop-out"
volume: "Volume" volume: "Volume"
@@ -578,7 +578,7 @@ useFullReactionPicker: "Usa la totalità del pannello di reazioni"
width: "Larghezza" width: "Larghezza"
height: "Altezza" height: "Altezza"
large: "Grande" large: "Grande"
medium: "Predefinito" medium: "Medio"
small: "Piccolo" small: "Piccolo"
generateAccessToken: "Genera token di accesso" generateAccessToken: "Genera token di accesso"
permission: "Autorizzazioni " permission: "Autorizzazioni "
@@ -649,13 +649,13 @@ instanceTicker: "Informazioni sull'istanza da cui vengono le note"
waitingFor: "Aspettando {x}" waitingFor: "Aspettando {x}"
random: "Casuale" random: "Casuale"
system: "Sistema" system: "Sistema"
switchUi: "Cambiare interfaccia utente" switchUi: "Cambiare interfaccia"
desktop: "Desktop" desktop: "Desktop"
clip: "Nota" clip: "Clip"
createNew: "Crea" createNew: "Crea"
optional: "Opzionale" optional: "facoltativo"
createNewClip: "Nuova Nota" createNewClip: "Crea una Clip"
unclip: "Rimuovi la nota" unclip: "Togli Nota dalla Clip"
confirmToUnclipAlreadyClippedNote: "Questa nota è già inclusa in \"{name}\". Si desidera escludere la nota?" confirmToUnclipAlreadyClippedNote: "Questa nota è già inclusa in \"{name}\". Si desidera escludere la nota?"
public: "Pubblica" public: "Pubblica"
i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}." i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}."
@@ -799,7 +799,7 @@ received: "Ricevuto"
searchResult: "Risultati della Ricerca" searchResult: "Risultati della Ricerca"
hashtags: "Hashtag" hashtags: "Hashtag"
troubleshooting: "Risoluzione problemi" troubleshooting: "Risoluzione problemi"
useBlurEffect: "Utilizza effetto sfocatura per l'interfaccia utente" useBlurEffect: "Utilizza effetto sfocatura nell'interfaccia"
learnMore: "Più dettagli" learnMore: "Più dettagli"
misskeyUpdated: "Misskey è stato aggiornato!" misskeyUpdated: "Misskey è stato aggiornato!"
whatIsNew: "Visualizza le informazioni sull'aggiornamento" whatIsNew: "Visualizza le informazioni sull'aggiornamento"
@@ -917,7 +917,80 @@ tools: "Strumenti"
cannotLoad: "Caricamento impossibile" cannotLoad: "Caricamento impossibile"
numberOfProfileView: "Visualizzazioni profilo" numberOfProfileView: "Visualizzazioni profilo"
like: "Mi piace!" like: "Mi piace!"
unlike: "Non mi piace"
numberOfLikes: "Numero di Like"
show: "Visualizza" show: "Visualizza"
neverShow: "Non mostrare più"
remindMeLater: "Rimanda"
didYouLikeMisskey: "Ti piace Misskey?"
pleaseDonate: "Misskey è il software libero utilizzato su {host}. Offrendo una donazione è più facile continuare a svilupparlo!"
roles: "Ruoli"
role: "Ruolo"
normalUser: "Profilo standard"
undefined: "Indefinito"
assign: "Assegna"
unassign: "Disassegna"
color: "Colore"
manageCustomEmojis: "Gestisci le emoji personalizzate"
youCannotCreateAnymore: "Non puoi creare, hai raggiunto il limite."
cannotPerformTemporary: "Indisponibilità temporanea"
cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi."
_role:
new: "Nuovo ruolo"
edit: "Modifica ruolo"
name: "Nome del ruolo"
description: "Descrizione del ruolo"
permission: "Permessi globali del ruolo"
descriptionOfPermission: "<b>Moderatori</b> possono svolgere le attività di moderazione basilari.\n<b>Amministratori</b> possono modificare la configurazione dell'istanza."
assignTarget: "Modalità di assegnazione del ruolo"
descriptionOfAssignTarget: "<b>Manuale</b>: per assegnare manualmente questo ruolo ai profili.\n<b>Condizionale</b>: per assegnare o rimuovere automaticamente questo ruolo ai profili, a precise condizioni."
manual: "Manuale"
conditional: "Condizionale"
condition: "Condizioni"
isConditionalRole: "Questo è un ruolo condizionato"
isPublic: "Ruolo pubblico"
descriptionOfIsPublic: "La lista di profili assegnati a questo ruolo è visibile a chiunque. Inoltre, il nome del ruolo verrà mostrato pubblicamente nei relativi profili."
options: "Opzioni del ruolo"
policies: "Policy"
baseRole: "Ruolo di base"
useBaseValue: "Eredita dal ruolo base"
chooseRoleToAssign: "Seleziona il ruolo da assegnare"
canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo"
descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori."
priority: "Priorità"
_priority:
low: "Bassa"
middle: "Medio"
high: "Alta"
_options:
gtlAvailable: "Disponibilità della Timeline Federata"
ltlAvailable: "Disponibilità della Timeline Locale"
canPublicNote: "Può scrivere Note con Visibilità Pubblica"
canInvite: "Genera codici di invito all'istanza"
canManageCustomEmojis: "Gestire le emoji personalizzate"
driveCapacity: "Capienza del Drive"
pinMax: "Quantità massima di Note in primo piano"
antennaMax: "Quantità massima di Antenne"
wordMuteMax: "Lunghezza massima del filtro parole"
webhookMax: "Quantità massima di Webhook"
clipMax: "Quantità massima di Clip"
noteEachClipsMax: "Quantità massima di Note nella Clip"
userListMax: "Quantità massima di liste"
userEachUserListsMax: "Quantità massima di profili per lista"
rateLimitFactor: "Limite del rapporto"
descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più."
_condition:
isLocal: "Profilo locale"
isRemote: "Profilo remoto"
createdLessThan: "Creato meno di"
createdMoreThan: "Creato più di"
followersLessThanOrEq: "Ha meno di N follower"
followersMoreThanOrEq: "Ha più di N follower"
followingLessThanOrEq: "Segue N profili o meno"
followingMoreThanOrEq: "Segue N profili o più"
and: "E"
or: "O"
not: "NON"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "L'apprendimento automatico può essere utilizzato per individuare automaticamente i media sensibili da moderare. Il carico del server aumenta leggermente." description: "L'apprendimento automatico può essere utilizzato per individuare automaticamente i media sensibili da moderare. Il carico del server aumenta leggermente."
sensitivity: "Sensibilità di rilevamento" sensitivity: "Sensibilità di rilevamento"
@@ -950,6 +1023,7 @@ _accountDelete:
_ad: _ad:
back: "Indietro" back: "Indietro"
reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso" reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso"
hide: "Nascondi"
_forgotPassword: _forgotPassword:
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo." enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo."
ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare l'amministratore·trice dell'istanza." ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare l'amministratore·trice dell'istanza."
@@ -1090,9 +1164,9 @@ _channel:
usersCount: "{n} partecipanti" usersCount: "{n} partecipanti"
notesCount: "{n} note" notesCount: "{n} note"
_menuDisplay: _menuDisplay:
sideFull: "laro" sideFull: "Laterale"
sideIcon: "Orizzontale (icona)" sideIcon: "Laterale (solo icone)"
top: "superficie" top: "In alto"
hide: "Nascondere" hide: "Nascondere"
_wordMute: _wordMute:
muteWords: "Parole da filtrare" muteWords: "Parole da filtrare"
@@ -1194,10 +1268,10 @@ _ago:
future: "Futuro" future: "Futuro"
justNow: "Ora" justNow: "Ora"
secondsAgo: "{n}s fa" secondsAgo: "{n}s fa"
minutesAgo: "{n}min fa" minutesAgo: "{n} min fa"
hoursAgo: "{n} ore fa" hoursAgo: "{n} ore fa"
daysAgo: "{n} giorni fa" daysAgo: "{n} gg fa"
weeksAgo: "{n} settimane fa" weeksAgo: "{n} sett. fa"
monthsAgo: "{n} mesi fa" monthsAgo: "{n} mesi fa"
yearsAgo: "{n} anni fa" yearsAgo: "{n} anni fa"
_time: _time:
@@ -1296,6 +1370,8 @@ _weekday:
friday: "Venerdì" friday: "Venerdì"
saturday: "Sabato" saturday: "Sabato"
_widgets: _widgets:
profile: "Profilo"
instanceInfo: "Informazioni sull'istanza"
memo: "Promemoria" memo: "Promemoria"
notifications: "Notifiche" notifications: "Notifiche"
timeline: "Timeline" timeline: "Timeline"
@@ -1317,10 +1393,12 @@ _widgets:
jobQueue: "Coda di lavoro" jobQueue: "Coda di lavoro"
serverMetric: "Statistiche server" serverMetric: "Statistiche server"
aiscript: "Console AiScript" aiscript: "Console AiScript"
aiscriptApp: "App AiScript"
aichan: "Mascotte Ai" aichan: "Mascotte Ai"
userList: "Elenco utenti" userList: "Elenco utenti"
_userList: _userList:
chooseList: "Seleziona una lista" chooseList: "Seleziona una lista"
clicker: "Cliccaggio"
_cw: _cw:
hide: "Nascondere" hide: "Nascondere"
show: "Mostra di più" show: "Mostra di più"
@@ -1329,7 +1407,7 @@ _cw:
_poll: _poll:
noOnlyOneChoice: "Sono necessarie almeno 2 risposte" noOnlyOneChoice: "Sono necessarie almeno 2 risposte"
choiceN: "Opzione {n}" choiceN: "Opzione {n}"
noMore: "Hai aggiunto il numero massimo di opzioni." noMore: "Hai raggiunto il limite di opzioni."
canMultipleVote: "Possibilità di risposte multiple" canMultipleVote: "Possibilità di risposte multiple"
expiration: "Scadenza" expiration: "Scadenza"
infinite: "Non scade" infinite: "Non scade"
@@ -1423,7 +1501,16 @@ _timelines:
social: "Sociale" social: "Sociale"
global: "Federata" global: "Federata"
_play: _play:
new: "Crea un Play"
edit: "Modifica i Play"
created: "Il Play è stato creato"
updated: "Il Play è stato aggiornato"
deleted: "Il Play è stato eliminato"
pageSetting: "Impostazioni di Play"
editThisPage: "Modifica il Play"
viewSource: "Visualizza sorgente" viewSource: "Visualizza sorgente"
my: "I miei Play"
liked: "Play piaciuti"
featured: "Popolari" featured: "Popolari"
title: "Titolo" title: "Titolo"
script: "Script" script: "Script"
@@ -1487,7 +1574,6 @@ _notification:
youGotReply: "{name} ti ha risposto" youGotReply: "{name} ti ha risposto"
youGotQuote: "{name} ha citato il tuo Nota e ha detto" youGotQuote: "{name} ha citato il tuo Nota e ha detto"
youRenoted: "{name} ha rinotato" youRenoted: "{name} ha rinotato"
youGotPoll: "{name} ha votato"
youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio"
youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat"
youWereFollowed: "Ha iniziato a seguirti" youWereFollowed: "Ha iniziato a seguirti"
@@ -1505,7 +1591,6 @@ _notification:
renote: "Rinota" renote: "Rinota"
quote: "Cita" quote: "Cita"
reaction: "Reazioni" reaction: "Reazioni"
pollVote: "Voti ricevuti"
pollEnded: "Sondaggio chiuso." pollEnded: "Sondaggio chiuso."
receiveFollowRequest: "Richiesta di follow ricevuta" receiveFollowRequest: "Richiesta di follow ricevuta"
followRequestAccepted: "Richiesta di follow accettata" followRequestAccepted: "Richiesta di follow accettata"

View File

@@ -193,7 +193,7 @@ clearQueueConfirmText: "未配達の投稿は配送されなくなります。
clearCachedFiles: "キャッシュをクリア" clearCachedFiles: "キャッシュをクリア"
clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?" clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?"
blockedInstances: "ブロックしたインスタンス" blockedInstances: "ブロックしたインスタンス"
blockedInstancesDescription: "ブロックしたいインスタンスのホストを改行で区切って設定します。ブロックされたインスタンスは、このインスタンスとやり取りできなくなります。" blockedInstancesDescription: "ブロックしたいインスタンスのホストを改行で区切って設定します。ブロックされたインスタンスは、このインスタンスとやり取りできなくなります。サブドメインもブロックされます。"
muteAndBlock: "ミュートとブロック" muteAndBlock: "ミュートとブロック"
mutedUsers: "ミュートしたユーザー" mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー" blockedUsers: "ブロックしたユーザー"
@@ -924,6 +924,77 @@ neverShow: "今後表示しない"
remindMeLater: "また後で" remindMeLater: "また後で"
didYouLikeMisskey: "Misskeyを気に入っていただけましたか" didYouLikeMisskey: "Misskeyを気に入っていただけましたか"
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!" pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!"
roles: "ロール"
role: "ロール"
normalUser: "一般ユーザー"
undefined: "未定義"
assign: "アサイン"
unassign: "アサインを解除"
color: "色"
manageCustomEmojis: "カスタム絵文字の管理"
youCannotCreateAnymore: "これ以上作成することはできません。"
cannotPerformTemporary: "一時的に利用できません"
cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。"
preset: "プリセット"
selectFromPresets: "プリセットから選択"
_role:
new: "ロールの作成"
edit: "ロールの編集"
name: "ロール名"
description: "ロールの説明"
permission: "ロールの権限"
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関する操作を行えます。\n<b>管理者</b>はインスタンスの全ての設定を変更できます。"
assignTarget: "アサインターゲット"
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれるかを手動で管理します。\n<b>コンディショナル</b>は条件を設定し、それに合致するユーザーが自動で含まれるようになります。"
manual: "マニュアル"
conditional: "コンディショナル"
condition: "条件"
isConditionalRole: "これはコンディショナルロールです。"
isPublic: "ロールを公開"
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。"
options: "オプション"
policies: "ポリシー"
baseRole: "ベースロール"
useBaseValue: "ベースロールの値を使用"
chooseRoleToAssign: "アサインするロールを選択"
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。"
priority: "優先度"
_priority:
low: "低"
middle: "中"
high: "高"
_options:
gtlAvailable: "グローバルタイムラインの閲覧"
ltlAvailable: "ローカルタイムラインの閲覧"
canPublicNote: "パブリック投稿の許可"
canInvite: "インスタンス招待コードの発行"
canManageCustomEmojis: "カスタム絵文字の管理"
driveCapacity: "ドライブ容量"
pinMax: "ノートのピン留めの最大数"
antennaMax: "アンテナの作成可能数"
wordMuteMax: "ワードミュートの最大文字数"
webhookMax: "Webhookの作成可能数"
clipMax: "クリップの作成可能数"
noteEachClipsMax: "クリップ内のノートの最大数"
userListMax: "ユーザーリストの作成可能数"
userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
rateLimitFactor: "レートリミット"
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
canHideAds: "広告の非表示"
_condition:
isLocal: "ローカルユーザー"
isRemote: "リモートユーザー"
createdLessThan: "アカウント作成から~以内"
createdMoreThan: "アカウント作成から~経過"
followersLessThanOrEq: "フォロワー数が~以下"
followersMoreThanOrEq: "フォロワー数が~以上"
followingLessThanOrEq: "フォロー数が~以下"
followingMoreThanOrEq: "フォロー数が~以上"
and: "~かつ~"
or: "~または~"
not: "~ではない"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
@@ -962,6 +1033,7 @@ _accountDelete:
_ad: _ad:
back: "戻る" back: "戻る"
reduceFrequencyOfThisAd: "この広告の表示頻度を下げる" reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"
hide: "表示しない"
_forgotPassword: _forgotPassword:
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。" enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
@@ -1335,6 +1407,8 @@ _weekday:
saturday: "土曜日" saturday: "土曜日"
_widgets: _widgets:
profile: "プロフィール"
instanceInfo: "インスタンス情報"
memo: "付箋" memo: "付箋"
notifications: "通知" notifications: "通知"
timeline: "タイムライン" timeline: "タイムライン"

View File

@@ -915,8 +915,85 @@ caption: "キャプション"
loggedInAsBot: "Botアカウントでログイン中やで" loggedInAsBot: "Botアカウントでログイン中やで"
tools: "ツール" tools: "ツール"
cannotLoad: "読み込めへんで" cannotLoad: "読み込めへんで"
numberOfProfileView: "プロフィール表示回数"
like: "ええやん!" like: "ええやん!"
unlike: "いいねを解除"
numberOfLikes: "いいね数"
show: "表示" show: "表示"
neverShow: "今後表示しない"
remindMeLater: "また後で"
didYouLikeMisskey: "Misskeyを気に入っとっただけましたん"
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
roles: "ロール"
role: "ロール"
normalUser: "一般ユーザー"
undefined: "未定義"
assign: "アサイン"
unassign: "アサインを解除"
color: "色"
manageCustomEmojis: "カスタム絵文字の管理"
youCannotCreateAnymore: "これ以上作れなさそうや"
cannotPerformTemporary: "一時的に利用できへんで"
cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。"
preset: "プリセット"
selectFromPresets: "プリセットから選ぶ"
_role:
new: "ロールの作成"
edit: "ロールの編集"
name: "ロール名"
description: "ロールの説明"
permission: "ロールの権限"
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関わる操作を行えるで。\n<b>管理者</b>はインスタンスの全ての設定を変更できるで。"
assignTarget: "アサインターゲット"
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれてるかを手動で管理するで。\n<b>コンディショナル</b>は条件を設定して、それに合うユーザーが自動で含まれるようになるで。"
manual: "マニュアル"
conditional: "コンディショナル"
condition: "条件"
isConditionalRole: "これはコンディショナルロールやで"
isPublic: "ロールを公開"
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができるで。そんで、ユーザーのプロフィールでこのロールが表示されるで。"
options: "オプション"
policies: "ポリシー"
baseRole: "ベースロール"
useBaseValue: "ベースロールの値を使用"
chooseRoleToAssign: "アサインするロールを選択"
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになるで。オフにすると管理者のみが行えるで。"
priority: "優先度"
_priority:
low: "低い"
middle: "中"
high: "高い"
_options:
gtlAvailable: "グローバルタイムラインの閲覧"
ltlAvailable: "ローカルタイムラインの閲覧"
canPublicNote: "パブリック投稿の許可"
canInvite: "インスタンス招待コードの発行"
canManageCustomEmojis: "カスタム絵文字の管理"
driveCapacity: "ドライブ容量"
pinMax: "ノートのピン留めの最大数"
antennaMax: "アンテナの作成可能数"
wordMuteMax: "ワードミュートの最大文字数"
webhookMax: "Webhockの作成可能数"
clipMax: "クリップの作成可能数"
noteEachClipsMax: "クリップ内のノートの最大数"
userListMax: "ユーザーリストの作成可能数"
userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
rateLimitFactor: "レートリミット"
descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩くなって、大きいほど制限されるで。"
canHideAds: "広告を表示させへん"
_condition:
isLocal: "ローカルユーザー"
isRemote: "リモートユーザー"
createdLessThan: "アカウント作成から~以内"
createdMoreThan: "アカウント作成から~経過"
followersLessThanOrEq: "フォロワー数が~以下"
followersMoreThanOrEq: "フォロワー数が~以上"
followingLessThanOrEq: "フォロー数が~以下"
followingMoreThanOrEq: "フォロー数が~以上"
and: "~かつ~"
or: "~または~"
not: "~ではない"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。" description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。"
sensitivity: "検出感度やで" sensitivity: "検出感度やで"
@@ -949,6 +1026,7 @@ _accountDelete:
_ad: _ad:
back: "戻る" back: "戻る"
reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで" reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで"
hide: "表示せん"
_forgotPassword: _forgotPassword:
enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。" enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。"
ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。" ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。"
@@ -1295,6 +1373,8 @@ _weekday:
friday: "金曜日" friday: "金曜日"
saturday: "土曜日" saturday: "土曜日"
_widgets: _widgets:
profile: "プロフィール"
instanceInfo: "インスタンス情報"
memo: "付箋" memo: "付箋"
notifications: "通知" notifications: "通知"
timeline: "タイムライン" timeline: "タイムライン"
@@ -1316,10 +1396,12 @@ _widgets:
jobQueue: "ジョブキュー" jobQueue: "ジョブキュー"
serverMetric: "サーバーメトリクス" serverMetric: "サーバーメトリクス"
aiscript: "AiScriptコンソール" aiscript: "AiScriptコンソール"
aiscriptApp: "AiScript App"
aichan: "藍" aichan: "藍"
userList: "ユーザーリスト" userList: "ユーザーリスト"
_userList: _userList:
chooseList: "リストを選ぶ" chooseList: "リストを選ぶ"
clicker: "クリッカー"
_cw: _cw:
hide: "隠す" hide: "隠す"
show: "続き見して!" show: "続き見して!"
@@ -1383,6 +1465,7 @@ _profile:
changeBanner: "バナー画像を変更するで" changeBanner: "バナー画像を変更するで"
_exportOrImport: _exportOrImport:
allNotes: "全てのノート" allNotes: "全てのノート"
favoritedNotes: "お気に入りにしたノート"
followingList: "フォロー" followingList: "フォロー"
muteList: "ミュート" muteList: "ミュート"
blockingList: "ブロック" blockingList: "ブロック"
@@ -1421,7 +1504,16 @@ _timelines:
social: "ソーシャル" social: "ソーシャル"
global: "グローバル" global: "グローバル"
_play: _play:
new: "Playの作成"
edit: "Playの編集"
created: "Playを作ったで"
updated: "Playを更新したで"
deleted: "Playを消したで"
pageSetting: "Play設定"
editThisPage: "このPlayを編集"
viewSource: "ソースを表示" viewSource: "ソースを表示"
my: "自分のPlay"
liked: "いいねしたPlay"
featured: "人気" featured: "人気"
title: "タイトル" title: "タイトル"
script: "スクリプト" script: "スクリプト"
@@ -1485,7 +1577,6 @@ _notification:
youGotReply: "{name}からのリプライ" youGotReply: "{name}からのリプライ"
youGotQuote: "{name}による引用" youGotQuote: "{name}による引用"
youRenoted: "{name}がRenoteしたみたいやで" youRenoted: "{name}がRenoteしたみたいやで"
youGotPoll: "{name}が投票したみたいやで"
youGotMessagingMessageFromUser: "{name}からのチャットがあるで" youGotMessagingMessageFromUser: "{name}からのチャットがあるで"
youGotMessagingMessageFromGroup: "{name}のチャットがあるで" youGotMessagingMessageFromGroup: "{name}のチャットがあるで"
youWereFollowed: "フォローされたで" youWereFollowed: "フォローされたで"
@@ -1503,7 +1594,6 @@ _notification:
renote: "Renote" renote: "Renote"
quote: "引用" quote: "引用"
reaction: "リアクション" reaction: "リアクション"
pollVote: "アンケートに投票されたで"
pollEnded: "アンケートが終了したで" pollEnded: "アンケートが終了したで"
receiveFollowRequest: "フォロー許可してほしいみたいやで" receiveFollowRequest: "フォロー許可してほしいみたいやで"
followRequestAccepted: "フォローが受理されたで" followRequestAccepted: "フォローが受理されたで"

View File

@@ -73,6 +73,7 @@ _sfx:
_permissions: _permissions:
"write:account": "Ẓreg talɣut n umiḍan-ik·im" "write:account": "Ẓreg talɣut n umiḍan-ik·im"
_widgets: _widgets:
profile: "Amaɣnu"
notifications: "Ilɣuyen" notifications: "Ilɣuyen"
_userList: _userList:
chooseList: "Fren tabdart" chooseList: "Fren tabdart"

View File

@@ -69,6 +69,7 @@ _mfm:
_sfx: _sfx:
notification: "ಅಧಿಸೂಚನೆಗಳು" notification: "ಅಧಿಸೂಚನೆಗಳು"
_widgets: _widgets:
profile: "ಪ್ರೊಫೈಲು"
notifications: "ಅಧಿಸೂಚನೆಗಳು" notifications: "ಅಧಿಸೂಚನೆಗಳು"
timeline: "ಸಮಯಸಾಲು" timeline: "ಸಮಯಸಾಲು"
_cw: _cw:

View File

@@ -15,7 +15,7 @@ gotIt: "알겠어요"
cancel: "취소" cancel: "취소"
noThankYou: "나중에" noThankYou: "나중에"
enterUsername: "유저명 입력" enterUsername: "유저명 입력"
renotedBy: "{user}님 리노트" renotedBy: "{user}님 리노트"
noNotes: "노트가 없습니다" noNotes: "노트가 없습니다"
noNotifications: "표시할 알림이 없습니다" noNotifications: "표시할 알림이 없습니다"
instance: "인스턴스" instance: "인스턴스"
@@ -907,7 +907,7 @@ subscribePushNotification: "푸시 알림 켜기"
unsubscribePushNotification: "푸시 알림 끄기" unsubscribePushNotification: "푸시 알림 끄기"
pushNotificationAlreadySubscribed: "푸시 알림이 이미 켜져 있습니다" pushNotificationAlreadySubscribed: "푸시 알림이 이미 켜져 있습니다"
pushNotificationNotSupported: "브라우저나 인스턴스에서 푸시 알림이 지원되지 않습니다" pushNotificationNotSupported: "브라우저나 인스턴스에서 푸시 알림이 지원되지 않습니다"
sendPushNotificationReadMessage: "푸시 알림이 메시지를 읽으면 푸시 알림을 삭제합니다" sendPushNotificationReadMessage: "푸시 알림이 메시지를 읽은 뒤 푸시 알림을 삭제"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」이라는 알림이 잠깐 표시됩니다. 기기의 전력 소비량이 증가할 수 있습니다." sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」이라는 알림이 잠깐 표시됩니다. 기기의 전력 소비량이 증가할 수 있습니다."
windowMaximize: "최대화" windowMaximize: "최대화"
windowRestore: "복구" windowRestore: "복구"
@@ -924,6 +924,73 @@ neverShow: "다시 보지 않기"
remindMeLater: "나중에 알림" remindMeLater: "나중에 알림"
didYouLikeMisskey: "Misskey가 마음에 드시나요?" didYouLikeMisskey: "Misskey가 마음에 드시나요?"
pleaseDonate: "{host}은(는) 무료 소프트웨어 Misskey를 사용합니다. 후원을 통해 저희의 개발이 이어질 수 있게 도와주세요!" pleaseDonate: "{host}은(는) 무료 소프트웨어 Misskey를 사용합니다. 후원을 통해 저희의 개발이 이어질 수 있게 도와주세요!"
roles: "역할"
role: "역할"
normalUser: "일반 사용자"
undefined: "정의되지 않음"
assign: "할당"
unassign: "할당 취소"
color: "색"
manageCustomEmojis: "커스텀 이모지 관리"
youCannotCreateAnymore: "더 이상 생성할 수 없습니다."
cannotPerformTemporary: "일시적으로 사용할 수 없음"
cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시적으로 사용이 불가합니다. 잠시 후 다시 시도해 주세요."
_role:
new: "새 역할 생성"
edit: "역할 수정"
name: "역할 이름"
description: "역할 설명"
permission: "역할의 권한"
descriptionOfPermission: "<b>모더레이터</b>는 기본적인 중재와 관련된 작업을 수행할 수 있습니다.\n<b>관리자</b>는 인스턴스의 모든 설정을 변경할 수 있습니다."
assignTarget: "할당 대상"
descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다."
manual: "수동"
conditional: "조건부"
condition: "조건"
isConditionalRole: "조건부 역할입니다."
isPublic: "공개 역할"
descriptionOfIsPublic: "역할에 할당된 사용자를 누구나 볼 수 있습니다. 또한 사용자 프로필에 이 역할이 표시됩니다."
options: "옵션"
policies: "정책"
baseRole: "기본 역할"
useBaseValue: "기본값 사용"
chooseRoleToAssign: "할당할 역할 선택"
canEditMembersByModerator: "모더레이터의 역할 수정 허용"
descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 추가하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 가능합니다."
priority: "우선순위"
_priority:
low: "낮음"
middle: "보통"
high: "높음"
_options:
gtlAvailable: "글로벌 타임라인 보이기"
ltlAvailable: "로컬 타임라인 보이기"
canPublicNote: "공개 노트 허용"
canInvite: "인스턴스 초대 코드 발행"
canManageCustomEmojis: "커스텀 이모지 관리"
driveCapacity: "드라이브 용량"
pinMax: "고정할 수 있는 노트 수"
antennaMax: "최대 안테나 생성 허용 수"
wordMuteMax: "뮤트할 수 있는 단어의 수"
webhookMax: "생성할 수 있는 WebHook의 수"
clipMax: "생성할 수 있는 클립 수"
noteEachClipsMax: "각 클립에 추가할 수 있는 노트 수"
userListMax: "생성할 수 있는 리스트 수"
userEachUserListsMax: "리스트당 최대 사용자 수"
rateLimitFactor: "속도 제한"
descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다."
_condition:
isLocal: "로컬 사용자"
isRemote: "리모트 사용자"
createdLessThan: "다음 일수 이내에 가입한 유저"
createdMoreThan: "다음 일수 이상 활동한 유저"
followersLessThanOrEq: "팔로워 수가 다음 이하인 유저"
followersMoreThanOrEq: "팔로워 수가 다음 이상인 유저"
followingLessThanOrEq: "팔로잉 수가 다음 이하인 유저"
followingMoreThanOrEq: "팔로잉 수가 다음 이상인 유저"
and: "다음을 모두 만족"
or: "다음을 하나라도 만족"
not: "다음을 만족하지 않음"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다." description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다."
sensitivity: "탐지 민감도" sensitivity: "탐지 민감도"
@@ -956,6 +1023,7 @@ _accountDelete:
_ad: _ad:
back: "뒤로" back: "뒤로"
reduceFrequencyOfThisAd: "이 광고의 표시 빈도 낮추기" reduceFrequencyOfThisAd: "이 광고의 표시 빈도 낮추기"
hide: "보이지 않음"
_forgotPassword: _forgotPassword:
enterEmail: "여기에 계정에 등록한 메일 주소를 입력해 주세요. 입력한 메일 주소로 비밀번호 재설정 링크를 발송합니다." enterEmail: "여기에 계정에 등록한 메일 주소를 입력해 주세요. 입력한 메일 주소로 비밀번호 재설정 링크를 발송합니다."
ifNoEmail: "메일 주소를 등록하지 않은 경우, 관리자에 문의해 주십시오." ifNoEmail: "메일 주소를 등록하지 않은 경우, 관리자에 문의해 주십시오."
@@ -1302,6 +1370,8 @@ _weekday:
friday: "금요일" friday: "금요일"
saturday: "토요일" saturday: "토요일"
_widgets: _widgets:
profile: "프로필"
instanceInfo: "인스턴스 정보"
memo: "스티커 메모" memo: "스티커 메모"
notifications: "알림" notifications: "알림"
timeline: "타임라인" timeline: "타임라인"
@@ -1325,9 +1395,10 @@ _widgets:
aiscript: "AiScript 콘솔" aiscript: "AiScript 콘솔"
aiscriptApp: "AiScript 앱" aiscriptApp: "AiScript 앱"
aichan: "아이" aichan: "아이"
userList: "사용자 목록" userList: "유저 리스트"
_userList: _userList:
chooseList: "리스트 선택" chooseList: "리스트 선택"
clicker: "클리커"
_cw: _cw:
hide: "숨기기" hide: "숨기기"
show: "더 보기" show: "더 보기"
@@ -1503,7 +1574,6 @@ _notification:
youGotReply: "{name}님이 답글함" youGotReply: "{name}님이 답글함"
youGotQuote: "{name}님이 인용함" youGotQuote: "{name}님이 인용함"
youRenoted: "{name}님이 Renote" youRenoted: "{name}님이 Renote"
youGotPoll: "{name}님이 투표함"
youGotMessagingMessageFromUser: "{name} 님이 보낸 채팅이 있어요" youGotMessagingMessageFromUser: "{name} 님이 보낸 채팅이 있어요"
youGotMessagingMessageFromGroup: "{name}에서 보낸 채팅이 있어요" youGotMessagingMessageFromGroup: "{name}에서 보낸 채팅이 있어요"
youWereFollowed: "새로운 팔로워가 있습니다" youWereFollowed: "새로운 팔로워가 있습니다"
@@ -1521,7 +1591,6 @@ _notification:
renote: "리노트" renote: "리노트"
quote: "인용" quote: "인용"
reaction: "리액션" reaction: "리액션"
pollVote: "투표 참여"
pollEnded: "투표가 종료됨" pollEnded: "투표가 종료됨"
receiveFollowRequest: "팔로우 요청을 받았을 때" receiveFollowRequest: "팔로우 요청을 받았을 때"
followRequestAccepted: "팔로우 요청이 승인되었을 때" followRequestAccepted: "팔로우 요청이 승인되었을 때"

View File

@@ -440,6 +440,8 @@ _sfx:
notification: "Meldingen" notification: "Meldingen"
chat: "Chat" chat: "Chat"
_widgets: _widgets:
profile: "Profiel"
instanceInfo: "Serverinformatie"
notifications: "Meldingen" notifications: "Meldingen"
timeline: "Tijdlijn" timeline: "Tijdlijn"
activity: "Activiteit" activity: "Activiteit"

View File

@@ -868,6 +868,13 @@ sendPushNotificationReadMessageCaption: "Chwilowo pojawi się powiadomienie \"{e
loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot" loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot"
like: "Polub" like: "Polub"
show: "Wyświetlanie" show: "Wyświetlanie"
color: "Kolor"
_role:
priority: "Priorytet"
_priority:
low: "Niski"
middle: "Średnie"
high: "Wysoki"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera." description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera."
setSensitiveFlagAutomatically: "Oznacz jako NSFW" setSensitiveFlagAutomatically: "Oznacz jako NSFW"
@@ -895,6 +902,7 @@ _accountDelete:
_ad: _ad:
back: "Wróć" back: "Wróć"
reduceFrequencyOfThisAd: "Pokazuj tę reklamę rzadziej" reduceFrequencyOfThisAd: "Pokazuj tę reklamę rzadziej"
hide: "Nigdy nie pokazuj"
_forgotPassword: _forgotPassword:
enterEmail: "Wpisz adres e-mail użyty do rejestracji. Zostanie do niego wysłany link, za pomocą którego możesz zresetować hasło." enterEmail: "Wpisz adres e-mail użyty do rejestracji. Zostanie do niego wysłany link, za pomocą którego możesz zresetować hasło."
ifNoEmail: "Jeżeli nie podano adresu e-mail podczas rejestracji, skontaktuj się z administratorem zamiast tego." ifNoEmail: "Jeżeli nie podano adresu e-mail podczas rejestracji, skontaktuj się z administratorem zamiast tego."
@@ -1213,6 +1221,8 @@ _weekday:
friday: "Piątek" friday: "Piątek"
saturday: "Sobota" saturday: "Sobota"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Informacje o instancji"
memo: "Przypięte notatki" memo: "Przypięte notatki"
notifications: "Powiadomienia" notifications: "Powiadomienia"
timeline: "Oś czasu" timeline: "Oś czasu"
@@ -1380,7 +1390,6 @@ _notification:
youGotReply: "{name} odpowiedział(a) Tobie" youGotReply: "{name} odpowiedział(a) Tobie"
youGotQuote: "{name} zacytował(a) Ciebie" youGotQuote: "{name} zacytował(a) Ciebie"
youRenoted: "{name} udostępnił(a) Twój wpis" youRenoted: "{name} udostępnił(a) Twój wpis"
youGotPoll: "{name} zagłosował(a) w Twojej ankiecie"
youGotMessagingMessageFromUser: "{name} wysłał(a) Ci wiadomość" youGotMessagingMessageFromUser: "{name} wysłał(a) Ci wiadomość"
youGotMessagingMessageFromGroup: "Została wysłana wiadomość do grupy {name}" youGotMessagingMessageFromGroup: "Została wysłana wiadomość do grupy {name}"
youWereFollowed: "Zaobserwował(a) Cię" youWereFollowed: "Zaobserwował(a) Cię"
@@ -1398,7 +1407,6 @@ _notification:
renote: "Udostępnij" renote: "Udostępnij"
quote: "Cytuj" quote: "Cytuj"
reaction: "Reakcja" reaction: "Reakcja"
pollVote: "Głosy w ankietach"
receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji" receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji"
followRequestAccepted: "Przyjęto prośbę o możliwość obserwacji" followRequestAccepted: "Przyjęto prośbę o możliwość obserwacji"
groupInvited: "Zaproszono do grup" groupInvited: "Zaproszono do grup"

View File

@@ -488,6 +488,8 @@ _sfx:
notification: "Notificações" notification: "Notificações"
chat: "Chat" chat: "Chat"
_widgets: _widgets:
profile: "Perfil"
instanceInfo: "Informações da instância"
notifications: "Notificações" notifications: "Notificações"
timeline: "Timeline" timeline: "Timeline"
activity: "atividade" activity: "atividade"
@@ -524,7 +526,6 @@ _notification:
youGotMention: "{name} te mencionou" youGotMention: "{name} te mencionou"
youGotReply: "{name} te respondeu" youGotReply: "{name} te respondeu"
youGotQuote: "{name} te citou" youGotQuote: "{name} te citou"
youGotPoll: "{name} votou em sua enquete"
youGotMessagingMessageFromUser: "{name} te mandou uma mensagem de bate-papo" youGotMessagingMessageFromUser: "{name} te mandou uma mensagem de bate-papo"
youGotMessagingMessageFromGroup: "Uma mensagem foi mandada para o grupo {name}" youGotMessagingMessageFromGroup: "Uma mensagem foi mandada para o grupo {name}"
youWereFollowed: "Você tem um novo seguidor" youWereFollowed: "Você tem um novo seguidor"
@@ -541,7 +542,6 @@ _notification:
renote: "Repostar" renote: "Repostar"
quote: "Citar" quote: "Citar"
reaction: "Reações" reaction: "Reações"
pollVote: "Votações em enquetes"
pollEnded: "Enquetes terminando" pollEnded: "Enquetes terminando"
receiveFollowRequest: "Recebeu pedidos de seguimento" receiveFollowRequest: "Recebeu pedidos de seguimento"
followRequestAccepted: "Aceitou pedidos de seguimento" followRequestAccepted: "Aceitou pedidos de seguimento"

View File

@@ -648,6 +648,9 @@ sent: "Trimite"
searchByGoogle: "Caută" searchByGoogle: "Caută"
file: "Fișiere" file: "Fișiere"
show: "Arată" show: "Arată"
_role:
_priority:
middle: "Mediu"
_email: _email:
_follow: _follow:
title: "te-a urmărit" title: "te-a urmărit"
@@ -667,6 +670,8 @@ _sfx:
notification: "Notificări" notification: "Notificări"
chat: "Chat" chat: "Chat"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Informații despre instanță"
notifications: "Notificări" notifications: "Notificări"
timeline: "Cronologie" timeline: "Cronologie"
activity: "Activitate" activity: "Activitate"

View File

@@ -866,6 +866,13 @@ windowMaximize: "Развернуть"
windowRestore: "Восстановить" windowRestore: "Восстановить"
like: "Нравится!" like: "Нравится!"
show: "Отображение" show: "Отображение"
color: "Цвет"
_role:
priority: "Приоритет"
_priority:
low: "Низкий"
middle: "Средне"
high: "Высокий"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно."
setSensitiveFlagAutomatically: "Установить флаг NSFW" setSensitiveFlagAutomatically: "Установить флаг NSFW"
@@ -893,6 +900,7 @@ _accountDelete:
_ad: _ad:
back: "Выход" back: "Выход"
reduceFrequencyOfThisAd: "Реже показывать эту рекламу" reduceFrequencyOfThisAd: "Реже показывать эту рекламу"
hide: "Не показывать"
_forgotPassword: _forgotPassword:
enterEmail: "Введите адрес электронной почты, который ввели при регистрации. На неё будет выслана ссылка для смены пароля." enterEmail: "Введите адрес электронной почты, который ввели при регистрации. На неё будет выслана ссылка для смены пароля."
ifNoEmail: "Если вы не ввели свой адрес электронной почты, свяжитесь с администратором ресурса, чтобы сменить пароль." ifNoEmail: "Если вы не ввели свой адрес электронной почты, свяжитесь с администратором ресурса, чтобы сменить пароль."
@@ -1213,6 +1221,8 @@ _weekday:
friday: "Пятница" friday: "Пятница"
saturday: "Суббота" saturday: "Суббота"
_widgets: _widgets:
profile: "Профиль"
instanceInfo: "Информация об инстансе"
memo: "Напоминания" memo: "Напоминания"
notifications: "Уведомления" notifications: "Уведомления"
timeline: "Лента" timeline: "Лента"
@@ -1399,7 +1409,6 @@ _notification:
youGotReply: "{name} отвечает вам." youGotReply: "{name} отвечает вам."
youGotQuote: "{name} цитирует вас." youGotQuote: "{name} цитирует вас."
youRenoted: "{name} передаёт вашу заметку." youRenoted: "{name} передаёт вашу заметку."
youGotPoll: "{name} участвует в вашем опросе."
youGotMessagingMessageFromUser: "{name} пишет вам." youGotMessagingMessageFromUser: "{name} пишет вам."
youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»." youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»."
youWereFollowed: "У вас новый подписчик." youWereFollowed: "У вас новый подписчик."
@@ -1414,7 +1423,6 @@ _notification:
renote: "Репосты" renote: "Репосты"
quote: "Цитаты" quote: "Цитаты"
reaction: "Реакции" reaction: "Реакции"
pollVote: "Голосования"
receiveFollowRequest: "Получен запрос на подписку" receiveFollowRequest: "Получен запрос на подписку"
followRequestAccepted: "Запрос на подписку одобрен" followRequestAccepted: "Запрос на подписку одобрен"
groupInvited: "Приглашение в группы" groupInvited: "Приглашение в группы"

View File

@@ -917,6 +917,13 @@ neverShow: "Nabudúce nezobrazovať"
remindMeLater: "Pripomenúť neskôr" remindMeLater: "Pripomenúť neskôr"
didYouLikeMisskey: "Páči sa vám Misskey?" didYouLikeMisskey: "Páči sa vám Misskey?"
pleaseDonate: "Misskey je bezplatný softvér, ktorý používa {host}. Prosím, prispejte, aby sme ho mohli ďalej rozvíjať!" pleaseDonate: "Misskey je bezplatný softvér, ktorý používa {host}. Prosím, prispejte, aby sme ho mohli ďalej rozvíjať!"
color: "Farba"
_role:
priority: "Priorita"
_priority:
low: "Málo"
middle: "Stredné"
high: "Vysoká"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera." description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera."
sensitivity: "Citlivosť detekcie" sensitivity: "Citlivosť detekcie"
@@ -949,6 +956,7 @@ _accountDelete:
_ad: _ad:
back: "Späť" back: "Späť"
reduceFrequencyOfThisAd: "Túto reklamu zobrazovať menej" reduceFrequencyOfThisAd: "Túto reklamu zobrazovať menej"
hide: "Nikdy nezobrazovať"
_forgotPassword: _forgotPassword:
enterEmail: "Zadajte emailovú adresu, ktorú ste použili pri registrácii. Pošleme vám na ňu odkaz, cez ktorý si môžete obnoviť heslo." enterEmail: "Zadajte emailovú adresu, ktorú ste použili pri registrácii. Pošleme vám na ňu odkaz, cez ktorý si môžete obnoviť heslo."
ifNoEmail: "Ak ste pri registrácii nepoužili email, prosím kontaktujte administrátora." ifNoEmail: "Ak ste pri registrácii nepoužili email, prosím kontaktujte administrátora."
@@ -1295,6 +1303,8 @@ _weekday:
friday: "Piatok" friday: "Piatok"
saturday: "Sobota" saturday: "Sobota"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Informácie o serveri"
memo: "Prilepené poznámky" memo: "Prilepené poznámky"
notifications: "Oznámenia" notifications: "Oznámenia"
timeline: "Časová os" timeline: "Časová os"
@@ -1484,7 +1494,6 @@ _notification:
youGotReply: "{name} vám odpovedal/a" youGotReply: "{name} vám odpovedal/a"
youGotQuote: "{name} vás citoval/a" youGotQuote: "{name} vás citoval/a"
youRenoted: "{name} preposlal/a vašu poznámku" youRenoted: "{name} preposlal/a vašu poznámku"
youGotPoll: "{name} hlasoval/a"
youGotMessagingMessageFromUser: "{name} vám poslal/a správu" youGotMessagingMessageFromUser: "{name} vám poslal/a správu"
youGotMessagingMessageFromGroup: "Prišla správa do skupiny {name}" youGotMessagingMessageFromGroup: "Prišla správa do skupiny {name}"
youWereFollowed: "Máte nového sledujúceho" youWereFollowed: "Máte nového sledujúceho"
@@ -1502,7 +1511,6 @@ _notification:
renote: "Preposlať" renote: "Preposlať"
quote: "Citovať" quote: "Citovať"
reaction: "Reakcie" reaction: "Reakcie"
pollVote: "Hlasy v hlasovaniach"
pollEnded: "Hlasovanie skončilo" pollEnded: "Hlasovanie skončilo"
receiveFollowRequest: "Doručené žiadosti o sledovanie" receiveFollowRequest: "Doručené žiadosti o sledovanie"
followRequestAccepted: "Schválené žiadosti o sledovanie" followRequestAccepted: "Schválené žiadosti o sledovanie"

View File

@@ -1,7 +1,7 @@
--- ---
_lang_: "Svenska" _lang_: "Svenska"
headlineMisskey: "Ett nätverk kopplat av noter" headlineMisskey: "Ett nätverk kopplat av noter"
introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.👍\nLåt oss utforska en nya värld!🚀" introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter. 👍\nLåt oss utforska en ny värld! 🚀"
poweredByMisskeyDescription: "{name} är en tjänst driven av den öppna källkodsplatformen <b>Misskey</b> (benämns \"Misskey instans\")." poweredByMisskeyDescription: "{name} är en tjänst driven av den öppna källkodsplatformen <b>Misskey</b> (benämns \"Misskey instans\")."
monthAndDay: "{day}/{month}" monthAndDay: "{day}/{month}"
search: "Sök" search: "Sök"
@@ -17,7 +17,7 @@ noThankYou: "Nej tack"
enterUsername: "Ange användarnamn" enterUsername: "Ange användarnamn"
renotedBy: "Omnoterad av {user}" renotedBy: "Omnoterad av {user}"
noNotes: "Inga noteringar" noNotes: "Inga noteringar"
noNotifications: "Inga aviseringar" noNotifications: "Inga notifikationer"
instance: "Instanser" instance: "Instanser"
settings: "Inställningar" settings: "Inställningar"
basicSettings: "Basinställningar" basicSettings: "Basinställningar"
@@ -30,13 +30,13 @@ login: "Logga in"
loggingIn: "Loggar in" loggingIn: "Loggar in"
logout: "Logga ut" logout: "Logga ut"
signup: "Registrera" signup: "Registrera"
uploading: "Uppladdning sker..." uploading: "Laddar upp..."
save: "Spara" save: "Spara"
users: "Användare" users: "Användare"
addUser: "Lägg till användare" addUser: "Lägg till användare"
favorite: "Lägg till i favoriter" favorite: "Lägg till i favoriter"
favorites: "Favoriter" favorites: "Favoriter"
unfavorite: "Avfavorisera" unfavorite: "Ta bort från favoriter"
favorited: "Tillagd i favoriter." favorited: "Tillagd i favoriter."
alreadyFavorited: "Redan tillagd i favoriter." alreadyFavorited: "Redan tillagd i favoriter."
cantFavorite: "Gick inte att lägga till i favoriter." cantFavorite: "Gick inte att lägga till i favoriter."
@@ -146,7 +146,7 @@ flagAsBotDescription: "Aktivera det här alternativet om kontot är kontrollerat
flagAsCat: "Markera konto som katt" flagAsCat: "Markera konto som katt"
flagAsCatDescription: "Aktivera denna inställning för att markera kontot som en katt." flagAsCatDescription: "Aktivera denna inställning för att markera kontot som en katt."
flagShowTimelineReplies: "Visa svar i tidslinje" flagShowTimelineReplies: "Visa svar i tidslinje"
flagShowTimelineRepliesDescription: "Visar användarsvar till andra användares noter i tidslinjen om påslagen." flagShowTimelineRepliesDescription: "Visar användarsvar till andra användares noter i tidslinjen om aktiverad."
autoAcceptFollowed: "Godkänn följarförfrågningar från användare du följer automatiskt" autoAcceptFollowed: "Godkänn följarförfrågningar från användare du följer automatiskt"
addAccount: "Lägg till konto" addAccount: "Lägg till konto"
loginFailed: "Inloggningen misslyckades" loginFailed: "Inloggningen misslyckades"
@@ -253,16 +253,120 @@ explore: "Utforska"
messageRead: "Läs" messageRead: "Läs"
noMoreHistory: "Det finns ingen mer historik" noMoreHistory: "Det finns ingen mer historik"
startMessaging: "Starta en chatt" startMessaging: "Starta en chatt"
nUsersRead: "läst av {n}"
agreeTo: "Jag accepterar {0}"
tos: "Användarvillkor"
home: "Hem"
remoteUserCaution: "Då denna användaren kommer från en fjärrinstans, kan informationen visad vara ofullständig."
activity: "Aktivitet"
images: "Bilder"
birthday: "Födelsedag"
yearsOld: "{age} år gammal"
registeredDate: "Gick med"
location: "Plats"
theme: "Teman"
themeForLightMode: "Tema att använda i Ljust Läge"
themeForDarkMode: "Tema att använda i Mörkt Läge"
light: "Ljust"
dark: "Mörk"
lightThemes: "Ljusa teman"
darkThemes: "Mörka teman"
syncDeviceDarkMode: "Synka Mörkt Läge med din enhets inställningar"
drive: "Drive"
fileName: "Filnamn"
selectFile: "Välj en fil"
selectFiles: "Välj filer"
selectFolder: "Välj en mapp"
selectFolders: "Välj mappar"
renameFile: "Byt namn på filen"
folderName: "Mappnamn"
createFolder: "Skapa en mapp"
renameFolder: "Byt namn på mappen"
deleteFolder: "Ta bort mappen"
addFile: "Lägg till fil"
emptyDrive: "Din Drive är tom"
emptyFolder: "Denna mappen är tom"
unableToDelete: "Kunde inte ta bort"
inputNewFileName: "Ange nytt filnamn"
inputNewDescription: "Ange ny bildtext"
inputNewFolderName: "Ange nytt mappnamn"
circularReferenceFolder: "Destinationsmappen är en undermapp av mappen du vill flytta."
hasChildFilesOrFolders: "Då denna mappen inte är tom, kan den inte tas bort."
copyUrl: "Kopiera URL"
rename: "Byt namn"
avatar: "Profilbild"
banner: "Banner"
nsfw: "Känsligt innehåll" nsfw: "Känsligt innehåll"
reload: "Ladda om"
doNothing: "Ignorera"
reloadConfirm: "Vill du ladda om tidslinjen?"
accept: "Tillåt"
reject: "Neka"
normal: "Normal"
instanceName: "Instansnamn"
instanceDescription: "Instansbeskrivning"
maintainerEmail: "Administratörens epost"
tosUrl: "URL till användarvillkår"
thisYear: "Detta året"
thisMonth: "Denna månaden"
today: "Idag"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Sidor"
integration: "Integrationer"
connectService: "Anslut"
disconnectService: "Koppla från"
enableLocalTimeline: "Aktivera lokal tidslinje"
enableGlobalTimeline: "Aktivera global tidslinje"
enableRegistration: "Aktivera registrering av nya användare"
inMb: "I megabyte"
iconUrl: "URL till profilbilden"
bannerUrl: "URL till banner-bilden"
pinnedNotes: "Fästad not" pinnedNotes: "Fästad not"
enableHcaptcha: "Aktivera hCaptcha"
enableRecaptcha: "Aktivera reCAPTCHA"
enableTurnstile: "Aktivera Turnstile"
antennas: "Antenner"
manageAntennas: "Hantera Antenner"
antennaSource: "Antennkälla"
antennaKeywords: "Nyckelord att lyssna efter"
antennaExcludeKeywords: "Nyckelord att exkludera"
antennaKeywordsDescription: "Separera med mellanslag för en AND kondition, eller med nya linjer för en OR kondition"
notifyAntenna: "Notifiera om nya noter"
withFileAntenna: "Endast noter med filer"
enableServiceworker: "Aktivera pushnotiser i denna webbläsaren"
antennaUsersDescription: "Ange ett användarnamn per linje"
recentlyUpdatedUsers: "Nyligen aktiva användare"
recentlyRegisteredUsers: "Nyligen registrerade användare"
userList: "Listor" userList: "Listor"
aboutMisskey: "Om Misskey"
administrator: "Administratör"
newPasswordIs: "Det nya lösenordet är \"{password}\""
share: "Dela"
enable: "Aktivera"
serviceworkerInfo: "Måste vara aktiverad för pushnotiser."
enableInfiniteScroll: "Ladda mer automatiskt"
enablePlayer: "Öppna videospelare"
enableAll: "Aktivera alla"
enableEmail: "Aktivera epost-utskick"
smtpHost: "Värd" smtpHost: "Värd"
smtpUser: "Användarnamn" smtpUser: "Användarnamn"
smtpPass: "Lösenord" smtpPass: "Lösenord"
clearCache: "Rensa cache" clearCache: "Rensa cache"
enabled: "Aktiverad"
user: "Användare" user: "Användare"
global: "Global"
squareAvatars: "Visa fyrkantiga profilbilder"
searchByGoogle: "Sök" searchByGoogle: "Sök"
file: "Filer" file: "Filer"
enableAutoSensitive: "Automatisk NSFW markering"
enableAutoSensitiveDescription: "Tillåter automatiskt detektering och marketing av NSFW media genom Maskininlärning när möjligt. Även om denna inställningen är avaktiverad, kan det vara aktiverat på hela instansen."
pushNotification: "Pushnotiser"
subscribePushNotification: "Aktivera pushnotiser"
unsubscribePushNotification: "Avaktivera pushnotiser"
pushNotificationAlreadySubscribed: "Pushnotiser är redan aktiverade"
pushNotificationNotSupported: "Din webbläsare eller instans har inte stöd för pushnotiser"
_email: _email:
_follow: _follow:
title: "följde dig" title: "följde dig"
@@ -271,6 +375,9 @@ _mfm:
quote: "Citat" quote: "Citat"
emoji: "Anpassa emoji" emoji: "Anpassa emoji"
search: "Sök" search: "Sök"
_channel:
setBanner: "Välj banner"
removeBanner: "Ta bort banner"
_theme: _theme:
keys: keys:
mention: "Nämn" mention: "Nämn"
@@ -279,9 +386,19 @@ _sfx:
note: "Noter" note: "Noter"
notification: "Notifikationer" notification: "Notifikationer"
chat: "Chatt" chat: "Chatt"
antenna: "Antenner"
_antennaSources:
all: "Alla noter"
homeTimeline: "Noter från följda användare"
users: "Noter från specifika användare"
userList: "Noter från en specificerad lista av användare"
userGroup: "Noter från användare i en specificerad grupp"
_widgets: _widgets:
profile: "Profil"
instanceInfo: "Instansinformation"
notifications: "Notifikationer" notifications: "Notifikationer"
timeline: "Tidslinje" timeline: "Tidslinje"
activity: "Aktivitet"
federation: "Federation" federation: "Federation"
jobQueue: "Jobbkö" jobQueue: "Jobbkö"
_userList: _userList:
@@ -289,18 +406,29 @@ _widgets:
_cw: _cw:
show: "Ladda mer" show: "Ladda mer"
_visibility: _visibility:
home: "Hem"
followers: "Följare" followers: "Följare"
_profile: _profile:
username: "Användarnamn" username: "Användarnamn"
changeAvatar: "Ändra profilbild"
changeBanner: "Ändra banner"
_exportOrImport: _exportOrImport:
allNotes: "Alla noter"
followingList: "Följer" followingList: "Följer"
muteList: "Tysta" muteList: "Tysta"
blockingList: "Blockera" blockingList: "Blockera"
userLists: "Listor" userLists: "Listor"
_charts: _charts:
federation: "Federation" federation: "Federation"
_timelines:
home: "Hem"
global: "Global"
_pages:
blocks:
image: "Bilder"
_notification: _notification:
youWereFollowed: "följde dig" youWereFollowed: "följde dig"
unreadAntennaNote: "Antenn {name}"
_types: _types:
follow: "Följer" follow: "Följer"
mention: "Nämn" mention: "Nämn"
@@ -314,5 +442,6 @@ _deck:
_columns: _columns:
notifications: "Notifikationer" notifications: "Notifikationer"
tl: "Tidslinje" tl: "Tidslinje"
antenna: "Antenner"
list: "Listor" list: "Listor"
mentions: "Omnämningar" mentions: "Omnämningar"

View File

@@ -8,7 +8,7 @@ search: "ค้นหา"
notifications: "การเเจ้งเตือน" notifications: "การเเจ้งเตือน"
username: "ชื่อผู้ใช้" username: "ชื่อผู้ใช้"
password: "รหัสผ่าน" password: "รหัสผ่าน"
forgotPassword: "ลืมรหัสผ่าน?" forgotPassword: "ลืมรหัสผ่านใช่ไหม"
fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส..." fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส..."
ok: "โอเค" ok: "โอเค"
gotIt: "เข้าใจแล้ว !" gotIt: "เข้าใจแล้ว !"
@@ -920,6 +920,64 @@ like: "ชื่นชอบ"
unlike: "ไม่ชอบ" unlike: "ไม่ชอบ"
numberOfLikes: "จำนวนไลค์" numberOfLikes: "จำนวนไลค์"
show: "แสดงผล" show: "แสดงผล"
neverShow: "ไม่ต้องแสดงข้อความนี้อีก"
remindMeLater: "ไว้ครั้งหน้าแล้วกัน"
didYouLikeMisskey: "คุณเคยชอบ Misskey ไหม?"
pleaseDonate: "{host} ใช้ซอฟต์แวร์ฟรี Misskey เราขอขอบคุณการบริจาคของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้นะ!"
roles: "บทบาท"
role: "บทบาท"
normalUser: "ผู้ใช้มาตรฐาน"
undefined: "ไม่ได้กำหนด"
assign: "กำหนด"
unassign: "ยังไม่มอบหมาย"
color: "สี"
manageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง"
_role:
new: "บทบาทใหม่"
edit: "แก้ไขบทบาท"
name: "ชื่อบทบาท"
description: "คำอธิบายบทบาท"
permission: "สิทธิ์ตามบทบาท"
descriptionOfPermission: "<b>ผู้ดูแลกลั่นกรองเนื้อหา</b> สามารถดำเนินการดูแลขั้นพื้นฐานได้นะ\n<b>ผู้ดูแลระบบ</b> สามารถเปลี่ยนการตั้งค่าทั้งหมดของอินสแตนซ์ได้นะ"
assignTarget: "กำหนดเป้าหมาย"
descriptionOfAssignTarget: "<b>แมนนวล</b> เพื่อเปลี่ยนผู้ที่เป็นส่วนหนึ่งของบทบาทนี้และใครที่ไม่ใช่ด้วยตนเอง\n<b>เงื่อนไข</b> เพื่อให้ผู้ใช้ได้รับการกำหนดและนำออกจากบทบาทนี้โดยอัตโนมัติตามเงื่อนไขชุดหนึ่ง"
manual: "ปรับเอง"
conditional: "มีเงื่อนไข"
condition: "เงื่อนไข"
isConditionalRole: "นี่คือบทบาทที่มีเงื่อนไข"
isPublic: "บทบาทสาธารณะ"
descriptionOfIsPublic: "ทุกคนสามารถดูได้ว่าผู้ใช้งานนั้นได้รับมอบหมายบทบาทด้วยหรือไม่ \n\nบทบาทจะแสดงในโปรไฟล์ของผู้ใช้ด้วย"
options: "ตัวเลือกบทบาท"
baseRole: "บทบาทพื้นฐาน"
useBaseValue: "ใช้บทบาทพื้นฐานเริ่มต้น"
chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
canEditMembersByModerator: "อนุญาตให้ผู้ดูแลแก้ไขสมาชิก"
descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ ผู้ดูแลนอกเหนือจากผู้ดูแลระบบแล้ว จะสามารถกำหนดและยกเลิกการมอบหมายบทบาทนี้ให้กับผู้ใช้ได้ เมื่อปิด เฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถกำหนดผู้ใช้ได้นะ"
priority: "ลำดับความสำคัญ"
_priority:
low: "ต่ำ"
middle: "ปานกลาง"
high: "สูง"
_options:
gtlAvailable: "การดูไทม์ไลน์ทั่วโลก"
ltlAvailable: "การดูไทม์ไลน์ในท้องถิ่น"
canPublicNote: "สามารถส่งโน้ตสาธารณะ"
canInvite: "สร้างรหัสเชิญอินสแตนซ์"
canManageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง"
driveCapacity: "ความจุของไดรฟ์"
antennaMax: "จำนวนสูงสุดของเสาอากาศ"
_condition:
isLocal: "ผู้ใช้ภายใน"
isRemote: "ผู้ใช้ระยะไกล"
createdLessThan: "สร้างน้อยกว่า"
createdMoreThan: "สร้างมากกว่า"
followersLessThanOrEq: "จำนวนผู้ติดตามน้อยกว่าหรือเท่ากับ\n"
followersMoreThanOrEq: "จำนวนผู้ติดตามมากกว่าหรือเท่ากับ\n"
followingLessThanOrEq: "จำนวนบัญชีต่อไปนี้คือ น้อยกว่าหรือเท่ากับ"
followingMoreThanOrEq: "จำนวนบัญชีต่อไปนี้คือ มากกว่าหรือเท่ากับ"
and: "และ"
or: "หรือ"
not: "ไม่"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
sensitivity: "การตรวจจับความไว" sensitivity: "การตรวจจับความไว"
@@ -952,6 +1010,7 @@ _accountDelete:
_ad: _ad:
back: "ย้อนกลับ" back: "ย้อนกลับ"
reduceFrequencyOfThisAd: "แสดงโฆษณานี้ให้น้อยลง" reduceFrequencyOfThisAd: "แสดงโฆษณานี้ให้น้อยลง"
hide: "ไม่ต้องแสดง"
_forgotPassword: _forgotPassword:
enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ" enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ"
ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ" ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ"
@@ -1298,6 +1357,8 @@ _weekday:
friday: "วันศุกร์" friday: "วันศุกร์"
saturday: "วันเสาร์" saturday: "วันเสาร์"
_widgets: _widgets:
profile: "โปรไฟล์"
instanceInfo: "ข้อมูล อินสแตนซ์"
memo: "โน้ตแปะ" memo: "โน้ตแปะ"
notifications: "การเเจ้งเตือน" notifications: "การเเจ้งเตือน"
timeline: "ไทม์ไลน์" timeline: "ไทม์ไลน์"
@@ -1324,6 +1385,7 @@ _widgets:
userList: "รายชื่อผู้ใช้" userList: "รายชื่อผู้ใช้"
_userList: _userList:
chooseList: "เลือกรายการ" chooseList: "เลือกรายการ"
clicker: "คลิกเกอร์"
_cw: _cw:
hide: "ซ่อน" hide: "ซ่อน"
show: "โหลดเพิ่มเติม" show: "โหลดเพิ่มเติม"
@@ -1499,7 +1561,6 @@ _notification:
youGotReply: "{name} ตอบกลับถึงคุณ" youGotReply: "{name} ตอบกลับถึงคุณ"
youGotQuote: "{name} อ้างถึงคุณ" youGotQuote: "{name} อ้างถึงคุณ"
youRenoted: "รีโน้ตจาก {name}" youRenoted: "รีโน้ตจาก {name}"
youGotPoll: "{name} โหวตบนแบบสำรวจความคิดเห็นของคุณ"
youGotMessagingMessageFromUser: "{name} ได้ส่งข้อความแชทถึงคุณ" youGotMessagingMessageFromUser: "{name} ได้ส่งข้อความแชทถึงคุณ"
youGotMessagingMessageFromGroup: "ข้อความแชทถูกส่งไปยัง {name} กลุ่ม" youGotMessagingMessageFromGroup: "ข้อความแชทถูกส่งไปยัง {name} กลุ่ม"
youWereFollowed: "ได้ติดตามคุณ" youWereFollowed: "ได้ติดตามคุณ"
@@ -1517,7 +1578,6 @@ _notification:
renote: "รีโน้ต" renote: "รีโน้ต"
quote: "อ้างคำพูด" quote: "อ้างคำพูด"
reaction: "รีแอคชั่น" reaction: "รีแอคชั่น"
pollVote: "จำนวนโหวตที่ได้รับ"
pollEnded: "โพลนี้สิ้นสุดลงแล้ว" pollEnded: "โพลนี้สิ้นสุดลงแล้ว"
receiveFollowRequest: "ได้รับคำขอติดตาม\n" receiveFollowRequest: "ได้รับคำขอติดตาม\n"
followRequestAccepted: "ยอมรับคำขอติดตาม" followRequestAccepted: "ยอมรับคำขอติดตาม"

View File

@@ -53,6 +53,7 @@ _mfm:
_sfx: _sfx:
notification: "Bildirim" notification: "Bildirim"
_widgets: _widgets:
profile: "Profil"
notifications: "Bildirim" notifications: "Bildirim"
timeline: "Zaman çizelgesi" timeline: "Zaman çizelgesi"
_profile: _profile:

View File

@@ -894,6 +894,13 @@ windowRestore: "Відновити"
caption: "Підпис" caption: "Підпис"
like: "Вподобати" like: "Вподобати"
show: "Відображення" show: "Відображення"
color: "Колір"
_role:
priority: "Пріоритет"
_priority:
low: "Низький"
middle: "Середній"
high: "Високий"
_sensitiveMediaDetection: _sensitiveMediaDetection:
sensitivity: "Чутливість детектування" sensitivity: "Чутливість детектування"
setSensitiveFlagAutomatically: "Позначити як NSFW" setSensitiveFlagAutomatically: "Позначити як NSFW"
@@ -919,6 +926,7 @@ _accountDelete:
_ad: _ad:
back: "Назад" back: "Назад"
reduceFrequencyOfThisAd: "Показувати цю рекламу менше" reduceFrequencyOfThisAd: "Показувати цю рекламу менше"
hide: "Не відображати"
_gallery: _gallery:
my: "Моя галерея" my: "Моя галерея"
liked: "Вподобане" liked: "Вподобане"
@@ -1229,6 +1237,8 @@ _weekday:
friday: "П'ятниця" friday: "П'ятниця"
saturday: "Субота" saturday: "Субота"
_widgets: _widgets:
profile: "Профіль"
instanceInfo: "Про цей інстанс"
memo: "Нагадування" memo: "Нагадування"
notifications: "Сповіщення" notifications: "Сповіщення"
timeline: "Стрічка" timeline: "Стрічка"
@@ -1415,7 +1425,6 @@ _notification:
youGotReply: "{name} відповідає" youGotReply: "{name} відповідає"
youGotQuote: "{name} цитує вас" youGotQuote: "{name} цитує вас"
youRenoted: "{name} поширює" youRenoted: "{name} поширює"
youGotPoll: "{name} бере участь в опитуванні"
youGotMessagingMessageFromUser: "Повідомлення від {name}" youGotMessagingMessageFromUser: "Повідомлення від {name}"
youGotMessagingMessageFromGroup: "Нове повідомлення в групі {name}" youGotMessagingMessageFromGroup: "Нове повідомлення в групі {name}"
youWereFollowed: "Новий підписник" youWereFollowed: "Новий підписник"
@@ -1430,7 +1439,6 @@ _notification:
renote: "Поширення" renote: "Поширення"
quote: "Цитування" quote: "Цитування"
reaction: "Реакції" reaction: "Реакції"
pollVote: "Опитування"
receiveFollowRequest: "Запити на підписку" receiveFollowRequest: "Запити на підписку"
followRequestAccepted: "Прийняті підписки" followRequestAccepted: "Прийняті підписки"
groupInvited: "Запрошення до груп" groupInvited: "Запрошення до груп"

View File

@@ -896,6 +896,13 @@ account: "Tài khoản của bạn"
move: "Di chuyển" move: "Di chuyển"
like: "Thích" like: "Thích"
show: "Hiển thị" show: "Hiển thị"
color: "Màu sắc"
_role:
priority: "Ưu tiên"
_priority:
low: "Thấp"
middle: "Vừa"
high: "Cao"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ." description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ."
sensitivity: "Phát hiện nhạy cảm" sensitivity: "Phát hiện nhạy cảm"
@@ -928,6 +935,7 @@ _accountDelete:
_ad: _ad:
back: "Quay lại" back: "Quay lại"
reduceFrequencyOfThisAd: "Hiện ít lại" reduceFrequencyOfThisAd: "Hiện ít lại"
hide: "Không hiển thị"
_forgotPassword: _forgotPassword:
enterEmail: "Nhập địa chỉ email bạn đã sử dụng để đăng ký. Một liên kết mà bạn có thể đặt lại mật khẩu của mình sau đó sẽ được gửi đến nó." enterEmail: "Nhập địa chỉ email bạn đã sử dụng để đăng ký. Một liên kết mà bạn có thể đặt lại mật khẩu của mình sau đó sẽ được gửi đến nó."
ifNoEmail: "Nếu bạn không sử dụng email lúc đăng ký, vui lòng liên hệ với quản trị viên." ifNoEmail: "Nếu bạn không sử dụng email lúc đăng ký, vui lòng liên hệ với quản trị viên."
@@ -1271,6 +1279,8 @@ _weekday:
friday: "Thứ Sáu" friday: "Thứ Sáu"
saturday: "Thứ Bảy" saturday: "Thứ Bảy"
_widgets: _widgets:
profile: "Trang cá nhân"
instanceInfo: "Thông tin máy chủ"
memo: "Tút đã ghim" memo: "Tút đã ghim"
notifications: "Thông báo" notifications: "Thông báo"
timeline: "Bảng tin" timeline: "Bảng tin"
@@ -1460,7 +1470,6 @@ _notification:
youGotReply: "{name} trả lời bạn" youGotReply: "{name} trả lời bạn"
youGotQuote: "{name} trích dẫn tút của bạn" youGotQuote: "{name} trích dẫn tút của bạn"
youRenoted: "{name} đăng lại tút của bạn" youRenoted: "{name} đăng lại tút của bạn"
youGotPoll: "{name} bình chọn tút của bạn"
youGotMessagingMessageFromUser: "{name} nhắn tin cho bạn" youGotMessagingMessageFromUser: "{name} nhắn tin cho bạn"
youGotMessagingMessageFromGroup: "Một tin nhắn trong nhóm {name}" youGotMessagingMessageFromGroup: "Một tin nhắn trong nhóm {name}"
youWereFollowed: "đã theo dõi bạn" youWereFollowed: "đã theo dõi bạn"
@@ -1477,7 +1486,6 @@ _notification:
renote: "Đăng lại" renote: "Đăng lại"
quote: "Trích dẫn" quote: "Trích dẫn"
reaction: "Biểu cảm" reaction: "Biểu cảm"
pollVote: "Lượt bình chọn"
pollEnded: "Bình chọn kết thúc" pollEnded: "Bình chọn kết thúc"
receiveFollowRequest: "Yêu cầu theo dõi" receiveFollowRequest: "Yêu cầu theo dõi"
followRequestAccepted: "Yêu cầu theo dõi được chấp nhận" followRequestAccepted: "Yêu cầu theo dõi được chấp nhận"

View File

@@ -13,7 +13,7 @@ fetchingAsApObject: "在联邦宇宙查询中..."
ok: "OK" ok: "OK"
gotIt: "我明白了" gotIt: "我明白了"
cancel: "取消" cancel: "取消"
noThankYou: "不用" noThankYou: "不用,谢谢"
enterUsername: "输入用户名" enterUsername: "输入用户名"
renotedBy: "由 {user} 转贴" renotedBy: "由 {user} 转贴"
noNotes: "没有帖子" noNotes: "没有帖子"
@@ -920,6 +920,79 @@ like: "点赞!"
unlike: "取消赞" unlike: "取消赞"
numberOfLikes: "点赞数" numberOfLikes: "点赞数"
show: "显示" show: "显示"
neverShow: "不再显示"
remindMeLater: "稍后提醒我"
didYouLikeMisskey: "您喜欢Misskey吗"
pleaseDonate: "Misskey是{host}所使用的免费软件。为了今后也能够维持Misskey的开发请在有余力的情况下进行捐助"
roles: "角色"
role: "角色"
normalUser: "普通用户"
undefined: "未定义"
assign: "分配"
unassign: "取消分配"
color: "颜色"
manageCustomEmojis: "管理自定义表情符号"
youCannotCreateAnymore: "抱歉,您无法再创建更多了。"
cannotPerformTemporary: "暂时不可用"
cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用,请稍后再试。"
preset: "預設值"
selectFromPresets: "從預設值中選擇"
_role:
new: "创建角色"
edit: "编辑角色"
name: "角色名称"
description: "角色描述"
permission: "角色权限"
descriptionOfPermission: "<b>监察员</b>可以执行基本的审核操作。\n<b>管理员</b>可以更改实例的所有设置。"
assignTarget: "授权对象"
descriptionOfAssignTarget: "<b>手动</b>指手动选择谁被包括在这个角色中。\n<b>符合条件</b>指设置条件以自动包括符合条件的用户。"
manual: "手动"
conditional: "符合条件"
condition: "条件"
isConditionalRole: "这是一个条件控制的角色。"
isPublic: "角色公开"
descriptionOfIsPublic: "任何人都可以看到分配该角色的用户。而用户的个人资料也将显示该角色。"
options: "选项"
policies: "策略"
baseRole: "基本角色"
useBaseValue: "使用基本角色的值"
chooseRoleToAssign: "选择要分配的角色"
canEditMembersByModerator: "允许监察者编辑成员"
descriptionOfCanEditMembersByModerator: "如果选中,监察者和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
priority: "优先级"
_priority:
low: "低"
middle: "中"
high: "高"
_options:
gtlAvailable: "查看全局时间线"
ltlAvailable: "查看本地时间线"
canPublicNote: "允许公开发帖"
canInvite: "发放实例邀请码"
canManageCustomEmojis: "管理自定义表情符号"
driveCapacity: "网盘容量"
pinMax: "帖子置顶数量限制"
antennaMax: "可创建的最大天线数量"
wordMuteMax: "屏蔽词的字数限制"
webhookMax: "Webhook 创建数量限制"
clipMax: "便签创建数量限制"
noteEachClipsMax: "单个便签内的贴文数量限制"
userListMax: "用户列表创建数量限制"
userEachUserListsMax: "单个用户列表内用户数量限制"
rateLimitFactor: "速率限制"
descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。"
_condition:
isLocal: "是本地用户"
isRemote: "是远程用户"
createdLessThan: "账户创建时间少于"
createdMoreThan: "账户创建时间超过"
followersLessThanOrEq: "关注者不多于"
followersMoreThanOrEq: "关注者不少于"
followingLessThanOrEq: "关注中不多于"
followingMoreThanOrEq: "关注中不少于"
and: "符合以下全部条件"
or: "符合以下任一条件"
not: "不符合以下任何条件"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。"
sensitivity: "检测敏感度" sensitivity: "检测敏感度"
@@ -952,6 +1025,7 @@ _accountDelete:
_ad: _ad:
back: "返回" back: "返回"
reduceFrequencyOfThisAd: "减少此广告的频率" reduceFrequencyOfThisAd: "减少此广告的频率"
hide: "不显示"
_forgotPassword: _forgotPassword:
enterEmail: "请输入您验证账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。" enterEmail: "请输入您验证账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。"
ifNoEmail: "如果您没有使用电子邮件地址进行验证,请联系管理员。" ifNoEmail: "如果您没有使用电子邮件地址进行验证,请联系管理员。"
@@ -1298,6 +1372,8 @@ _weekday:
friday: "星期五" friday: "星期五"
saturday: "星期六" saturday: "星期六"
_widgets: _widgets:
profile: "个人资料"
instanceInfo: "实例信息"
memo: "便签" memo: "便签"
notifications: "通知" notifications: "通知"
timeline: "时间线" timeline: "时间线"
@@ -1324,6 +1400,7 @@ _widgets:
userList: "用户列表" userList: "用户列表"
_userList: _userList:
chooseList: "选择列表" chooseList: "选择列表"
clicker: "点击器"
_cw: _cw:
hide: "隐藏" hide: "隐藏"
show: "查看更多" show: "查看更多"
@@ -1499,7 +1576,6 @@ _notification:
youGotReply: "来自{name}的回复" youGotReply: "来自{name}的回复"
youGotQuote: "来自{name}的引用" youGotQuote: "来自{name}的引用"
youRenoted: "来自{name}的转发" youRenoted: "来自{name}的转发"
youGotPoll: "来自{name}的投票"
youGotMessagingMessageFromUser: "来自{name}的聊天" youGotMessagingMessageFromUser: "来自{name}的聊天"
youGotMessagingMessageFromGroup: "来自{name}的群聊" youGotMessagingMessageFromGroup: "来自{name}的群聊"
youWereFollowed: "关注了你。" youWereFollowed: "关注了你。"
@@ -1517,7 +1593,6 @@ _notification:
renote: "转发" renote: "转发"
quote: "引用" quote: "引用"
reaction: "回应" reaction: "回应"
pollVote: "问卷调查被投票"
pollEnded: "问卷调查结束" pollEnded: "问卷调查结束"
receiveFollowRequest: "收到关注请求" receiveFollowRequest: "收到关注请求"
followRequestAccepted: "关注请求已通过" followRequestAccepted: "关注请求已通过"

View File

@@ -252,7 +252,7 @@ uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。"
explore: "探索" explore: "探索"
messageRead: "已讀" messageRead: "已讀"
noMoreHistory: "沒有更多歷史紀錄" noMoreHistory: "沒有更多歷史紀錄"
startMessaging: "開始傳送訊息" startMessaging: "開始聊天"
nUsersRead: "{n}人已讀" nUsersRead: "{n}人已讀"
agreeTo: "我同意{0}" agreeTo: "我同意{0}"
tos: "使用條款" tos: "使用條款"
@@ -324,8 +324,8 @@ integration: "整合"
connectService: "己連結" connectService: "己連結"
disconnectService: "己斷開 " disconnectService: "己斷開 "
enableLocalTimeline: "開啟本地時間軸" enableLocalTimeline: "開啟本地時間軸"
enableGlobalTimeline: "啟用公開時間軸" enableGlobalTimeline: "啟用全域時間軸"
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。" disablingTimelinesInfo: "為了方便,即使您關閉了時間線功能,管理員和審核員仍可以繼續使用。"
registration: "註冊" registration: "註冊"
enableRegistration: "開啟新使用者註冊" enableRegistration: "開啟新使用者註冊"
invite: "邀請" invite: "邀請"
@@ -388,7 +388,7 @@ aboutMisskey: "關於 Misskey"
administrator: "管理員" administrator: "管理員"
token: "權杖" token: "權杖"
twoStepAuthentication: "兩階段驗證" twoStepAuthentication: "兩階段驗證"
moderator: "板主" moderator: "監察員"
moderation: "言論調節" moderation: "言論調節"
nUsersMentioned: "提到了{n}" nUsersMentioned: "提到了{n}"
securityKey: "安全金鑰" securityKey: "安全金鑰"
@@ -797,7 +797,7 @@ squareAvatars: "頭像以方形顯示"
sent: "發送" sent: "發送"
received: "收取" received: "收取"
searchResult: "搜尋結果" searchResult: "搜尋結果"
hashtags: "#tag" hashtags: "標籤"
troubleshooting: "故障排除" troubleshooting: "故障排除"
useBlurEffect: "在 UI 上使用模糊效果" useBlurEffect: "在 UI 上使用模糊效果"
learnMore: "更多資訊" learnMore: "更多資訊"
@@ -869,7 +869,7 @@ recommended: "推薦"
check: "檢查" check: "檢查"
driveCapOverrideLabel: "更改這個使用者的雲端硬碟容量上限" driveCapOverrideLabel: "更改這個使用者的雲端硬碟容量上限"
driveCapOverrideCaption: "如果指定0以下的值就會被取消。" driveCapOverrideCaption: "如果指定0以下的值就會被取消。"
requireAdminForView: "必須以管理帳號登入才可以檢視。" requireAdminForView: "必須以管理帳號登入才可以檢視。"
isSystemAccount: "由系統自動建立與管理的帳號。" isSystemAccount: "由系統自動建立與管理的帳號。"
typeToConfirm: "要執行這項操作,請輸入 {x} " typeToConfirm: "要執行這項操作,請輸入 {x} "
deleteAccount: "刪除帳號" deleteAccount: "刪除帳號"
@@ -923,6 +923,67 @@ show: "檢視"
neverShow: "不再顯示" neverShow: "不再顯示"
remindMeLater: "以後再說" remindMeLater: "以後再說"
didYouLikeMisskey: "您是否喜愛Misskey呢" didYouLikeMisskey: "您是否喜愛Misskey呢"
pleaseDonate: "Misskey是由{host}使用的免費軟體。請贊助我們,讓開發能夠持續!"
roles: "角色"
role: "角色"
normalUser: "一般使用者"
undefined: "未定義"
assign: "指派"
unassign: "取消指派"
color: "顏色"
manageCustomEmojis: "管理自訂表情符號"
cannotPerformTemporary: "暫時無法進行"
cannotPerformTemporaryDescription: "由於超過操作次數限制,暫時無法進行。請過一段時間之後再嘗試。"
_role:
new: "建立角色"
edit: "編輯角色"
name: "角色名稱"
description: "角色描述 "
permission: "角色的權限"
descriptionOfPermission: "<b>審核員</b>執行與審核相關的基本操作。\n<b>管理員</b>能變更實例的全部設定。"
assignTarget: "指派目標"
descriptionOfAssignTarget: "<b>手動</b>是以手動管理這個角色包含的人員。\n<b>符合條件</b>是設定條件以自動包含符合條件的使用者。"
manual: "手動"
conditional: "符合條件"
condition: "條件"
isConditionalRole: "這是條件角色。"
isPublic: "角色為公開"
descriptionOfIsPublic: "任何人都可以看到被指派了角色的使用者。此外,使用者的個人檔案將顯示這個角色。"
options: "選項"
policies: "政策"
baseRole: "基本角色"
useBaseValue: "使用基本角色的值"
chooseRoleToAssign: "選擇要指派的角色"
canEditMembersByModerator: "允許編輯監察員的成員"
descriptionOfCanEditMembersByModerator: "如果開啟,管理員與監察員都可以為使用者指派/解除指派該角色。如果關閉,則只有管理員可以執行。"
priority: "優先級"
_priority:
low: "低"
middle: "中"
high: "高"
_options:
gtlAvailable: "瀏覽全域時間軸"
ltlAvailable: "瀏覽本地時間軸"
canPublicNote: "允許公開貼文"
canInvite: "發行實例邀請碼"
canManageCustomEmojis: "管理自訂表情符號"
driveCapacity: "雲端硬碟容量"
pinMax: "置頂貼文的最大數量"
antennaMax: "可建立的天線數量"
webhookMax: "可建立的Webhook數量"
clipMax: "可建立的摘錄數量"
_condition:
isLocal: "本地使用者"
isRemote: "遠端使用者"
createdLessThan: "自建立帳戶開始~以內"
createdMoreThan: "自建立帳戶開始~經過"
followersLessThanOrEq: "追隨者人數在~以下"
followersMoreThanOrEq: "追隨者人數在~以上"
followingLessThanOrEq: "追隨人數在~以下"
followingMoreThanOrEq: "追隨人數在~以上"
and: "~和~"
or: "~或~"
not: "~否"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。"
sensitivity: "檢測敏感度" sensitivity: "檢測敏感度"
@@ -955,6 +1016,7 @@ _accountDelete:
_ad: _ad:
back: "返回" back: "返回"
reduceFrequencyOfThisAd: "降低此廣告的頻率 " reduceFrequencyOfThisAd: "降低此廣告的頻率 "
hide: "隱藏"
_forgotPassword: _forgotPassword:
enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。"
ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 "
@@ -1158,7 +1220,7 @@ _theme:
navActive: "側邊欄文本 (活動)" navActive: "側邊欄文本 (活動)"
navIndicator: "側邊欄指示符" navIndicator: "側邊欄指示符"
link: "鏈接" link: "鏈接"
hashtag: "#tag" hashtag: "標籤"
mention: "提到" mention: "提到"
mentionMe: "提到了我" mentionMe: "提到了我"
renote: "轉發貼文" renote: "轉發貼文"
@@ -1191,7 +1253,7 @@ _sfx:
note: "貼文" note: "貼文"
noteMy: "我的貼文" noteMy: "我的貼文"
notification: "通知" notification: "通知"
chat: "傳送訊息" chat: "聊天"
chatBg: "聊天背景" chatBg: "聊天背景"
antenna: "天線接收" antenna: "天線接收"
channel: "頻道通知" channel: "頻道通知"
@@ -1301,6 +1363,8 @@ _weekday:
friday: "週五" friday: "週五"
saturday: "週六" saturday: "週六"
_widgets: _widgets:
profile: "個人檔案"
instanceInfo: "實例資訊"
memo: "備忘錄" memo: "備忘錄"
notifications: "通知" notifications: "通知"
timeline: "時間軸" timeline: "時間軸"
@@ -1327,6 +1391,7 @@ _widgets:
userList: "使用者列表" userList: "使用者列表"
_userList: _userList:
chooseList: "選擇清單" chooseList: "選擇清單"
clicker: "點擊器"
_cw: _cw:
hide: "隱藏" hide: "隱藏"
show: "瀏覽更多" show: "瀏覽更多"
@@ -1502,7 +1567,6 @@ _notification:
youGotReply: "{name}回覆了您" youGotReply: "{name}回覆了您"
youGotQuote: "{name}引用了您" youGotQuote: "{name}引用了您"
youRenoted: "{name} 轉發了你的貼文" youRenoted: "{name} 轉發了你的貼文"
youGotPoll: "{name}已投票"
youGotMessagingMessageFromUser: "{name}發送給您的訊息" youGotMessagingMessageFromUser: "{name}發送給您的訊息"
youGotMessagingMessageFromGroup: "{name}發送給您的訊息" youGotMessagingMessageFromGroup: "{name}發送給您的訊息"
youWereFollowed: "您有新的追隨者" youWereFollowed: "您有新的追隨者"
@@ -1520,7 +1584,6 @@ _notification:
renote: "轉發貼文" renote: "轉發貼文"
quote: "引用" quote: "引用"
reaction: "反應" reaction: "反應"
pollVote: "統計已投票數"
pollEnded: "問卷調查結束" pollEnded: "問卷調查結束"
receiveFollowRequest: "已收到追隨請求" receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接受" followRequestAccepted: "追隨請求已接受"

View File

@@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "13.0.0-beta.35", "version": "13.0.0",
"codename": "indigo", "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": "yarn@3.3.0", "packageManager": "pnpm@7.24.3",
"workspaces": [ "workspaces": [
"packages/frontend", "packages/frontend",
"packages/backend", "packages/backend",
@@ -15,27 +15,27 @@
"private": true, "private": true,
"scripts": { "scripts": {
"build-pre": "node ./scripts/build-pre.js", "build-pre": "node ./scripts/build-pre.js",
"build": "yarn build-pre && yarn workspaces foreach run build && yarn run gulp", "build": "pnpm build-pre && pnpm -r build && pnpm gulp",
"start": "cd packages/backend && node ./built/boot/index.js", "start": "cd packages/backend && node ./built/boot/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js",
"init": "yarn migrate", "init": "pnpm migrate",
"migrate": "cd packages/backend && yarn run typeorm migration:run -d ormconfig.js", "migrate": "cd packages/backend && pnpm typeorm migration:run -d ormconfig.js",
"migrateandstart": "yarn migrate && yarn start", "migrateandstart": "pnpm migrate && pnpm start",
"gulp": "gulp build", "gulp": "pnpm exec gulp build",
"watch": "yarn dev", "watch": "pnpm dev",
"dev": "node ./scripts/dev.js", "dev": "node ./scripts/dev.js",
"lint": "yarn workspaces foreach run lint", "lint": "pnpm -r lint",
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts", "cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run", "cy:run": "pnpm cypress run",
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run", "e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"jest": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand", "jest": "cd packages/backend && pnpm cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
"jest-and-coverage": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand", "jest-and-coverage": "cd packages/backend && pnpm cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
"test": "yarn jest", "test": "pnpm jest",
"test-and-coverage": "yarn jest-and-coverage", "test-and-coverage": "pnpm jest-and-coverage",
"format": "gulp format", "format": "pnpm exec gulp format",
"clean": "node ./scripts/clean.js", "clean": "node ./scripts/clean.js",
"clean-all": "node ./scripts/clean-all.js", "clean-all": "node ./scripts/clean-all.js",
"cleanall": "yarn clean-all" "cleanall": "pnpm clean-all"
}, },
"resolutions": { "resolutions": {
"chokidar": "^3.3.1", "chokidar": "^3.3.1",
@@ -48,17 +48,20 @@
"gulp-rename": "2.0.0", "gulp-rename": "2.0.0",
"gulp-replace": "1.1.4", "gulp-replace": "1.1.4",
"gulp-terser": "2.1.0", "gulp-terser": "2.1.0",
"js-yaml": "4.1.0" "js-yaml": "4.1.0",
"typescript": "4.9.4"
}, },
"devDependencies": { "devDependencies": {
"@types/gulp": "4.0.10", "@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.1", "@types/gulp-rename": "2.0.1",
"@typescript-eslint/eslint-plugin": "5.48.0", "@typescript-eslint/eslint-plugin": "5.48.1",
"@typescript-eslint/parser": "5.48.0", "@typescript-eslint/parser": "5.48.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "12.3.0", "cypress": "12.3.0",
"eslint": "^8.31.0", "eslint": "^8.31.0",
"start-server-and-test": "1.15.2", "start-server-and-test": "1.15.2"
"typescript": "4.9.4" },
"optionalDependencies": {
"@tensorflow/tfjs-core": "^4.2.0"
} }
} }

View File

@@ -0,0 +1,11 @@
export class PollChoiceLength1673336077243 {
name = 'PollChoiceLength1673336077243'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(256) array`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(128) array`);
}
}

View File

@@ -0,0 +1,37 @@
export class Role1673500412259 {
name = 'Role1673500412259'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "role" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(256) NOT NULL, "description" character varying(1024) NOT NULL, "isPublic" boolean NOT NULL DEFAULT false, "isModerator" boolean NOT NULL DEFAULT false, "isAdministrator" boolean NOT NULL DEFAULT false, "options" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_b36bcfe02fc8de3c57a8b2391c2" PRIMARY KEY ("id")); COMMENT ON COLUMN "role"."createdAt" IS 'The created date of the Role.'; COMMENT ON COLUMN "role"."updatedAt" IS 'The updated date of the Role.'`);
await queryRunner.query(`CREATE TABLE "role_assignment" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "roleId" character varying(32) NOT NULL, CONSTRAINT "PK_7e79671a8a5db18936173148cb4" PRIMARY KEY ("id")); COMMENT ON COLUMN "role_assignment"."createdAt" IS 'The created date of the RoleAssignment.'; COMMENT ON COLUMN "role_assignment"."userId" IS 'The user ID.'; COMMENT ON COLUMN "role_assignment"."roleId" IS 'The role ID.'`);
await queryRunner.query(`CREATE INDEX "IDX_db5b72c16227c97ca88734d5c2" ON "role_assignment" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_f0de67fd09cd3cd0aabca79994" ON "role_assignment" ("roleId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0953deda7ce6e1448e935859e5" ON "role_assignment" ("userId", "roleId") `);
await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isAdmin" TO "isRoot"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isModerator"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "driveCapacityOverrideMb"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableLocalTimeline"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableGlobalTimeline"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "localDriveCapacityMb"`);
await queryRunner.query(`ALTER TABLE "meta" ADD "defaultRoleOverride" jsonb NOT NULL DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "role_assignment" ADD CONSTRAINT "FK_db5b72c16227c97ca88734d5c2b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "role_assignment" ADD CONSTRAINT "FK_f0de67fd09cd3cd0aabca79994d" FOREIGN KEY ("roleId") REFERENCES "role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "role_assignment" DROP CONSTRAINT "FK_f0de67fd09cd3cd0aabca79994d"`);
await queryRunner.query(`ALTER TABLE "role_assignment" DROP CONSTRAINT "FK_db5b72c16227c97ca88734d5c2b"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultRoleOverride"`);
await queryRunner.query(`ALTER TABLE "meta" ADD "localDriveCapacityMb" integer NOT NULL DEFAULT '1024'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "disableGlobalTimeline" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "meta" ADD "disableLocalTimeline" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "user" ADD "driveCapacityOverrideMb" integer`);
await queryRunner.query(`ALTER TABLE "user" ADD "isModerator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isRoot" TO "isAdmin"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0953deda7ce6e1448e935859e5"`);
await queryRunner.query(`DROP INDEX "public"."IDX_f0de67fd09cd3cd0aabca79994"`);
await queryRunner.query(`DROP INDEX "public"."IDX_db5b72c16227c97ca88734d5c2"`);
await queryRunner.query(`DROP TABLE "role_assignment"`);
await queryRunner.query(`DROP TABLE "role"`);
}
}

View File

@@ -0,0 +1,11 @@
export class RoleColor1673515526953 {
name = 'RoleColor1673515526953'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" ADD "color" character varying(256)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "color"`);
}
}

View File

@@ -0,0 +1,13 @@
export class RoleIroiro1673522856499 {
name = 'RoleIroiro1673522856499'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isSilenced"`);
await queryRunner.query(`ALTER TABLE "role" ADD "canEditMembersByModerator" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "canEditMembersByModerator"`);
await queryRunner.query(`ALTER TABLE "user" ADD "isSilenced" boolean NOT NULL DEFAULT false`);
}
}

View File

@@ -0,0 +1,13 @@
export class RoleLastUsedAt1673524604156 {
name = 'RoleLastUsedAt1673524604156'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" ADD "lastUsedAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`COMMENT ON COLUMN "role"."lastUsedAt" IS 'The last used date of the Role.'`);
}
async down(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "role"."lastUsedAt" IS 'The last used date of the Role.'`);
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "lastUsedAt"`);
}
}

View File

@@ -0,0 +1,15 @@
export class RoleConditional1673570377815 {
name = 'RoleConditional1673570377815'
async up(queryRunner) {
await queryRunner.query(`CREATE TYPE "public"."role_target_enum" AS ENUM('manual', 'conditional')`);
await queryRunner.query(`ALTER TABLE "role" ADD "target" "public"."role_target_enum" NOT NULL DEFAULT 'manual'`);
await queryRunner.query(`ALTER TABLE "role" ADD "condFormula" jsonb NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "condFormula"`);
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "target"`);
await queryRunner.query(`DROP TYPE "public"."role_target_enum"`);
}
}

View File

@@ -0,0 +1,11 @@
export class MetaClean1673575973645 {
name = 'MetaClean1673575973645'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "remoteDriveCapacityMb"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "remoteDriveCapacityMb" integer NOT NULL DEFAULT '32'`);
}
}

View File

@@ -0,0 +1,13 @@
export class Policies1673783015567 {
name = 'Policies1673783015567'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`);
await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`);
await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`);
}
}

View File

@@ -0,0 +1,11 @@
export class firstRetrievedAt1673812883772 {
name = 'firstRetrievedAt1673812883772'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "caughtAt" TO "firstRetrievedAt"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "firstRetrievedAt" TO "caughtAt"`);
}
}

View File

@@ -6,32 +6,32 @@
"scripts": { "scripts": {
"start": "node ./built/index.js", "start": "node ./built/index.js",
"start:test": "NODE_ENV=test node ./built/index.js", "start:test": "NODE_ENV=test node ./built/index.js",
"migrate": "typeorm migration:run -d ormconfig.js", "migrate": "pnpm typeorm migration:run -d ormconfig.js",
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs", "watch": "node watch.mjs",
"lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"", "lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"",
"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand", "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand", "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
"jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache", "jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache",
"test": "yarn jest", "test": "pnpm jest",
"test-and-coverage": "yarn jest-and-coverage" "test-and-coverage": "pnpm jest-and-coverage"
}, },
"optionalDependencies": { "optionalDependencies": {
"@tensorflow/tfjs": "^4.1.0", "@tensorflow/tfjs": "^4.1.0",
"@tensorflow/tfjs-node": "4.1.0" "@tensorflow/tfjs-node": "4.1.0"
}, },
"dependencies": { "dependencies": {
"@bull-board/api": "^4.10.1", "@bull-board/api": "^4.10.2",
"@bull-board/fastify": "^4.10.1", "@bull-board/fastify": "^4.10.2",
"@bull-board/ui": "^4.10.1", "@bull-board/ui": "^4.10.2",
"@discordapp/twemoji": "14.0.2", "@discordapp/twemoji": "14.0.2",
"@fastify/accepts": "4.1.0", "@fastify/accepts": "4.1.0",
"@fastify/cookie": "^8.3.0", "@fastify/cookie": "^8.3.0",
"@fastify/cors": "8.2.0", "@fastify/cors": "8.2.0",
"@fastify/http-proxy": "^8.4.0", "@fastify/http-proxy": "^8.4.0",
"@fastify/multipart": "7.3.0", "@fastify/multipart": "7.4.0",
"@fastify/static": "6.6.0", "@fastify/static": "6.6.1",
"@fastify/view": "7.3.0", "@fastify/view": "7.4.0",
"@nestjs/common": "9.2.1", "@nestjs/common": "9.2.1",
"@nestjs/core": "9.2.1", "@nestjs/core": "9.2.1",
"@nestjs/testing": "9.2.1", "@nestjs/testing": "9.2.1",
@@ -41,7 +41,7 @@
"ajv": "8.12.0", "ajv": "8.12.0",
"archiver": "5.3.1", "archiver": "5.3.1",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.1289.0", "aws-sdk": "2.1295.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.4", "blurhash": "2.0.4",
"bull": "4.10.2", "bull": "4.10.2",
@@ -58,7 +58,7 @@
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"fastify": "4.11.0", "fastify": "4.11.0",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "18.0.0", "file-type": "18.1.0",
"fluent-ffmpeg": "2.1.2", "fluent-ffmpeg": "2.1.2",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"got": "12.5.3", "got": "12.5.3",
@@ -67,18 +67,17 @@
"ip-cidr": "3.0.11", "ip-cidr": "3.0.11",
"is-svg": "4.3.2", "is-svg": "4.3.2",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "20.0.3", "jsdom": "21.0.0",
"json5": "2.2.3", "json5": "2.2.3",
"json5-loader": "4.0.1", "json5-loader": "4.0.1",
"jsonld": "8.1.0", "jsonld": "8.1.0",
"jsrsasign": "10.6.1", "jsrsasign": "10.6.1",
"mfm-js": "0.23.1", "mfm-js": "0.23.3",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "0.0.14", "misskey-js": "0.0.14",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.3.0", "nodemailer": "6.9.0",
"nodemailer": "6.8.0",
"nsfwjs": "2.4.2", "nsfwjs": "2.4.2",
"oauth": "^0.10.0", "oauth": "^0.10.0",
"os-utils": "0.0.14", "os-utils": "0.0.14",
@@ -88,7 +87,7 @@
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"pug": "3.0.2", "pug": "3.0.2",
"punycode": "2.1.1", "punycode": "2.2.0",
"pureimage": "0.3.15", "pureimage": "0.3.15",
"qrcode": "1.5.1", "qrcode": "1.5.1",
"random-seed": "0.3.0", "random-seed": "0.3.0",
@@ -110,25 +109,27 @@
"stringz": "2.1.0", "stringz": "2.1.0",
"summaly": "2.7.0", "summaly": "2.7.0",
"syslog-pro": "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2", "syslog-pro": "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2",
"systeminformation": "5.17.1", "systeminformation": "5.17.3",
"tinycolor2": "1.5.2", "tinycolor2": "1.5.2",
"tmp": "0.2.1", "tmp": "0.2.1",
"tsc-alias": "1.8.2", "tsc-alias": "1.8.2",
"tsconfig-paths": "4.1.2", "tsconfig-paths": "4.1.2",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typeorm": "0.3.11", "typeorm": "0.3.11",
"typescript": "4.9.4",
"ulid": "2.3.0", "ulid": "2.3.0",
"undici": "^5.15.0",
"unzipper": "0.10.11", "unzipper": "0.10.11",
"uuid": "9.0.0", "uuid": "9.0.0",
"vary": "1.1.2", "vary": "1.1.2",
"web-push": "3.5.0", "web-push": "3.5.0",
"websocket": "1.0.34", "websocket": "1.0.34",
"ws": "8.11.0", "ws": "8.12.0",
"xev": "3.0.2" "xev": "3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.117", "@redocly/openapi-core": "1.0.0-beta.120",
"@swc/core": "1.3.25", "@swc/core": "1.3.26",
"@swc/jest": "0.2.24", "@swc/jest": "0.2.24",
"@types/accepts": "1.3.5", "@types/accepts": "1.3.5",
"@types/archiver": "5.3.1", "@types/archiver": "5.3.1",
@@ -172,14 +173,14 @@
"@types/web-push": "3.3.2", "@types/web-push": "3.3.2",
"@types/websocket": "1.0.5", "@types/websocket": "1.0.5",
"@types/ws": "8.5.4", "@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "5.48.0", "@typescript-eslint/eslint-plugin": "5.48.1",
"@typescript-eslint/parser": "5.48.0", "@typescript-eslint/parser": "5.48.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "8.31.0", "eslint": "8.31.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.27.4",
"execa": "6.1.0", "execa": "6.1.0",
"jest": "29.3.1", "jest": "29.3.1",
"jest-mock": "^29.3.1", "jest-mock": "^29.3.1",
"typescript": "4.9.4" "node-fetch": "3.3.0"
} }
} }

View File

@@ -16,6 +16,7 @@ import { DI } from '@/di-symbols.js';
import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common'; import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
@@ -73,7 +74,7 @@ export class AntennaService 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; const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) { switch (type) {
case 'antennaCreated': case 'antennaCreated':
this.antennas.push(body); this.antennas.push(body);

View File

@@ -1,7 +1,4 @@
import { Inject, Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@@ -13,9 +10,6 @@ type CaptchaResponse = {
@Injectable() @Injectable()
export class CaptchaService { export class CaptchaService {
constructor( constructor(
@Inject(DI.config)
private config: Config,
private httpRequestService: HttpRequestService, private httpRequestService: HttpRequestService,
) { ) {
} }
@@ -27,16 +21,16 @@ export class CaptchaService {
response, response,
}); });
const res = await fetch(url, { const res = await this.httpRequestService.fetch(
method: 'POST', url,
body: params, {
headers: { method: 'POST',
'User-Agent': this.config.userAgent, body: params,
}, },
// TODO {
//timeout: 10 * 1000, noOkError: true,
agent: (url, bypassProxy) => this.httpRequestService.getAgentByUrl(url, bypassProxy), }
}).catch(err => { ).catch(err => {
throw `${err.message ?? err}`; throw `${err.message ?? err}`;
}); });

View File

@@ -35,6 +35,7 @@ import { PushNotificationService } from './PushNotificationService.js';
import { QueryService } from './QueryService.js'; import { QueryService } from './QueryService.js';
import { ReactionService } from './ReactionService.js'; import { ReactionService } from './ReactionService.js';
import { RelayService } from './RelayService.js'; import { RelayService } from './RelayService.js';
import { RoleService } from './RoleService.js';
import { S3Service } from './S3Service.js'; import { S3Service } from './S3Service.js';
import { SignupService } from './SignupService.js'; import { SignupService } from './SignupService.js';
import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js'; import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js';
@@ -97,6 +98,7 @@ import { UserGroupInvitationEntityService } from './entities/UserGroupInvitation
import { UserListEntityService } from './entities/UserListEntityService.js'; import { UserListEntityService } from './entities/UserListEntityService.js';
import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashEntityService } from './entities/FlashEntityService.js';
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
import { RoleEntityService } from './entities/RoleEntityService.js';
import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApAudienceService } from './activitypub/ApAudienceService.js';
import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js';
import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js';
@@ -158,6 +160,7 @@ const $PushNotificationService: Provider = { provide: 'PushNotificationService',
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService }; const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService };
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService }; const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService };
const $RelayService: Provider = { provide: 'RelayService', useExisting: RelayService }; const $RelayService: Provider = { provide: 'RelayService', useExisting: RelayService };
const $RoleService: Provider = { provide: 'RoleService', useExisting: RoleService };
const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service };
const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService };
const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService }; const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService };
@@ -220,6 +223,7 @@ const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitat
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService };
const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
@@ -283,6 +287,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
QueryService, QueryService,
ReactionService, ReactionService,
RelayService, RelayService,
RoleService,
S3Service, S3Service,
SignupService, SignupService,
TwoFactorAuthenticationService, TwoFactorAuthenticationService,
@@ -344,6 +349,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
UserListEntityService, UserListEntityService,
FlashEntityService, FlashEntityService,
FlashLikeEntityService, FlashLikeEntityService,
RoleEntityService,
ApAudienceService, ApAudienceService,
ApDbResolverService, ApDbResolverService,
ApDeliverManagerService, ApDeliverManagerService,
@@ -402,6 +408,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$QueryService, $QueryService,
$ReactionService, $ReactionService,
$RelayService, $RelayService,
$RoleService,
$S3Service, $S3Service,
$SignupService, $SignupService,
$TwoFactorAuthenticationService, $TwoFactorAuthenticationService,
@@ -463,6 +470,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$UserListEntityService, $UserListEntityService,
$FlashEntityService, $FlashEntityService,
$FlashLikeEntityService, $FlashLikeEntityService,
$RoleEntityService,
$ApAudienceService, $ApAudienceService,
$ApDbResolverService, $ApDbResolverService,
$ApDeliverManagerService, $ApDeliverManagerService,
@@ -522,6 +530,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
QueryService, QueryService,
ReactionService, ReactionService,
RelayService, RelayService,
RoleService,
S3Service, S3Service,
SignupService, SignupService,
TwoFactorAuthenticationService, TwoFactorAuthenticationService,
@@ -582,6 +591,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
UserListEntityService, UserListEntityService,
FlashEntityService, FlashEntityService,
FlashLikeEntityService, FlashLikeEntityService,
RoleEntityService,
ApAudienceService, ApAudienceService,
ApDbResolverService, ApDbResolverService,
ApDeliverManagerService, ApDeliverManagerService,
@@ -640,6 +650,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$QueryService, $QueryService,
$ReactionService, $ReactionService,
$RelayService, $RelayService,
$RoleService,
$S3Service, $S3Service,
$SignupService, $SignupService,
$TwoFactorAuthenticationService, $TwoFactorAuthenticationService,
@@ -700,6 +711,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$UserListEntityService, $UserListEntityService,
$FlashEntityService, $FlashEntityService,
$FlashLikeEntityService, $FlashLikeEntityService,
$RoleEntityService,
$ApAudienceService, $ApAudienceService,
$ApDbResolverService, $ApDbResolverService,
$ApDeliverManagerService, $ApDeliverManagerService,

View File

@@ -53,7 +53,7 @@ export class CreateSystemUserService {
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: null, host: null,
token: secret, token: secret,
isAdmin: false, isRoot: false,
isLocked: true, isLocked: true,
isExplorable: false, isExplorable: false,
isBot: true, isBot: true,

View File

@@ -23,6 +23,9 @@ export class DeleteAccountService {
id: string; id: string;
host: string | null; host: string | null;
}): Promise<void> { }): Promise<void> {
const _user = await this.usersRepository.findOneByOrFail({ id: user.id });
if (_user.isRoot) throw new Error('cannot delete a root account');
// 物理削除する前にDelete activityを送信する // 物理削除する前にDelete activityを送信する
await this.userSuspendService.doPostSuspend(user).catch(e => {}); await this.userSuspendService.doPostSuspend(user).catch(e => {});

View File

@@ -8,11 +8,12 @@ import got, * as Got from 'got';
import chalk from 'chalk'; import chalk from 'chalk';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
import { createTemp } from '@/misc/create-temp.js'; import { createTemp } from '@/misc/create-temp.js';
import { StatusError } from '@/misc/status-error.js'; import { StatusError } from '@/misc/status-error.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { buildConnector } from 'undici';
const pipeline = util.promisify(stream.pipeline); const pipeline = util.promisify(stream.pipeline);
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@@ -20,6 +21,7 @@ import { bindThis } from '@/decorators.js';
@Injectable() @Injectable()
export class DownloadService { export class DownloadService {
private logger: Logger; private logger: Logger;
private undiciFetcher: UndiciFetcher;
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
@@ -29,70 +31,42 @@ export class DownloadService {
private loggerService: LoggerService, private loggerService: LoggerService,
) { ) {
this.logger = this.loggerService.getLogger('download'); this.logger = this.loggerService.getLogger('download');
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption(
{
connect: process.env.NODE_ENV === 'development' ?
this.httpRequestService.clientDefaults.connect
:
this.httpRequestService.getConnectorWithIpCheck(
buildConnector({
...this.httpRequestService.clientDefaults.connect,
}),
(ip) => !this.isPrivateIp(ip)
),
bodyTimeout: 30 * 1000,
},
{
connect: this.httpRequestService.clientDefaults.connect,
}
), this.logger);
} }
@bindThis @bindThis
public async downloadUrl(url: string, path: string): Promise<void> { public async downloadUrl(url: string, path: string): Promise<void> {
this.logger.info(`Downloading ${chalk.cyan(url)} to ${chalk.cyanBright(path)} ...`); this.logger.info(`Downloading ${chalk.cyan(url)} to ${chalk.cyanBright(path)} ...`);
const timeout = 30 * 1000; const timeout = 30 * 1000;
const operationTimeout = 60 * 1000; const operationTimeout = 60 * 1000;
const maxSize = this.config.maxFileSize ?? 262144000; const maxSize = this.config.maxFileSize ?? 262144000;
const req = got.stream(url, { const response = await this.undiciFetcher.fetch(url);
headers: {
'User-Agent': this.config.userAgent, if (response.body === null) {
}, throw new StatusError('No body', 400, 'No body');
timeout: {
lookup: timeout,
connect: timeout,
secureConnect: timeout,
socket: timeout, // read timeout
response: timeout,
send: timeout,
request: operationTimeout, // whole operation timeout
},
agent: {
http: this.httpRequestService.httpAgent,
https: this.httpRequestService.httpsAgent,
},
http2: false, // default
retry: {
limit: 0,
},
}).on('response', (res: Got.Response) => {
if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) {
if (this.isPrivateIp(res.ip)) {
this.logger.warn(`Blocked address: ${res.ip}`);
req.destroy();
}
}
const contentLength = res.headers['content-length'];
if (contentLength != null) {
const size = Number(contentLength);
if (size > maxSize) {
this.logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`);
req.destroy();
}
}
}).on('downloadProgress', (progress: Got.Progress) => {
if (progress.transferred > maxSize) {
this.logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`);
req.destroy();
}
});
try {
await pipeline(req, fs.createWriteStream(path));
} catch (e) {
if (e instanceof Got.HTTPError) {
throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage);
} else {
throw e;
}
} }
await pipeline(stream.Readable.fromWeb(response.body), fs.createWriteStream(path));
this.logger.succ(`Download finished: ${chalk.cyan(url)}`); this.logger.succ(`Download finished: ${chalk.cyan(url)}`);
} }
@@ -114,7 +88,7 @@ export class DownloadService {
cleanup(); cleanup();
} }
} }
@bindThis @bindThis
private isPrivateIp(ip: string): boolean { private isPrivateIp(ip: string): boolean {
for (const net of this.config.allowedPrivateNetworks ?? []) { for (const net of this.config.allowedPrivateNetworks ?? []) {
@@ -124,6 +98,6 @@ export class DownloadService {
} }
} }
return PrivateIp(ip); return PrivateIp(ip) ?? false;
} }
} }

View File

@@ -32,11 +32,12 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { FileInfoService } from '@/core/FileInfoService.js'; import { FileInfoService } from '@/core/FileInfoService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import type S3 from 'aws-sdk/clients/s3.js'; import type S3 from 'aws-sdk/clients/s3.js';
type AddFileArgs = { type AddFileArgs = {
/** User who wish to add file */ /** User who wish to add file */
user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; user: { id: User['id']; host: User['host'] } | null;
/** File path */ /** File path */
path: string; path: string;
/** Name */ /** Name */
@@ -62,7 +63,7 @@ type AddFileArgs = {
type UploadFromUrlArgs = { type UploadFromUrlArgs = {
url: string; url: string;
user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; user: { id: User['id']; host: User['host'] } | null;
folderId?: DriveFolder['id'] | null; folderId?: DriveFolder['id'] | null;
uri?: string | null; uri?: string | null;
sensitive?: boolean; sensitive?: boolean;
@@ -106,6 +107,7 @@ export class DriveService {
private videoProcessingService: VideoProcessingService, private videoProcessingService: VideoProcessingService,
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private queueService: QueueService, private queueService: QueueService,
private roleService: RoleService,
private driveChart: DriveChart, private driveChart: DriveChart,
private perUserDriveChart: PerUserDriveChart, private perUserDriveChart: PerUserDriveChart,
private instanceChart: InstanceChart, private instanceChart: InstanceChart,
@@ -373,8 +375,19 @@ export class DriveService {
partSize: s3.endpoint.hostname === 'storage.googleapis.com' ? 500 * 1024 * 1024 : 8 * 1024 * 1024, partSize: s3.endpoint.hostname === 'storage.googleapis.com' ? 500 * 1024 * 1024 : 8 * 1024 * 1024,
}); });
const result = await upload.promise(); await upload.promise()
if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); .then(
result => {
if (result) {
this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`);
} else {
this.registerLogger.error(`Upload Result Empty: key = ${key}, filename = ${filename}`);
}
},
err => {
this.registerLogger.error(`Upload Failed: key = ${key}, filename = ${filename}`, err);
},
);
} }
@bindThis @bindThis
@@ -460,19 +473,16 @@ export class DriveService {
} }
} }
this.registerLogger.debug(`ADD DRIVE FILE: user ${user?.id ?? 'not set'}, name ${detectedName}, tmp ${path}`);
//#region Check drive usage //#region Check drive usage
if (user && !isLink) { if (user && !isLink) {
const usage = await this.driveFileEntityService.calcDriveUsageOf(user); const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
const u = await this.usersRepository.findOneBy({ id: user.id });
const instance = await this.metaService.fetch(); const policies = await this.roleService.getUserPolicies(user.id);
let driveCapacity = 1024 * 1024 * (this.userEntityService.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
this.registerLogger.debug('drive capacity override applied');
if (this.userEntityService.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
}
this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`); this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`);

View File

@@ -34,7 +34,7 @@ export class FederatedInstanceService {
const i = await this.instancesRepository.insert({ const i = await this.instancesRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
host, host,
caughtAt: new Date(), firstRetrievedAt: new Date(),
}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));
this.cache.set(host, i); this.cache.set(host, i);

View File

@@ -1,7 +1,6 @@
import { URL } from 'node:url'; import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import fetch from 'node-fetch';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import type { Instance } from '@/models/entities/Instance.js'; import type { Instance } from '@/models/entities/Instance.js';
import type { InstancesRepository } from '@/models/index.js'; import type { InstancesRepository } from '@/models/index.js';
@@ -191,11 +190,7 @@ export class FetchInstanceMetadataService {
const faviconUrl = url + '/favicon.ico'; const faviconUrl = url + '/favicon.ico';
const favicon = await fetch(faviconUrl, { const favicon = await this.httpRequestService.fetch(faviconUrl, {}, { noOkError: true });
// TODO
//timeout: 10000,
agent: url => this.httpRequestService.getAgentByUrl(url),
});
if (favicon.ok) { if (favicon.ok) {
return faviconUrl; return faviconUrl;

View File

@@ -1,67 +1,257 @@
import * as http from 'node:http'; import * as http from 'node:http';
import * as https from 'node:https'; import * as https from 'node:https';
import CacheableLookup from 'cacheable-lookup'; import CacheableLookup from 'cacheable-lookup';
import fetch from 'node-fetch';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { StatusError } from '@/misc/status-error.js'; import { StatusError } from '@/misc/status-error.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { Response } from 'node-fetch'; import * as undici from 'undici';
import type { URL } from 'node:url'; import { LookupFunction } from 'node:net';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
// true to allow, false to deny
export type IpChecker = (ip: string) => boolean;
/*
* Child class to create and save Agent for fetch.
* You should construct this when you want
* to change timeout, size limit, socket connect function, etc.
*/
export class UndiciFetcher {
/**
* Get http non-proxy agent (undici)
*/
public nonProxiedAgent: undici.Agent;
/**
* Get http proxy or non-proxy agent (undici)
*/
public agent: undici.ProxyAgent | undici.Agent;
private proxyBypassHosts: string[];
private userAgent: string | undefined;
private logger: Logger | undefined;
constructor(
args: {
agentOptions: undici.Agent.Options;
proxy?: {
uri: string;
options?: undici.Agent.Options; // Override of agentOptions
},
proxyBypassHosts?: string[];
userAgent?: string;
},
logger?: Logger,
) {
this.logger = logger;
this.logger?.debug('UndiciFetcher constructor', args);
this.proxyBypassHosts = args.proxyBypassHosts ?? [];
this.userAgent = args.userAgent;
this.nonProxiedAgent = new undici.Agent({
...args.agentOptions,
connect: (process.env.NODE_ENV !== 'production' && typeof args.agentOptions.connect !== 'function')
? (options, cb) => {
// Custom connector for debug
undici.buildConnector(args.agentOptions.connect as undici.buildConnector.BuildOptions)(options, (err, socket) => {
this.logger?.debug('Socket connector called', socket);
if (err) {
this.logger?.debug(`Socket error`, err);
cb(new Error(`Error while socket connecting\n${err}`), null);
return;
}
this.logger?.debug(`Socket connected: port ${socket.localPort} => remote ${socket.remoteAddress}`);
cb(null, socket);
});
} : args.agentOptions.connect,
});
this.agent = args.proxy
? new undici.ProxyAgent({
...args.agentOptions,
...args.proxy.options,
uri: args.proxy.uri,
connect: (process.env.NODE_ENV !== 'production' && typeof (args.proxy?.options?.connect ?? args.agentOptions.connect) !== 'function')
? (options, cb) => {
// Custom connector for debug
undici.buildConnector((args.proxy?.options?.connect ?? args.agentOptions.connect) as undici.buildConnector.BuildOptions)(options, (err, socket) => {
this.logger?.debug('Socket connector called (secure)', socket);
if (err) {
this.logger?.debug(`Socket error`, err);
cb(new Error(`Error while socket connecting\n${err}`), null);
return;
}
this.logger?.debug(`Socket connected (secure): port ${socket.localPort} => remote ${socket.remoteAddress}`);
cb(null, socket);
});
} : (args.proxy?.options?.connect ?? args.agentOptions.connect),
})
: this.nonProxiedAgent;
}
/**
* Get agent by URL
* @param url URL
* @param bypassProxy Allways bypass proxy
*/
@bindThis
public getAgentByUrl(url: URL, bypassProxy = false): undici.Agent | undici.ProxyAgent {
if (bypassProxy || this.proxyBypassHosts.includes(url.hostname)) {
return this.nonProxiedAgent;
} else {
return this.agent;
}
}
@bindThis
public async fetch(
url: string | URL,
options: undici.RequestInit = {},
privateOptions: { noOkError?: boolean; bypassProxy?: boolean; } = { noOkError: false, bypassProxy: false }
): Promise<undici.Response> {
const res = await undici.fetch(url, {
dispatcher: this.getAgentByUrl(new URL(url), privateOptions.bypassProxy),
...options,
headers: {
'User-Agent': this.userAgent ?? '',
...(options.headers ?? {}),
},
}).catch((err) => {
this.logger?.error('fetch error', err);
throw new StatusError('Resource Unreachable', 500, 'Resource Unreachable');
});
if (!res.ok && !privateOptions.noOkError) {
throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText);
}
return res;
}
@bindThis
public async getJson<T extends unknown>(url: string, accept = 'application/json, */*', headers?: Record<string, string>): Promise<T> {
const res = await this.fetch(
url,
{
headers: Object.assign({
Accept: accept,
}, headers ?? {}),
}
);
return await res.json() as T;
}
@bindThis
public async getHtml(url: string, accept = 'text/html, */*', headers?: Record<string, string>): Promise<string> {
const res = await this.fetch(
url,
{
headers: Object.assign({
Accept: accept,
}, headers ?? {}),
}
);
return await res.text();
}
}
@Injectable() @Injectable()
export class HttpRequestService { export class HttpRequestService {
/** public defaultFetcher: UndiciFetcher;
* Get http non-proxy agent public fetch: UndiciFetcher['fetch'];
*/ public getHtml: UndiciFetcher['getHtml'];
public defaultJsonFetcher: UndiciFetcher;
public getJson: UndiciFetcher['getJson'];
//#region for old http/https, only used in S3Service
// http non-proxy agent
private http: http.Agent; private http: http.Agent;
/** // https non-proxy agent
* Get https non-proxy agent
*/
private https: https.Agent; private https: https.Agent;
/** // http proxy or non-proxy agent
* Get http proxy or non-proxy agent
*/
public httpAgent: http.Agent; public httpAgent: http.Agent;
/** // https proxy or non-proxy agent
* Get https proxy or non-proxy agent
*/
public httpsAgent: https.Agent; public httpsAgent: https.Agent;
//#endregion
public readonly dnsCache: CacheableLookup;
public readonly clientDefaults: undici.Agent.Options;
private maxSockets: number;
private logger: Logger;
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
private config: Config, private config: Config,
private loggerService: LoggerService,
) { ) {
const cache = new CacheableLookup({ this.logger = this.loggerService.getLogger('http-request');
this.dnsCache = new CacheableLookup({
maxTtl: 3600, // 1hours maxTtl: 3600, // 1hours
errorTtl: 30, // 30secs errorTtl: 30, // 30secs
lookup: false, // nativeのdns.lookupにfallbackしない lookup: false, // nativeのdns.lookupにfallbackしない
}); });
this.clientDefaults = {
keepAliveTimeout: 30 * 1000,
keepAliveMaxTimeout: 10 * 60 * 1000,
keepAliveTimeoutThreshold: 1 * 1000,
strictContentLength: true,
headersTimeout: 10 * 1000,
bodyTimeout: 10 * 1000,
maxHeaderSize: 16364, // default
maxResponseSize: 10 * 1024 * 1024,
maxRedirections: 3,
connect: {
timeout: 10 * 1000, // コネクションが確立するまでのタイムアウト
maxCachedSessions: 300, // TLSセッションのキャッシュ数 https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L80
lookup: this.dnsCache.lookup as LookupFunction, // https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L98
},
}
this.maxSockets = Math.max(64, this.config.deliverJobConcurrency ?? 128);
this.defaultFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption(), this.logger);
this.fetch = this.defaultFetcher.fetch;
this.getHtml = this.defaultFetcher.getHtml;
this.defaultJsonFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption({
maxResponseSize: 1024 * 256,
}), this.logger);
this.getJson = this.defaultJsonFetcher.getJson;
//#region for old http/https, only used in S3Service
this.http = new http.Agent({ this.http = new http.Agent({
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
lookup: cache.lookup, lookup: this.dnsCache.lookup,
} as http.AgentOptions); } as http.AgentOptions);
this.https = new https.Agent({ this.https = new https.Agent({
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
lookup: cache.lookup, lookup: this.dnsCache.lookup,
} as https.AgentOptions); } as https.AgentOptions);
const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
this.httpAgent = config.proxy this.httpAgent = config.proxy
? new HttpProxyAgent({ ? new HttpProxyAgent({
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
maxSockets, maxSockets: this.maxSockets,
maxFreeSockets: 256, maxFreeSockets: 256,
scheduling: 'lifo', scheduling: 'lifo',
proxy: config.proxy, proxy: config.proxy,
@@ -72,21 +262,42 @@ export class HttpRequestService {
? new HttpsProxyAgent({ ? new HttpsProxyAgent({
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
maxSockets, maxSockets: this.maxSockets,
maxFreeSockets: 256, maxFreeSockets: 256,
scheduling: 'lifo', scheduling: 'lifo',
proxy: config.proxy, proxy: config.proxy,
}) })
: this.https; : this.https;
//#endregion
}
@bindThis
public getStandardUndiciFetcherOption(opts: undici.Agent.Options = {}, proxyOpts: undici.Agent.Options = {}) {
return {
agentOptions: {
...this.clientDefaults,
...opts,
},
...(this.config.proxy ? {
proxy: {
uri: this.config.proxy,
options: {
connections: this.maxSockets,
...proxyOpts,
}
}
} : {}),
userAgent: this.config.userAgent,
}
} }
/** /**
* Get agent by URL * Get http agent by URL
* @param url URL * @param url URL
* @param bypassProxy Allways bypass proxy * @param bypassProxy Allways bypass proxy
*/ */
@bindThis @bindThis
public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { public getHttpAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent {
if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) {
return url.protocol === 'http:' ? this.http : this.https; return url.protocol === 'http:' ? this.http : this.https;
} else { } else {
@@ -94,67 +305,37 @@ export class HttpRequestService {
} }
} }
/**
* check ip
*/
@bindThis @bindThis
public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record<string, string>): Promise<unknown> { public getConnectorWithIpCheck(connector: undici.buildConnector.connector, checkIp: IpChecker): undici.buildConnector.connectorAsync {
const res = await this.getResponse({ return (options, cb) => {
url, connector(options, (err, socket) => {
method: 'GET', this.logger.debug('Socket connector (with ip checker) called', socket);
headers: Object.assign({ if (err) {
'User-Agent': this.config.userAgent, this.logger.error(`Socket error`, err)
Accept: accept, cb(new Error(`Error while socket connecting\n${err}`), null);
}, headers ?? {}), return;
timeout, }
size: 1024 * 256,
});
return await res.json(); if (socket.remoteAddress == undefined) {
} this.logger.error(`Socket error: remoteAddress is undefined`);
cb(new Error('remoteAddress is undefined (maybe socket destroyed)'), null);
return;
}
@bindThis // allow
public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record<string, string>): Promise<string> { if (checkIp(socket.remoteAddress)) {
const res = await this.getResponse({ this.logger.debug(`Socket connected (ip ok): ${socket.localPort} => ${socket.remoteAddress}`);
url, cb(null, socket);
method: 'GET', return;
headers: Object.assign({ }
'User-Agent': this.config.userAgent,
Accept: accept,
}, headers ?? {}),
timeout,
});
return await res.text(); this.logger.error('IP is not allowed', socket);
} cb(new StatusError('IP is not allowed', 403, 'IP is not allowed'), null);
socket.destroy();
@bindThis });
public async getResponse(args: { };
url: string,
method: string,
body?: string,
headers: Record<string, string>,
timeout?: number,
size?: number,
}): Promise<Response> {
const timeout = args.timeout ?? 10 * 1000;
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, timeout * 6);
const res = await fetch(args.url, {
method: args.method,
headers: args.headers,
body: args.body,
timeout,
size: args.size ?? 10 * 1024 * 1024,
agent: (url) => this.getAgentByUrl(url),
signal: controller.signal,
});
if (!res.ok) {
throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText);
}
return res;
} }
} }

View File

@@ -4,8 +4,9 @@ import Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { Meta } from '@/models/entities/Meta.js'; import { Meta } from '@/models/entities/Meta.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class MetaService implements OnApplicationShutdown { export class MetaService implements OnApplicationShutdown {
@@ -40,7 +41,7 @@ export class MetaService 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; const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) { switch (type) {
case 'metaUpdated': { case 'metaUpdated': {
this.cache = body; this.cache = body;

View File

@@ -42,6 +42,7 @@ import { NoteReadService } from '@/core/NoteReadService.js';
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { RoleService } from '@/core/RoleService.js';
const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
@@ -186,6 +187,7 @@ export class NoteCreateService {
private remoteUserResolveService: RemoteUserResolveService, private remoteUserResolveService: RemoteUserResolveService,
private apDeliverManagerService: ApDeliverManagerService, private apDeliverManagerService: ApDeliverManagerService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private roleService: RoleService,
private notesChart: NotesChart, private notesChart: NotesChart,
private perUserNotesChart: PerUserNotesChart, private perUserNotesChart: PerUserNotesChart,
private activeUsersChart: ActiveUsersChart, private activeUsersChart: ActiveUsersChart,
@@ -197,7 +199,6 @@ export class NoteCreateService {
id: User['id']; id: User['id'];
username: User['username']; username: User['username'];
host: User['host']; host: User['host'];
isSilenced: User['isSilenced'];
createdAt: User['createdAt']; createdAt: User['createdAt'];
isBot: User['isBot']; isBot: User['isBot'];
}, data: Option, silent = false): Promise<Note> { }, data: Option, silent = false): Promise<Note> {
@@ -224,9 +225,10 @@ export class NoteCreateService {
if (data.channel != null) data.visibleUsers = []; if (data.channel != null) data.visibleUsers = [];
if (data.channel != null) data.localOnly = true; if (data.channel != null) data.localOnly = true;
// サイレンス if (data.visibility === 'public' && data.channel == null) {
if (user.isSilenced && data.visibility === 'public' && data.channel == null) { if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
data.visibility = 'home'; data.visibility = 'home';
}
} }
// Renote対象が「ホームまたは全体」以外の公開範囲ならreject // Renote対象が「ホームまたは全体」以外の公開範囲ならreject
@@ -418,7 +420,6 @@ export class NoteCreateService {
id: User['id']; id: User['id'];
username: User['username']; username: User['username'];
host: User['host']; host: User['host'];
isSilenced: User['isSilenced'];
createdAt: User['createdAt']; createdAt: User['createdAt'];
isBot: User['isBot']; isBot: User['isBot'];
}, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) {

View File

@@ -12,6 +12,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
@Injectable() @Injectable()
export class NotePiningService { export class NotePiningService {
@@ -30,6 +31,7 @@ export class NotePiningService {
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService, private idService: IdService,
private roleService: RoleService,
private relayService: RelayService, private relayService: RelayService,
private apDeliverManagerService: ApDeliverManagerService, private apDeliverManagerService: ApDeliverManagerService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
@@ -55,7 +57,7 @@ export class NotePiningService {
const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id }); const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id });
if (pinings.length >= 5) { if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) {
throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.');
} }

View File

@@ -0,0 +1,298 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { Cache } from '@/misc/cache.js';
import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { MetaService } from '@/core/MetaService.js';
import { UserCacheService } from '@/core/UserCacheService.js';
import type { RoleCondFormulaValue } from '@/models/entities/Role.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common';
export type RolePolicies = {
gtlAvailable: boolean;
ltlAvailable: boolean;
canPublicNote: boolean;
canInvite: boolean;
canManageCustomEmojis: boolean;
canHideAds: boolean;
driveCapacityMb: number;
pinLimit: number;
antennaLimit: number;
wordMuteLimit: number;
webhookLimit: number;
clipLimit: number;
noteEachClipsLimit: number;
userListLimit: number;
userEachUserListsLimit: number;
rateLimitFactor: number;
};
export const DEFAULT_POLICIES: RolePolicies = {
gtlAvailable: true,
ltlAvailable: true,
canPublicNote: true,
canInvite: false,
canManageCustomEmojis: false,
canHideAds: false,
driveCapacityMb: 100,
pinLimit: 5,
antennaLimit: 5,
wordMuteLimit: 200,
webhookLimit: 3,
clipLimit: 10,
noteEachClipsLimit: 200,
userListLimit: 10,
userEachUserListsLimit: 50,
rateLimitFactor: 1,
};
@Injectable()
export class RoleService implements OnApplicationShutdown {
private rolesCache: Cache<Role[]>;
private roleAssignmentByUserIdCache: Cache<RoleAssignment[]>;
constructor(
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.rolesRepository)
private rolesRepository: RolesRepository,
@Inject(DI.roleAssignmentsRepository)
private roleAssignmentsRepository: RoleAssignmentsRepository,
private metaService: MetaService,
private userCacheService: UserCacheService,
private userEntityService: UserEntityService,
) {
//this.onMessage = this.onMessage.bind(this);
this.rolesCache = new Cache<Role[]>(Infinity);
this.roleAssignmentByUserIdCache = new Cache<RoleAssignment[]>(Infinity);
this.redisSubscriber.on('message', this.onMessage);
}
@bindThis
private async onMessage(_: string, data: string): Promise<void> {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {
const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) {
case 'roleCreated': {
const cached = this.rolesCache.get(null);
if (cached) {
body.createdAt = new Date(body.createdAt);
body.updatedAt = new Date(body.updatedAt);
body.lastUsedAt = new Date(body.lastUsedAt);
cached.push(body);
}
break;
}
case 'roleUpdated': {
const cached = this.rolesCache.get(null);
if (cached) {
const i = cached.findIndex(x => x.id === body.id);
if (i > -1) {
body.createdAt = new Date(body.createdAt);
body.updatedAt = new Date(body.updatedAt);
body.lastUsedAt = new Date(body.lastUsedAt);
cached[i] = body;
}
}
break;
}
case 'roleDeleted': {
const cached = this.rolesCache.get(null);
if (cached) {
this.rolesCache.set(null, cached.filter(x => x.id !== body.id));
}
break;
}
case 'userRoleAssigned': {
const cached = this.roleAssignmentByUserIdCache.get(body.userId);
if (cached) {
body.createdAt = new Date(body.createdAt);
cached.push(body);
}
break;
}
case 'userRoleUnassigned': {
const cached = this.roleAssignmentByUserIdCache.get(body.userId);
if (cached) {
this.roleAssignmentByUserIdCache.set(body.userId, cached.filter(x => x.id !== body.id));
}
break;
}
default:
break;
}
}
}
@bindThis
private evalCond(user: User, value: RoleCondFormulaValue): boolean {
try {
switch (value.type) {
case 'and': {
return value.values.every(v => this.evalCond(user, v));
}
case 'or': {
return value.values.some(v => this.evalCond(user, v));
}
case 'not': {
return !this.evalCond(user, value.value);
}
case 'isLocal': {
return this.userEntityService.isLocalUser(user);
}
case 'isRemote': {
return this.userEntityService.isRemoteUser(user);
}
case 'createdLessThan': {
return user.createdAt.getTime() > (Date.now() - (value.sec * 1000));
}
case 'createdMoreThan': {
return user.createdAt.getTime() < (Date.now() - (value.sec * 1000));
}
case 'followersLessThanOrEq': {
return user.followersCount <= value.value;
}
case 'followersMoreThanOrEq': {
return user.followersCount >= value.value;
}
case 'followingLessThanOrEq': {
return user.followingCount <= value.value;
}
case 'followingMoreThanOrEq': {
return user.followingCount >= value.value;
}
default:
return false;
}
} catch (err) {
// TODO: log error
return false;
}
}
@bindThis
public async getUserRoles(userId: User['id']) {
const assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
const assignedRoleIds = assigns.map(x => x.roleId);
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const assignedRoles = roles.filter(r => assignedRoleIds.includes(r.id));
const user = roles.some(r => r.target === 'conditional') ? await this.userCacheService.findById(userId) : null;
const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
return [...assignedRoles, ...matchedCondRoles];
}
@bindThis
public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> {
const meta = await this.metaService.fetch();
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
if (userId == null) return basePolicies;
const roles = await this.getUserRoles(userId);
function calc<T extends keyof RolePolicies>(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) {
if (roles.length === 0) return basePolicies[name];
const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true });
const p2 = policies.filter(policy => policy.priority === 2);
if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
const p1 = policies.filter(policy => policy.priority === 1);
if (p1.length > 0) return aggregate(p1.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
}
return {
gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
canInvite: calc('canInvite', vs => vs.some(v => v === true)),
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),
webhookLimit: calc('webhookLimit', vs => Math.max(...vs)),
clipLimit: calc('clipLimit', vs => Math.max(...vs)),
noteEachClipsLimit: calc('noteEachClipsLimit', vs => Math.max(...vs)),
userListLimit: calc('userListLimit', vs => Math.max(...vs)),
userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)),
rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)),
};
}
@bindThis
public async isModerator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> {
if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
}
@bindThis
public async isAdministrator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> {
if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
}
@bindThis
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> {
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
roleId: In(moderatorRoles.map(r => r.id)),
}) : [];
// TODO: isRootなアカウントも含める
return assigns.map(a => a.userId);
}
@bindThis
public async getModerators(includeAdmins = true): Promise<User[]> {
const ids = await this.getModeratorIds(includeAdmins);
const users = ids.length > 0 ? await this.usersRepository.findBy({
id: In(ids),
}) : [];
return users;
}
@bindThis
public async getAdministratorIds(): Promise<User['id'][]> {
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const administratorRoles = roles.filter(r => r.isAdministrator);
const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
roleId: In(administratorRoles.map(r => r.id)),
}) : [];
// TODO: isRootなアカウントも含める
return assigns.map(a => a.userId);
}
@bindThis
public async getAdministrators(): Promise<User[]> {
const ids = await this.getAdministratorIds();
const users = ids.length > 0 ? await this.usersRepository.findBy({
id: In(ids),
}) : [];
return users;
}
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onMessage);
}
}

View File

@@ -33,7 +33,7 @@ export class S3Service {
? false ? false
: meta.objectStorageS3ForcePathStyle, : meta.objectStorageS3ForcePathStyle,
httpOptions: { httpOptions: {
agent: this.httpRequestService.getAgentByUrl(new URL(u), !meta.objectStorageUseProxy), agent: this.httpRequestService.getHttpAgentByUrl(new URL(u), !meta.objectStorageUseProxy),
}, },
}); });
} }

View File

@@ -11,10 +11,10 @@ import { IdService } from '@/core/IdService.js';
import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UserKeypair } from '@/models/entities/UserKeypair.js';
import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js';
import generateUserToken from '@/misc/generate-native-user-token.js'; import generateUserToken from '@/misc/generate-native-user-token.js';
import UsersChart from './chart/charts/users.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { UtilityService } from './UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import UsersChart from './chart/charts/users.js';
import { UtilityService } from './UtilityService.js';
@Injectable() @Injectable()
export class SignupService { export class SignupService {
@@ -112,7 +112,7 @@ export class SignupService {
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: this.utilityService.toPunyNullable(host), host: this.utilityService.toPunyNullable(host),
token: secret, token: secret,
isAdmin: (await this.usersRepository.countBy({ isRoot: (await this.usersRepository.countBy({
host: IsNull(), host: IsNull(),
})) === 0, })) === 0,
})); }));

View File

@@ -2,11 +2,12 @@ import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis'; import Redis from 'ioredis';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import { Cache } from '@/misc/cache.js'; import { Cache } from '@/misc/cache.js';
import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/User.js'; import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class UserCacheService implements OnApplicationShutdown { export class UserCacheService implements OnApplicationShutdown {
@@ -39,11 +40,9 @@ export class UserCacheService 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; const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) { switch (type) {
case 'userChangeSuspendedState': case 'userChangeSuspendedState':
case 'userChangeSilencedState':
case 'userChangeModeratorState':
case 'remoteUserUpdated': { case 'remoteUserUpdated': {
const user = await this.usersRepository.findOneByOrFail({ id: body.id }); const user = await this.usersRepository.findOneByOrFail({ id: body.id });
this.userByIdCache.set(user.id, user); this.userByIdCache.set(user.id, user);
@@ -64,12 +63,24 @@ export class UserCacheService implements OnApplicationShutdown {
this.localUserByNativeTokenCache.set(body.newToken, user); this.localUserByNativeTokenCache.set(body.newToken, user);
break; break;
} }
case 'follow': {
const follower = this.userByIdCache.get(body.followerId);
if (follower) follower.followingCount++;
const followee = this.userByIdCache.get(body.followeeId);
if (followee) followee.followersCount++;
break;
}
default: default:
break; break;
} }
} }
} }
@bindThis
public findById(userId: User['id']) {
return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId }));
}
@bindThis @bindThis
public onApplicationShutdown(signal?: string | undefined) { public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onMessage); this.redisSubscriber.off('message', this.onMessage);

View File

@@ -62,6 +62,7 @@ export class UserFollowingService {
private federatedInstanceService: FederatedInstanceService, private federatedInstanceService: FederatedInstanceService,
private webhookService: WebhookService, private webhookService: WebhookService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private globalEventService: GlobalEventService,
private perUserFollowingChart: PerUserFollowingChart, private perUserFollowingChart: PerUserFollowingChart,
private instanceChart: InstanceChart, private instanceChart: InstanceChart,
) { ) {
@@ -195,6 +196,8 @@ export class UserFollowingService {
} }
if (alreadyFollowed) return; if (alreadyFollowed) return;
this.globalEventService.publishInternalEvent('follow', { followerId: follower.id, followeeId: followee.id });
//#region Increment counts //#region Increment counts
await Promise.all([ await Promise.all([
@@ -314,6 +317,8 @@ export class UserFollowingService {
follower: {id: User['id']; host: User['host']; }, follower: {id: User['id']; host: User['host']; },
followee: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; },
): Promise<void> { ): Promise<void> {
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
//#region Decrement following / followers counts //#region Decrement following / followers counts
await Promise.all([ await Promise.all([
this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1), this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1),

View File

@@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
@Injectable() @Injectable()
export class UserListService { export class UserListService {
@@ -23,13 +24,21 @@ export class UserListService {
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService, private idService: IdService,
private userFollowingService: UserFollowingService, private userFollowingService: UserFollowingService,
private roleService: RoleService,
private globalEventServie: GlobalEventService, private globalEventServie: GlobalEventService,
private proxyAccountService: ProxyAccountService, private proxyAccountService: ProxyAccountService,
) { ) {
} }
@bindThis @bindThis
public async push(target: User, list: UserList) { public async push(target: User, list: UserList, me: User) {
const currentCount = await this.userListJoiningsRepository.countBy({
userListId: list.id,
});
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
throw new Error('Too many users');
}
await this.userListJoiningsRepository.insert({ await this.userListJoiningsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),

View File

@@ -24,6 +24,12 @@ export class UtilityService {
return this.toPuny(this.config.host) === this.toPuny(host); return this.toPuny(this.config.host) === this.toPuny(host);
} }
@bindThis
public isBlockedHost(blockedHosts: string[], host: string | null): boolean {
if (host == null) return false;
return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}
@bindThis @bindThis
public extractDbHost(uri: string): string { public extractDbHost(uri: string): string {
const url = new URL(uri); const url = new URL(uri);

View File

@@ -30,7 +30,7 @@ export class WebfingerService {
public async webfinger(query: string): Promise<IWebFinger> { public async webfinger(query: string): Promise<IWebFinger> {
const url = this.genUrl(query); const url = this.genUrl(query);
return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; return await this.httpRequestService.getJson<IWebFinger>(url, 'application/jrd+json, application/json');
} }
@bindThis @bindThis

View File

@@ -3,8 +3,9 @@ import Redis from 'ioredis';
import type { WebhooksRepository } from '@/models/index.js'; import type { WebhooksRepository } from '@/models/index.js';
import type { Webhook } from '@/models/entities/Webhook.js'; import type { Webhook } from '@/models/entities/Webhook.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class WebhookService implements OnApplicationShutdown { export class WebhookService implements OnApplicationShutdown {
@@ -39,7 +40,7 @@ export class WebhookService 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; const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) { switch (type) {
case 'webhookCreated': case 'webhookCreated':
if (body.active) { if (body.active) {

View File

@@ -159,7 +159,7 @@ export class ApDbResolverService {
if (key == null) return null; if (key == null) return null;
return { return {
user: await this.userCacheService.userByIdCache.fetch(key.userId, () => this.usersRepository.findOneByOrFail({ id: key.userId })) as CacheableRemoteUser, user: await this.userCacheService.findById(key.userId) as CacheableRemoteUser,
key, key,
}; };
} }

View File

@@ -291,7 +291,7 @@ export class ApInboxService {
// アナウンス先をブロックしてたら中断 // アナウンス先をブロックしてたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return; if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) return;
const unlock = await this.appLockService.getApLock(uri); const unlock = await this.appLockService.getApLock(uri);

View File

@@ -5,8 +5,10 @@ import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { User } from '@/models/entities/User.js'; import type { User } from '@/models/entities/User.js';
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type Logger from '@/logger.js';
type Request = { type Request = {
url: string; url: string;
@@ -28,13 +30,21 @@ type PrivateKey = {
@Injectable() @Injectable()
export class ApRequestService { export class ApRequestService {
private undiciFetcher: UndiciFetcher;
private logger: Logger;
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
private config: Config, private config: Config,
private userKeypairStoreService: UserKeypairStoreService, private userKeypairStoreService: UserKeypairStoreService,
private httpRequestService: HttpRequestService, private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
) { ) {
this.logger = this.loggerService?.getLogger('ap-request'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({
maxRedirections: 0,
}), this.logger );
} }
@bindThis @bindThis
@@ -148,16 +158,17 @@ export class ApRequestService {
url, url,
body, body,
additionalHeaders: { additionalHeaders: {
'User-Agent': this.config.userAgent,
}, },
}); });
await this.httpRequestService.getResponse({ await this.undiciFetcher.fetch(
url, url,
method: req.request.method, {
headers: req.request.headers, method: req.request.method,
body, headers: req.request.headers,
}); body,
}
);
} }
/** /**
@@ -176,15 +187,16 @@ export class ApRequestService {
}, },
url, url,
additionalHeaders: { additionalHeaders: {
'User-Agent': this.config.userAgent,
}, },
}); });
const res = await this.httpRequestService.getResponse({ const res = await this.httpRequestService.fetch(
url, url,
method: req.request.method, {
headers: req.request.headers, method: req.request.method,
}); headers: req.request.headers,
}
);
return await res.json(); return await res.json();
} }

View File

@@ -4,7 +4,7 @@ import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@@ -12,11 +12,15 @@ 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 { LoggerService } from '@/core/LoggerService.js';
import type { IObject, ICollection, IOrderedCollection } from './type.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js';
import type Logger from '@/logger.js';
export class Resolver { export class Resolver {
private history: Set<string>; private history: Set<string>;
private user?: ILocalUser; private user?: ILocalUser;
private undiciFetcher: UndiciFetcher;
private logger: Logger;
constructor( constructor(
private config: Config, private config: Config,
@@ -31,9 +35,14 @@ export class Resolver {
private httpRequestService: HttpRequestService, private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService, private apDbResolverService: ApDbResolverService,
private loggerService: LoggerService,
private recursionLimit = 100, private recursionLimit = 100,
) { ) {
this.history = new Set(); this.history = new Set();
this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({
maxRedirections: 0,
}), this.logger);
} }
@bindThis @bindThis
@@ -87,7 +96,7 @@ export class Resolver {
} }
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (meta.blockedHosts.includes(host)) { if (this.utilityService.isBlockedHost(meta.blockedHosts, host)) {
throw new Error('Instance is blocked'); throw new Error('Instance is blocked');
} }
@@ -96,8 +105,8 @@ export class Resolver {
} }
const object = (this.user const object = (this.user
? await this.apRequestService.signedGet(value, this.user) ? await this.apRequestService.signedGet(value, this.user) as IObject
: await this.httpRequestService.getJson(value, 'application/activity+json, application/ld+json')) as IObject; : await this.undiciFetcher.getJson<IObject>(value, 'application/activity+json, application/ld+json'));
if (object == null || ( if (object == null || (
Array.isArray(object['@context']) ? Array.isArray(object['@context']) ?

View File

@@ -1,6 +1,5 @@
import * as crypto from 'node:crypto'; import * as crypto from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import fetch from 'node-fetch';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { CONTEXTS } from './misc/contexts.js'; import { CONTEXTS } from './misc/contexts.js';
@@ -116,14 +115,19 @@ class LdSignature {
@bindThis @bindThis
private async fetchDocument(url: string) { private async fetchDocument(url: string) {
const json = await fetch(url, { const json = await this.httpRequestService.fetch(
headers: { url,
Accept: 'application/ld+json, application/json', {
headers: {
Accept: 'application/ld+json, application/json',
},
// TODO
//timeout: this.loderTimeout,
}, },
// TODO {
//timeout: this.loderTimeout, noOkError: true,
agent: u => u.protocol === 'http:' ? this.httpRequestService.httpAgent : this.httpRequestService.httpsAgent, }
}).then(res => { ).then(res => {
if (!res.ok) { if (!res.ok) {
throw `${res.status} ${res.statusText}`; throw `${res.status} ${res.statusText}`;
} else { } else {

View File

@@ -324,7 +324,7 @@ export class ApNoteService {
// ブロックしてたら中断 // ブロックしてたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) throw { statusCode: 451 }; if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) throw { statusCode: 451 };
const unlock = await this.appLockService.getApLock(uri); const unlock = await this.appLockService.getApLock(uri);

View File

@@ -61,21 +61,21 @@ export default class FederationChart extends Chart<typeof schema> {
this.followingsRepository.createQueryBuilder('following') this.followingsRepository.createQueryBuilder('following')
.select('COUNT(DISTINCT following.followeeHost)') .select('COUNT(DISTINCT following.followeeHost)')
.where('following.followeeHost IS NOT NULL') .where('following.followeeHost IS NOT NULL')
.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
.andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`)
.getRawOne() .getRawOne()
.then(x => parseInt(x.count, 10)), .then(x => parseInt(x.count, 10)),
this.followingsRepository.createQueryBuilder('following') this.followingsRepository.createQueryBuilder('following')
.select('COUNT(DISTINCT following.followerHost)') .select('COUNT(DISTINCT following.followerHost)')
.where('following.followerHost IS NOT NULL') .where('following.followerHost IS NOT NULL')
.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
.andWhere(`following.followerHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .andWhere(`following.followerHost NOT IN (${ suspendedInstancesQuery.getQuery() })`)
.getRawOne() .getRawOne()
.then(x => parseInt(x.count, 10)), .then(x => parseInt(x.count, 10)),
this.followingsRepository.createQueryBuilder('following') this.followingsRepository.createQueryBuilder('following')
.select('COUNT(DISTINCT following.followeeHost)') .select('COUNT(DISTINCT following.followeeHost)')
.where('following.followeeHost IS NOT NULL') .where('following.followeeHost IS NOT NULL')
.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
.andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`)
.andWhere(`following.followeeHost IN (${ pubsubSubQuery.getQuery() })`) .andWhere(`following.followeeHost IN (${ pubsubSubQuery.getQuery() })`)
.setParameters(pubsubSubQuery.getParameters()) .setParameters(pubsubSubQuery.getParameters())
@@ -84,7 +84,7 @@ export default class FederationChart extends Chart<typeof schema> {
this.instancesRepository.createQueryBuilder('instance') this.instancesRepository.createQueryBuilder('instance')
.select('COUNT(instance.id)') .select('COUNT(instance.id)')
.where(`instance.host IN (${ subInstancesQuery.getQuery() })`) .where(`instance.host IN (${ subInstancesQuery.getQuery() })`)
.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
.andWhere('instance.isSuspended = false') .andWhere('instance.isSuspended = false')
.andWhere('instance.isNotResponding = false') .andWhere('instance.isNotResponding = false')
.getRawOne() .getRawOne()
@@ -92,7 +92,7 @@ export default class FederationChart extends Chart<typeof schema> {
this.instancesRepository.createQueryBuilder('instance') this.instancesRepository.createQueryBuilder('instance')
.select('COUNT(instance.id)') .select('COUNT(instance.id)')
.where(`instance.host IN (${ pubInstancesQuery.getQuery() })`) .where(`instance.host IN (${ pubInstancesQuery.getQuery() })`)
.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
.andWhere('instance.isSuspended = false') .andWhere('instance.isSuspended = false')
.andWhere('instance.isNotResponding = false') .andWhere('instance.isNotResponding = false')
.getRawOne() .getRawOne()

View File

@@ -7,8 +7,8 @@ import type { } from '@/models/entities/Blocking.js';
import type { User } from '@/models/entities/User.js'; import type { User } from '@/models/entities/User.js';
import type { Instance } from '@/models/entities/Instance.js'; import type { Instance } from '@/models/entities/Instance.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '../UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
export class InstanceEntityService { export class InstanceEntityService {
@@ -17,6 +17,8 @@ export class InstanceEntityService {
private instancesRepository: InstancesRepository, private instancesRepository: InstancesRepository,
private metaService: MetaService, private metaService: MetaService,
private utilityService: UtilityService,
) { ) {
} }
@@ -27,7 +29,7 @@ export class InstanceEntityService {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
return { return {
id: instance.id, id: instance.id,
caughtAt: instance.caughtAt.toISOString(), firstRetrievedAt: instance.firstRetrievedAt.toISOString(),
host: instance.host, host: instance.host,
usersCount: instance.usersCount, usersCount: instance.usersCount,
notesCount: instance.notesCount, notesCount: instance.notesCount,
@@ -35,7 +37,7 @@ export class InstanceEntityService {
followersCount: instance.followersCount, followersCount: instance.followersCount,
isNotResponding: instance.isNotResponding, isNotResponding: instance.isNotResponding,
isSuspended: instance.isSuspended, isSuspended: instance.isSuspended,
isBlocked: meta.blockedHosts.includes(instance.host), isBlocked: this.utilityService.isBlockedHost(meta.blockedHosts, instance.host),
softwareName: instance.softwareName, softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion, softwareVersion: instance.softwareVersion,
openRegistrations: instance.openRegistrations, openRegistrations: instance.openRegistrations,

View File

@@ -0,0 +1,84 @@
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import type { Packed } from '@/misc/schema.js';
import type { User } from '@/models/entities/User.js';
import type { Role } from '@/models/entities/Role.js';
import { bindThis } from '@/decorators.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable()
export class RoleEntityService {
constructor(
@Inject(DI.rolesRepository)
private rolesRepository: RolesRepository,
@Inject(DI.roleAssignmentsRepository)
private roleAssignmentsRepository: RoleAssignmentsRepository,
private userEntityService: UserEntityService,
) {
}
@bindThis
public async pack(
src: Role['id'] | Role,
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: boolean;
},
) {
const opts = Object.assign({
detail: true,
}, options);
const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src });
const assigns = await this.roleAssignmentsRepository.findBy({
roleId: role.id,
});
const policies = { ...role.policies };
for (const [k, v] of Object.entries(DEFAULT_POLICIES)) {
if (policies[k] == null) policies[k] = {
useDefault: true,
priority: 0,
value: v,
};
}
return await awaitAll({
id: role.id,
createdAt: role.createdAt.toISOString(),
updatedAt: role.updatedAt.toISOString(),
name: role.name,
description: role.description,
color: role.color,
target: role.target,
condFormula: role.condFormula,
isPublic: role.isPublic,
isAdministrator: role.isAdministrator,
isModerator: role.isModerator,
canEditMembersByModerator: role.canEditMembersByModerator,
policies: policies,
usersCount: assigns.length,
...(opts.detail ? {
users: this.userEntityService.packMany(assigns.map(x => x.userId), me),
} : {}),
});
}
@bindThis
public packMany(
roles: any[],
me: { id: User['id'] },
options?: {
detail?: boolean;
},
) {
return Promise.all(roles.map(x => this.pack(x, me, options)));
}
}

View File

@@ -13,6 +13,8 @@ import type { Instance } from '@/models/entities/Instance.js';
import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js';
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository } from '@/models/index.js'; import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import type { OnModuleInit } from '@nestjs/common'; import type { OnModuleInit } from '@nestjs/common';
import type { AntennaService } from '../AntennaService.js'; import type { AntennaService } from '../AntennaService.js';
import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js';
@@ -41,7 +43,6 @@ function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & {
function isRemoteUser(user: User | { host: User['host'] }): boolean { function isRemoteUser(user: User | { host: User['host'] }): boolean {
return !isLocalUser(user); return !isLocalUser(user);
} }
import { bindThis } from '@/decorators.js';
@Injectable() @Injectable()
export class UserEntityService implements OnModuleInit { export class UserEntityService implements OnModuleInit {
@@ -50,6 +51,7 @@ export class UserEntityService implements OnModuleInit {
private pageEntityService: PageEntityService; private pageEntityService: PageEntityService;
private customEmojiService: CustomEmojiService; private customEmojiService: CustomEmojiService;
private antennaService: AntennaService; private antennaService: AntennaService;
private roleService: RoleService;
private userInstanceCache: Cache<Instance | null>; private userInstanceCache: Cache<Instance | null>;
constructor( constructor(
@@ -120,6 +122,7 @@ export class UserEntityService implements OnModuleInit {
//private pageEntityService: PageEntityService, //private pageEntityService: PageEntityService,
//private customEmojiService: CustomEmojiService, //private customEmojiService: CustomEmojiService,
//private antennaService: AntennaService, //private antennaService: AntennaService,
//private roleService: RoleService,
) { ) {
this.userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3); this.userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
} }
@@ -130,6 +133,7 @@ export class UserEntityService implements OnModuleInit {
this.pageEntityService = this.moduleRef.get('PageEntityService'); this.pageEntityService = this.moduleRef.get('PageEntityService');
this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService');
this.antennaService = this.moduleRef.get('AntennaService'); this.antennaService = this.moduleRef.get('AntennaService');
this.roleService = this.moduleRef.get('RoleService');
} }
//#region Validators //#region Validators
@@ -383,6 +387,9 @@ export class UserEntityService implements OnModuleInit {
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
null; null;
const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null;
const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null;
const falsy = opts.detail ? false : undefined; const falsy = opts.detail ? false : undefined;
const packed = { const packed = {
@@ -392,8 +399,6 @@ export class UserEntityService implements OnModuleInit {
host: user.host, host: user.host,
avatarUrl: this.getAvatarUrlSync(user), avatarUrl: this.getAvatarUrlSync(user),
avatarBlurhash: user.avatar?.blurhash ?? null, avatarBlurhash: user.avatar?.blurhash ?? null,
isAdmin: user.isAdmin ?? falsy,
isModerator: user.isModerator ?? falsy,
isBot: user.isBot ?? falsy, isBot: user.isBot ?? falsy,
isCat: user.isCat ?? falsy, isCat: user.isCat ?? falsy,
instance: user.host ? this.userInstanceCache.fetch(user.host, instance: user.host ? this.userInstanceCache.fetch(user.host,
@@ -418,7 +423,7 @@ export class UserEntityService implements OnModuleInit {
bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null,
bannerBlurhash: user.banner?.blurhash ?? null, bannerBlurhash: user.banner?.blurhash ?? null,
isLocked: user.isLocked, isLocked: user.isLocked,
isSilenced: user.isSilenced ?? falsy, isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
isSuspended: user.isSuspended ?? falsy, isSuspended: user.isSuspended ?? falsy,
description: profile!.description, description: profile!.description,
location: profile!.location, location: profile!.location,
@@ -443,14 +448,21 @@ export class UserEntityService implements OnModuleInit {
userId: user.id, userId: user.id,
}).then(result => result >= 1) }).then(result => result >= 1)
: false, : false,
...(isMe || opts.includeSecrets ? { roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).map(role => ({
driveCapacityOverrideMb: user.driveCapacityOverrideMb, id: role.id,
} : {}), name: role.name,
color: role.color,
description: role.description,
isModerator: role.isModerator,
isAdministrator: role.isAdministrator,
}))),
} : {}), } : {}),
...(opts.detail && isMe ? { ...(opts.detail && isMe ? {
avatarId: user.avatarId, avatarId: user.avatarId,
bannerId: user.bannerId, bannerId: user.bannerId,
isModerator: isModerator,
isAdmin: isAdmin,
injectFeaturedNote: profile!.injectFeaturedNote, injectFeaturedNote: profile!.injectFeaturedNote,
receiveAnnouncementEmail: profile!.receiveAnnouncementEmail, receiveAnnouncementEmail: profile!.receiveAnnouncementEmail,
alwaysMarkNsfw: profile!.alwaysMarkNsfw, alwaysMarkNsfw: profile!.alwaysMarkNsfw,
@@ -484,6 +496,7 @@ export class UserEntityService implements OnModuleInit {
} : {}), } : {}),
...(opts.includeSecrets ? { ...(opts.includeSecrets ? {
policies: this.roleService.getUserPolicies(user.id),
email: profile!.email, email: profile!.email,
emailVerified: profile!.emailVerified, emailVerified: profile!.emailVerified,
securityKeysList: profile!.twoFactorEnabled securityKeysList: profile!.twoFactorEnabled

View File

@@ -69,6 +69,8 @@ export const DI = {
adsRepository: Symbol('adsRepository'), adsRepository: Symbol('adsRepository'),
passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'),
retentionAggregationsRepository: Symbol('retentionAggregationsRepository'), retentionAggregationsRepository: Symbol('retentionAggregationsRepository'),
rolesRepository: Symbol('rolesRepository'),
roleAssignmentsRepository: Symbol('roleAssignmentsRepository'),
flashsRepository: Symbol('flashsRepository'), flashsRepository: Symbol('flashsRepository'),
flashLikesRepository: Symbol('flashLikesRepository'), flashLikesRepository: Symbol('flashLikesRepository'),
//#endregion //#endregion

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash } from './index.js'; import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js';
import type { DataSource } from 'typeorm'; import type { DataSource } from 'typeorm';
import type { Provider } from '@nestjs/common'; import type { Provider } from '@nestjs/common';
@@ -400,6 +400,18 @@ const $flashLikesRepository: Provider = {
inject: [DI.db], inject: [DI.db],
}; };
const $rolesRepository: Provider = {
provide: DI.rolesRepository,
useFactory: (db: DataSource) => db.getRepository(Role),
inject: [DI.db],
};
const $roleAssignmentsRepository: Provider = {
provide: DI.roleAssignmentsRepository,
useFactory: (db: DataSource) => db.getRepository(RoleAssignment),
inject: [DI.db],
};
@Module({ @Module({
imports: [ imports: [
], ],
@@ -468,6 +480,8 @@ const $flashLikesRepository: Provider = {
$adsRepository, $adsRepository,
$passwordResetRequestsRepository, $passwordResetRequestsRepository,
$retentionAggregationsRepository, $retentionAggregationsRepository,
$rolesRepository,
$roleAssignmentsRepository,
$flashsRepository, $flashsRepository,
$flashLikesRepository, $flashLikesRepository,
], ],
@@ -536,6 +550,8 @@ const $flashLikesRepository: Provider = {
$adsRepository, $adsRepository,
$passwordResetRequestsRepository, $passwordResetRequestsRepository,
$retentionAggregationsRepository, $retentionAggregationsRepository,
$rolesRepository,
$roleAssignmentsRepository,
$flashsRepository, $flashsRepository,
$flashLikesRepository, $flashLikesRepository,
], ],

View File

@@ -13,7 +13,7 @@ export class Instance {
@Column('timestamp with time zone', { @Column('timestamp with time zone', {
comment: 'The caught date of the Instance.', comment: 'The caught date of the Instance.',
}) })
public caughtAt: Date; public firstRetrievedAt: Date;
/** /**
* ホスト * ホスト

View File

@@ -42,16 +42,6 @@ export class Meta {
}) })
public disableRegistration: boolean; public disableRegistration: boolean;
@Column('boolean', {
default: false,
})
public disableLocalTimeline: boolean;
@Column('boolean', {
default: false,
})
public disableGlobalTimeline: boolean;
@Column('boolean', { @Column('boolean', {
default: false, default: false,
}) })
@@ -227,18 +217,6 @@ export class Meta {
}) })
public enableSensitiveMediaDetectionForVideos: boolean; public enableSensitiveMediaDetectionForVideos: boolean;
@Column('integer', {
default: 1024,
comment: 'Drive capacity of a local user (MB)',
})
public localDriveCapacityMb: number;
@Column('integer', {
default: 32,
comment: 'Drive capacity of a remote user (MB)',
})
public remoteDriveCapacityMb: number;
@Column('varchar', { @Column('varchar', {
length: 128, length: 128,
nullable: true, nullable: true,
@@ -476,4 +454,9 @@ export class Meta {
default: true, default: true,
}) })
public enableActiveEmailValidation: boolean; public enableActiveEmailValidation: boolean;
@Column('jsonb', {
default: { },
})
public policies: Record<string, any>;
} }

View File

@@ -24,7 +24,7 @@ export class Poll {
public multiple: boolean; public multiple: boolean;
@Column('varchar', { @Column('varchar', {
length: 128, array: true, default: '{}', length: 256, array: true, default: '{}',
}) })
public choices: string[]; public choices: string[];

View File

@@ -0,0 +1,144 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { id } from '../id.js';
type CondFormulaValueAnd = {
type: 'and';
values: RoleCondFormulaValue[];
};
type CondFormulaValueOr = {
type: 'or';
values: RoleCondFormulaValue[];
};
type CondFormulaValueNot = {
type: 'not';
value: RoleCondFormulaValue;
};
type CondFormulaValueIsLocal = {
type: 'isLocal';
};
type CondFormulaValueIsRemote = {
type: 'isRemote';
};
type CondFormulaValueCreatedLessThan = {
type: 'createdLessThan';
sec: number;
};
type CondFormulaValueCreatedMoreThan = {
type: 'createdMoreThan';
sec: number;
};
type CondFormulaValueFollowersLessThanOrEq = {
type: 'followersLessThanOrEq';
value: number;
};
type CondFormulaValueFollowersMoreThanOrEq = {
type: 'followersMoreThanOrEq';
value: number;
};
type CondFormulaValueFollowingLessThanOrEq = {
type: 'followingLessThanOrEq';
value: number;
};
type CondFormulaValueFollowingMoreThanOrEq = {
type: 'followingMoreThanOrEq';
value: number;
};
export type RoleCondFormulaValue =
CondFormulaValueAnd |
CondFormulaValueOr |
CondFormulaValueNot |
CondFormulaValueIsLocal |
CondFormulaValueIsRemote |
CondFormulaValueCreatedLessThan |
CondFormulaValueCreatedMoreThan |
CondFormulaValueFollowersLessThanOrEq |
CondFormulaValueFollowersMoreThanOrEq |
CondFormulaValueFollowingLessThanOrEq |
CondFormulaValueFollowingMoreThanOrEq;
@Entity()
export class Role {
@PrimaryColumn(id())
public id: string;
@Column('timestamp with time zone', {
comment: 'The created date of the Role.',
})
public createdAt: Date;
@Column('timestamp with time zone', {
comment: 'The updated date of the Role.',
})
public updatedAt: Date;
@Column('timestamp with time zone', {
comment: 'The last used date of the Role.',
})
public lastUsedAt: Date;
@Column('varchar', {
length: 256,
})
public name: string;
@Column('varchar', {
length: 1024,
})
public description: string;
@Column('varchar', {
length: 256, nullable: true,
})
public color: string | null;
@Column('enum', {
enum: ['manual', 'conditional'],
default: 'manual',
})
public target: 'manual' | 'conditional';
@Column('jsonb', {
default: { },
})
public condFormula: RoleCondFormulaValue;
@Column('boolean', {
default: false,
})
public isPublic: boolean;
@Column('boolean', {
default: false,
})
public isModerator: boolean;
@Column('boolean', {
default: false,
})
public isAdministrator: boolean;
@Column('boolean', {
default: false,
})
public canEditMembersByModerator: boolean;
@Column('jsonb', {
default: { },
})
public policies: Record<string, {
useDefault: boolean;
priority: number;
value: any;
}>;
}

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