Compare commits

..

84 Commits

Author SHA1 Message Date
misskey-release-bot[bot]
bef73ff530 Merge pull request #15615 from misskey-dev/develop
Release: 2025.3.1
2025-03-09 03:29:58 +00:00
github-actions[bot]
7c1dc3d632 Release: 2025.3.1 2025-03-09 03:29:54 +00:00
github-actions[bot]
c53349c3b4 Bump version to 2025.3.1-beta.3 2025-03-09 00:33:40 +00:00
饺子w (Yumechi)
a710af54ed fix(backend): fix ApPersonService unsound type cast (#15629)
Signed-off-by: eternal-flame-AD <yume@yumechi.jp>
2025-03-09 00:11:34 +00:00
github-actions[bot]
ac07bb8d92 Bump version to 2025.3.1-beta.2 2025-03-08 10:15:42 +00:00
かっこかり
698505030e fix(test): fix federation test (#15630) 2025-03-08 19:11:09 +09:00
かっこかり
e16a14dcef fix(deps): pnpm v10でre2のインストールに失敗することがある問題を修正 (#15627)
* fix(deps): pnpm v10でre2のインストールに失敗することがある問題を修正

* fix

* fix docker build

* fix build failure on Windows
2025-03-08 18:56:53 +09:00
github-actions[bot]
6d93725084 Bump version to 2025.3.1-beta.1 2025-03-08 01:03:17 +00:00
おさむのひと
cb9981d4eb fix: Dockerのrunnerにpnpmのインストール手順が欠けていたのを修正 (#15623) 2025-03-08 10:02:15 +09:00
かっこかり
bee4db82bb fix(backend): follow-up of #15620
Removes unnecessary arg `disableGlobbing` from chokidar FSWatcher, as it is no longer supports globging
2025-03-07 20:10:32 +09:00
renovate[bot]
d7706ef1b5 fix(deps): update [root] update dependencies (major) (#15620)
* fix(deps): update [root] update dependencies

* fix: migrate tar library

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-03-07 19:42:39 +09:00
renovate[bot]
baf3f4a1d1 chore(deps): update [frontend] update dependencies to v10 (#15619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 19:41:34 +09:00
renovate[bot]
c7a56c2c2b fix(deps): update [root] update dependencies (#15543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 19:12:50 +09:00
github-actions[bot]
8dfff79ca2 Bump version to 2025.3.1-beta.0 2025-03-07 07:07:13 +00:00
かっこかり
83c3bb839f deps: update pnpm to v10 (#15588)
* Revert "fix(build): corepackのバグの回避 (#15387)"

This reverts commit 9c70a4e631.

* deps: update pnpm to v10

* fix broken lockfile

* update changelog

* fix

* fix

* Revert "fix"

This reverts commit 4abc6c194e.

* fix

* fix

* attempt to fix docker build

* lint fixes

* fix: revertしすぎた

* detect pnpm version and install it

* fix: そもそもpnpmを2回入れる必要がないかも

* fix

* refactor

* fix

* refactor: remove unnecessary arg

* Update Dockerfile

* update pnpm to v10.6.1

* Update Changelog

* chore: use node to avoid installing jq
2025-03-07 07:03:52 +00:00
syuilo
a9fe7eff0a New translations ja-jp.yml (Chinese Traditional) (#15618) 2025-03-07 16:01:32 +09:00
syuilo
d49ecab792 Update CHANGELOG.md 2025-03-07 14:46:10 +09:00
renovate[bot]
56459bbe68 chore(deps): update [tools] update dependencies (#15616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 11:44:20 +09:00
renovate[bot]
6c150ef1fb fix(deps): update [frontend] update dependencies (#15617)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 11:44:04 +09:00
syuilo
c78f45ea20 Update CHANGELOG.md 2025-03-07 09:12:18 +09:00
syuilo
82481c01e0 tweak MkDisableSection style 🎨 2025-03-07 09:11:51 +09:00
syuilo
741cbc34e6 Update CHANGELOG.md 2025-03-07 08:53:03 +09:00
github-actions[bot]
5e86550de3 Bump version to 2025.3.1-alpha.0 2025-03-06 23:51:21 +00:00
syuilo
92aef300ee New Crowdin updates (#15611)
* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Italian)
2025-03-07 08:50:54 +09:00
syuilo
9ce1b68fd7 Update CHANGELOG.md 2025-03-07 08:50:32 +09:00
饺子w (Yumechi)
5be5c8bec4 fix(backend): fixup migration for incorrect extraction on system accounts table (#15613)
Signed-off-by: eternal-flame-AD <yume@yumechi.jp>
2025-03-06 23:47:30 +00:00
syuilo
0214a0001f feat(frontend): 設定の検索 (#15505)
* wip

* wip

* wip

* test

* wip rollup pluginでsearchIndexの情報生成

* wip

* SPDX

* wip: markerIdを自動付与

* rollupでビルド時・devモード時に毎回uuidを生成するように

* 開発サーバーでだけ必要な挙動は開発サーバーのみで

* 条件が逆

* wip: childrenの生成

* update comment

* update comment

* rename auto generated file

* hashをパスと行数から決定

* Update privacy.vue

* Update privacy.vue

* wip

* Update general.vue

* Update general.vue

* wip

* wip

* Update SearchMarker.vue

* wip

* Update profile.vue

* Update mute-block.vue

* Update mute-block.vue

* Update general.vue

* Update general.vue

* childrenがduplicate key errorを吐く問題をいったん解決

* マーカーの形を成形

* loggerを置きかえ

* とりあえず省略記法に対応

* Refactor and Format codes

* wip

* Update settings-search-index.ts

* wip

* wip

* とりあえず不確定要因の仮置きidを削除

* hashの生成を正規化(絶対パスになっていたのを緩和)

* pathの入力を省略可能に

* adminでもパス生成できるように

* Update settings-search-index.ts

* Update privacy.vue

* wip

* build searchIndex

* wip

* build

* Update general.vue

* build

* Update sounds.vue

* build

* build

* Update sounds.vue

* 🎨

* 🎨

* Update privacy.vue

* Update privacy.vue

* Update security.vue

* create-search-indexを多少改善

* build

* Update 2fa.vue

* wip

* 必ずtransformCodeCacheを利用するように, キャッシュの明確な受け渡しを定義

* キャッシュはdevServerでなくても更新

* Revert "wip"

This reverts commit 41bffd3a13.

* inlining

* wip

* Update theme.vue

* 🎨

* wip normalize

* Update theme.vue

* キャッシュのパス変換

* build

* wip

* wip

* Update SearchMarker.vue

* i18n.ts['key'] の形式が取り出せない問題のFix

* build

* 仮でpath入れ

* 必ず絶対パスが使われるように

* wip

* 🎨

* storybookビルド時はcreateSearchIndexをしない

* inliningの構造化

* format code

* Update index.vue

* wip

* wip

* 🎨

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* clean up

* Update navbar.vue

* enhance: 検索で上下矢印を使用することで検索結果を移動できるように

* refactor

* fix(frontend): PageWindowでSearchMarkerが動作するように

* enhance(frontend): SearchMarkerの点滅を一定時間で止める

* lint fix

* fix: 子要素監視が抜けていたのを修正

* アニメーションの回数はCSSで制御するように

* refactor

* enhance(frontend): 検索インデックス作成時のログを削減

* revert

* fix

* fix

---------

Co-authored-by: tai-cha <dev@taichan.site>
Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-03-06 23:15:19 +09:00
github-actions[bot]
46067f6e17 [skip ci] Update CHANGELOG.md (prepend template) 2025-03-06 10:31:36 +00:00
misskey-release-bot[bot]
4d31c0b1de Merge pull request #15585 from misskey-dev/develop
Release: 2025.3.0
2025-03-06 10:31:34 +00:00
github-actions[bot]
2b71bdf114 Release: 2025.3.0 2025-03-06 10:31:30 +00:00
github-actions[bot]
9d6b521351 Bump version to 2025.3.0-beta.2 2025-03-06 08:19:56 +00:00
syuilo
ad708d896b Merge pull request #15562 from misskey-dev/l10n_develop
New Crowdin updates
2025-03-06 17:06:09 +09:00
かっこかり
22228b6756 enhance: OAuth2 (IndieAuth) でロゴが提供されている場合は表示するように (#15578)
* enhance: OAuthでロゴが提供されている場合は表示するように

* Update Changelog

* refactor

* fix

* fix test
2025-03-06 08:05:14 +00:00
かっこかり
f7ea0c6991 fix(backend): S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正 (#15583)
* fix(backend/object-storage): disable data integrity protections (MisskeyIO#895)

Cloudflare R2 does not support 'x-amz-checksum-*'

* Update Changelog

---------

Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
2025-03-06 08:03:57 +00:00
Sayamame-beans
60a3513cfc enhance(frontend): invert how to show the number of attachments(remains) on postform (#15599)
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-03-06 07:45:46 +00:00
github-actions[bot]
377f002d68 Bump version to 2025.3.0-beta.1 2025-03-06 07:31:52 +00:00
syuilo
896bde1005 revert https://github.com/misskey-dev/misskey/pull/15545
see https://github.com/misskey-dev/misskey/issues/14498
2025-03-06 16:28:25 +09:00
かっこかり
6d0242277d fix(frontend): tabler-iconsが読み込めない問題を修正(正式リリースに差し替え) (#15608) 2025-03-06 05:34:24 +00:00
renovate[bot]
60f90ca649 chore(deps): update [misskey-js] update dependencies (#15594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 07:09:01 +09:00
renovate[bot]
f3be426383 fix(deps): update [frontend] update dependencies (#15595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 21:01:44 +09:00
かっこかり
e8a6629cb5 fix(backend): システムアカウント系のマイグレーション不足を修正 (#15586)
* fix(backend): プロキシアカウントのロールバック用マイグレーションを追加

* fix

* separate newly-added `up` command

* drop backwards-compatibility

* docs
2025-03-05 16:49:49 +09:00
github-actions[bot]
44658ae981 Bump version to 2025.3.0-beta.0 2025-03-05 07:44:38 +00:00
syuilo
19384efbc5 clean up 2025-03-04 18:48:14 +09:00
かっこかり
adf22143aa Revert "enhance(frontend): チャンネル投稿をユーザーページと前後ノートに表示する" (#15589)
* Revert "enhance(frontend): チャンネル投稿をユーザーページと前後ノートに表示する (#15532)"

This reverts commit a4711ab4c1.

* Update CHANGELOG.md
2025-03-03 21:05:50 +09:00
renovate[bot]
a17acf647b fix(deps): update [frontend] update dependencies (#15587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 17:48:17 +09:00
かっこかり
01a3eabc4e enhance(frontend): アニメーション設定で画面上のエフェクトも考慮するように (#15576)
* enhance(frontend): アニメーション設定で画面上のエフェクトも考慮するように

* Update Changelog
2025-03-03 08:46:38 +00:00
かっこかり
59567a7ccc fix(frontend): 照会処理を統一 (#15536)
* fix(frontend): 照会処理を統一

* fix

* doLookup -> apLookup
2025-03-03 08:45:04 +00:00
かっこかり
7fb8fccd57 Update CHANGELOG.md for #15532 2025-03-03 17:29:49 +09:00
鴇峰 朔華
a4711ab4c1 enhance(frontend): チャンネル投稿をユーザーページと前後ノートに表示する (#15532)
* enhance(frontend): ユーザーページで常にチャンネル投稿が含まれるように

* enhance(frontend): ノート詳細の前後の投稿にチャンネル投稿を含めるように

* ログイン有無の削除
2025-03-03 08:28:29 +00:00
かっこかり
bbe404a0b2 fix(frontend): 投稿フォームがオーバーフローした際にスクロールできるように (#15571)
* fix(frontend): 投稿フォームがオーバーフローした際にスクロールできるように

* Update Changelog

* remove unused props
2025-03-03 08:17:20 +00:00
かっこかり
0610bd657f fix(frontend): フォローされたときのメッセージのshadowがちらつくことがある問題を修正 (#15584)
* fix(frontend): フォローされたときのメッセージがちらつく問題を修正

* Update Changelog
2025-03-03 08:09:41 +00:00
かっこかり
77667cf80d enhance(frontend): モデレーターがセンシティブ設定を変更する際に確認ダイアログを出すように (#15462)
* enhance(frontend): モデレーターがセンシティブ設定を変更する際に確認ダイアログを出すように

* use MkSwitch

* Update Changelog
2025-03-03 08:06:34 +00:00
tetsuya-ki
801a2ec1db fix(frontend): 削除して編集の削除タイミングを投稿後になるように #14498 (#15545)
* fix #14498

- 「削除して編集」の削除タイミングを投稿したタイミングへ変更

* update CHANGELOG.md

* 指摘対応

- InitialNoteがあれば必ず削除するべきものでもないため、投稿後にノートを削除するフラグをプロパティに追加

* 指摘対応のミス修正

- フラグを条件に追加
- 実績のdateが数値になってなかった点を修正

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-03-03 08:05:18 +00:00
github-actions[bot]
2a96e39bb3 Bump version to 2025.3.0-alpha.0 2025-03-02 12:12:06 +00:00
syuilo
616cccf251 enhance(backend): refine system account (#15530)
* wip

* wip

* wip

* Update SystemAccountService.ts

* Update 1740121393164-system-accounts.js

* Update DeleteAccountService.ts

* wip

* wip

* wip

* wip

* Update 1740121393164-system-accounts.js

* Update RepositoryModule.ts

* wip

* wip

* wip

* Update ApRendererService.ts

* wip

* wip

* Update SystemAccountService.ts

* fix tests

* fix tests

* fix tests

* fix tests

* fix tests

* fix tests

* add print logs

* ログが長すぎて出てないかもしれない

* fix migration

* refactor

* fix fed-tests

* Update RelayService.ts

* merge

* Update user.test.ts

* chore: emit log

* fix: tweak sleep duration

* fix: exit 1

* fix: wait for misskey processes to become healthy

* fix: longer sleep for user deletion

* fix: make sleep longer again

* デッドロック解消の試み

https://github.com/misskey-dev/misskey/issues/15005

* Revert "デッドロック解消の試み"

This reverts commit 266141f66f.

* wip

* Update SystemAccountService.ts

---------

Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com>
Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
2025-03-02 20:06:20 +09:00
syuilo
7114523d84 Update CHANGELOG.md 2025-03-01 17:02:52 +09:00
syuilo
5d683728f3 デッドロック解消の試み (#15574)
https://github.com/misskey-dev/misskey/issues/15005

Co-authored-by: 饺子w (Yumechi) <35571479+eternal-flame-AD@users.noreply.github.com>
2025-03-01 16:12:42 +09:00
おさむのひと
b8632f389d chore(ci): Renovateが作ったprにラベルつける (#15573) 2025-03-01 04:37:11 +00:00
renovate[bot]
830da5e9f1 fix(deps): update [frontend] update dependencies (#15566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 13:11:09 +09:00
renovate[bot]
e2eddd5b1a chore(deps): update actions/cache action to v4.2.2 (#15564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 13:07:59 +09:00
renovate[bot]
d4f9bf1f11 chore(deps): update [misskey-js] update dependencies (#15565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 13:07:47 +09:00
renovate[bot]
734c78ddd1 chore(deps): update [tools] update dependencies (#15563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 13:07:36 +09:00
syuilo
c63c3462dd refactor 2025-02-28 09:34:21 +09:00
github-actions[bot]
a3bba23b7d [skip ci] Update CHANGELOG.md (prepend template) 2025-02-27 08:58:46 +00:00
misskey-release-bot[bot]
a5f28c21e4 Merge pull request #15507 from misskey-dev/develop
Release: 2025.2.1
2025-02-27 08:58:43 +00:00
github-actions[bot]
94a3e37ba8 Release: 2025.2.1 2025-02-27 08:58:40 +00:00
syuilo
cc09de7b27 New Crowdin updates (#15560)
* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Turkish)

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

* New translations ja-jp.yml (English)

* 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 (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Polish)

* 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 (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Catalan)
2025-02-27 16:13:37 +09:00
かっこかり
da66079c29 enhance(frontend): ノート検索ページのデザイン調整 (#14780)
* enhance(frontend): 検索ページのホスト指定とユーザー指定を統合する (#273)

(cherry picked from commit c79392c88d6bf58ede39d8bba9ca2778c58521ef)

* fix

* 🎨

* Update Changelog

* Update Changelog

* refactor

---------

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
2025-02-27 02:56:17 +00:00
syuilo
ec83815227 fix(frontend): MkSelectの初期値が表示されない場合がある (#15559)
* fix

* Update CHANGELOG.md
2025-02-27 00:32:39 +00:00
syuilo
6199139307 Update CHANGELOG.md 2025-02-26 16:30:25 +09:00
おさむのひと
15b0345335 enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように (#15438)
* enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように

* sessionStorageよりも更に短命な方法で持つように変更

* add comment

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 07:28:35 +00:00
かっこかり
28b40691d5 fix(frontend): リノート経由でノートの詳細情報を見るとき、クリップ一覧がappearNoteのものになるように (#15417)
* fix(frontend): リノート経由でノートの詳細情報を見るとき、クリップ一覧がappearNoteのものになるように

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 06:51:28 +00:00
syuilo
a778a63a12 New Crowdin updates (#15421)
* New translations ja-jp.yml (Spanish)

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

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

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

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

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Catalan)

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

* 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 (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Catalan)

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

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

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Portuguese)

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

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Italian)

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

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

* 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 (Japanese, Kansai)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Catalan)
2025-02-26 14:58:18 +09:00
github-actions[bot]
96fc7e307a Bump version to 2025.2.1-beta.2 2025-02-26 02:23:02 +00:00
Esurio/1673beta
bd13fb626c fix(backend): S3_SAFEかつURL_SAFEでない文字列をprefixに使えないように (#15455)
* fix(backend): S3_SAFEかつURL_SAFEでない文字列をprefixに使えないように

* update CHANGELOG

* fix validation

* fix: remove unused import

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 01:56:23 +00:00
おさむのひと
d87488a5f0 fix(frontend): ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように (#15435)
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 01:51:23 +00:00
おさむのひと
495db27433 fix(backend): カスタム絵文字の一括インポートをした時にHTTPプロキシの除外設定が効かないのを修正 (#15431)
* pxory

* fix

* fix CHANGELOG.md

* allow localAddress

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 01:48:38 +00:00
鴇峰 朔華
39c487e1d1 fix(backend): ローカル判定でisUriLocalを使用していない箇所を修正 (#15069)
* fix(backend): ローカル判定でisUriLocalを使用していない箇所を修正

* fix(test backend): RelayServiceでUtilityServiceを使う
2025-02-26 01:28:53 +00:00
かっこかり
b5799351d0 fix(backend): clips/updateのdescriptionで空文字を許容するように (#15429)
* fix(backend): clips/updateのdescriptionで空文字を許容するように

* Update Changelog

* fix: createの際も空文字を許容するように

* fix test

* fix test

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-02-26 01:27:38 +00:00
misskey-release-bot[bot]
c93ead7474 Merge pull request #15378 from misskey-dev/develop
Release: 2025.2.0
2025-02-05 08:58:45 +00:00
misskey-release-bot[bot]
36880493cb Merge pull request #15279 from misskey-dev/develop
Release: 2025.1.0
2025-01-28 12:29:14 +00:00
misskey-release-bot[bot]
e8518de054 Merge pull request #14924 from misskey-dev/develop
Release: 2024.11.0
2024-11-22 09:15:34 +00:00
misskey-release-bot[bot]
b99e13e667 Merge pull request #14741 from misskey-dev/develop
Release: 2024.10.1
2024-10-15 04:53:46 +00:00
misskey-release-bot[bot]
2518cf36d0 Merge pull request #14675 from misskey-dev/develop
Release: 2024.10.0
2024-10-09 05:17:29 +00:00
215 changed files with 8724 additions and 4395 deletions

View File

@@ -7,8 +7,8 @@
"ghcr.io/devcontainers/features/node:1": {
"version": "22.11.0"
},
"ghcr.io/devcontainers-extra/features/corepack:1": {
"version": "0.31.0"
"ghcr.io/devcontainers-extra/features/pnpm:2": {
"version": "10.6.1"
}
},
"forwardPorts": [3000],

View File

@@ -7,8 +7,6 @@ sudo apt-get update
sudo apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb
git config --global --add safe.directory /workspace
git submodule update --init
corepack install
corepack enable
pnpm config set store-dir /home/node/.local/share/pnpm/store
pnpm install --frozen-lockfile
cp .devcontainer/devcontainer.yml .config/default.yml

View File

@@ -9,10 +9,6 @@ on:
paths:
- packages/misskey-js/**
- .github/workflows/api-misskey-js.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
report:
@@ -22,7 +18,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v4.2.2
- run: corepack enable
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Setup Node.js
uses: actions/setup-node@v4.2.0

View File

@@ -9,10 +9,6 @@ on:
paths:
- packages/backend/**
- .github/workflows/get-api-diff.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
get-from-misskey:
runs-on: ubuntu-latest
@@ -34,14 +30,13 @@ jobs:
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml

View File

@@ -28,10 +28,6 @@ on:
- packages/misskey-reversi/**
- packages/shared/eslint.config.js
- .github/workflows/lint.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
pnpm_install:
runs-on: ubuntu-latest
@@ -40,12 +36,12 @@ jobs:
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- uses: actions/setup-node@v4.2.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
lint:
@@ -71,15 +67,15 @@ jobs:
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- uses: actions/setup-node@v4.2.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Restore eslint cache
uses: actions/cache@v4.2.1
uses: actions/cache@v4.2.2
with:
path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
@@ -101,12 +97,12 @@ jobs:
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- uses: actions/setup-node@v4.2.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- run: pnpm --filter misskey-js run build
if: ${{ matrix.workspace == 'backend' || matrix.workspace == 'sw' }}

View File

@@ -9,10 +9,6 @@ on:
paths:
- locales/**
- .github/workflows/locale.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
locale_verify:
runs-on: ubuntu-latest
@@ -22,11 +18,11 @@ jobs:
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- uses: actions/setup-node@v4.2.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- run: cd locales && node verify.js

View File

@@ -6,9 +6,6 @@ on:
workflow_dispatch:
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
publish-misskey-js:
name: Publish misskey-js
@@ -26,8 +23,8 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
@@ -36,7 +33,6 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- name: Publish package
run: |
corepack enable
pnpm i --frozen-lockfile
pnpm build
pnpm --filter misskey-js publish --access public --no-git-checks --provenance

View File

@@ -13,9 +13,6 @@ on:
# This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master.
- master
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
build:
# chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
@@ -43,14 +40,13 @@ jobs:
run: |
echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js 20.x
uses: actions/setup-node@v4.2.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml

View File

@@ -18,10 +18,6 @@ on:
- packages/misskey-js/**
- .github/workflows/test-backend.yml
- .github/misskey/test.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
unit:
name: Unit tests (backend)
@@ -48,8 +44,8 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Install FFmpeg
run: |
for i in {1..3}; do
@@ -70,7 +66,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
@@ -111,14 +106,13 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml

View File

@@ -15,9 +15,6 @@ on:
- packages/misskey-js/**
- .github/workflows/test-federation.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
test:
name: Federation test
@@ -29,8 +26,8 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Install FFmpeg
run: |
for i in {1..3}; do
@@ -53,7 +50,6 @@ jobs:
cache: 'pnpm'
- name: Build Misskey
run: |
corepack enable && corepack prepare
pnpm i --frozen-lockfile
pnpm build
- name: Setup
@@ -62,14 +58,30 @@ jobs:
bash ./setup.sh
sudo chmod 644 ./certificates/*.test.key
- name: Start servers
id: start_servers
continue-on-error: true
# https://github.com/docker/compose/issues/1294#issuecomment-374847206
run: |
cd packages/backend/test-federation
docker compose up -d --scale tester=0
- name: Print start_servers error
if: ${{ steps.start_servers.outcome == 'failure' }}
run: |
cd packages/backend/test-federation
docker compose logs | tail -n 300
exit 1
- name: Test
id: test
continue-on-error: true
run: |
cd packages/backend/test-federation
docker compose run --no-deps tester
- name: Log
if: ${{ steps.test.outcome == 'failure' }}
run: |
cd packages/backend/test-federation
docker compose logs
exit 1
- name: Stop servers
run: |
cd packages/backend/test-federation

View File

@@ -22,10 +22,6 @@ on:
- packages/backend/**
- .github/workflows/test-frontend.yml
- .github/misskey/test.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
vitest:
name: Unit tests (frontend)
@@ -39,14 +35,13 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
@@ -95,14 +90,13 @@ jobs:
# if: ${{ matrix.browser == 'firefox' }}
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Copy Configure
run: cp .github/misskey/test.yml .config

View File

@@ -14,10 +14,6 @@ on:
paths:
- packages/misskey-js/**
- .github/workflows/test-misskey-js.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
test:
name: Unit tests (misskey.js)
@@ -33,7 +29,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v4.2.2
- run: corepack enable
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0

View File

@@ -9,7 +9,6 @@ on:
env:
NODE_ENV: production
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
production:
@@ -24,14 +23,13 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml

View File

@@ -12,10 +12,6 @@ on:
paths:
- packages/backend/**
- .github/workflows/validate-api-json.yml
env:
COREPACK_DEFAULT_TO_LATEST: 0
jobs:
validate-api-json:
runs-on: ubuntu-latest
@@ -28,8 +24,8 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.2.0
with:
@@ -37,7 +33,6 @@ jobs:
cache: 'pnpm'
- name: Install Redocly CLI
run: npm i -g @redocly/cli
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml

2
.npmrc
View File

@@ -1 +1,3 @@
engine-strict = true
save-exact = true
shell-emulator = true

View File

@@ -1,3 +1,40 @@
## 2025.3.1
### General
- pnpmをv10に更新
- Corepackを削除
### Client
- Feat: 設定の検索を追加(実験的)
- Enhance: 設定項目の再配置
### Server
- Fix: DBマイグレーション際にシステムアカウントのユーザーID判定が正しくない問題を修正
- Fix: user.featured列が状況によってJSON文字列になっていたのを修正
## 2025.3.0
### General
- Enhance: プロキシアカウントをシステムアカウントとして作成するように
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
書式は https://indieauth.spec.indieweb.org/20220212/#example-2 に準じます。
- Fix: システムアカウントが削除できる問題を修正
### Client
- Enhance: モデレーターがセンシティブ設定を変更する際に確認ダイアログを出すように
- Enhance: 「UIのアニメーションを減らす」で画面上のエフェクトも減らせるように
- Enhance: 投稿フォームにおける、メディアの添付可能個数のカウントを反転しました
- これまでの表示は`添付可能残り個数/上限数`でしたが、`添付個数/上限数`としました
- Fix: フォローされたときのメッセージがちらつくことがある問題を修正
- Fix: 投稿ダイアログがサイズ限界を超えた際にスクロールできない問題を修正
### Server
- Fix: 特定のケースでActivityPubの処理がデッドロックになることがあるのを修正
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/895)
## 2025.2.1
### General
@@ -13,20 +50,30 @@
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
- Enhance: リアクションする際に確認ダイアログを表示できるように
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
- Enhance: CWの注釈で入力済みの文字数を表示
- Enhance: ノート検索ページのデザイン調整
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
- Fix: 埋め込みプレイヤーから外部ページに移動できない問題を修正
- Fix: Play の再読込時に UI が以前の状態を引き継いでしまう問題を修正 `#14378`
- Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 )
- Fix: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
- Fix: CWの注釈が100文字を超えている場合、ート投稿ボタンを非アクティブに
- Fix: テーマ選択で現在のテーマが初期表示されていない問題を修正
- 翻訳の更新
### Server
- Enhance: 成り済まし対策として、ActivityPub照会された時にリモートのリダイレクトを拒否できるように (config.disallowExternalApRedirect)
- Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように
- Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正
- Fix: HTTPプロキシとその除外設定を行った状態でカスタム絵文字の一括インポートをしたとき、除外設定が効かないのを修正( #8766 )
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
- Fix: `update-meta`でobjectStoragePrefixにS3_SAFEかつURL-safeでない文字列を使えないように
- Fix: クリップの説明欄を更新する際に空にできない問題を修正
- Fix: フォロワーではないユーザーにリートもしくは返信された場合にートのDeleteアクティビティが送られていない問題を修正
## 2025.2.0

View File

@@ -6,8 +6,6 @@ ARG NODE_VERSION=22.11.0-bookworm
FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder
ENV COREPACK_DEFAULT_TO_LATEST=0
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
rm -f /etc/apt/apt.conf.d/docker-clean \
@@ -16,8 +14,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
&& apt-get install -yqq --no-install-recommends \
build-essential
RUN corepack enable
WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
@@ -33,6 +29,8 @@ COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bu
ARG NODE_ENV=production
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
pnpm i --frozen-lockfile --aggregate-output
@@ -46,14 +44,10 @@ RUN rm -rf .git/
FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder
ENV COREPACK_DEFAULT_TO_LATEST=0
RUN apt-get update \
&& apt-get install -yqq --no-install-recommends \
build-essential
RUN corepack enable
WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
@@ -65,6 +59,8 @@ COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bu
ARG NODE_ENV=production
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
pnpm i --frozen-lockfile --aggregate-output
@@ -72,13 +68,11 @@ FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner
ARG UID="991"
ARG GID="991"
ENV COREPACK_DEFAULT_TO_LATEST=0
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ffmpeg tini curl libjemalloc-dev libjemalloc2 \
&& ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
&& corepack enable \
&& groupadd -g "${GID}" misskey \
&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \
&& find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
@@ -86,13 +80,13 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists
# add package.json to add pnpm
COPY ./package.json ./package.json
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
USER misskey
WORKDIR /misskey
# add package.json to add pnpm
COPY --chown=misskey:misskey ./package.json ./package.json
RUN corepack install
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules

View File

@@ -233,7 +233,7 @@ describe('After user setup', () => {
cy.get('[data-cy-post-form-text]').type('Hello, Misskey!');
cy.get('[data-cy-open-post-form-submit]').click();
cy.contains('Hello, Misskey!');
cy.contains('Hello, Misskey!', { timeout: 15000 });
});
it('open note form with hotkey', () => {

View File

@@ -1584,3 +1584,7 @@ _offlineScreen:
_remoteLookupErrors:
_noSuchObject:
title: "غير موجود"
_search:
searchScopeAll: "الكل"
searchScopeLocal: "المحلي"
searchScopeUser: "مستخدم محدد"

View File

@@ -1348,3 +1348,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "পাওয়া যায়নি"
_search:
searchScopeAll: "সবগুলো"
searchScopeLocal: "স্থানীয়"

View File

@@ -66,7 +66,7 @@ copyFolderId: "Copiar ID de la carpeta"
copyProfileUrl: "Copiar adreça URL del perfil"
searchUser: "Cercar un usuari"
searchThisUsersNotes: "Cercar les publicacions de l'usuari"
reply: "Respon"
reply: "Respostes"
loadMore: "Carregar més"
showMore: "Veure més"
showLess: "Mostrar menys"
@@ -111,7 +111,7 @@ followRequests: "Peticions de seguiment"
unfollow: "Deixar de seguir"
followRequestPending: "Sol·licituds de seguiment pendents"
enterEmoji: "Introduir un emoji"
renote: "Impulsar "
renote: "Impulsar"
unrenote: "Anul·la l'impuls"
renoted: "S'ha impulsat"
renotedToX: "Impulsat per {name}."
@@ -260,7 +260,7 @@ noCustomEmojis: "No hi ha emojis personalitzats"
noJobs: "No hi ha feines"
federating: "Federant"
blocked: "Bloquejat"
suspended: "Suspés"
suspended: "Anul·lar subscripció "
all: "tot"
subscribing: "Subscrit a"
publishing: "S'està publicant"
@@ -646,7 +646,7 @@ disablePlayer: "Tanca el reproductor de vídeo"
expandTweet: "Expandir post"
themeEditor: "Editor de temes"
description: "Descripció"
describeFile: "Afegir subtitulació"
describeFile: "Afegeix una descripció "
enterFileDescription: "Escriu un peu de foto"
author: "Autor"
leaveConfirm: "Hi ha canvis sense guardar. Els vols descartar?"
@@ -1114,7 +1114,7 @@ forceShowAds: "Mostra els anuncis sempre "
addMemo: "Afegir recordatori"
editMemo: "Editar recordatori"
reactionsList: "Reaccions"
renotesList: "Impulsos"
renotesList: "Llistat d'impulsos "
notificationDisplay: "Notificacions"
leftTop: "Dalt a l'esquerra "
rightTop: "Dalt a la dreta "
@@ -1189,8 +1189,8 @@ currentAnnouncements: "Informes actuals"
pastAnnouncements: "Informes passats"
youHaveUnreadAnnouncements: "Tens informes per llegir."
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
replies: "Respon"
renotes: "Impulsar "
replies: "Respostes"
renotes: "Impulsar"
loadReplies: "Mostrar les respostes"
loadConversation: "Mostrar la conversació "
pinnedList: "Llista fixada"
@@ -1309,6 +1309,10 @@ availableRoles: "Roles disponibles "
acknowledgeNotesAndEnable: "Activa'l després de comprendre els possibles perills."
federationSpecified: "Aquest servidor treballa amb una federació de llistes blanques. No pot interactuar amb altres servidors que no siguin els especificats per l'administrador."
federationDisabled: "La unió es troba deshabilitada en aquest servidor. No es pot interactuar amb usuaris d'altres servidors."
confirmOnReact: "Confirmar en reaccionar"
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
markAsSensitiveConfirm: "Vols marcar aquest contingut com a sensible?"
unmarkAsSensitiveConfirm: "Vols deixar de marcar com a sensible aquest contingut?"
_accountSettings:
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
@@ -1330,7 +1334,7 @@ _abuseUserReport:
resolveTutorial: "Si l'informe és legítim selecciona \"Acceptar\" per resoldre'l positivament. Però si l'informe no és legítim selecciona \"Rebutjar\" per resoldre'l negativament."
_delivery:
status: "Estat d'entrega "
stop: "Suspés"
stop: "Anul·lar subscripció "
resume: "Torna a enviar"
_type:
none: "S'està publicant"
@@ -2440,13 +2444,15 @@ _notification:
flushNotification: "Netejar notificacions"
exportOfXCompleted: "Completada l'exportació de {x}"
login: "Algú ha iniciat sessió "
createToken: "Token d'accés generat"
createTokenDescription: "Si no saps què és, esborra el token des de {text}."
_types:
all: "Tots"
note: "Notes noves"
follow: "Segueix-me"
mention: "Menció"
reply: "Respostes"
renote: "Renotar"
renote: "Impulsar"
quote: "Citar"
reaction: "Reaccions"
pollEnded: "Enquesta terminada"
@@ -2461,7 +2467,7 @@ _notification:
_actions:
followBack: "També et segueix"
reply: "Respondre"
renote: "Renotar"
renote: "Impulsos"
_deck:
alwaysShowMainColumn: "Mostrar sempre la columna principal"
columnAlign: "Alinea les columnes"
@@ -2590,6 +2596,7 @@ _moderationLogTypes:
deletePage: "Esborrar la pàgina"
deleteFlash: "Esborrar el guió"
deleteGalleryPost: "Esborrar la publicació de la galeria"
updateProxyAccountDescription: "Actualitzar descripció del compte proxy"
_fileViewer:
title: "Detall del fitxer"
type: "Tipus de fitxer"
@@ -2822,8 +2829,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "La resposta no és correcta "
description: "Hem pogut comunicar-nos amb aquest servidor, però les dades rebudes no són correctes."
_responseInvalidIdHostNotMatch:
description: "El domini de l'adreça introduïda no és el mateix que el domini de l'adreça final obtinguda. Si estàs consultant continguts remots mitjançant servidors tercers, torna a fer la consulta fent servir l'adreça que es pot obtenir en el servidor origen."
_noSuchObject:
title: "No s'ha trobat"
description: "No es pot trobar el recurs sol·licitat, si us plau comprova l'adreça una altra vegada."
@@ -2840,3 +2845,23 @@ _captcha:
_unknown:
title: "Error CAPTCHA"
text: "S'ha produït un error inesperat."
_bootErrors:
title: "Hi ha hagut en error en carregar"
serverError: "Si el problema persisteix després d'esperar una mica i recarregar, posa't en contacte amb l'administrador del servidor amb el següent codi d'error."
solution: "Per intentar resoldre el problema pots fer el següent."
solution1: "Actualitza el navegador i el sistema operatiu a l'última versió "
solution2: "Desactiva els adblockers"
solution3: "Esborra la memòria cau del navegador"
solution4: "(Navegador Tor) configura dom.webaudio.enabled a true"
otherOption: "Altres opcions"
otherOption1: "Esborrar la configuració i la memòria cau del client"
otherOption2: "Iniciar client senzill"
otherOption3: "Iniciar l'eina de reparació "
_search:
searchScopeAll: "Tot"
searchScopeLocal: "Local"
searchScopeServer: "Instància "
searchScopeUser: "Especificar usuari"
pleaseEnterServerHost: "Introdueix l'adreça de la instància "
pleaseSelectUser: "Selecciona un usuari"
serverHostPlaceholder: "Ex: misskey.example.com"

View File

@@ -5,6 +5,7 @@ introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový
poweredByMisskeyDescription: "{name} je jeden ze serverů využívající open source platformu <b>Misskey<b> (nazývaná \"Misskey instance\")."
monthAndDay: "{day}. {month}."
search: "Vyhledávání"
reset: "Obnovit"
notifications: "Oznámení"
username: "Uživatelské jméno"
password: "Heslo"
@@ -365,8 +366,11 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "Aktivovat hCaptchu"
hcaptchaSiteKey: "Klíč stránky"
hcaptchaSecretKey: "Tajný Klíč (Secret Key)"
mcaptcha: "mCaptcha"
enableMcaptcha: "Aktivovat mCaptchu"
mcaptchaSiteKey: "Klíč stránky"
mcaptchaSecretKey: "Tajný Klíč (Secret Key)"
mcaptchaInstanceUrl: "URL mCaptcha serveru"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Zapnout ReCAPTCHu"
recaptchaSiteKey: "Klíč stránky"
@@ -2024,3 +2028,7 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Nenalezeno"
_search:
searchScopeAll: "Vše"
searchScopeLocal: "Místní"
searchScopeUser: "Upřesnit uživatele"

View File

@@ -1383,6 +1383,9 @@ _initialTutorial:
title: "Was sind Notizen?"
description: "Beiträge auf Misskey heißen \"Notizen\". Notizen werden chronologisch in der Chronik angeordnet und in Echtzeit aktualisiert."
reply: "Klicke auf diesen Button, um auf eine Nachricht zu antworten. Es ist auch möglich, auf Antworten zu antworten und die Unterhaltung wie einen Thread fortzusetzen."
renote: "Du kannst diese Notiz in deiner eigenen Chronik teilen. Du kannst sie auch mit deinen Kommentaren zitieren."
reaction: "Du kannst der Notiz Reaktionen hinzufügen. Weitere Einzelheiten werden auf der nächsten Seite erläutert."
menu: "Du kannst Details zu Notizen anzeigen, Links kopieren und verschiedene andere Aktionen durchführen."
_reaction:
title: "Was sind Reaktionen?"
description: "Auf Notizen kann mit verschiedenen Emojis reagiert werden. Reaktionen ermöglichen es dir, Nuancen auszudrücken, die mit einem einfachen „Gefällt mir“ vielleicht nicht ausgedrückt werden können."
@@ -1401,13 +1404,21 @@ _initialTutorial:
_visibility:
description: "Du kannst einschränken, wer deine Notiz sehen kann."
public: "Deine Notiz wird für alle Nutzer sichtbar sein."
direct: "Die Notiz wird nur für den angegebenen Benutzer veröffentlicht und der Empfänger wird benachrichtigt. Kann anstelle von Direktnachrichten verwendet werden."
doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!"
doNotSendConfidencialOnDirect2: "Die Administratoren des Servers können den Inhalt der Notiz sehen. Sei vorsichtig mit sensiblen Informationen, wenn du Direktnachrichten an Benutzer auf nicht vertrauenswürdigen Servern sendest."
localOnly: "Wenn du eine Notiz mit dieser Einstellung veröffentlichst, wird sie nicht an andere Server weitergeleitet. Benutzer auf anderen Servern können diese Notizen nicht direkt sehen, unabhängig von den obigen Anzeigeeinstellungen."
_cw:
title: "Inhaltswarnung"
description: "Anstelle des Textes wird das angezeigt, was du im Abschnitt „Anmerkungen“ angibst. Drücke auf „Inhalt anzeigen“, um den vollständigen Text zu sehen."
_exampleNote:
cw: "Das wird dich bestimmt hungrig machen!"
note: "Ich hatte gerade einen Donut mit Schokoladenüberzug 🍩😋"
_howToMakeAttachmentsSensitive:
title: "Wie markiert man Anhänge als sensibel?"
tryThisFile: "Versuche, das angehängte Bild als sensibel zu markieren!"
_exampleNote:
note: "Ups, ich habe es vergeigt, den Natto-Deckel zu öffnen..."
method: "Um einen Anhang als sensibel zu kennzeichnen, klicke auf das Vorschaubild der Datei, um das Menü zu öffnen, und klicke auf „Als sensibel markieren“."
sensitiveSucceeded: "Wenn du Dateien anhängst, stelle bitte die Sensibilität entsprechend der Serverrichtlinien ein."
doItToContinue: "Markiere die angehängte Datei als sensibel, um fortzufahren."
@@ -1431,6 +1442,7 @@ _serverSettings:
fanoutTimelineDescription: "Ist diese Option aktiviert, kann eine erhebliche Verbesserung im Abrufen von Chroniken und eine Reduzierung der Datenbankbelastung erzielt werden, im Gegenzug zu einer Steigerung in der Speichernutzung von Redis. Bei geringem Serverspeicher oder Serverinstabilität kann diese Option deaktiviert werden."
fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen"
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann."
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern."
_accountMigration:
@@ -1835,6 +1847,7 @@ _plugin:
installWarn: "Installiere bitte nur vertrauenswürdige Plugins."
manage: "Plugins verwalten"
viewSource: "Quelltext anzeigen"
viewLog: "Protokoll anzeigen"
_preferencesBackups:
list: "Erstellte Backups"
saveNew: "Neu erstellen"
@@ -1864,6 +1877,8 @@ _aboutMisskey:
contributors: "Hauptmitwirkende"
allContributors: "Alle Mitwirkenden"
source: "Quellcode"
original: "Original"
thisIsModifiedVersion: "{name} verwendet eine modifizierte Version des ursprünglichen Misskey."
translation: "Misskey übersetzen"
donate: "An Misskey spenden"
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter Personen sehr. Danke! 🥰"
@@ -1989,6 +2004,8 @@ _soundSettings:
driveFileTypeWarn: "Diese Datei wird nicht unterstützt"
driveFileTypeWarnDescription: "Bitte wähle eine Audiodatei"
driveFileDurationWarn: "Audio zu lang."
driveFileDurationWarnDescription: "Lange Töne kann die Verwendung von Misskey stören. Trotzdem fortfahren?"
driveFileError: "Audio konnte nicht geladen werden. Bitte ändere die Einstellung."
_ago:
future: "Zukunft"
justNow: "Gerade eben"
@@ -2000,6 +2017,10 @@ _ago:
monthsAgo: "vor {n} Monat(en)"
yearsAgo: "vor {n} Jahr(en)"
invalid: "Ungültig"
_timeIn:
seconds: "In {n}s"
minutes: "In {n} Min."
hours: "In {n} Std."
_time:
second: "Sekunde(n)"
minute: "Minute(n)"
@@ -2105,6 +2126,7 @@ _auth:
permissionAsk: "Diese Anwendung fordert folgende Berechtigungen"
pleaseGoBack: "Bitte kehre zur Anwendung zurück"
callback: "Es wird zur Anwendung zurückgekehrt"
accepted: "Zugriff gewährt"
denied: "Zugriff verweigert"
pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren."
_antennaSources:
@@ -2215,6 +2237,7 @@ _profile:
changeBanner: "Banner ändern"
verifiedLinkDescription: "Gibst du hier eine URL ein, die einen Link zu deinem Profile enthält, wird neben diesem Feld ein Icon zur Besitzbestätigung angezeigt."
avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen."
followedMessage: "Nachricht, wenn dir jemand folgt"
followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt."
_exportOrImport:
allNotes: "Alle Notizen"
@@ -2343,8 +2366,11 @@ _notification:
sendTestNotification: "Testbenachrichtigung senden"
notificationWillBeDisplayedLikeThis: "Benachrichtigungen sehen so aus"
reactedBySomeUsers: "{n} Benutzer haben eine Reaktion geschickt"
likedBySomeUsers: "{n} Benutzer mochten deine Notiz"
renotedBySomeUsers: "Renote von {n} Benutzern"
followedBySomeUsers: "Von {n} Benutzern gefolgt"
flushNotification: "Benachrichtigungen löschen"
exportOfXCompleted: "Der Export von {x} ist abgeschlossen"
login: "Neue Anmeldung erfolgt"
_types:
all: "Alle"
@@ -2360,7 +2386,9 @@ _notification:
followRequestAccepted: "Akzeptierte Follow-Anfragen"
roleAssigned: "Rolle zugewiesen"
achievementEarned: "Errungenschaft freigeschaltet"
login: "Anmelden"
exportCompleted: "Der Export ist abgeschlossen"
login: "Anmeldung"
test: "Test-Benachrichtigungen"
app: "Benachrichtigungen von Apps"
_actions:
followBack: "folgt dir nun auch"
@@ -2370,6 +2398,7 @@ _deck:
alwaysShowMainColumn: "Hauptspalte immer zeigen"
columnAlign: "Spaltenausrichtung"
addColumn: "Spalte hinzufügen"
newNoteNotificationSettings: "Benachrichtigungseinstellungen für neue Notizen"
configureColumn: "Spalteneinstellungen"
swapLeft: "Mit linker Spalte tauschen"
swapRight: "Mit rechter Spalte tauschen"
@@ -2408,6 +2437,7 @@ _drivecleaner:
orderByCreatedAtAsc: "Aufsteigendes Erstelldatum"
_webhookSettings:
createWebhook: "Webhook erstellen"
modifyWebhook: "Webhook bearbeiten"
name: "Name"
secret: "Secret"
trigger: "Auslöser"
@@ -2420,12 +2450,22 @@ _webhookSettings:
renote: "Wenn du ein Renote erhältst"
reaction: "Wenn du eine Reaktion erhältst"
mention: "Wenn du erwähnt wirst"
deleteConfirm: "Bist du sicher, dass du den Webhook löschen willst?"
_abuseReport:
_notificationRecipient:
createRecipient: "Meldungsempfänger hinzufügen"
modifyRecipient: "Bearbeite einen Empfänger für Meldungen"
recipientType: "Art der Benachrichtigung"
_recipientType:
mail: "Email"
webhook: "Webhook"
_captions:
mail: "Die Benachrichtigung wird bei Eingang einer Meldung an die E-Mail-Adressen der Moderatoren gesendet"
webhook: "Sendet eine Benachrichtigung an den System Webhook, wenn eine Meldung eingegangen ist oder gelöst wurde"
keywords: "Schlüsselwort"
notifiedUser: "Zu benachrichtigender Benutzer"
notifiedWebhook: "Zu verwendender Webhook"
deleteConfirm: "Bist du sicher, dass du den Empfänger der Benachrichtigung entfernen möchtest?"
_moderationLogTypes:
createRole: "Rolle erstellt"
deleteRole: "Rolle gelöscht"
@@ -2465,6 +2505,7 @@ _moderationLogTypes:
createSystemWebhook: "System-Webhook erstellt"
updateSystemWebhook: "System-Webhook aktualisiert"
deleteSystemWebhook: "System-Webhook gelöscht"
deleteAccount: "Benutzerkonto gelöscht"
deletePage: "Seite gelöscht"
deleteGalleryPost: "Galeriebeitrag gelöscht"
_fileViewer:
@@ -2573,3 +2614,7 @@ _remoteLookupErrors:
_noSuchObject:
title: "Nicht gefunden"
description: "Die angeforderte Ressource konnte nicht gefunden werden, bitte überprüfe die URI erneut."
_search:
searchScopeAll: "Alle"
searchScopeLocal: "Lokal"
searchScopeUser: "Spezifischer Benutzer"

View File

@@ -397,3 +397,5 @@ _moderationLogTypes:
suspend: "Αποβολή"
_reversi:
total: "Σύνολο"
_search:
searchScopeLocal: "Τοπικό"

View File

@@ -132,7 +132,7 @@ reaction: "Reactions"
reactions: "Reactions"
emojiPicker: "Emoji picker"
pinnedEmojisForReactionSettingDescription: "Set the emojis to be pinned and displayed when reacting."
pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker."
pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker"
emojiPickerDisplay: "Emoji picker display"
overwriteFromPinnedEmojisForReaction: "Override from reaction settings"
overwriteFromPinnedEmojis: "Override from general settings"
@@ -586,7 +586,7 @@ popout: "Pop-out"
volume: "Volume"
masterVolume: "Master volume"
notUseSound: "Disable sound"
useSoundOnlyWhenActive: "Output sounds only if Misskey is active."
useSoundOnlyWhenActive: "Output sounds only if Misskey is active"
details: "Details"
renoteDetails: "Renote details"
chooseEmoji: "Select an emoji"
@@ -1261,7 +1261,7 @@ copyReplayData: "Copy replay data"
ranking: "Ranking"
lastNDays: "Last {n} days"
backToTitle: "Go back to title"
hemisphere: "Where are you located"
hemisphere: "Where you live"
withSensitive: "Include notes with sensitive files"
userSaysSomethingSensitive: "Post by {name} contains sensitive content"
enableHorizontalSwipe: "Swipe to switch tabs"
@@ -1309,6 +1309,10 @@ availableRoles: "Available roles"
acknowledgeNotesAndEnable: "Turn on after understanding the precautions."
federationSpecified: "This server is operated in a whitelist federation. Interacting with servers other than those designated by the administrator is not allowed."
federationDisabled: "Federation is disabled on this server. You cannot interact with users on other servers."
confirmOnReact: "Confirm when reacting"
reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?"
markAsSensitiveConfirm: "Do you want to set this media as sensitive?"
unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for this media?"
_accountSettings:
requireSigninToViewContents: "Require sign-in to view contents"
requireSigninToViewContentsDescription1: "Require login to view all notes and other content you have created. This will have the effect of preventing crawlers from collecting your information."
@@ -2440,6 +2444,8 @@ _notification:
flushNotification: "Clear notifications"
exportOfXCompleted: "Export of {x} has been completed"
login: "Someone logged in"
createToken: "An access token has been created"
createTokenDescription: "If you have no idea, delete the access token through \"{text}\"."
_types:
all: "All"
note: "New notes"
@@ -2590,6 +2596,7 @@ _moderationLogTypes:
deletePage: "Page deleted"
deleteFlash: "Play deleted"
deleteGalleryPost: "Gallery post deleted"
updateProxyAccountDescription: "Update the description of the proxy account"
_fileViewer:
title: "File details"
type: "File type"
@@ -2645,7 +2652,7 @@ _dataSaver:
description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
_avatar:
title: "Avatar image"
description: "Stop avatar image animation. Animated images can be larger in file size than normal images, potentially leading to further reductions in data traffic."
description: "Stop avatar image animation. Animated images can be larger in file size than normal images, potentially leading to further reductions in data traffic."
_urlPreview:
title: "URL preview thumbnails"
description: "URL preview thumbnail images will no longer be loaded."
@@ -2822,8 +2829,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "Response is invalid"
description: "It could communicate with this server, but the data obtained was incorrect."
_responseInvalidIdHostNotMatch:
description: "The domain of the entered URI differs from the domain of the final obtained URI. If you are looking up remote content through a third-party server, please look up again using a URI that can be obtained from the origin server."
_noSuchObject:
title: "Not found"
description: "The requested resource was not found, please recheck the URI."
@@ -2840,3 +2845,23 @@ _captcha:
_unknown:
title: "CAPTCHA error"
text: "An unexpected error occurred."
_bootErrors:
title: "Failed to load"
serverError: "If the problem persists after waiting a moment and reloading, please contact the server administrator with the following Error ID."
solution: "The following may solve the problem."
solution1: "Update your browser and OS to the latest version"
solution2: "Disable ad blocker"
solution3: "Clear the browser cache"
solution4: "Set the dom.webaudio.enabled to true for Tor Browser"
otherOption: "Other options"
otherOption1: "Delete client settings and cache"
otherOption2: "Start the simple client"
otherOption3: "Launch the repair tool"
_search:
searchScopeAll: "All"
searchScopeLocal: "Local"
searchScopeServer: "Specific server"
searchScopeUser: "Specific user"
pleaseEnterServerHost: "Enter the server host"
pleaseSelectUser: "Select user"
serverHostPlaceholder: "Example: misskey.example.com"

View File

@@ -605,6 +605,7 @@ descendingOrder: "Descendente"
scratchpad: "Scratch pad"
scratchpadDescription: "Scratchpad proporciona un entorno experimental para AiScript. Puede escribir, ejecutar y verificar los resultados que interactúan con Misskey."
uiInspector: "Inspector de UI"
uiInspectorDescription: "Puedes visualizar una lista de elementos UI presentes en la memoria. Los componentes de la interfaz de usuario son generados por las funciones UI:C:"
output: "Salida"
script: "Script"
disablePagesScript: "Deshabilitar AiScript en Páginas"
@@ -693,6 +694,7 @@ regexpError: "Error de la expresión regular"
regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}"
instanceMute: "Instancias silenciadas"
userSaysSomething: "{name} dijo algo"
userSaysSomethingAbout: "{name} dijo algo sobre {word}"
makeActive: "Activar"
display: "Apariencia"
copy: "Copiar"
@@ -861,6 +863,7 @@ administration: "Administrar"
accounts: "Cuentas"
switch: "Cambiar"
noMaintainerInformationWarning: "No se ha establecido la información del administrador"
noInquiryUrlWarning: "No se ha guardado la URL de consulta."
noBotProtectionWarning: "La protección contra los bots no está configurada"
configure: "Configurar"
postToGallery: "Crear una nueva publicación en la galería"
@@ -925,6 +928,7 @@ followersVisibility: "Visibilidad de seguidores"
continueThread: "Ver la continuación del hilo"
deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
incorrectPassword: "La contraseña es incorrecta"
incorrectTotp: "La contraseña de un solo uso es incorrecta o ha caducado."
voteConfirm: "¿Confirma su voto a {choice}?"
hide: "Ocultar"
useDrawerReactionPickerForMobile: "Mostrar panel de reacciones en móviles"
@@ -1053,6 +1057,7 @@ thisPostMayBeAnnoyingHome: "Publicar en línea de tiempo 'Inicio'"
thisPostMayBeAnnoyingCancel: "detener"
thisPostMayBeAnnoyingIgnore: "Publicar de todos modos"
collapseRenotes: "Colapsar renotas que ya hayas visto"
collapseRenotesDescription: "Contrae notas a las que ya has reaccionado o renotado "
internalServerError: "Error interno del servidor"
internalServerErrorDescription: "El servidor tuvo un error inesperado."
copyErrorInfo: "Copiar detalles del error"
@@ -1091,6 +1096,7 @@ retryAllQueuesConfirmTitle: "Desea ¿reintentar inmediatamente todas las colas?"
retryAllQueuesConfirmText: "La carga del servidor está incrementándose temporalmente "
enableChartsForRemoteUser: "Generar gráficas de usuarios remotos."
enableChartsForFederatedInstances: "Generar gráficos de servidores remotos"
enableStatsForFederatedInstances: "Activar las estadísticas de las instancias remotas federadas"
showClipButtonInNoteFooter: "Añadir \"Clip\" al menú de notas"
reactionsDisplaySize: "Tamaño de las reacciones"
limitWidthOfReaction: "Limitar ancho de las reacciones"
@@ -1139,6 +1145,7 @@ preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los text
options: "Opción"
specifyUser: "Especificar usuario"
lookupConfirm: "¿Quiere informarse?"
openTagPageConfirm: "¿Quieres abrir la página de etiquetas?"
specifyHost: "Especificar Host"
failedToPreviewUrl: "No se pudo generar la vista previa"
update: "Actualizar"
@@ -1267,13 +1274,29 @@ useBackupCode: "Usar códigos de respaldo"
launchApp: "Ejecutar la app"
useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reproduce audio y vídeo"
keepOriginalFilename: "Mantener el nombre original del archivo"
keepOriginalFilenameDescription: "Si desactivas esta opción, los nombres de los archivos serán remplazados por una cadena de caracteres aleatoria cuando subas los archivos."
noDescription: "No hay descripción"
alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
inquiry: "Contacto"
tryAgain: "Por favor , inténtalo de nuevo"
confirmWhenRevealingSensitiveMedia: "Confirmación cuando se revele contenido sensible"
sensitiveMediaRevealConfirm: "Esto puede contener contenido sensible. ¿Estás seguro/a de querer mostrarlo?"
createdLists: "Listas creadas"
createdAntennas: "Antenas creadas"
fromX: "De {x}"
genEmbedCode: "Obtener el código para incrustar"
noteOfThisUser: "Notas de este usuario"
clipNoteLimitExceeded: "No se pueden añadir más notas a este clip."
performance: "Rendimiento"
modified: "Modificado"
discard: "Descartar"
thereAreNChanges: "Hay {n} cambio(s)"
signinWithPasskey: "Iniciar sesión con clave de acceso"
unknownWebAuthnKey: "Esto no se ha registrado llave maestra."
passkeyVerificationFailed: "La verificación de la clave de acceso ha fallado."
passkeyVerificationSucceededButPasswordlessLoginDisabled: "La verificación de la clave de acceso ha sido satisfactoria pero se ha deshabilitado el inicio de sesión sin contraseña."
messageToFollower: "Mensaje a seguidores"
target: "Para"
federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador."
federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores"
_accountSettings:
@@ -2560,6 +2583,13 @@ _mediaControls:
pip: "Picture in Picture"
playbackRate: "Velocidad de reproducción"
loop: "Reproducción en bucle"
_followRequest:
recieved: "Petición de seguimiento recibida"
sent: "Petición de seguimiento enviada"
_remoteLookupErrors:
_noSuchObject:
title: "No se encuentra"
_search:
searchScopeAll: "Todo"
searchScopeLocal: "Local"
searchScopeUser: "Especificar usuario"

View File

@@ -2364,3 +2364,7 @@ _embedCodeGen:
_remoteLookupErrors:
_noSuchObject:
title: "Non trouvé"
_search:
searchScopeAll: "Tous"
searchScopeLocal: "Local"
searchScopeUser: "Spécifier l'utilisateur·rice"

View File

@@ -2610,3 +2610,7 @@ _mediaControls:
_remoteLookupErrors:
_noSuchObject:
title: "Tidak dapat ditemukan"
_search:
searchScopeAll: "Semua"
searchScopeLocal: "Lokal"
searchScopeUser: "Pengguna spesifik"

52
locales/index.d.ts vendored
View File

@@ -4971,7 +4971,7 @@ export interface Locale extends ILocale {
*/
"disableStreamingTimeline": string;
/**
* 通知をグルーピングして表示する
* 通知をグルーピング
*/
"useGroupedNotifications": string;
/**
@@ -5262,6 +5262,22 @@ export interface Locale extends ILocale {
* " {emoji} " をリアクションしますか?
*/
"reactAreYouSure": ParameterizedString<"emoji">;
/**
* このメディアをセンシティブとして設定しますか?
*/
"markAsSensitiveConfirm": string;
/**
* このメディアのセンシティブ指定を解除しますか?
*/
"unmarkAsSensitiveConfirm": string;
/**
* 環境設定
*/
"preferences": string;
/**
* アクセシビリティ
*/
"accessibility": string;
"_accountSettings": {
/**
* コンテンツの表示にログインを必須にする
@@ -10058,6 +10074,10 @@ export interface Locale extends ILocale {
* ギャラリーの投稿を削除
*/
"deleteGalleryPost": string;
/**
* プロキシアカウントの説明を更新
*/
"updateProxyAccountDescription": string;
};
"_fileViewer": {
/**
@@ -11000,6 +11020,36 @@ export interface Locale extends ILocale {
*/
"otherOption3": string;
};
"_search": {
/**
* 全て
*/
"searchScopeAll": string;
/**
* ローカル
*/
"searchScopeLocal": string;
/**
* サーバー指定
*/
"searchScopeServer": string;
/**
* ユーザー指定
*/
"searchScopeUser": string;
/**
* サーバーのホストを入力してください
*/
"pleaseEnterServerHost": string;
/**
* ユーザーを選択してください
*/
"pleaseSelectUser": string;
/**
* 例: misskey.example.com
*/
"serverHostPlaceholder": string;
};
}
declare const locales: {
[lang: string]: Locale;

View File

@@ -126,7 +126,7 @@ pinnedNote: "Nota in primo piano"
pinned: "Fissa sul profilo"
you: "Tu"
clickToShow: "Contenuto occultato, cliccare solo se si intende vedere"
sensitive: "Allegato esplicito"
sensitive: "Esplicito"
add: "Aggiungi"
reaction: "Reazioni"
reactions: "Reazioni"
@@ -228,7 +228,7 @@ jobQueue: "Coda di lavoro"
cpuAndMemory: "CPU e Memoria"
network: "Rete"
disk: "Disco"
instanceInfo: "Informazioni sull'istanza"
instanceInfo: "Informazioni sul server"
statistics: "Statistiche"
clearQueue: "Svuota coda"
clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?"
@@ -445,7 +445,7 @@ exploreFediverse: "Esplora il Fediverso"
popularTags: "Hashtag popolari"
userList: "Liste"
about: "Informazioni"
aboutMisskey: "Informazioni di Misskey"
aboutMisskey: "A proposito di Misskey"
administrator: "Amministratore"
token: "Token"
2fa: "Autenticazione a due fattori"
@@ -893,7 +893,7 @@ searchResult: "Risultati della Ricerca"
hashtags: "Hashtag"
troubleshooting: "Risoluzione problemi"
useBlurEffect: "Utilizza effetto sfocatura"
learnMore: "Più dettagli"
learnMore: "Per saperne di più"
misskeyUpdated: "Misskey è stato aggiornato!"
whatIsNew: "Informazioni sull'aggiornamento"
translate: "Traduci"
@@ -901,7 +901,7 @@ translatedFrom: "Traduzione da {x}"
accountDeletionInProgress: "È in corso l'eliminazione del profilo"
usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito."
aiChanMode: "Modalità Ai"
devMode: "Modalità sviluppatori"
devMode: "Modalità sviluppo"
keepCw: "Mostra i contenuti espliciti"
pubSub: "Publish/Subscribe del profilo"
lastCommunication: "La comunicazione più recente"
@@ -1049,7 +1049,7 @@ permissionDeniedError: "Errore, attività non autorizzata"
permissionDeniedErrorDescription: "Non si dispone dell'autorizzazione per eseguire questa operazione."
preset: "Preimpostato"
selectFromPresets: "Seleziona preimpostato"
achievements: "Obiettivi raggiunti"
achievements: "Conquiste"
gotInvalidResponseError: "Risposta del server non valida"
gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi."
thisPostMayBeAnnoying: "Questa nota potrebbe essere offensiva"
@@ -1309,6 +1309,12 @@ availableRoles: "Ruoli disponibili"
acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento."
federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione."
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
confirmOnReact: "Confermare le reazioni"
reactAreYouSure: "Vuoi davvero reagire con {emoji} ?"
markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?"
unmarkAsSensitiveConfirm: "Vuoi davvero indicare come non esplicito il contenuto multimediale?"
preferences: "Preferenze"
accessibility: "Accessibilità"
_accountSettings:
requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione"
requireSigninToViewContentsDescription1: "Richiedere l'iscrizione per visualizzare tutte le Note e gli altri contenuti che hai creato. Probabilmente l'effetto è impedire la raccolta di informazioni da parte dei bot crawler."
@@ -1445,9 +1451,9 @@ _initialTutorial:
description: "Queste sono solamente alcune delle funzionalità principali di Misskey. Per ulteriori informazioni, {link}."
_timelineDescription:
home: "Nella Timeline Home, la tua cronologia principale, puoi vedere le Note provenienti dai profili che segui (Following)."
local: "La Timeline Locale, è una cronologia di Note pubblicate da tutti i profili iscritti su questo server."
social: "La Timeline Sociale, unisce in ordine cronologico l'elenco di Note presenti nella Timeline Home e quella Locale."
global: "La Timeline Federata ti consente di vedere le Note pubblicate dai profili di tutti gli altri server federati a questo."
local: "La Timeline Locale è un flusso di Note pubblicate dai profili iscritti a questo server."
social: "La Timeline Sociale elenca, in ordine cronologico, il flusso di Note nella Timeline Home e Locale."
global: "Nella Timeline Federata trovi il flusso di Note provenienti da profili iscritti ad altri server, federati a questo."
_serverRules:
description: "In Europa è necessario mostrare l'informativa sul trattamento dei dati personali, prima della registrazione al servizio."
_serverSettings:
@@ -1471,8 +1477,8 @@ _serverSettings:
_accountMigration:
moveFrom: "Migra un altro profilo dentro a questo"
moveFromSub: "Crea un alias verso un altro profilo remoto"
moveFromLabel: "Profilo da cui migrare #{n}"
moveFromDescription: "Se desideri spostare i Follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@istanza.it"
moveFromLabel: "Profilo da cui migrare n. {n}"
moveFromDescription: "Se desideri spostare i Follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@vecchia.istanza.it"
moveTo: "Migrare questo profilo verso un un altro"
moveToLabel: "Profilo verso cui migrare"
moveCannotBeUndone: "La migrazione è irreversibile, non può essere interrotta o annullata."
@@ -1548,13 +1554,13 @@ _achievements:
title: "Principiante III"
description: "Hai totalizzato 15 accessi!"
_login30:
title: "Misskist I"
title: "Missalcolista I"
description: "Hai totalizzato 30 accessi!"
_login60:
title: "Misskeist II"
title: "Missalcolista II"
description: "Hai totalizzato 60 accessi!"
_login100:
title: "Misskeist III"
title: "Missalcolista III"
description: "Hai totalizzato 100 accessi!"
flavor: "Violent Misskeist"
_login200:
@@ -1640,10 +1646,10 @@ _achievements:
description: "Hai superato i 1.000 profili Follower"
_collectAchievements30:
title: "Collezionista di successi"
description: "Hai raggiunto 30 obiettivi"
description: "Hai raggiunto 30 conquiste"
_viewAchievements3min:
title: "Mi piacciono i risultati"
description: "Guarda la tua collezione di obiettivi per almeno 3 minuti"
description: "Ammira la tua collezione di conquiste per almeno 3 minuti"
_iLoveMisskey:
title: "I LOVE Misskey"
description: "Pubblica «I ♥ #Misskey»"
@@ -1908,7 +1914,7 @@ _registry:
domain: "Dominio"
createKey: "Crea chiave"
_aboutMisskey:
about: "Misskey è un software libero e open source, sviluppato da syuilo dal 2014."
about: "Misskey è software libero, open source, sviluppato da Syuilo fin dal lontano 2014."
contributors: "Principali sostenitori"
allContributors: "Tutti i sostenitori"
source: "Codice sorgente"
@@ -2235,7 +2241,7 @@ _widgets:
userList: "Elenco utenti"
_userList:
chooseList: "Seleziona una lista"
clicker: "Cliccaggio"
clicker: "Cliccheria"
birthdayFollowings: "Compleanni del giorno"
_cw:
hide: "Nascondere"
@@ -2298,7 +2304,7 @@ _profile:
metadataContent: "Contenuto"
changeAvatar: "Modifica immagine profilo"
changeBanner: "Cambia intestazione"
verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo."
verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo.\nPer verificare il profilo tramite la spunta di conferma, devi inserire la url alla pagina che contiene un link al tuo profilo Misskey. Deve avere attributo rel='me'."
avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni."
followedMessage: "Messaggio, quando qualcuno ti segue"
followedMessageDescription: "Puoi impostare un breve messaggio da mostrare agli altri profili quando ti seguono."
@@ -2440,6 +2446,8 @@ _notification:
flushNotification: "Azzera le notifiche"
exportOfXCompleted: "Abbiamo completato l'esportazione di {x}"
login: "Autenticazione avvenuta"
createToken: "È stato creato un token di accesso"
createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
_types:
all: "Tutto"
note: "Nuove Note"
@@ -2590,6 +2598,7 @@ _moderationLogTypes:
deletePage: "Pagina eliminata"
deleteFlash: "Play eliminato"
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria"
updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy"
_fileViewer:
title: "Dettagli del file"
type: "Tipo di file"
@@ -2807,8 +2816,8 @@ _selfXssPrevention:
description2: "Se non sai esattamente cosa stai facendo, %c smetti subito e chiudi questa finestra."
description3: "Per favore, controlla questo collegamento per avere maggiori dettagli. {link}"
_followRequest:
recieved: "Ricezione richiesta di Follow"
sent: "Richiesta di Follow, inviata"
recieved: "Richieste in ingresso"
sent: "Richieste in uscita"
_remoteLookupErrors:
_federationNotAllowed:
title: "Server irraggiungibile"
@@ -2822,8 +2831,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "Risposta non valida"
description: "La comunicazione col server è andata a buon fine, ma abbiamo ricevuto dati non validi."
_responseInvalidIdHostNotMatch:
description: "L'indirizzo immesso non coincide con la URL finale. Interrogando i server per un contenuto remoto, assicurarsi di utilizzare la URL finale e non quella di un server intermedio."
_noSuchObject:
title: "Non trovato"
description: "La risorsa richiesta non è stata trovata. Verificare nuovamente la URL."
@@ -2840,3 +2847,23 @@ _captcha:
_unknown:
title: "Errore CAPTCHA"
text: "Si è verificato un errore imprevisto."
_bootErrors:
title: "Caricamento non riuscito"
serverError: "Dopo una breve attesa, e dopo aver ricaricato la pagina, se il problema persiste, contatta l'amministrazione comunicando il seguente ID di errore."
solution: "Di seguito, alcune probabili soluzioni al problema."
solution1: "Aggiornare browser e il sistema operativo all'ultima versione"
solution2: "Disattivare gli adblocker"
solution3: "Cancellare la cache del browser"
solution4: "(Per chi utilizza il Browser Tor) Impostare dom.webaudio.enabled = vero"
otherOption: "Altre opzioni"
otherOption1: "Nelle impostazioni, cancellare le impostazioni del client e svuotare la cache"
otherOption2: "Avviare il client predefinito"
otherOption3: "Avviare lo strumento di riparazione"
_search:
searchScopeAll: "Tutte"
searchScopeLocal: "Locale"
searchScopeServer: "Specifiche del server"
searchScopeUser: "Profilo specifico"
pleaseEnterServerHost: "Inserire il nome host"
pleaseSelectUser: "Per favore, seleziona un profilo"
serverHostPlaceholder: "Es: misskey.example.com"

View File

@@ -1238,7 +1238,7 @@ releaseToRefresh: "離してリロード"
refreshing: "リロード中"
pullDownToRefresh: "引っ張ってリロード"
disableStreamingTimeline: "タイムラインのリアルタイム更新を無効にする"
useGroupedNotifications: "通知をグルーピングして表示する"
useGroupedNotifications: "通知をグルーピング"
signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
doReaction: "リアクションする"
@@ -1311,6 +1311,10 @@ federationSpecified: "このサーバーはホワイトリスト連合で運用
federationDisabled: "このサーバーは連合が無効化されています。他のサーバーのユーザーとやり取りすることはできません。"
confirmOnReact: "リアクションする際に確認する"
reactAreYouSure: "\" {emoji} \" をリアクションしますか?"
markAsSensitiveConfirm: "このメディアをセンシティブとして設定しますか?"
unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?"
preferences: "環境設定"
accessibility: "アクセシビリティ"
_accountSettings:
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
@@ -2664,6 +2668,7 @@ _moderationLogTypes:
deletePage: "ページを削除"
deleteFlash: "Playを削除"
deleteGalleryPost: "ギャラリーの投稿を削除"
updateProxyAccountDescription: "プロキシアカウントの説明を更新"
_fileViewer:
title: "ファイルの詳細"
@@ -2942,3 +2947,12 @@ _bootErrors:
otherOption1: "クライアント設定とキャッシュを削除"
otherOption2: "簡易クライアントを起動"
otherOption3: "修復ツールを起動"
_search:
searchScopeAll: "全て"
searchScopeLocal: "ローカル"
searchScopeServer: "サーバー指定"
searchScopeUser: "ユーザー指定"
pleaseEnterServerHost: "サーバーのホストを入力してください"
pleaseSelectUser: "ユーザーを選択してください"
serverHostPlaceholder: "例: misskey.example.com"

View File

@@ -5,6 +5,7 @@ introMisskey: "ようお越しMisskeyは、オープンソースの分散型
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>Misskey</b>のサーバーのひとつなんやで。"
monthAndDay: "{month}月 {day}日"
search: "探す"
reset: "リセット"
notifications: "通知"
username: "ユーザー名"
password: "パスワード"
@@ -48,6 +49,7 @@ pin: "ピン留めしとく"
unpin: "ピン留めやめる"
copyContent: "内容をコピー"
copyLink: "リンクをコピー"
copyRemoteLink: "リモートのリンクをコピーするで?"
copyLinkRenote: "リノートのリンクをコピーするで?"
delete: "ほかす"
deleteAndEdit: "ほかして直す"
@@ -684,11 +686,15 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
smtpSecureInfo: "STARTTLS使っとる時はオフにしてや。"
testEmail: "配信テスト"
wordMute: "ワードミュート"
wordMuteDescription: "指定した語句が入ってるノートを最小化するで。最小化されたノートをクリックしたら、表示できるようになるで。"
hardWordMute: "ハードワードミュート"
showMutedWord: "ミュートされたワードを表示するで"
hardWordMuteDescription: "指定した語句が入ってるノートを隠すで。ワードミュートとちゃうて、ノートは完全に表示されんようになるで。"
regexpError: "正規表現エラー"
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:"
instanceMute: "サーバーミュート"
userSaysSomething: "{name}が何か言うとるわ"
userSaysSomethingAbout: "{name}が「{word}」についてなんか言うてたで"
makeActive: "使うで"
display: "表示"
copy: "コピー"
@@ -1301,6 +1307,10 @@ lockdown: "ロックダウン"
pleaseSelectAccount: "アカウント選んでや"
availableRoles: "使えるロール"
acknowledgeNotesAndEnable: "注意事項をわかった上でオンにする。"
federationSpecified: "このサーバーはホワイトリスト連合で運用されてるで。管理者が指定したサーバー以外とはやり取りできひんで。"
federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。"
confirmOnReact: "ツッコむときに確認とる"
reactAreYouSure: "\" {emoji} \" でツッコむ?"
_accountSettings:
requireSigninToViewContents: "ログインしてもらってからコンテンツ見てもらう"
requireSigninToViewContentsDescription1: "あなたが作成した全部のノートとかのコンテンツを見れるようにするのにログインがいるようにするで。クローラーにいろいろ収集されるんを防げるかもしれん。"
@@ -2432,6 +2442,8 @@ _notification:
flushNotification: "通知の履歴をリセットする"
exportOfXCompleted: "{x}のエクスポートが終わったわ"
login: "ログインしとったで"
createToken: "アクセストークンが作成されたで"
createTokenDescription: "心当たりないんやったら「{text}」でアクセストークンを削除してやって。"
_types:
all: "すべて"
note: "あんたらの新規投稿"
@@ -2718,6 +2730,66 @@ _contextMenu:
app: "アプリ"
appWithShift: "Shiftキーでアプリ"
native: "ブラウザのUI"
_gridComponent:
_error:
requiredValue: "この値は必須項目やで"
columnTypeNotSupport: "正規表現によるバリデーションはtype:textのカラムだけサポートしてるで"
patternNotMatch: "この値は{pattern}のパターンに一致しいひんで"
notUnique: "この値は一意でなあかんで"
_roleSelectDialog:
notSelected: "選択されとらんで"
_customEmojisManager:
_gridCommon:
copySelectionRows: "選択行をコピーするで"
copySelectionRanges: "選択範囲をコピーするで"
deleteSelectionRows: "選択行を削除するで"
deleteSelectionRanges: "選択範囲の値をクリアするで"
searchSettings: "検索設定"
searchSettingCaption: "検索条件を詳しく設定するで。"
searchLimit: "表示件数"
sortOrder: "並び順"
registrationLogs: "登録ログ"
registrationLogsCaption: "絵文字更新・削除時のログが表示されるで。更新・削除操作をしたり、ページを遷移・リロードしたら消えるから気ぃつけてな。"
alertEmojisRegisterFailedDescription: "絵文字の更新・削除に失敗したで。詳細は登録ログを確認してな。"
_logs:
showSuccessLogSwitch: "成功ログを表示するで"
failureLogNothing: "失敗ログはあらへん。"
logNothing: "失敗ログはあらへん。"
_remote:
selectionRowDetail: "選択行の詳細やで"
importSelectionRows: "選択行をインポートするで"
importSelectionRangesRows: "選択範囲の行をインポートするで"
importEmojisButton: "チェックされた絵文字をインポートするで"
confirmImportEmojisTitle: "絵文字のインポートするで"
confirmImportEmojisDescription: "リモートから受信した{count}個の絵文字をインポートするで。絵文字のライセンスには十分気ぃつけてな。実行してもええか?"
_local:
tabTitleList: "登録済み絵文字一覧"
tabTitleRegister: "絵文字の登録"
_list:
emojisNothing: "登録された絵文字はないで。"
markAsDeleteTargetRows: "選択行を削除対象にするで"
markAsDeleteTargetRanges: "選択範囲の行を削除対象にするで"
alertUpdateEmojisNothingDescription: "変更された絵文字はないで。"
alertDeleteEmojisNothingDescription: "削除対象の絵文字はないで。"
confirmMovePage: "ページを移動してもええんか?"
confirmChangeView: "表示を変更してもええんか?"
confirmUpdateEmojisDescription: "{count}個の絵文字を更新するで。実行してもええか?"
confirmDeleteEmojisDescription: "チェックがつけられた{count}個の絵文字を削除するで。ほんまにええか?"
confirmResetDescription: "今までやった変更が全部リセットされるで。"
confirmMovePageDesciption: "このページの絵文字に変更が加えられてるで。\n保存せずページを移動してまうと、このページで加えた変更が全てパーになるで。"
dialogSelectRoleTitle: "絵文字に設定されたロールで検索"
_register:
uploadSettingTitle: "アップロード設定"
uploadSettingDescription: "この画面で絵文字アップロードするときの動きを設定できるで。"
directoryToCategoryLabel: "ディレクトリ名を\"category\"に入力する"
directoryToCategoryCaption: "ディレクトリをドラッグ・ドロップした時に、ディレクトリ名を\"category\"に入力します。"
emojiInputAreaCaption: "どれかの方法で登録する絵文字を選択して。"
emojiInputAreaList1: "この枠に画像ファイルかディレクトリをドラッグ&ドロップ"
emojiInputAreaList2: "このリンクをクリックしてPCから選択する"
emojiInputAreaList3: "このリンクをクリックしてドライブから選択する"
confirmRegisterEmojisDescription: "リストに表示されてる絵文字を新たなカスタム絵文字として登録するで。ほんまにええか? (サーバーがしんどくなるから、一回で登録できる絵文字は{count}件までやで)"
confirmClearEmojisDescription: "編集内容をほかして、リストに表示されている絵文字をクリアするで。ほんまにええか?"
confirmUploadEmojisDescription: "ドラッグ&ドロップされた{count}個のファイルをドライブにアップロードするで。ほんまにええか?"
_embedCodeGen:
title: "埋め込みコードをカスタム"
header: "ヘッダー出す"
@@ -2754,8 +2826,35 @@ _remoteLookupErrors:
_responseInvalid:
title: "レスポンスがおかしいで"
description: "このサーバーと通信することはできたけど、もらったデータがおかしかったで。"
_responseInvalidIdHostNotMatch:
description: "入力されたURIのドメインと最終的に得られたURIのドメインとが違うで。第三者のサーバーを介してリモートのコンテンツを照会してるんやったら、発信元のサーバーで取得できるURIを使って照会し直して。"
_noSuchObject:
title: "見つからへんね"
description: "求められたリソースが見つからんかったで。URIをもっかい確かめてや。"
_captcha:
verify: "CAPTCHAしばいたって"
testSiteKeyMessage: "サイトキーとシークレットキーにテスト用の値を入力することでプレビューを確認できるで。\n詳細は下記ページを確認してな。"
_error:
_requestFailed:
title: "CAPTCHAのリクエストに失敗してもうた"
text: "しばらく後で実行するか、設定をもっかい確認してや。"
_verificationFailed:
title: "CAPTCHAのリクエストに失敗してもうた"
text: "設定がほんまに合ってるかもっかい確認してや。"
_unknown:
title: "CAPTCHAエラー"
text: "思いもせんかったエラーが起きたわ。"
_bootErrors:
title: "読み込みに失敗したで"
serverError: "少し待ってからリロードしてもまだ問題が解決されんのやったら、以下のError IDを添えてサーバー管理者に連絡して。"
solution: "以下のことやったら解決するかもやで。"
solution1: "ブラウザとかOSを最新バージョンに更新する"
solution2: "アドブロッカーを無効にする"
solution3: "ブラウザのキャッシュをクリアする"
solution4: "(Tor Browser) dom.webaudio.enabledをtrueに設定する"
otherOption: "ほかのオプション"
otherOption1: "クライアント設定とキャッシュをほかす"
otherOption2: "簡易クライアントを起動"
otherOption3: "修復ツールを起動"
_search:
searchScopeAll: "みんな"
searchScopeLocal: "ローカル"
searchScopeUser: "ユーザー指定"

View File

@@ -843,3 +843,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "몬 찾앗십니다"
_search:
searchScopeAll: "말캉"
searchScopeUser: "사용자 지정"

View File

@@ -2822,8 +2822,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "유효하지 않은 반응입니다."
description: "이 서버와 통신할 수 있지만, 데이터가 올바르지 않습니다."
_responseInvalidIdHostNotMatch:
description: "입력된 URI과 실제 URI가 다릅니다. 제 3자 서버를 통한 리모트 컨텐츠를 조회하는 경우, 원래 서버 측에서 받아올 수 있는 URI를 사용하여 조회하시길 바랍니다."
_noSuchObject:
title: "찾을 수 없습니다"
description: "요구된 리소스를 찾을 수 없습니다. URI를 다시 한 번 확인해보세요."
@@ -2840,3 +2838,10 @@ _captcha:
_unknown:
title: "CAPTCHA 에러"
text: "알 수 없는 에러가 발생했습니다."
_bootErrors:
title: "로딩이 실패함"
solution4: "(Tor Browser) dom.webaudio.enabled를 true로 설정하세요"
_search:
searchScopeAll: "전체"
searchScopeLocal: "로컬"
searchScopeUser: "사용자 지정"

View File

@@ -477,3 +477,5 @@ _moderationLogTypes:
_remoteLookupErrors:
_noSuchObject:
title: "ບໍ່ພົບ"
_search:
searchScopeAll: "ທັງໝົດ"

View File

@@ -540,3 +540,5 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Niet gevonden"
_search:
searchScopeAll: "Alle"

View File

@@ -730,3 +730,5 @@ _moderationLogTypes:
_remoteLookupErrors:
_noSuchObject:
title: "Ikke funnet"
_search:
searchScopeAll: "Alle"

View File

@@ -1583,3 +1583,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Nie znaleziono"
_search:
searchScopeAll: "Wszystkie"
searchScopeLocal: "Lokalne"

View File

@@ -2754,8 +2754,10 @@ _remoteLookupErrors:
_responseInvalid:
title: "Resposta inválida"
description: "Foi possível comunicar com o servidor, porém os dados obtidos foram incorretos."
_responseInvalidIdHostNotMatch:
description: "O domínio do endereço inserido difere do domínio do endereço final. Se você estiver pesquisando por um servidor de terceiros, tente buscar novamente com um endereço que pode ser obtido através do servidor original."
_noSuchObject:
title: "Não encontrado"
description: "O recurso solicitado não foi encontrado, confira o endereço."
_search:
searchScopeAll: "Todos"
searchScopeLocal: "Local"
searchScopeUser: "Usuário específico"

View File

@@ -736,3 +736,5 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Nu a fost găsit"
_search:
searchScopeAll: "Tot"

View File

@@ -2147,3 +2147,7 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Не найдено"
_search:
searchScopeAll: "Все"
searchScopeLocal: "Местная"
searchScopeUser: "Указанный пользователь"

View File

@@ -1449,3 +1449,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Nenájdené"
_search:
searchScopeAll: "Všetko"
searchScopeLocal: "Lokálne"

View File

@@ -707,3 +707,5 @@ _reversi:
white: "Vit"
_selfXssPrevention:
warning: "VARNING"
_search:
searchScopeAll: "Allt"

View File

@@ -2709,3 +2709,7 @@ _embedCodeGen:
_remoteLookupErrors:
_noSuchObject:
title: "ไม่พบหน้าที่ต้องการ"
_search:
searchScopeAll: "ทั้งหมด"
searchScopeLocal: "ท้องถิ่น"
searchScopeUser: "ผู้ใช้เฉพาะ"

View File

@@ -460,3 +460,5 @@ _deck:
_moderationLogTypes:
suspend: "askıya al"
resetPassword: "Şifre sıfırlama"
_search:
searchScopeAll: "Tümü"

View File

@@ -1624,3 +1624,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Не знайдено"
_search:
searchScopeAll: "Всі"
searchScopeLocal: "Локальна"

View File

@@ -1094,3 +1094,6 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Topilmadi"
_search:
searchScopeAll: "Barcha"
searchScopeLocal: "Mahalliy"

View File

@@ -1930,3 +1930,7 @@ _reversi:
_remoteLookupErrors:
_noSuchObject:
title: "Không tìm thấy"
_search:
searchScopeAll: "Tất cả"
searchScopeLocal: "Máy chủ này"
searchScopeUser: "Người dùng chỉ định"

View File

@@ -1309,6 +1309,12 @@ availableRoles: "可用角色"
acknowledgeNotesAndEnable: "理解注意事项后再开启。"
federationSpecified: "此服务器已开启联合白名单。只能与管理员指定的服务器通信。"
federationDisabled: "此服务器已禁用联合。无法与其它服务器上的用户通信。"
confirmOnReact: "发送回应前需要确认"
reactAreYouSure: "要用「{emoji}」进行回应吗?"
markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
preferences: "设置"
accessibility: "辅助功能"
_accountSettings:
requireSigninToViewContents: "需要登录才能显示内容"
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
@@ -2440,6 +2446,8 @@ _notification:
flushNotification: "重置通知历史"
exportOfXCompleted: "已完成 {x} 的导出"
login: "有新的登录"
createToken: "访问令牌已创建"
createTokenDescription: "如果不明白其用途,请遵循「{text}」的指示删除访问令牌。"
_types:
all: "全部"
note: "用户的新帖子"
@@ -2590,6 +2598,7 @@ _moderationLogTypes:
deletePage: "删除了页面"
deleteFlash: "删除了 Play"
deleteGalleryPost: "删除了图库稿件"
updateProxyAccountDescription: "更新代理账户的说明"
_fileViewer:
title: "文件信息"
type: "文件类型"
@@ -2777,8 +2786,8 @@ _customEmojisManager:
_register:
uploadSettingTitle: "上传设置"
uploadSettingDescription: "可以在此页面设置上传表情符号时的行为。"
directoryToCategoryLabel: "目录名请输入「category」"
directoryToCategoryCaption: "拖放目录时,目录名请输入「category」"
directoryToCategoryLabel: "目录名设为「category」"
directoryToCategoryCaption: "拖放目录时,目录名设置为「category」"
emojiInputAreaCaption: "请使用其中一种方法选择要注册的表情符号。"
emojiInputAreaList1: "在此区域内拖放图像文件或者目录"
emojiInputAreaList2: "单击此链接以从电脑中选择"
@@ -2822,8 +2831,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "响应无效"
description: "成功与此服务器通信,但返回的数据无效。"
_responseInvalidIdHostNotMatch:
description: "输入 URI 的域名和最终取得的 URI 的域名不同。如果是通过第三方服务器获取远程内容,请使用可以从原始服务器获取内容的 URI 再试一次。"
_noSuchObject:
title: "未找到"
description: "未找到请求的资源。请再次检查 URI。"
@@ -2840,3 +2847,23 @@ _captcha:
_unknown:
title: "CAPTCHA 错误"
text: "发生意外错误。"
_bootErrors:
title: "加载失败"
serverError: "请稍等片刻再重试。若问题仍无法解决,请将以下 Error ID 一起发送给管理员。"
solution: "以下方法或许可以解决问题:"
solution1: "将浏览器及操作系统更新到最新版本"
solution2: "禁用广告屏蔽插件"
solution3: "清除浏览器缓存"
solution4: "Tor Browser将 dom.webaudio.enabled 设定为 true"
otherOption: "其它选项"
otherOption1: "清除客户端设定与缓存"
otherOption2: "使用简易客户端"
otherOption3: "启动修复工具"
_search:
searchScopeAll: "全部"
searchScopeLocal: "本地"
searchScopeServer: "指定服务器"
searchScopeUser: "指定用户"
pleaseEnterServerHost: "请填写服务器主机名"
pleaseSelectUser: "请选择用户"
serverHostPlaceholder: "如misskey.example.com"

View File

@@ -368,7 +368,7 @@ normal: "正常"
instanceName: "伺服器名稱"
instanceDescription: "伺服器介紹"
maintainerName: "管理員名稱"
maintainerEmail: "管理員箱"
maintainerEmail: "管理員箱"
tosUrl: "服務條款 URL"
thisYear: "本年"
thisMonth: "本月"
@@ -464,7 +464,7 @@ securityKey: "安全金鑰"
lastUsed: "上次使用"
lastUsedAt: "上次使用:{t}"
unregister: "註銷"
passwordLessLogin: "設置無密碼登入"
passwordLessLogin: "無密碼登入"
passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入"
resetPassword: "重設密碼"
newPasswordIs: "新密碼為「{password}」"
@@ -521,7 +521,7 @@ menuStyle: "選單風格"
style: "風格"
drawer: "側邊欄"
popup: "彈出式視窗"
showNoteActionsOnlyHover: "僅游標停時顯示貼文"
showNoteActionsOnlyHover: "僅游標停時顯示貼文選項"
showReactionsCount: "顯示貼文的反應數目"
noHistory: "沒有歷史紀錄"
signinHistory: "登入歷史"
@@ -558,12 +558,12 @@ useObjectStorage: "使用物件儲存"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "用於引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL例如 S3https://<bucket>.s3.amazonaws.com、GCShttps://storage.googleapis.com/<bucket>)。"
objectStorageBucket: "儲存空間Bucket"
objectStorageBucketDesc: "請填寫所用服務的儲存空間Bucket名稱。 "
objectStorageBucketDesc: "請填寫所用服務的儲存Bucket名稱。 "
objectStoragePrefix: "前綴"
objectStoragePrefixDesc: "它儲存在此前綴目錄下。"
objectStorageEndpoint: "端點Endpoint"
objectStorageEndpointDesc: "如使用 AWS S3請留空。如使用其他服務請按照其說明文件以「<host>」或「<host>:<port>」的形式設定端點Endpoint。"
objectStorageRegion: "Region"
objectStorageRegion: "Region"
objectStorageRegionDesc: "請填寫一個分區例如「xx-east-1」。 如果您使用的服務不設分區請留空或填寫「us-east-1」。"
objectStorageUseSSL: "使用 SSL"
objectStorageUseSSLDesc: "請在不使用 https 連接 API 時關閉"
@@ -586,7 +586,7 @@ popout: "彈出式視窗"
volume: "音量"
masterVolume: "主音量"
notUseSound: "關閉音效"
useSoundOnlyWhenActive: "瀏覽器在前景運作時,Misskey 才會發出音效"
useSoundOnlyWhenActive: "僅在 Misskey 於前景運作時發出音效"
details: "詳細資訊"
renoteDetails: "轉發貼文的細節"
chooseEmoji: "選擇您的表情符號"
@@ -681,7 +681,7 @@ smtpHost: "主機"
smtpPort: "埠"
smtpUser: "使用者名稱"
smtpPass: "密碼"
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以關閉SMTP驗證。"
emptyToDisableSmtpAuth: "使用者名稱和密碼留空以關閉 SMTP 驗證。"
smtpSecure: "在 SMTP 連接中使用隱式 SSL/TLS"
smtpSecureInfo: "使用 STARTTLS 時關閉。"
testEmail: "測試郵件發送"
@@ -711,7 +711,7 @@ useGlobalSetting: "使用全域設定"
useGlobalSettingDesc: "啟用時,將使用帳戶通知設定。停用時,則可以單獨設定。"
other: "其他"
regenerateLoginToken: "重新產生登入權杖"
regenerateLoginTokenDescription: "重新產生用於登入的內部權杖。一般情況下是不需要這樣做的。重新產生後,所有裝置將被登出。"
regenerateLoginTokenDescription: "重新產生用於登入的內部權杖。通常不需要使用此功能。重新產生後,所有裝置將被登出。"
theKeywordWhenSearchingForCustomEmoji: "這是搜尋自訂表情符號時的關鍵字"
setMultipleBySeparatingWithSpace: "您可以使用空格分隔多個項目。"
fileIdOrUrl: "檔案 ID 或 URL"
@@ -745,7 +745,7 @@ unclip: "解除摘錄"
confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?"
public: "公開"
private: "私密"
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以瀏覽 {link} 助翻譯。"
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以前往 {link} 以協助翻譯。"
manageAccessTokens: "管理存取權杖"
accountInfo: "帳戶資訊"
notesCount: "貼文數量"
@@ -781,7 +781,7 @@ useSystemFont: "使用系統預設的字型"
clips: "摘錄"
experimentalFeatures: "實驗中的功能"
experimental: "實驗性"
thisIsExperimentalFeature: "這是實驗性功能。可能會有變更規格和不能正常動作的可能性。"
thisIsExperimentalFeature: "這是一項實驗性功能,其行為會隨需要進行調整,也可能無法正常運作。"
developer: "開發者"
makeExplorable: "使自己的帳戶更容易被找到"
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在「探索」頁面中。"
@@ -1207,7 +1207,7 @@ notificationRecieveConfig: "接受通知的設定"
mutualFollow: "互相追隨"
followingOrFollower: "追隨中或者追隨者"
fileAttachedOnly: "只顯示包含附件的貼文"
showRepliesToOthersInTimeline: "顯示給其他人的回覆"
showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆"
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
showRepliesToOthersInTimelineAll: "在時間軸包含追隨中所有人的回覆"
hideRepliesToOthersInTimelineAll: "在時間軸不包含追隨中所有人的回覆"
@@ -1247,7 +1247,7 @@ reloadRequiredToApplySettings: "需要重新載入頁面設定才能生效。"
remainingN: "剩餘:{n}"
overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
seasonalScreenEffect: "隨季節變換畫面的呈現"
decorate: "設置頭像裝飾"
decorate: "裝飾"
addMfmFunction: "插入 MFM 功能語法"
enableQuickAddMfmFunction: "顯示進階 MFM 選擇器"
bubbleGame: "氣泡遊戲"
@@ -1274,7 +1274,7 @@ useBackupCode: "使用備用驗證碼"
launchApp: "啟動 APP"
useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
keepOriginalFilename: "保留原始檔名"
keepOriginalFilenameDescription: "如果關閉此設,上傳時檔案名稱會自動替換為隨機字串。"
keepOriginalFilenameDescription: "如果關閉此設,上傳時檔案名稱會自動替換為隨機字串。"
noDescription: "沒有說明文字"
alwaysConfirmFollow: "追隨時總是確認"
inquiry: "聯絡我們"
@@ -1309,6 +1309,12 @@ availableRoles: "可用角色"
acknowledgeNotesAndEnable: "了解注意事項後再開啟。"
federationSpecified: "此伺服器以白名單聯邦的方式運作。除了管理員指定的伺服器外,它無法與其他伺服器互動。"
federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。"
confirmOnReact: "反應時確認"
reactAreYouSure: "用「 {emoji} 」反應嗎?"
markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?"
unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?"
preferences: "環境設定"
accessibility: "輔助工具"
_accountSettings:
requireSigninToViewContents: "須登入以顯示內容"
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
@@ -1433,7 +1439,7 @@ _initialTutorial:
useCases: "伺服器的服務條款可能會規範特定的貼文需要使用隱藏內容,除此之外也會用在隱藏劇情洩漏與敏感內容的貼文。"
_howToMakeAttachmentsSensitive:
title: "如何標記上傳附件為敏感內容?"
description: "如果伺服器服務條款有規範,又或者不希望上傳附件直接被看見,可以設置為「敏感內容」"
description: "如果伺服器服務條款有規範,又或者不適合直接展示的附件,請記得加上「敏感」標記。"
tryThisFile: "試試看!把附加在發文表單的圖像檔案標記為敏感內容。"
_exampleNote:
note: "打開納豆的包裝失敗了…"
@@ -1467,7 +1473,7 @@ _serverSettings:
inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址,或包含運營者聯絡資訊網頁的網址。"
openRegistration: "允許建立帳戶"
openRegistrationWarning: "開放註冊伴隨著風險。 建議只有在伺服器受到持續監控,並準備好在出現問題時能立即處理的情況下才開放註冊。"
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "為了防止 spam如果一段期間內沒有偵測到審查員活動,此設定將自動關閉。"
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "如果一段期間內沒有偵測到任何審查員活動,此設定將自動關閉,以防止垃圾內容。"
_accountMigration:
moveFrom: "從其他帳戶遷移到這個帳戶"
moveFromSub: "為另一個帳戶建立別名"
@@ -1481,7 +1487,7 @@ _accountMigration:
startMigration: "遷移"
migrationConfirm: "確定要將這個帳戶遷移至 {account} 嗎?一旦遷移就無法撤銷,也就無法以原來的狀態使用這個帳戶。\n另外請確認在要遷移到的帳戶已經建立了一個別名。"
movedAndCannotBeUndone: "帳戶已遷移。\n遷移無法撤消。"
postMigrationNote: "將在完成遷移的 24 小時取消追隨所有帳號。\n此帳戶的追隨中/追隨者人數將歸零。由於不會解除粉絲對您的追隨,因此他們仍然可以繼續閱覽此帳戶僅對追隨者公開的貼文。"
postMigrationNote: "將在完成遷移的 24 小時取消追隨所有帳號。\n此帳戶的追隨中/追隨者人數將歸零。由於不會解除粉絲對您的追隨,因此他們仍然可以繼續閱覽此帳戶僅對追隨者公開的貼文。"
movedTo: "要遷移到的帳戶:"
_achievements:
earnedAt: "獲得日期"
@@ -1798,7 +1804,7 @@ _role:
canHideAds: "不顯示廣告"
canSearchNotes: "可否搜尋貼文"
canUseTranslator: "使用翻譯功能"
avatarDecorationLimit: "頭像裝飾的最大設置量"
avatarDecorationLimit: "頭像可掛上的最大裝飾數量"
canImportAntennas: "允許匯入天線"
canImportBlocking: "允許匯入封鎖名單"
canImportFollowing: "允許匯入追隨名單"
@@ -1957,7 +1963,7 @@ _instanceMute:
instanceMuteDescription: "包括對被靜音伺服器上的使用者的回覆,被設定的伺服器上所有貼文及轉發都會被靜音。"
instanceMuteDescription2: "設定時以換行進行分隔"
title: "將隱藏被設定的伺服器貼文。"
heading: "伺服器靜音"
heading: "要靜音的伺服器"
_theme:
explore: "探索佈景主題"
install: "安裝佈景主題"
@@ -2408,7 +2414,7 @@ _pages:
note: "嵌式貼文"
_note:
id: "貼文ID"
idDescription: "您也可以粘貼筆記 URL 進行設。 "
idDescription: "您也可以貼上貼文 URL 進行設。 "
detailed: "顯示詳細內容"
_relayStatus:
requesting: "等待核准"
@@ -2440,6 +2446,8 @@ _notification:
flushNotification: "重置通知歷史紀錄"
exportOfXCompleted: "{x} 的匯出已完成。"
login: "已登入"
createToken: "已產生存取權杖"
createTokenDescription: "如果您不知道,請透過「{text}」刪除存取權杖。"
_types:
all: "全部 "
note: "使用者的最新貼文"
@@ -2590,6 +2598,7 @@ _moderationLogTypes:
deletePage: "刪除頁面"
deleteFlash: "刪除 Play"
deleteGalleryPost: "刪除相簿的貼文"
updateProxyAccountDescription: "更新代理帳戶的說明"
_fileViewer:
title: "檔案詳細資訊"
type: "檔案類型 "
@@ -2655,7 +2664,7 @@ _dataSaver:
_hemisphere:
N: "北半球"
S: "南半球"
caption: "某些客戶端的設定中,用於判斷季節。"
caption: "某些客戶端的設定會用此來判斷季節。"
_reversi:
reversi: "黑白棋"
gameSettings: "對弈設定"
@@ -2822,8 +2831,6 @@ _remoteLookupErrors:
_responseInvalid:
title: "回應不正確"
description: "雖然能夠與這個伺服器通訊,但是取得的資料不正確。"
_responseInvalidIdHostNotMatch:
description: "輸入的 URI 的網域與最終取得的 URI 的網域不同。 如果您是透過第三方伺服器查詢遠端內容,請使用可在原始伺服器上取得的 URI 再次查詢。"
_noSuchObject:
title: "查無項目"
description: "無法找到所要求的資源,請再次檢查 URI。"
@@ -2840,3 +2847,23 @@ _captcha:
_unknown:
title: "CAPTCHA 錯誤"
text: "發生了意外的錯誤。"
_bootErrors:
title: "載入失敗"
serverError: "如果稍等片刻並重新載入後問題仍然存在,請聯絡您的伺服器管理員並提供以下的錯誤 ID。"
solution: "執行以下操作或許可以解決問題。"
solution1: "將瀏覽器和作業系統更新至最新版本"
solution2: "停用廣告攔截器"
solution3: "清除瀏覽器的快取"
solution4: "Tor 瀏覽器)將 dom.webaudio.enabled 設為 true"
otherOption: "其他選項"
otherOption1: "刪除用戶端設定和快取"
otherOption2: "啟動簡易用戶端"
otherOption3: "啟動修復工具"
_search:
searchScopeAll: "全部"
searchScopeLocal: "本地"
searchScopeServer: "指定伺服器"
searchScopeUser: "指定使用者"
pleaseEnterServerHost: "請輸入伺服器的主機名稱"
pleaseSelectUser: "請選擇使用者"
serverHostPlaceholder: "例misskey.example.com"

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2025.2.1-beta.1",
"version": "2025.3.1",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@9.15.4",
"packageManager": "pnpm@10.6.1",
"workspaces": [
"packages/frontend-shared",
"packages/frontend",
@@ -25,7 +25,7 @@
"build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"init": "pnpm migrate",
"migrate": "cd packages/backend && pnpm migrate",
"revert": "cd packages/backend && pnpm revert",
@@ -37,7 +37,7 @@
"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "pnpm cypress run",
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"e2e-dev-container": "cp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"e2e-dev-container": "ncp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"jest": "cd packages/backend && pnpm jest",
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
"test": "pnpm -r test",
@@ -47,35 +47,44 @@
"cleanall": "pnpm clean-all"
},
"resolutions": {
"chokidar": "3.6.0",
"chokidar": "4.0.3",
"lodash": "4.17.21"
},
"dependencies": {
"cssnano": "7.0.6",
"execa": "8.0.1",
"execa": "9.5.2",
"fast-glob": "3.3.3",
"ignore-walk": "6.0.5",
"ignore-walk": "7.0.0",
"js-yaml": "4.1.0",
"postcss": "8.5.2",
"tar": "6.2.1",
"postcss": "8.5.3",
"tar": "7.4.3",
"terser": "5.39.0",
"typescript": "5.7.3",
"typescript": "5.8.2",
"esbuild": "0.25.0",
"glob": "11.0.1"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0",
"@types/node": "22.13.4",
"@typescript-eslint/eslint-plugin": "8.24.0",
"@typescript-eslint/parser": "8.24.0",
"@types/node": "22.13.9",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"cross-env": "7.0.3",
"cypress": "14.0.3",
"eslint": "9.20.1",
"globals": "15.15.0",
"cypress": "14.1.0",
"eslint": "9.21.0",
"globals": "16.0.0",
"ncp": "2.0.0",
"pnpm": "10.6.1",
"start-server-and-test": "2.0.10"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
},
"pnpm": {
"overrides": {
"@aiscript-dev/aiscript-languageserver": "-"
},
"patchedDependencies": {
"re2": "scripts/dependency-patches/re2.patch"
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SystemAccounts1740121393164 {
name = 'SystemAccounts1740121393164'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "system_account" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "type" character varying(256) NOT NULL, CONSTRAINT "PK_edb56f4aaf9ddd50ee556da97ba" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_41a3c87a37aea616ee459369e1" ON "system_account" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c362033aee0ea51011386a5a7e" ON "system_account" ("type") `);
await queryRunner.query(`ALTER TABLE "system_account" ADD CONSTRAINT "FK_41a3c87a37aea616ee459369e12" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
const instanceActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'instance.actor'`);
if (instanceActor.length > 0) {
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('${instanceActor[0].id}', '${instanceActor[0].id}', 'actor')`);
}
const relayActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'relay.actor'`);
if (relayActor.length > 0) {
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('${relayActor[0].id}', '${relayActor[0].id}', 'relay')`);
}
const meta = await queryRunner.query(`SELECT "proxyAccountId" FROM "meta" ORDER BY "id" DESC LIMIT 1`);
if (!meta && meta.length >= 1 && meta[0].proxyAccountId) {
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('${meta[0].proxyAccountId}', '${meta[0].proxyAccountId}', 'proxy')`);
}
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "system_account" DROP CONSTRAINT "FK_41a3c87a37aea616ee459369e12"`);
await queryRunner.query(`DROP INDEX "public"."IDX_c362033aee0ea51011386a5a7e"`);
await queryRunner.query(`DROP INDEX "public"."IDX_41a3c87a37aea616ee459369e1"`);
await queryRunner.query(`DROP TABLE "system_account"`);
}
}

View File

@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SystemAccounts21740129169650 {
name = 'SystemAccounts21740129169650'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "proxyAccountId" character varying(32)`);
const proxyAccountId = await queryRunner.query(`SELECT "userId" FROM "system_account" WHERE "type" = 'proxy' ORDER BY "id" DESC LIMIT 1`);
if (proxyAccountId && proxyAccountId.length >= 1) {
await queryRunner.query(`UPDATE "meta" SET "proxyAccountId" = '${proxyAccountId[0].userId}'`);
}
await queryRunner.query(`ALTER TABLE "meta" ADD CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad" FOREIGN KEY ("proxyAccountId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
}
}

View File

@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SystemAccounts31740133121105 {
name = 'SystemAccounts31740133121105'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "rootUserId" character varying(32)`);
await queryRunner.query(`ALTER TABLE "meta" ADD CONSTRAINT "FK_c80e4079d632f95eac06a9d28cc" FOREIGN KEY ("rootUserId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
const users = await queryRunner.query(`SELECT "id" FROM "user" WHERE "isRoot" = true LIMIT 1`);
if (users.length > 0) {
await queryRunner.query(`UPDATE "meta" SET "rootUserId" = $1`, [users[0].id]);
}
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP CONSTRAINT "FK_c80e4079d632f95eac06a9d28cc"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "rootUserId"`);
}
}

View File

@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SystemAccounts41740993126937 {
name = 'SystemAccounts41740993126937'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isRoot"`);
}
async down(queryRunner) {
// down 実行時は isRoot = true のユーザーが存在しなくなるため手動で対応する必要あり
await queryRunner.query(`ALTER TABLE "user" ADD "isRoot" boolean NOT NULL DEFAULT false`);
}
}

View File

@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SystemAccounts1741279404074 {
name = 'SystemAccounts1741279404074'
async up(queryRunner) {
const instanceActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'instance.actor' AND "host" IS NULL AND "id" NOT IN (SELECT "userId" FROM "system_account" WHERE "type" = 'actor')`);
if (instanceActor.length > 0) {
console.warn('instance.actor was incorrect, updating...');
await queryRunner.query(`UPDATE "system_account" SET "id" = '${instanceActor[0].id}', "userId" = '${instanceActor[0].id}' WHERE "type" = 'actor'`);
}
const relayActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'relay.actor' AND "host" IS NULL AND "id" NOT IN (SELECT "userId" FROM "system_account" WHERE "type" = 'relay')`);
if (relayActor.length > 0) {
console.warn('relay.actor was incorrect, updating...');
await queryRunner.query(`UPDATE "system_account" SET "id" = '${relayActor[0].id}', "userId" = '${relayActor[0].id}' WHERE "type" = 'relay'`);
}
}
async down(queryRunner) {
// fixup migration, no down migration
}
}

View File

@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class UserFeaturedFixup1741424411879 {
name = 'UserFeaturedFixup1741424411879'
async up(queryRunner) {
await queryRunner.query(`CREATE OR REPLACE FUNCTION pg_temp.extract_ap_id(text) RETURNS text AS $$
SELECT
CASE
WHEN $1 ~ '^https?://' THEN $1
WHEN $1 LIKE '{%' THEN COALESCE(jsonb_extract_path_text($1::jsonb, 'id'), null)
ELSE null
END;
$$ LANGUAGE sql IMMUTABLE;`);
// "host" is NOT NULL is not needed but just in case add it to prevent overwriting irreplaceable data
await queryRunner.query(`UPDATE "user" SET "featured" = pg_temp.extract_ap_id("featured") WHERE "host" IS NOT NULL`);
}
async down(queryRunner) {
// fixup migration, no down migration
}
}

View File

@@ -133,7 +133,7 @@ const $meta: Provider = {
for (const key in body.after) {
(meta as any)[key] = (body.after as any)[key];
}
meta.proxyAccount = null; // joinなカラムは通常取ってこないので
meta.rootUser = null; // joinなカラムは通常取ってこないので
break;
}
default:

View File

@@ -10,9 +10,9 @@ import { bindThis } from '@/decorators.js';
import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
import { QueueService } from '@/core/QueueService.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { IdService } from './IdService.js';
@Injectable()
@@ -27,7 +27,7 @@ export class AbuseReportService {
private idService: IdService,
private abuseReportNotificationService: AbuseReportNotificationService,
private queueService: QueueService,
private instanceActorService: InstanceActorService,
private systemAccountService: SystemAccountService,
private apRendererService: ApRendererService,
private moderationLogService: ModerationLogService,
) {
@@ -136,7 +136,7 @@ export class AbuseReportService {
forwarded: true,
});
const actor = await this.instanceActorService.getInstanceActor();
const actor = await this.systemAccountService.fetch('actor');
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);

View File

@@ -20,10 +20,10 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
@Injectable()
export class AccountMoveService {
@@ -55,12 +55,12 @@ export class AccountMoveService {
private apRendererService: ApRendererService,
private apDeliverManagerService: ApDeliverManagerService,
private globalEventService: GlobalEventService,
private proxyAccountService: ProxyAccountService,
private perUserFollowingChart: PerUserFollowingChart,
private federatedInstanceService: FederatedInstanceService,
private instanceChart: InstanceChart,
private relayService: RelayService,
private queueService: QueueService,
private systemAccountService: SystemAccountService,
) {
}
@@ -126,11 +126,11 @@ export class AccountMoveService {
}
// follow the new account
const proxy = await this.proxyAccountService.fetch();
const proxy = await this.systemAccountService.fetch('proxy');
const followings = await this.followingsRepository.findBy({
followeeId: src.id,
followerHost: IsNull(), // follower is local
followerId: proxy ? Not(proxy.id) : undefined,
followerId: Not(proxy.id),
});
const followJobs = followings.map(following => ({
from: { id: following.followerId },
@@ -250,10 +250,8 @@ export class AccountMoveService {
// Have the proxy account follow the new account in the same way as UserListService.push
if (this.userEntityService.isRemoteUser(dst)) {
const proxy = await this.proxyAccountService.fetch();
if (proxy) {
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
}
const proxy = await this.systemAccountService.fetch('proxy');
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
}
}

View File

@@ -24,7 +24,6 @@ import { AppLockService } from './AppLockService.js';
import { AchievementService } from './AchievementService.js';
import { AvatarDecorationService } from './AvatarDecorationService.js';
import { CaptchaService } from './CaptchaService.js';
import { CreateSystemUserService } from './CreateSystemUserService.js';
import { CustomEmojiService } from './CustomEmojiService.js';
import { DeleteAccountService } from './DeleteAccountService.js';
import { DownloadService } from './DownloadService.js';
@@ -37,7 +36,7 @@ import { HashtagService } from './HashtagService.js';
import { HttpRequestService } from './HttpRequestService.js';
import { IdService } from './IdService.js';
import { ImageProcessingService } from './ImageProcessingService.js';
import { InstanceActorService } from './InstanceActorService.js';
import { SystemAccountService } from './SystemAccountService.js';
import { InternalStorageService } from './InternalStorageService.js';
import { MetaService } from './MetaService.js';
import { MfmService } from './MfmService.js';
@@ -69,7 +68,6 @@ import { UserSuspendService } from './UserSuspendService.js';
import { UserAuthService } from './UserAuthService.js';
import { VideoProcessingService } from './VideoProcessingService.js';
import { UserWebhookService } from './UserWebhookService.js';
import { ProxyAccountService } from './ProxyAccountService.js';
import { UtilityService } from './UtilityService.js';
import { FileInfoService } from './FileInfoService.js';
import { SearchService } from './SearchService.js';
@@ -167,7 +165,6 @@ const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppL
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService };
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService };
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService };
@@ -180,7 +177,6 @@ const $HashtagService: Provider = { provide: 'HashtagService', useExisting: Hash
const $HttpRequestService: Provider = { provide: 'HttpRequestService', useExisting: HttpRequestService };
const $IdService: Provider = { provide: 'IdService', useExisting: IdService };
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService };
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
@@ -191,7 +187,7 @@ const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
const $ProxyAccountService: Provider = { provide: 'ProxyAccountService', useExisting: ProxyAccountService };
const $SystemAccountService: Provider = { provide: 'SystemAccountService', useExisting: SystemAccountService };
const $PushNotificationService: Provider = { provide: 'PushNotificationService', useExisting: PushNotificationService };
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService };
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService };
@@ -318,7 +314,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AchievementService,
AvatarDecorationService,
CaptchaService,
CreateSystemUserService,
CustomEmojiService,
DeleteAccountService,
DownloadService,
@@ -331,7 +326,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
HttpRequestService,
IdService,
ImageProcessingService,
InstanceActorService,
InternalStorageService,
MetaService,
MfmService,
@@ -342,7 +336,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
NoteReadService,
NotificationService,
PollService,
ProxyAccountService,
SystemAccountService,
PushNotificationService,
QueryService,
ReactionService,
@@ -465,7 +459,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
$CreateSystemUserService,
$CustomEmojiService,
$DeleteAccountService,
$DownloadService,
@@ -478,7 +471,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$HttpRequestService,
$IdService,
$ImageProcessingService,
$InstanceActorService,
$InternalStorageService,
$MetaService,
$MfmService,
@@ -489,7 +481,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$NoteReadService,
$NotificationService,
$PollService,
$ProxyAccountService,
$SystemAccountService,
$PushNotificationService,
$QueryService,
$ReactionService,
@@ -613,7 +605,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AchievementService,
AvatarDecorationService,
CaptchaService,
CreateSystemUserService,
CustomEmojiService,
DeleteAccountService,
DownloadService,
@@ -626,7 +617,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
HttpRequestService,
IdService,
ImageProcessingService,
InstanceActorService,
InternalStorageService,
MetaService,
MfmService,
@@ -637,7 +627,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
NoteReadService,
NotificationService,
PollService,
ProxyAccountService,
SystemAccountService,
PushNotificationService,
QueryService,
ReactionService,
@@ -759,7 +749,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
$CreateSystemUserService,
$CustomEmojiService,
$DeleteAccountService,
$DownloadService,
@@ -772,7 +761,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$HttpRequestService,
$IdService,
$ImageProcessingService,
$InstanceActorService,
$InternalStorageService,
$MetaService,
$MfmService,
@@ -783,7 +771,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$NoteReadService,
$NotificationService,
$PollService,
$ProxyAccountService,
$SystemAccountService,
$PushNotificationService,
$QueryService,
$ReactionService,

View File

@@ -1,86 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { randomUUID } from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common';
import bcrypt from 'bcryptjs';
import { IsNull, DataSource } from 'typeorm';
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
import { MiUser } from '@/models/User.js';
import { MiUserProfile } from '@/models/UserProfile.js';
import { IdService } from '@/core/IdService.js';
import { MiUserKeypair } from '@/models/UserKeypair.js';
import { MiUsedUsername } from '@/models/UsedUsername.js';
import { DI } from '@/di-symbols.js';
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class CreateSystemUserService {
constructor(
@Inject(DI.db)
private db: DataSource,
private idService: IdService,
) {
}
@bindThis
public async createSystemUser(username: string): Promise<MiUser> {
const password = randomUUID();
// Generate hash of password
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(password, salt);
// Generate secret
const secret = generateNativeUserToken();
const keyPair = await genRsaKeyPair();
let account!: MiUser;
// Start transaction
await this.db.transaction(async transactionalEntityManager => {
const exist = await transactionalEntityManager.findOneBy(MiUser, {
usernameLower: username.toLowerCase(),
host: IsNull(),
});
if (exist) throw new Error('the user is already exists');
account = await transactionalEntityManager.insert(MiUser, {
id: this.idService.gen(),
username: username,
usernameLower: username.toLowerCase(),
host: null,
token: secret,
isRoot: false,
isLocked: true,
isExplorable: false,
isBot: true,
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
await transactionalEntityManager.insert(MiUserKeypair, {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
userId: account.id,
});
await transactionalEntityManager.insert(MiUserProfile, {
userId: account.id,
autoAcceptFollowed: false,
password: hash,
});
await transactionalEntityManager.insert(MiUsedUsername, {
createdAt: new Date(),
username: username.toLowerCase(),
});
});
return account;
}
}

View File

@@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Not, IsNull } from 'typeorm';
import type { FollowingsRepository, MiUser, UsersRepository } from '@/models/_.js';
import type { FollowingsRepository, MiMeta, MiUser, UsersRepository } from '@/models/_.js';
import { QueueService } from '@/core/QueueService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
@@ -13,10 +13,14 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
@Injectable()
export class DeleteAccountService {
constructor(
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -28,6 +32,7 @@ export class DeleteAccountService {
private queueService: QueueService,
private globalEventService: GlobalEventService,
private moderationLogService: ModerationLogService,
private systemAccountService: SystemAccountService,
) {
}
@@ -36,8 +41,13 @@ export class DeleteAccountService {
id: string;
host: string | null;
}, moderator?: MiUser): Promise<void> {
if (this.meta.rootUserId === user.id) throw new Error('cannot delete a root account');
const _user = await this.usersRepository.findOneByOrFail({ id: user.id });
if (_user.isRoot) throw new Error('cannot delete a root account');
if (user.host === null && _user.username.includes('.')) {
throw new Error('cannot delete a system account');
}
if (moderator != null) {
this.moderationLogService.log(moderator, 'deleteAccount', {

View File

@@ -60,8 +60,8 @@ export class DownloadService {
request: operationTimeout, // whole operation timeout
},
agent: {
http: this.httpRequestService.httpAgent,
https: this.httpRequestService.httpsAgent,
http: this.httpRequestService.getAgentForHttp(urlObj, true),
https: this.httpRequestService.getAgentForHttps(urlObj, true),
},
http2: false, // default
retry: {

View File

@@ -268,7 +268,6 @@ export class FileInfoService {
private async *asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator<string, void> {
const watcher = new FSWatcher({
cwd,
disableGlobbing: true,
});
let finished = false;
command.once('end', () => {

View File

@@ -115,32 +115,32 @@ export class HttpRequestService {
/**
* Get http non-proxy agent (without local address filtering)
*/
private httpNative: http.Agent;
private readonly httpNative: http.Agent;
/**
* Get https non-proxy agent (without local address filtering)
*/
private httpsNative: https.Agent;
private readonly httpsNative: https.Agent;
/**
* Get http non-proxy agent
*/
private http: http.Agent;
private readonly http: http.Agent;
/**
* Get https non-proxy agent
*/
private https: https.Agent;
private readonly https: https.Agent;
/**
* Get http proxy or non-proxy agent
*/
public httpAgent: http.Agent;
public readonly httpAgent: http.Agent;
/**
* Get https proxy or non-proxy agent
*/
public httpsAgent: https.Agent;
public readonly httpsAgent: https.Agent;
constructor(
@Inject(DI.config)
@@ -197,7 +197,8 @@ export class HttpRequestService {
/**
* Get agent by URL
* @param url URL
* @param bypassProxy Allways bypass proxy
* @param bypassProxy Always bypass proxy
* @param isLocalAddressAllowed
*/
@bindThis
public getAgentByUrl(url: URL, bypassProxy = false, isLocalAddressAllowed = false): http.Agent | https.Agent {
@@ -214,6 +215,38 @@ export class HttpRequestService {
}
}
/**
* Get agent for http by URL
* @param url URL
* @param isLocalAddressAllowed
*/
@bindThis
public getAgentForHttp(url: URL, isLocalAddressAllowed = false): http.Agent {
if ((this.config.proxyBypassHosts ?? []).includes(url.hostname)) {
return isLocalAddressAllowed
? this.httpNative
: this.http;
} else {
return this.httpAgent;
}
}
/**
* Get agent for https by URL
* @param url URL
* @param isLocalAddressAllowed
*/
@bindThis
public getAgentForHttps(url: URL, isLocalAddressAllowed = false): https.Agent {
if ((this.config.proxyBypassHosts ?? []).includes(url.hostname)) {
return isLocalAddressAllowed
? this.httpsNative
: this.https;
} else {
return this.httpsAgent;
}
}
@bindThis
public async getActivityJson(url: string, isLocalAddressAllowed = false, allowSoftfail: FetchAllowSoftFailMask = FetchAllowSoftFailMask.Strict): Promise<IObject> {
const res = await this.send(url, {

View File

@@ -1,57 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { IsNull, Not } from 'typeorm';
import type { MiLocalUser } from '@/models/User.js';
import type { UsersRepository } from '@/models/_.js';
import { MemorySingleCache } from '@/misc/cache.js';
import { DI } from '@/di-symbols.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
import { bindThis } from '@/decorators.js';
const ACTOR_USERNAME = 'instance.actor' as const;
@Injectable()
export class InstanceActorService {
private cache: MemorySingleCache<MiLocalUser>;
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private createSystemUserService: CreateSystemUserService,
) {
this.cache = new MemorySingleCache<MiLocalUser>(Infinity);
}
@bindThis
public async realLocalUsersPresent(): Promise<boolean> {
return await this.usersRepository.existsBy({
host: IsNull(),
username: Not(ACTOR_USERNAME),
});
}
@bindThis
public async getInstanceActor(): Promise<MiLocalUser> {
const cached = this.cache.get();
if (cached) return cached;
const user = await this.usersRepository.findOneBy({
host: IsNull(),
username: ACTOR_USERNAME,
}) as MiLocalUser | undefined;
if (user) {
this.cache.set(user);
return user;
} else {
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as MiLocalUser;
this.cache.set(created);
return created;
}
}
}

View File

@@ -53,7 +53,7 @@ export class MetaService implements OnApplicationShutdown {
case 'metaUpdated': {
this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
...(body.after),
proxyAccount: null, // joinなカラムは通常取ってこないので
rootUser: null, // joinなカラムは通常取ってこないので
};
break;
}
@@ -113,17 +113,20 @@ export class MetaService implements OnApplicationShutdown {
if (before) {
await transactionalEntityManager.update(MiMeta, before.id, data);
const metas = await transactionalEntityManager.find(MiMeta, {
order: {
id: 'DESC',
},
});
return metas[0];
} else {
return await transactionalEntityManager.save(MiMeta, data);
await transactionalEntityManager.save(MiMeta, {
...data,
id: 'x',
});
}
const afters = await transactionalEntityManager.find(MiMeta, {
order: {
id: 'DESC',
},
});
return afters[0];
});
if (data.hiddenTags) {

View File

@@ -1,28 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import type { MiMeta, UsersRepository } from '@/models/_.js';
import type { MiLocalUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class ProxyAccountService {
constructor(
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
) {
}
@bindThis
public async fetch(): Promise<MiLocalUser | null> {
if (this.meta.proxyAccountId == null) return null;
return await this.usersRepository.findOneByOrFail({ id: this.meta.proxyAccountId }) as MiLocalUser;
}
}

View File

@@ -4,53 +4,34 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm';
import type { MiLocalUser, MiUser } from '@/models/User.js';
import type { RelaysRepository, UsersRepository } from '@/models/_.js';
import type { MiUser } from '@/models/User.js';
import type { RelaysRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import { MemorySingleCache } from '@/misc/cache.js';
import type { MiRelay } from '@/models/Relay.js';
import { QueueService } from '@/core/QueueService.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { DI } from '@/di-symbols.js';
import { deepClone } from '@/misc/clone.js';
import { bindThis } from '@/decorators.js';
const ACTOR_USERNAME = 'relay.actor' as const;
import { SystemAccountService } from '@/core/SystemAccountService.js';
@Injectable()
export class RelayService {
private relaysCache: MemorySingleCache<MiRelay[]>;
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.relaysRepository)
private relaysRepository: RelaysRepository,
private idService: IdService,
private queueService: QueueService,
private createSystemUserService: CreateSystemUserService,
private systemAccountService: SystemAccountService,
private apRendererService: ApRendererService,
) {
this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10); // 10m
}
@bindThis
private async getRelayActor(): Promise<MiLocalUser> {
const user = await this.usersRepository.findOneBy({
host: IsNull(),
username: ACTOR_USERNAME,
});
if (user) return user as MiLocalUser;
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
return created as MiLocalUser;
}
@bindThis
public async addRelay(inbox: string): Promise<MiRelay> {
const relay = await this.relaysRepository.insertOne({
@@ -59,8 +40,8 @@ export class RelayService {
status: 'requesting',
});
const relayActor = await this.getRelayActor();
const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
const relayActor = await this.systemAccountService.fetch('relay');
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
const activity = this.apRendererService.addContext(follow);
this.queueService.deliver(relayActor, activity, relay.inbox, false);
@@ -77,7 +58,7 @@ export class RelayService {
throw new Error('relay not found');
}
const relayActor = await this.getRelayActor();
const relayActor = await this.systemAccountService.fetch('relay');
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
const undo = this.apRendererService.renderUndo(follow, relayActor);
const activity = this.apRendererService.addContext(undo);

View File

@@ -74,7 +74,7 @@ export class RemoteUserResolveService {
if (user == null) {
const self = await this.resolveSelf(acctLower);
if (self.href.startsWith(this.config.url)) {
if (this.utilityService.isUriLocal(self.href)) {
const local = this.apDbResolverService.parseUri(self.href);
if (local.local && local.type === 'users') {
// the LR points to local

View File

@@ -101,7 +101,6 @@ export const DEFAULT_POLICIES: RolePolicies = {
@Injectable()
export class RoleService implements OnApplicationShutdown, OnModuleInit {
private rootUserIdCache: MemorySingleCache<MiUser['id']>;
private rolesCache: MemorySingleCache<MiRole[]>;
private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
private notificationService: NotificationService;
@@ -137,7 +136,6 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
private moderationLogService: ModerationLogService,
private fanoutTimelineService: FanoutTimelineService,
) {
this.rootUserIdCache = new MemorySingleCache<MiUser['id']>(1000 * 60 * 60 * 24 * 7); // 1week. rootユーザのIDは不変なので長めに
this.rolesCache = new MemorySingleCache<MiRole[]>(1000 * 60 * 60); // 1h
this.roleAssignmentByUserIdCache = new MemoryKVCache<MiRoleAssignment[]>(1000 * 60 * 5); // 5m
@@ -406,15 +404,15 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
}
@bindThis
public async isModerator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
public async isModerator(user: { id: MiUser['id'] } | null): Promise<boolean> {
if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
return (this.meta.rootUserId === user.id) || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
}
@bindThis
public async isAdministrator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
public async isAdministrator(user: { id: MiUser['id'] } | null): Promise<boolean> {
if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
return (this.meta.rootUserId === user.id) || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
}
@bindThis
@@ -463,16 +461,8 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
.map(a => a.userId),
);
if (includeRoot) {
const rootUserId = await this.rootUserIdCache.fetch(async () => {
const it = await this.usersRepository.createQueryBuilder('users')
.select('id')
.where({ isRoot: true })
.getRawOne<{ id: string }>();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return it!.id;
});
resultSet.add(rootUserId);
if (includeRoot && this.meta.rootUserId) {
resultSet.add(this.meta.rootUserId);
}
return [...resultSet].sort((x, y) => x.localeCompare(y));

View File

@@ -46,6 +46,8 @@ export class S3Service {
tls: meta.objectStorageUseSSL,
forcePathStyle: meta.objectStorageEndpoint ? meta.objectStorageS3ForcePathStyle : false, // AWS with endPoint omitted
requestHandler: new NodeHttpHandler(handlerOption),
requestChecksumCalculation: 'WHEN_REQUIRED',
responseChecksumValidation: 'WHEN_REQUIRED',
});
}

View File

@@ -14,13 +14,14 @@ import { MiUserProfile } from '@/models/UserProfile.js';
import { IdService } from '@/core/IdService.js';
import { MiUserKeypair } from '@/models/UserKeypair.js';
import { MiUsedUsername } from '@/models/UsedUsername.js';
import generateUserToken from '@/misc/generate-native-user-token.js';
import { generateNativeUserToken } from '@/misc/token.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import { bindThis } from '@/decorators.js';
import UsersChart from '@/core/chart/charts/users.js';
import { UtilityService } from '@/core/UtilityService.js';
import { UserService } from '@/core/UserService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { MetaService } from '@/core/MetaService.js';
@Injectable()
export class SignupService {
@@ -41,7 +42,8 @@ export class SignupService {
private userService: UserService,
private userEntityService: UserEntityService,
private idService: IdService,
private instanceActorService: InstanceActorService,
private systemAccountService: SystemAccountService,
private metaService: MetaService,
private usersChart: UsersChart,
) {
}
@@ -74,7 +76,7 @@ export class SignupService {
}
// Generate secret
const secret = generateUserToken();
const secret = generateNativeUserToken();
// Check username duplication
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
@@ -86,9 +88,7 @@ export class SignupService {
throw new Error('USED_USERNAME');
}
const isTheFirstUser = !await this.instanceActorService.realLocalUsersPresent();
if (!opts.ignorePreservedUsernames && !isTheFirstUser) {
if (!opts.ignorePreservedUsernames && this.meta.rootUserId != null) {
const isPreserved = this.meta.preservedUsernames.map(x => x.toLowerCase()).includes(username.toLowerCase());
if (isPreserved) {
throw new Error('USED_USERNAME');
@@ -129,7 +129,6 @@ export class SignupService {
usernameLower: username.toLowerCase(),
host: this.utilityService.toPunyNullable(host),
token: secret,
isRoot: isTheFirstUser,
}));
await transactionalEntityManager.save(new MiUserKeypair({
@@ -153,6 +152,10 @@ export class SignupService {
this.usersChart.update(account, true);
this.userService.notifySystemWebhook(account, 'userCreated');
if (this.meta.rootUserId == null) {
await this.metaService.update({ rootUserId: account.id });
}
return { account, secret };
}
}

View File

@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { randomUUID } from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, IsNull } from 'typeorm';
import bcrypt from 'bcryptjs';
import { MiLocalUser, MiUser } from '@/models/User.js';
import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js';
import type { MiMeta, UserProfilesRepository } from '@/models/_.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { generateNativeUserToken } from '@/misc/token.js';
import { IdService } from '@/core/IdService.js';
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
export const SYSTEM_ACCOUNT_TYPES = ['actor', 'relay', 'proxy'] as const;
@Injectable()
export class SystemAccountService {
private cache: MemoryKVCache<MiLocalUser>;
constructor(
@Inject(DI.db)
private db: DataSource,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.systemAccountsRepository)
private systemAccountsRepository: SystemAccountsRepository,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private idService: IdService,
) {
this.cache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 10); // 10m
}
@bindThis
public async list(): Promise<MiSystemAccount[]> {
const accounts = await this.systemAccountsRepository.findBy({});
return accounts;
}
@bindThis
public async fetch(type: typeof SYSTEM_ACCOUNT_TYPES[number]): Promise<MiLocalUser> {
const cached = this.cache.get(type);
if (cached) return cached;
const systemAccount = await this.systemAccountsRepository.findOne({
where: { type: type },
relations: ['user'],
});
if (systemAccount) {
this.cache.set(type, systemAccount.user as MiLocalUser);
return systemAccount.user as MiLocalUser;
} else {
const created = await this.createCorrespondingUser(type, {
username: `system.${type}`, // NOTE: (できれば避けたいが) . が含まれるかどうかでシステムアカウントかどうかを判定している処理もあるので変えないように
name: this.meta.name,
});
this.cache.set(type, created);
return created;
}
}
@bindThis
private async createCorrespondingUser(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
username: MiUser['username'];
name?: MiUser['name'];
}): Promise<MiLocalUser> {
const password = randomUUID();
// Generate hash of password
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(password, salt);
// Generate secret
const secret = generateNativeUserToken();
const keyPair = await genRsaKeyPair();
let account!: MiUser;
// Start transaction
await this.db.transaction(async transactionalEntityManager => {
const exist = await transactionalEntityManager.findOneBy(MiUser, {
usernameLower: extra.username.toLowerCase(),
host: IsNull(),
});
if (exist) {
account = exist;
return;
}
account = await transactionalEntityManager.insert(MiUser, {
id: this.idService.gen(),
username: extra.username,
usernameLower: extra.username.toLowerCase(),
host: null,
token: secret,
isLocked: true,
isExplorable: false,
isBot: true,
name: extra.name,
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
await transactionalEntityManager.insert(MiUserKeypair, {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
userId: account.id,
});
await transactionalEntityManager.insert(MiUserProfile, {
userId: account.id,
autoAcceptFollowed: false,
password: hash,
});
await transactionalEntityManager.insert(MiUsedUsername, {
createdAt: new Date(),
username: extra.username.toLowerCase(),
});
await transactionalEntityManager.insert(MiSystemAccount, {
id: this.idService.gen(),
userId: account.id,
type: type,
});
});
return account as MiLocalUser;
}
@bindThis
public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
name?: string;
description?: MiUserProfile['description'];
}): Promise<MiLocalUser> {
const user = await this.fetch(type);
const updates = {} as Partial<MiUser>;
if (extra.name !== undefined) updates.name = extra.name;
if (Object.keys(updates).length > 0) {
await this.usersRepository.update(user.id, updates);
}
const profileUpdates = {} as Partial<MiUserProfile>;
if (extra.description !== undefined) profileUpdates.description = extra.description;
if (Object.keys(profileUpdates).length > 0) {
await this.userProfilesRepository.update(user.id, profileUpdates);
}
const updated = await this.usersRepository.findOneByOrFail({ id: user.id }) as MiLocalUser;
this.cache.set(type, updated);
return updated;
}
}

View File

@@ -15,11 +15,11 @@ import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
import { bindThis } from '@/decorators.js';
import { QueueService } from '@/core/QueueService.js';
import { RedisKVCache } from '@/misc/cache.js';
import { RoleService } from '@/core/RoleService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
@Injectable()
export class UserListService implements OnApplicationShutdown, OnModuleInit {
@@ -43,8 +43,8 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
private userEntityService: UserEntityService,
private idService: IdService,
private globalEventService: GlobalEventService,
private proxyAccountService: ProxyAccountService,
private queueService: QueueService,
private systemAccountService: SystemAccountService,
) {
this.membersCache = new RedisKVCache<Set<string>>(this.redisClient, 'userListMembers', {
lifetime: 1000 * 60 * 30, // 30m
@@ -111,10 +111,8 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
// このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする
if (this.userEntityService.isRemoteUser(target)) {
const proxy = await this.proxyAccountService.fetch();
if (proxy) {
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: target.id } }]);
}
const proxy = await this.systemAccountService.fetch('proxy');
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: target.id } }]);
}
}

View File

@@ -73,7 +73,6 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser {
isLocked: false,
isBot: false,
isCat: true,
isRoot: false,
isExplorable: true,
isHibernated: false,
isDeleted: false,

View File

@@ -507,19 +507,12 @@ export class ApInboxService {
return `skip: delete actor ${actor.uri} !== ${uri}`;
}
const user = await this.usersRepository.findOneBy({ id: actor.id });
if (user == null) {
return 'skip: actor not found';
} else if (user.isDeleted) {
return 'skip: already deleted';
if (!(await this.usersRepository.update({ id: actor.id, isDeleted: false }, { isDeleted: true })).affected) {
return 'skip: already deleted or actor not found';
}
const job = await this.queueService.createDeleteAccountJob(actor);
await this.usersRepository.update(actor.id, {
isDeleted: true,
});
this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: actor.id });
return `ok: queued ${job.name} ${job.id}`;

View File

@@ -23,10 +23,11 @@ import { MfmService } from '@/core/MfmService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import type { MiUserKeypair } from '@/models/UserKeypair.js';
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js';
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository, MiMeta } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { IdService } from '@/core/IdService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { JsonLdService } from './JsonLdService.js';
import { ApMfmService } from './ApMfmService.js';
import { CONTEXT } from './misc/contexts.js';
@@ -38,6 +39,9 @@ export class ApRendererService {
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -61,6 +65,7 @@ export class ApRendererService {
private apMfmService: ApMfmService,
private mfmService: MfmService,
private idService: IdService,
private utilityService: UtilityService,
) {
}
@@ -184,7 +189,7 @@ export class ApRendererService {
url: emoji.publicUrl || emoji.originalUrl,
},
_misskey_license: {
freeText: emoji.license
freeText: emoji.license,
},
};
}
@@ -253,6 +258,38 @@ export class ApRendererService {
};
}
@bindThis
public renderIdenticon(user: MiLocalUser): IApImage {
return {
type: 'Image',
url: this.userEntityService.getIdenticonUrl(user),
sensitive: false,
name: null,
};
}
@bindThis
public renderSystemAvatar(user: MiLocalUser): IApImage {
if (this.meta.iconUrl == null) return this.renderIdenticon(user);
return {
type: 'Image',
url: this.meta.iconUrl,
sensitive: false,
name: null,
};
}
@bindThis
public renderSystemBanner(): IApImage | null {
if (this.meta.bannerUrl == null) return null;
return {
type: 'Image',
url: this.meta.bannerUrl,
sensitive: false,
name: null,
};
}
@bindThis
public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
return {
@@ -501,8 +538,8 @@ export class ApRendererService {
_misskey_requireSigninToViewContents: user.requireSigninToViewContents,
_misskey_makeNotesFollowersOnlyBefore: user.makeNotesFollowersOnlyBefore,
_misskey_makeNotesHiddenBefore: user.makeNotesHiddenBefore,
icon: avatar ? this.renderImage(avatar) : null,
image: banner ? this.renderImage(banner) : null,
icon: avatar ? this.renderImage(avatar) : isSystem ? this.renderSystemAvatar(user) : this.renderIdenticon(user),
image: banner ? this.renderImage(banner) : isSystem ? this.renderSystemBanner() : null,
tag,
manuallyApprovesFollowers: user.isLocked,
discoverable: user.isExplorable,
@@ -577,7 +614,7 @@ export class ApRendererService {
@bindThis
public renderUndo(object: string | IObject, user: { id: MiUser['id'] }): IUndo {
const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
const id = typeof object !== 'string' && typeof object.id === 'string' && this.utilityService.isUriLocal(object.id) ? `${object.id}/undo` : undefined;
return {
type: 'Undo',

View File

@@ -6,7 +6,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { IsNull, Not } from 'typeorm';
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
@@ -15,13 +14,14 @@ import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { isCollectionOrOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js';
import type { IObject, ICollection, IOrderedCollection } from './type.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
import type { IObject, ICollection, IOrderedCollection } from './type.js';
export class Resolver {
private history: Set<string>;
@@ -37,7 +37,7 @@ export class Resolver {
private noteReactionsRepository: NoteReactionsRepository,
private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService,
private instanceActorService: InstanceActorService,
private systemAccountService: SystemAccountService,
private apRequestService: ApRequestService,
private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService,
@@ -105,7 +105,7 @@ export class Resolver {
}
if (this.config.signToActivityPubGet && !this.user) {
this.user = await this.instanceActorService.getInstanceActor();
this.user = await this.systemAccountService.fetch('actor');
}
const object = (this.user
@@ -119,7 +119,7 @@ export class Resolver {
) {
throw new IdentifiableError('72180409-793c-4973-868e-5a118eb5519b', 'invalid response');
}
return object;
}
@@ -202,7 +202,7 @@ export class ApResolverService {
private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService,
private instanceActorService: InstanceActorService,
private systemAccountService: SystemAccountService,
private apRequestService: ApRequestService,
private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService,
@@ -222,7 +222,7 @@ export class ApResolverService {
this.noteReactionsRepository,
this.followRequestsRepository,
this.utilityService,
this.instanceActorService,
this.systemAccountService,
this.apRequestService,
this.httpRequestService,
this.apRendererService,

View File

@@ -560,7 +560,7 @@ export class ApPersonService implements OnModuleInit {
inbox: person.inbox,
sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox ?? null,
followersUri: person.followers ? getApId(person.followers) : undefined,
featured: person.featured,
featured: person.featured ? getApId(person.featured) : undefined,
emojis: emojiNames,
name: truncate(person.name, nameLength),
tags,
@@ -594,7 +594,9 @@ export class ApPersonService implements OnModuleInit {
if (moving) updates.movedAt = new Date();
// Update user
await this.usersRepository.update(exist.id, updates);
if (!(await this.usersRepository.update({ id: exist.id, isDeleted: false }, updates)).affected) {
return 'skip';
}
if (person.publicKey) {
await this.userPublickeysRepository.update({ userId: exist.id }, {
@@ -699,7 +701,7 @@ export class ApPersonService implements OnModuleInit {
@bindThis
public async updateFeatured(userId: MiUser['id'], resolver?: Resolver): Promise<void> {
const user = await this.usersRepository.findOneByOrFail({ id: userId });
const user = await this.usersRepository.findOneByOrFail({ id: userId, isDeleted: false });
if (!this.userEntityService.isRemoteUser(user)) return;
if (!user.featured) return;

View File

@@ -11,8 +11,7 @@ import type { MiMeta } from '@/models/Meta.js';
import type { AdsRepository } from '@/models/_.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { bindThis } from '@/decorators.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
@@ -29,8 +28,7 @@ export class MetaEntityService {
@Inject(DI.adsRepository)
private adsRepository: AdsRepository,
private userEntityService: UserEntityService,
private instanceActorService: InstanceActorService,
private systemAccountService: SystemAccountService,
) { }
@bindThis
@@ -149,14 +147,14 @@ export class MetaEntityService {
const packed = await this.pack(instance);
const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
const proxyAccount = await this.systemAccountService.fetch('proxy');
const packDetailed: Packed<'MetaDetailed'> = {
...packed,
cacheRemoteFiles: instance.cacheRemoteFiles,
cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
proxyAccountName: proxyAccount ? proxyAccount.username : null,
requireSetup: this.meta.rootUserId == null,
proxyAccountName: proxyAccount.username,
features: {
localTimeline: instance.policies.ltlAvailable,
globalTimeline: instance.policies.gtlAvailable,

View File

@@ -28,6 +28,7 @@ import type {
FollowingsRepository,
FollowRequestsRepository,
MiFollowing,
MiMeta,
MiUserNotePining,
MiUserProfile,
MutingsRepository,
@@ -100,6 +101,9 @@ export class UserEntityService implements OnModuleInit {
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@@ -381,7 +385,11 @@ export class UserEntityService implements OnModuleInit {
@bindThis
public getIdenticonUrl(user: MiUser): string {
return `${this.config.url}/identicon/${user.username.toLowerCase()}@${user.host ?? this.config.host}`;
if ((user.host == null || user.host === this.config.host) && user.username.includes('.') && this.meta.iconUrl) { // ローカルのシステムアカウントの場合
return this.meta.iconUrl;
} else {
return `${this.config.url}/identicon/${user.username.toLowerCase()}@${user.host ?? this.config.host}`;
}
}
@bindThis

View File

@@ -74,6 +74,7 @@ export const DI = {
registryItemsRepository: Symbol('registryItemsRepository'),
webhooksRepository: Symbol('webhooksRepository'),
systemWebhooksRepository: Symbol('systemWebhooksRepository'),
systemAccountsRepository: Symbol('systemAccountsRepository'),
adsRepository: Symbol('adsRepository'),
passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'),
retentionAggregationsRepository: Symbol('retentionAggregationsRepository'),

View File

@@ -1,7 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
// eslint-disable-next-line import/no-default-export
export default (token: string) => token.length === 16;

View File

@@ -5,5 +5,6 @@
import { secureRndstr } from '@/misc/secure-rndstr.js';
// eslint-disable-next-line import/no-default-export
export default () => secureRndstr(16);
export const generateNativeUserToken = () => secureRndstr(16);
export const isNativeUserToken = (token: string) => token.length === 16;

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm';
import { Entity, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { id } from './util/id.js';
import { MiUser } from './User.js';
@@ -15,6 +15,18 @@ export class MiMeta {
})
public id: string;
@Column({
...id(),
nullable: true,
})
public rootUserId: MiUser['id'] | null;
@ManyToOne(type => MiUser, {
onDelete: 'SET NULL',
nullable: true,
})
public rootUser: MiUser | null;
@Column('varchar', {
length: 1024, nullable: true,
})
@@ -172,18 +184,6 @@ export class MiMeta {
})
public cacheRemoteSensitiveFiles: boolean;
@Column({
...id(),
nullable: true,
})
public proxyAccountId: MiUser['id'] | null;
@ManyToOne(type => MiUser, {
onDelete: 'SET NULL',
})
@JoinColumn()
public proxyAccount: MiUser | null;
@Column('boolean', {
default: false,
})

View File

@@ -3,7 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Provider } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import {
@@ -63,6 +62,7 @@ import {
MiRoleAssignment,
MiSignin,
MiSwSubscription,
MiSystemAccount,
MiSystemWebhook,
MiUsedUsername,
MiUser,
@@ -77,8 +77,9 @@ import {
MiUserProfile,
MiUserPublickey,
MiUserSecurityKey,
MiWebhook
MiWebhook,
} from './_.js';
import type { Provider } from '@nestjs/common';
import type { DataSource } from 'typeorm';
const $usersRepository: Provider = {
@@ -285,6 +286,12 @@ const $swSubscriptionsRepository: Provider = {
inject: [DI.db],
};
const $systemAccountsRepository: Provider = {
provide: DI.systemAccountsRepository,
useFactory: (db: DataSource) => db.getRepository(MiSystemAccount),
inject: [DI.db],
};
const $hashtagsRepository: Provider = {
provide: DI.hashtagsRepository,
useFactory: (db: DataSource) => db.getRepository(MiHashtag).extend(miRepository as MiRepository<MiHashtag>),
@@ -532,6 +539,7 @@ const $reversiGamesRepository: Provider = {
$renoteMutingsRepository,
$blockingsRepository,
$swSubscriptionsRepository,
$systemAccountsRepository,
$hashtagsRepository,
$abuseUserReportsRepository,
$abuseReportNotificationRecipientRepository,
@@ -603,6 +611,7 @@ const $reversiGamesRepository: Provider = {
$renoteMutingsRepository,
$blockingsRepository,
$swSubscriptionsRepository,
$systemAccountsRepository,
$hashtagsRepository,
$abuseUserReportsRepository,
$abuseReportNotificationRecipientRepository,

View File

@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
import { Serialized } from '@/types.js';
import { id } from './util/id.js';
import { MiUser } from './User.js';
@Entity('system_account')
@Index(['type'], { unique: true })
export class MiSystemAccount {
@PrimaryColumn(id())
public id: string;
@Index()
@Column(id())
public userId: MiUser['id'];
@ManyToOne(type => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
public user: MiUser | null;
@Column('varchar', {
length: 256,
})
public type: string;
}

View File

@@ -184,12 +184,6 @@ export class MiUser {
})
public isCat: boolean;
@Column('boolean', {
default: false,
comment: 'Whether the User is the root.',
})
public isRoot: boolean;
@Index()
@Column('boolean', {
default: true,

View File

@@ -56,6 +56,7 @@ import { MiRegistryItem } from '@/models/RegistryItem.js';
import { MiRelay } from '@/models/Relay.js';
import { MiSignin } from '@/models/Signin.js';
import { MiSwSubscription } from '@/models/SwSubscription.js';
import { MiSystemAccount } from '@/models/SystemAccount.js';
import { MiUsedUsername } from '@/models/UsedUsername.js';
import { MiUser } from '@/models/User.js';
import { MiUserIp } from '@/models/UserIp.js';
@@ -171,6 +172,7 @@ export {
MiRelay,
MiSignin,
MiSwSubscription,
MiSystemAccount,
MiUsedUsername,
MiUser,
MiUserIp,
@@ -242,6 +244,7 @@ export type RegistryItemsRepository = Repository<MiRegistryItem> & MiRepository<
export type RelaysRepository = Repository<MiRelay> & MiRepository<MiRelay>;
export type SigninsRepository = Repository<MiSignin> & MiRepository<MiSignin>;
export type SwSubscriptionsRepository = Repository<MiSwSubscription> & MiRepository<MiSwSubscription>;
export type SystemAccountsRepository = Repository<MiSystemAccount> & MiRepository<MiSystemAccount>;
export type UsedUsernamesRepository = Repository<MiUsedUsername> & MiRepository<MiUsedUsername>;
export type UsersRepository = Repository<MiUser> & MiRepository<MiUser>;
export type UserIpsRepository = Repository<MiUserIp> & MiRepository<MiUserIp>;

View File

@@ -82,6 +82,7 @@ import { MiReversiGame } from '@/models/ReversiGame.js';
import { Config } from '@/config.js';
import MisskeyLogger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { MiSystemAccount } from './models/SystemAccount.js';
pg.types.setTypeParser(20, Number);
@@ -206,6 +207,7 @@ export const entities = [
MiEmoji,
MiHashtag,
MiSwSubscription,
MiSystemAccount,
MiAbuseUserReport,
MiAbuseReportNotificationRecipient,
MiRegistrationTicket,

View File

@@ -751,7 +751,7 @@ export class ActivityPubServerService {
});
// follow
fastify.get<{ Params: { followRequestId: string ; } }>('/follows/:followRequestId', async (request, reply) => {
fastify.get<{ Params: { followRequestId: string; } }>('/follows/:followRequestId', async (request, reply) => {
// This may be used before the follow is completed, so we do not
// check if the following exists and only check if the follow request exists.

View File

@@ -497,7 +497,7 @@ export class FileServerService {
@bindThis
private async downloadAndDetectTypeFromUrl(url: string): Promise<
{ state: 'remote' ; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
{ state: 'remote'; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
> {
const [path, cleanup] = await createTemp();
try {

View File

@@ -9,11 +9,11 @@ import type { Config } from '@/config.js';
import { MetaService } from '@/core/MetaService.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { MemorySingleCache } from '@/misc/cache.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
import NotesChart from '@/core/chart/charts/notes.js';
import UsersChart from '@/core/chart/charts/users.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
const nodeinfo2_1path = '/nodeinfo/2.1';
@@ -26,7 +26,7 @@ export class NodeinfoServerService {
@Inject(DI.config)
private config: Config,
private userEntityService: UserEntityService,
private systemAccountService: SystemAccountService,
private metaService: MetaService,
private notesChart: NotesChart,
private usersChart: UsersChart,
@@ -70,7 +70,7 @@ export class NodeinfoServerService {
const activeHalfyear = null;
const activeMonth = null;
const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null;
const proxyAccount = await this.systemAccountService.fetch('proxy');
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
@@ -123,7 +123,7 @@ export class NodeinfoServerService {
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
enableEmail: meta.enableEmail,
enableServiceWorker: meta.enableServiceWorker,
proxyAccountName: proxyAccount ? proxyAccount.username : null,
proxyAccountName: proxyAccount.username,
themeColor: meta.themeColor ?? '#86b300',
},
};

View File

@@ -371,7 +371,7 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
if ((ep.meta.requireModerator || ep.meta.requireAdmin) && !user!.isRoot) {
if ((ep.meta.requireModerator || ep.meta.requireAdmin) && (this.meta.rootUserId !== user!.id)) {
const myRoles = await this.roleService.getUserRoles(user!.id);
if (ep.meta.requireModerator && !myRoles.some(r => r.isModerator || r.isAdministrator)) {
throw new ApiError({
@@ -391,7 +391,7 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
if (ep.meta.requireRolePolicy != null && !user!.isRoot) {
if (ep.meta.requireRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
const myRoles = await this.roleService.getUserRoles(user!.id);
const policies = await this.roleService.getUserPolicies(user!.id);
if (!policies[ep.meta.requireRolePolicy] && !myRoles.some(r => r.isAdministrator)) {

View File

@@ -11,7 +11,7 @@ import type { MiAccessToken } from '@/models/AccessToken.js';
import { MemoryKVCache } from '@/misc/cache.js';
import type { MiApp } from '@/models/App.js';
import { CacheService } from '@/core/CacheService.js';
import isNativeToken from '@/misc/is-native-token.js';
import { isNativeUserToken } from '@/misc/token.js';
import { bindThis } from '@/decorators.js';
export class AuthenticationError extends Error {
@@ -46,7 +46,7 @@ export class AuthenticateService implements OnApplicationShutdown {
return [null, null];
}
if (isNativeToken(token)) {
if (isNativeUserToken(token)) {
const user = await this.cacheService.localUserByNativeTokenCache.fetch(token,
() => this.usersRepository.findOneBy({ token }) as Promise<MiLocalUser | null>);

View File

@@ -100,6 +100,7 @@ export * as 'admin/unset-user-banner' from './endpoints/admin/unset-user-banner.
export * as 'admin/unsuspend-user' from './endpoints/admin/unsuspend-user.js';
export * as 'admin/update-abuse-user-report' from './endpoints/admin/update-abuse-user-report.js';
export * as 'admin/update-meta' from './endpoints/admin/update-meta.js';
export * as 'admin/update-proxy-account' from './endpoints/admin/update-proxy-account.js';
export * as 'admin/update-user-note' from './endpoints/admin/update-user-note.js';
export * as 'announcements' from './endpoints/announcements.js';
export * as 'announcements/show' from './endpoints/announcements/show.js';

View File

@@ -4,12 +4,10 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsersRepository } from '@/models/_.js';
import type { MiMeta, UsersRepository } from '@/models/_.js';
import { SignupService } from '@/core/SignupService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
@@ -62,18 +60,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private userEntityService: UserEntityService,
private signupService: SignupService,
private instanceActorService: InstanceActorService,
) {
super(meta, paramDef, async (ps, _me, token) => {
const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null;
const realUsers = await this.instanceActorService.realLocalUsersPresent();
if (!realUsers && me == null && token == null) {
if (this.serverSettings.rootUserId == null && me == null && token == null) {
// 初回セットアップの場合
if (this.config.setupPassword != null) {
// 初期パスワードが設定されている場合
@@ -85,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// 初期パスワードが設定されていないのに初期パスワードが入力された場合
throw new ApiError(meta.errors.wrongInitialPassword);
}
} else if ((realUsers && !me?.isRoot) || token !== null) {
} else if ((this.serverSettings.rootUserId != null && (this.serverSettings.rootUserId !== me?.id)) || token !== null) {
// 初回セットアップではなく、管理者でない場合 or 外部トークンを使用している場合
throw new ApiError(meta.errors.accessDenied);
}

View File

@@ -42,10 +42,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new Error('user not found');
}
if (user.isRoot) {
throw new Error('cannot delete a root account');
}
await this.deleteAccoountService.deleteAccount(user, me);
});
}

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