Compare commits

..

283 Commits

Author SHA1 Message Date
syuilo
b3b8ee46f0 13.12.0-beta.1 2023-05-05 10:15:00 +09:00
syuilo
b45bc3fd5d feat(frontend): in channel search 2023-05-05 10:05:33 +09:00
syuilo
5c08f2b93b feat: Introduce Meilisearch (#10755)
* wip

* wip

* Update SearchService.ts

* Update SearchService.ts

* wip

* wip

* Update SearchService.ts

* Update CHANGELOG.md

* wip

* Update SearchService.ts

* Update docker-compose.yml.example
2023-05-05 08:52:14 +09:00
syuilo
5f62cefe31 Update CHANGELOG.md 2023-05-05 08:50:25 +09:00
たーびん
8dab46470e fix #10666 チャンネル検索ですべてのチャンネルの取得/表示ができるようにする (#10667)
* Update CHANGELOG.md

* fix : able to search all channels

* add chennel/search test

* update Changelog

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com>
Co-authored-by: Masaya Suzuki <15100604+massongit@users.noreply.github.com>
Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com>
Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
Co-authored-by: xianon <xianon@hotmail.co.jp>
Co-authored-by: kabo2468 <28654659+kabo2468@users.noreply.github.com>
Co-authored-by: YS <47836716+yszkst@users.noreply.github.com>
Co-authored-by: Khsmty <me@khsmty.com>
Co-authored-by: Soni L <EnderMoneyMod@gmail.com>
Co-authored-by: mei23 <m@m544.net>
Co-authored-by: daima3629 <52790780+daima3629@users.noreply.github.com>
Co-authored-by: Windymelt <1113940+windymelt@users.noreply.github.com>
Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>
2023-05-05 08:48:14 +09:00
syuilo
8c70bbe74d 🎨 2023-05-05 08:47:02 +09:00
syuilo
9ee002285d 🎨 2023-05-05 08:37:20 +09:00
syuilo
febb9f388c enhance(frontend): make MkCondensedLine experimental 2023-05-05 08:34:05 +09:00
Acid Chicken (硫酸鶏)
2cfed3395e feat: condense acct (#10753)
* feat: condense acct

* fix: watch parent element size

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-05-05 08:16:55 +09:00
syuilo
53498991bb Update about-misskey.vue 2023-05-05 08:05:33 +09:00
syuilo
ae80dc9b1e Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-05-05 08:05:11 +09:00
syuilo
2c606028b3 :art 2023-05-05 08:05:04 +09:00
かっこかり
1f9f63df7c 「このファイルからノートを作成」ボタンを追加 (#10758)
* (add) note this file button

* Update CHANGELOG.md
2023-05-04 19:58:17 +09:00
syuilo
dbc24ce587 Update about-misskey.vue 2023-05-03 16:38:52 +09:00
tamaina
58c3fc6cd2 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-05-02 16:21:25 +00:00
tamaina
363eb73fb3 fix 2023-05-02 16:21:18 +00:00
okayurisotto
43593603f1 fix(backend): fieldsの誤った定義を修正 (#10737) 2023-05-02 21:14:22 +09:00
syuilo
bcd123371b update deps 2023-05-02 20:52:36 +09:00
tamaina
f3e43a0fc6 refactor 2023-05-02 10:26:18 +00:00
tamaina
b3ec47c3f4 初期ユーザー登録時にはpreservedUsernamesを無視する
Fix #10738
2023-05-02 10:18:57 +00:00
syuilo
a2e475f2e8 Update CHANGELOG.md 2023-05-02 12:37:42 +09:00
syuilo
379c5a8091 enhance(frontend): use MkColorInput 2023-05-02 12:32:21 +09:00
syuilo
8dc5edde76 fix(backend): フォローリクエストの通知が残る問題を修正
Fix #10611
2023-05-02 12:14:06 +09:00
syuilo
e9ba896431 Update CHANGELOG.md 2023-05-02 09:57:11 +09:00
tsukimizake
cbd183a7a9 fix: return null from Mk:dialog (#10676) 2023-05-02 09:56:20 +09:00
syuilo
d535ec21a2 feat: チャンネルに色を設定できるように 2023-05-02 09:36:40 +09:00
syuilo
0cbdbf24f1 Create MkColorInput.vue 2023-05-02 09:23:30 +09:00
syuilo
4495969d7f Update MkInput.vue 2023-05-02 09:22:37 +09:00
syuilo
f5e9886c70 Update MkInput.vue 2023-05-02 09:17:10 +09:00
syuilo
1631e62739 refactor(frontend): use css modules 2023-05-02 09:07:57 +09:00
syuilo
e48926b01d Update about-misskey.vue 2023-05-02 08:13:30 +09:00
syuilo
25580e8afc 🎨 2023-05-01 17:09:07 +09:00
syuilo
5b7482d8f4 Update about-misskey.vue 2023-05-01 10:02:57 +09:00
tamaina
5f4d20ac1d ThisIsExperimentalFeature → thisIsExperimentalFeature 2023-04-30 12:11:43 +00:00
nexryai
7de59a80a2 fix(backend): サーバーメトリクスのメモリ使用率が不正確になることがある不具合の修正 (#10728)
* FIX: サーバーメトリクスのメモリ使用率が不正確になることがある不具合の修正

* Update CHANGELOG
2023-04-30 06:47:00 +09:00
Namekuji
d28866f71a enhance: account migration (#10592)
* copy block and mute then create follow and unfollow jobs

* copy block and mute and update lists when detecting an account has moved

* no need to care promise orders

* refactor updating actor and target

* automatically accept if a locked account had accepted an old account

* fix exception format

* prevent the old account from calling some endpoints

* do not unfollow when moving

* adjust following and follower counts

* check movedToUri when receiving a follow request

* skip if no need to adjust

* Revert "disable account migration"

This reverts commit 2321214c98.

* fix translation specifier

* fix checking alsoKnownAs and uri

* fix updating account

* fix refollowing locked account

* decrease followersCount if followed by the old account

* adjust following and followers counts when unfollowing

* fix copying mutings

* prohibit moved account from moving again

* fix move service

* allow app creation after moving

* fix lint

* remove unnecessary field

* fix cache update

* add e2e test

* add e2e test of accepting the new account automatically

* force follow if any error happens

* remove unnecessary joins

* use Array.map instead of for const of

* ユーザーリストの移行は追加のみを行う

* nanka iroiro

* fix misskey-js?

* ✌️

* 移行を行ったアカウントからのフォローリクエストの自動許可を調整

* newUriを外に出す

* newUriを外に出す2

* clean up

* fix newUri

* prevent moving if the destination account has already moved

* set alsoKnownAs via /i/update

* fix database initialization

* add return type

* prohibit updating alsoKnownAs after moving

* skip to add to alsoKnownAs if toUrl is known

* skip adding to the list if it already has

* use Acct.parse instead

* rename error code

* 🎨

* 制限を5から10に緩和

* movedTo(Uri), alsoKnownAsはユーザーidを返すように

* test api res

* fix

* 元アカウントはミュートし続ける

* 🎨

* unfollow

* fix

* getUserUriをUserEntityServiceに

* ?

* job!

* 🎨

* instance => server

* accountMovedShort, forbiddenBecauseYouAreMigrated

* accountMovedShort

* fix test

* import, pin禁止

* 実績を凍結する

* clean up

* ✌️

* change message

* ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに

* Revert "ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに"

This reverts commit 3bd7be35d8.

* validateAlsoKnownAs

* 移行後2時間以内はインポート可能なファイルサイズを拡大

* clean up

* どうせactorをupdatePersonで更新するならupdatePersonしか移行処理を発行しないことにする

* handle error?

* リモートからの移行処理の条件を是正

* log, port

* fix

* fix

* enhance(dev): non-production環境でhttpサーバー間でもユーザー、ノートの連合が可能なように

* refactor (use checkHttps)

* MISSKEY_WEBFINGER_USE_HTTP

* Environment Variable readme

* NEVER USE IN PRODUCTION

* fix punyHost

* fix indent

* fix

* experimental

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-30 00:09:29 +09:00
Chocolate Pie
149ddebf16 fix(frontend): ロールのタイトルのバグを解決、Reactivity Transformで型エラーを出さないように (#10729)
* fix: #10569を解決

* fix: ロールが存在しない場合、タイトルにエラーメッセージを表示させる

* fix: Reactivity Transformで型エラーを出さないように

* feat: i18n対応

* feat: タブでエラー表示

* fix: エラーメッセージを分ける

* fix: 使う変数の間違えを修正

* productionビルドできない問題を修正

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-29 23:36:28 +09:00
tamaina
351bee325b perf(backend): Use ThinUser in admin/federation/remove-all-following 2023-04-29 14:35:48 +00:00
Namekuji
0ab50f87a2 fix #10651 (#10684) 2023-04-29 23:27:14 +09:00
tamaina
09764b909b enhance(dev): non-production環境でhttpサーバー間でもユーザー、ノートの連合が可能なように (#10717)
* enhance(dev): non-production環境でhttpサーバー間でもユーザー、ノートの連合が可能なように

* refactor (use checkHttps)

* MISSKEY_WEBFINGER_USE_HTTP

* Environment Variable readme

* NEVER USE IN PRODUCTION

* fix punyHost
2023-04-29 23:26:47 +09:00
tamaina
2d3d986d13 test: Check availability of production build (#10734) 2023-04-29 23:21:54 +09:00
tamaina
87657d0acf wip 2023-04-29 14:01:25 +00:00
Acid Chicken (硫酸鶏)
9d5911d4e4 feat: make MkImgWithBlurhash transitionable (#10500)
* feat: make `MkImgWithBlurhash` animatable

* refactor: split out transition styles

* fix: bug

* test: waitFor image loads

* style: remove unused await

* fix

* fix type error

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-29 22:57:46 +09:00
okayurisotto
e2d9c0efe2 fix(backend): alsoKnownAsの誤った定義を修正 (#10725) 2023-04-29 19:24:33 +09:00
syuilo
8fb5457c01 [ci skip] fix typo 2023-04-29 18:28:25 +09:00
syuilo
0ad7869249 feat: preserved usernames
Resolve #10704
2023-04-29 17:03:14 +09:00
syuilo
e8177ee311 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-29 16:40:58 +09:00
かっこかり
8fbca63cec feat(client): Renoteした人の一覧を表示するダイアログを追加 (#10647)
* (add) renote user dialog

* (change) noteMenu order

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

* (change) menu text

* Update CHANGELOG.md

* (change) dialog title text

* (fix) grammar mistakes in CHANGELOG.md

* (change) i18n keys

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
2023-04-29 15:48:06 +09:00
たーびん
5124db57d2 fix email test (#10719) 2023-04-27 19:43:00 +09:00
tamaina
6e0f998fb2 test: use pnpm v8 2023-04-26 15:17:40 +00:00
futchitwo
52a1d96218 fix(play preset): Set failback for notes without text or user.name in Timeline preset (#10718) 2023-04-26 14:10:04 +09:00
Yuriha
a986203b38 [fix] .wav .flac ファイルを再生可能にする (#10686)
* .wav .flac ファイルを再生可能にする
file-typeにより判定されたMIME TypeをHTML5 Audio/Video要素に認識されるものに書き換える

* fix typecheck error

* frontend側の FILE_TYPE_BROWSERSAFEも更新

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

* ✌️

* 後方互換を確保

* add tests

* update changelog.md

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-26 02:17:58 +09:00
tamaina
2aa75f5489 fix 2023-04-25 06:29:27 +00:00
tamaina
b9b9cd0c78 enhance(server): 環境変数MISSKEY_CONFIG_YMLでdefault.ymlを任意のymlに変更可能に (#10712)
* enhance(server): MISSKEY_CONFIG_YMLでconfigを設定可能に

* update changelog
2023-04-25 15:18:03 +09:00
syuilo
b2a28ad9d4 Update about-misskey.vue 2023-04-23 16:24:34 +09:00
Acid Chicken (硫酸鶏)
f3206d094d build: set default theme for Storybook 2023-04-23 12:47:43 +09:00
syuilo
59dc9516d0 refactor(frontend): use composition aoi 2023-04-23 08:13:12 +09:00
syuilo
62af89d433 🎨 2023-04-22 20:22:09 +09:00
syuilo
b57ee4dd96 fix of 34492f3c9a 2023-04-22 20:12:41 +09:00
syuilo
8876ae09ed .js 2023-04-22 20:05:36 +09:00
syuilo
34492f3c9a enhance(backend): tweak cache of federated instance
#10631
2023-04-22 19:59:08 +09:00
syuilo
918a96da24 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-22 17:24:22 +09:00
syuilo
e461fb169e refactor(frontend): refactor MkNumberDiff.vue 2023-04-22 17:24:19 +09:00
tinaxd
5ddffa728a fix: ドライブアップロードで413が返ってきたときにエラーメッセージを表示 (#10680) 2023-04-22 17:18:57 +09:00
syuilo
eb0e2ceef7 🎨 2023-04-22 17:11:13 +09:00
syuilo
2718d86171 🎨 2023-04-22 16:04:03 +09:00
syuilo
d437e148db 🎨 2023-04-22 12:50:40 +09:00
syuilo
eacdc0136f 🎨 2023-04-22 12:19:49 +09:00
syuilo
5a7a1d0be9 🎨 2023-04-22 10:50:54 +09:00
syuilo
9145302b3a fix(frontend): fix wrong icon name 2023-04-22 08:00:37 +09:00
syuilo
ca49ac28b8 chore: remove unused files 2023-04-22 07:53:46 +09:00
syuilo
7b721c2124 Update about-misskey.vue 2023-04-22 07:51:15 +09:00
syuilo
87ff004c73 🎨 2023-04-21 09:34:36 +09:00
syuilo
18df1c7a52 Revert "🎨"
This reverts commit af738d9ca9.
2023-04-21 09:29:32 +09:00
syuilo
1dac961784 enhance(frontend): INVALID_PARAMおよびROLE_PERMISSION_DENIEDエラーを分かりやすく表示するように 2023-04-21 09:17:44 +09:00
syuilo
8b833c88ad vite動かなかったため戻した 2023-04-21 09:17:14 +09:00
syuilo
4054f9cccf update deps 2023-04-21 09:02:49 +09:00
tamaina
5cae078e5e fix(backend): make isExplorable optional for backward compatibility
https://github.com/misskey-dev/misskey/pull/10677#issuecomment-1516394630
2023-04-20 16:09:54 +00:00
nenohi
8dc60cd327 Role timeline setting (#10677)
* ロールタイムライン設定

* isRoleTimeline to isExplorable

* ポリシーではないので削除

* 型からも

* wip

* 足りてなかった説

* wip

* listはpublicを表示

* 前回の記載修正( #10671 )

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-20 20:02:50 +09:00
syuilo
af738d9ca9 🎨 2023-04-20 20:01:54 +09:00
syuilo
40debf20d8 add new achievement 2023-04-20 19:40:02 +09:00
syuilo
3af99d075e enhance(frontend): サーバー情報ページでサーバールールを見れるように 2023-04-20 17:23:35 +09:00
syuilo
795cb1ecf4 🎨 2023-04-20 15:39:59 +09:00
tamaina
e89d0aa815 update pnpm and summaly 2023-04-20 04:34:59 +00:00
syuilo
206baa13e6 enhance(frontend): tweak retention rate heatmap rendering 2023-04-20 10:41:09 +09:00
syuilo
7cc797062d tweak MkSignupDialog.rules.vue 2023-04-20 10:11:48 +09:00
syuilo
67d218fe2b tweak MkSignupDialog.rules.vue 2023-04-20 09:52:08 +09:00
tamaina
dc8a3f210b fix(server): 1:1ではない画像のリアクション通知バッジが左や上に寄ってしまっていたのを中央に来るように修正 2023-04-19 14:30:48 +00:00
syuilo
e1f9ab77f8 feat: Server rules (#10660)
* enhance(frontend): サーバールールのデザイン調整

* enhance(frontend): i18n

* enhance(frontend): 利用規約URLの設定を「モデレーション」ページへ移動

* enhance(frontend): サーバールールのデザイン調整

* Update CHANGELOG.md

* 不要な差分を削除

* fix(frontend): lint

* ui tweak

* test: add stories

* tweak

* test: bind args

* test: add interaction tests

* fix bug

* Update packages/frontend/src/pages/admin/server-rules.vue

Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>

* Update misskey-js.api.md

* chore: windowを明示

* 🎨

* refactor

* 🎨

* 🎨

* fix e2e test

* 🎨

* 🎨

* fix icon

* fix e2e

---------

Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
2023-04-19 21:24:31 +09:00
syuilo
0f7defc14a Update test-frontend.yml 2023-04-19 16:42:14 +09:00
syuilo
323af09ae9 Update labeler.yml 2023-04-19 13:35:19 +09:00
syuilo
d868f8f356 Update labeler.yml 2023-04-19 13:34:45 +09:00
syuilo
8c9cb9ee15 update deps 2023-04-19 12:52:14 +09:00
syuilo
3a61af326e Update about-misskey.vue 2023-04-19 11:24:46 +09:00
syuilo
f5c502a436 Update labeler.yml 2023-04-19 11:08:56 +09:00
syuilo
b8dacaaac8 Update pull_request_template.md 2023-04-19 11:02:51 +09:00
nenohi
65ff2c2498 カスタム絵文字のライセンスを一括でできるように (#10671)
* setlicensebulk追加

* 5時に誤字った!w

* 並び順の変更(set,add,removeの順

* add changelog
2023-04-19 08:25:24 +09:00
SASAGAWA Kiyoshi
b26807b59b fix: text color of follow button (#10672) 2023-04-19 08:24:37 +09:00
tamaina
471b836a44 fix(sw): 通知全削除時にread_notification通知が消えないように
通知欄に現れたり消えたりするとうざい
2023-04-18 06:01:18 +00:00
tsukimizake
aa289c9cb0 use channels/my-favorites on deck/channel-column/setChannel (#10662) 2023-04-18 13:29:45 +09:00
syuilo
614f12386e feat(frontend): 通知の表示をカスタマイズできるように 2023-04-17 13:12:58 +09:00
syuilo
cc27c1486d Update CHANGELOG.md 2023-04-17 11:01:01 +09:00
Acid Chicken (硫酸鶏)
d2d17847dc ci: fix story impl files were ignored 2023-04-16 11:00:07 +00:00
syuilo
fa60f54bc5 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-16 18:49:32 +09:00
Nanashia
0ddc79bb91 fix(backend): アバターとバナーがリセットできない (#10643)
* fix(backend): avatar and banner couldn't be reset

* Update CHANGELOG.md
2023-04-16 15:23:49 +09:00
syuilo
9ad250bbb8 enhance(frontend): improve MkPostForm behaviour 2023-04-16 07:59:23 +09:00
tamaina
d2aba9b693 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-15 16:18:11 +00:00
tamaina
6c5b5f59dd update CHANGELOG.md 2023-04-15 16:17:57 +00:00
Nanashia
973e70bacc chore: Use node@18.16.0 on DevContainer (#10642) 2023-04-16 01:00:35 +09:00
tamaina
15761a0fa8 enhance(client): 1枚だけのメディアリストの画像のアスペクト比を画像に応じて縦長にする (#10452)
* ✌️

* fix

* ✌️

* 422px上限

* 334

* min-height: 130px

* 64px

* fix

* wip

* ✌️

* fix

* max-height: none

* MkImgWithBlurHashでratioを計算する

* wip

* fix

* fix?

* Revert "fix?"

This reverts commit e39d832dd1.

* Revert "fix"

This reverts commit 15be36ba55.

* Revert "wip"

This reverts commit af7d86f69d.

* fix

* Revert "Revert "wip""

This reverts commit bb0036ae22.

* Revert "Revert "fix""

This reverts commit c1d94a45c5.

* Revert "Revert "fix?""

This reverts commit 9cb4fbfd96.

* fix

* use clamp

* readable

* add 1:1, 3:4

* moveComment

* 3:4 → 2:3

* fix

* default

* fallback

* Revert "fallback"

This reverts commit 741717dd49.

* Fix?(server): Content-Dispositionのパースでエラーが発生した場合にもダウンロードが完了するように
#10626
2023-04-15 21:35:19 +09:00
tamaina
38fdc73d01 Fix?(server): Content-Dispositionのパースでエラーが発生した場合にもダウンロードが完了するように
#10626
2023-04-15 11:19:00 +00:00
かっこかり
bcbf06ac8c feat(client): データセーバーモードの追加 (#10478)
* change nsfw settings

* Update CHANGELOG.md

* (fix) eliminate warning message when manually hide

* Apply suggestions from code review

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

* (change) translation key

* revert nsfw settings (partial)

* (add) data saver setting

* Integrate MkMediaBlurhash and MkImgWithBlurhash

* Update CHANGELOG.md

* 🎨

* リモートのファイルでsizeが0の場合は表示しない, refを作らない

* fix

* かっこ

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-15 15:29:57 +09:00
syuilo
98383b2aa9 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-15 11:25:01 +09:00
Kohei Ota (inductor)
5a8748b2b0 Update node version (#10639) 2023-04-15 10:20:39 +09:00
syuilo
69adbdef15 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-14 19:35:16 +09:00
syuilo
da0c295114 Update about-misskey.vue 2023-04-14 19:35:11 +09:00
Kisaragi
83d0f819be refactor(backend): validateNoteの引数の型を強くし、anyを除去 (#10325)
* refactor(backend): validateNoteの引数の型を推論する

* fix(backend): アサーションの内容から推論してエラーの内容を期待されるであろう式へと変更する

* refactor

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

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: Acid-Chicken <root@acid-chicken.com>
2023-04-14 16:27:55 +09:00
Acid Chicken (硫酸鶏)
c47a0f78ff fix(client): cat ears are clipped in MkReactionsViewer (#10445)
* fix: cat ears are clipped in MkReactionsViewer

* fix: missing padding

* fix border

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-14 16:00:41 +09:00
tamaina
7d3b7986e5 update CHANGELOG.md 2023-04-14 05:57:02 +00:00
nenohi
9469b26eb2 カスタム絵文字の検索を絵文字ピッカー使用できるように (#10335)
* fix( #10013)

* add changelog

* also in about.emojis.vue

* fix changelog

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-14 14:49:41 +09:00
tamaina
168fe0e376 fix(server): Force the extention of exported file (#10630)
* fix(server): Force the extention of exported file

* update changelog.md
2023-04-14 14:35:38 +09:00
tamaina
14f30afd3c fix check_connect.js 2023-04-14 05:14:00 +00:00
syuilo
a67439981b fix types 2023-04-14 13:50:05 +09:00
syuilo
5f67ca434d update ioredis 5.x 2023-04-14 10:09:03 +09:00
Nanashia
21dfce2cbb test(backend): catching up with #10516 (#10624) 2023-04-14 01:10:36 +09:00
Acid Chicken (硫酸鶏)
47c7b4b9cc fix(#10609): remove isChromatic on real build (#10613)
* perf: remove isChromatic on real build

* revert: Revert #10475 in MkTime

This reverts commit 7d11cf8ec9.

* @rollup/plugin-replace as dependencies

* fix pnpm-lock,yaml

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-13 23:23:11 +09:00
tamaina
55c10d0d88 fix(client): fix narrow style of MkPostForm 2023-04-13 11:27:21 +00:00
tamaina
93dcd1c98e fix DriveService.ts 2023-04-13 11:27:05 +00:00
CGsama
2423fb8d38 fix: proper expire remote user drivefile over limits at adding time (#9426)
* delete remote user drivefile over limits at adding

* refactor

* delete → expire

* speed up by batch find

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-13 18:48:38 +09:00
KokiSakano
463446795d refactor: init.ts周りのeslintエラーと型の修正 (#10157)
* refactor/miLocalStorageのメソッドに戻り値追加

* refactor/miLocalStorageのキーとしてdebugがconfig.tsに存在するので追加

* fix/JSON.parseにnullは入らないのでnullの時は分岐させてnullにする

* refactor/修正したファイルの型調整+記法の統一

* fix/型のためにlangがnullの時はhtmlの言語の設定をしない

* refactor/catchで何もしないと警告が出るので修正

* refactor/細かい点の修正

* refactor/変数の二重定義になっていた二重定義になっていたので修正

* refactor/importの整理(通常のimportは最初に処理されるので影響はない想定)

* fix/vueファイルに型を与えてインポート時の型エラーを防ぐ

* refactor/開発環境のみで利用するので,eslintの設定を変更する

* fix/vueの定義を最小限にする

* fallback language to 'en-US'

* remove accounts migration

* fix:vueの型定義ファイルを消す

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-13 18:47:49 +09:00
happo31
dffefdad95 fix: #9998 MkNote.vue, MkNoteDetailed.vue で、特定のMFMによってフッターのボタンが押せなくなる (#9999)
* fix(client): add `overflow: clip;`

* fix(client): add `overflow: clip;`

* Revert "fix(client): add `overflow: clip;`"

This reverts commit c43226afde.

Revert "fix(client): add `overflow: clip;`"

This reverts commit c722515105.

* feat(client): add z-index to .footer
2023-04-13 15:43:10 +09:00
syuilo
e014c91899 enhance(frontend): ユーザーメニューからユーザーメモを編集できるように 2023-04-13 13:50:17 +09:00
syuilo
5cac1515fd fix(backend): user.memoはdetailがtrueな時だけに 2023-04-13 13:34:54 +09:00
syuilo
97abfd48ce refactor(backend): tweak repository name 2023-04-13 13:31:54 +09:00
Ebise Lutica
605f149235 feat: 自分用メモ機能 (#10516)
* 自分用メモを作成する機能

* 不要なCSSを削除

* メモ: デザイン調整

* デザイン崩れを修正

* fix: メモ機能のe2eテストで見つかった不具合を修正

* デザイン調整

* fix(frontend): 自分用メモtextareaにline-heightが適用されない問題を修正
2023-04-13 13:17:32 +09:00
tamaina
7d11cf8ec9 Revert #10475 in MkTime 2023-04-13 03:38:43 +00:00
Acid Chicken (硫酸鶏)
9bb6c536c0 test(#10336): add components/Mk[A-B].* stories (#10475)
* chore(#10336): register snippets

* test(#10336): add `components/Mk[A-B].*` stories

* build: desynced lockfile

* ci(#10336): preload assets

* ci(#10336): use pull_request

* build: update lockfile

* fix: reactivity transform

* chore: track upstream changes

* refactor: avoid temporary previous tapping declarations

* revert: avoid temporary previous tapping declarations

This reverts commit e649b1b1e6.

* test: flaky snapshots

* style: import
2023-04-13 12:20:39 +09:00
syuilo
2a7ba37996 [ci skip] improve readability 2023-04-13 09:09:29 +09:00
syuilo
3f57119aea [ci skip] remove outdated comment 2023-04-13 09:02:41 +09:00
syuilo
ddb1ab7fae 13.11.3 2023-04-13 08:54:47 +09:00
syuilo
8913e561db New Crowdin updates (#10585)
* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (German)

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

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

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

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

* New translations ja-JP.yml (Chinese Simplified)
2023-04-13 08:53:29 +09:00
syuilo
dcbaca4260 fix(backend): チャンネルのピン留めされたノートの順番が正しくない問題を修正
Fix #10541
2023-04-13 08:52:30 +09:00
syuilo
6839441ac6 🎨 2023-04-13 08:46:10 +09:00
syuilo
62b6c4d09b Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-13 08:43:15 +09:00
syuilo
1a7e5fb865 Update about-misskey.vue 2023-04-13 08:43:06 +09:00
syuilo
78a2feb24c Update CHANGELOG.md 2023-04-13 08:34:25 +09:00
futchitwo
04511ac141 fix(server): アンテナとロールTLのuntil/sinceプロパティが動くように (#10605)
* fix(server): アンテナとロールTLのuntil/sinceプロパティが動くように

* fix
2023-04-13 08:33:36 +09:00
tamaina
4c0ef07f6f fix 2023-04-12 12:34:34 +00:00
tamaina
3ff5a5ae29 fix type in CustomEmojiService 2 2023-04-12 12:32:27 +00:00
tamaina
6ea057f8f8 fix type in CustomEmojiService 2023-04-12 12:09:28 +00:00
hutchisr
b7d056fb22 Use unique identifier for each follow request (#10600)
Co-authored-by: anemone <anemoneya@icloud.com>
2023-04-12 20:22:50 +09:00
syuilo
e3aeab8122 fix type 2023-04-12 17:02:54 +09:00
syuilo
72031e49fc Update CustomEmojiService.ts 2023-04-12 16:10:17 +09:00
syuilo
d06d1e8682 fix(backend): カスタム絵文字でリアクションできないことがある問題を修正 2023-04-12 16:07:58 +09:00
Nanashia
5c3a4a8224 test(backend): Add tests for users (#10546)
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-12 13:20:16 +09:00
kakkokari-gtyih
49749b46c4 feat(server): Misskey Webでユーザーフレンドリーなエラーページを出す (#10590)
* (add) user-friendly error page

* Update CHANGELOG.md

* (add) cache-control header

* Add ClientLoggerService

* Log params and query

* remove error stack on client

* fix pug

* 文面を調整

* :art]

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-12 12:52:14 +09:00
syuilo
5d56799070 feat: role timeline
Resolve #10581
2023-04-12 11:40:08 +09:00
tamaina
81d2c5a4a7 enhance: カスタム絵文字関連の変更 (#9794)
* PackedNoteなどのemojisはプロキシしていないURLを返すように

* MFMでx3/x4もしくはscale.x/yが2.5以上に指定されていた場合にはオリジナル品質の絵文字を使用する

* update CHANGELOG.md

* fix changelog

* ??

* wip

* fix

* merge

* Update packages/frontend/src/scripts/media-proxy.ts

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

* merge

* calc scale

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-12 10:58:56 +09:00
tamaina
0db88a5a3b refactor: サウンド関連の設定をpizzaxに移行 (#8105)
* enhane: unison-reloadに指定したパスに移動できるように

* null

* null

* feat: ログインするアカウントのIDをクエリ文字列で指定する機能

* null

* await?

* rename

* rename

* Update read.ts

* merge

* get-note-summary

* fix

* swパッケージに

* add missing packages

* fix getNoteSummary

* add webpack-cli

* ✌️

* remove plugins

* sw-inject分離したがテストしてない

* fix notification.vue

* remove a blank line

* disconnect intersection observer

* disconnect2

* fix notification.vue

* remove a blank line

* disconnect intersection observer

* disconnect2

* fix

* ✌️

* clean up config

* typesを戻した

* backend/src/web/index.ts

* notification-badges

* add scripts

* change create-notification.ts

* Update packages/client/src/components/notification.vue

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

* disconnect

* oops

* Failed to load the script unexpectedly回避
sw.jsとlib.tsを分離してみた

* truncate notification

* Update packages/client/src/ui/_common_/common.vue

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

* clean up

* clean up

* refactor

* キャッシュ対策

* Truncate push notification message

* fix

* wip

* clean up

* migration

* migration

* comment

* move soundConfigStore

* ✌️

* clean up

* クライアントがあったらストリームに接続しているということなので通知しない判定の位置を修正

* components/drive-file-thumbnail.vue

* components/drive-select-dialog.vue

* components/drive-window.vue

* merge

* fix

* remove reversi setting

* Service Workerのビルドにesbuildを使うようにする

* return createEmptyNotification()

* fix

* fix

* i18n.ts

* update

* ✌️

* remove ts-loader

* fix

* fix

* enhance: Service Workerを常に登録するように

* pollEnded

* pollEnded

* URLをsw.jsに戻す

* clean up

* clean up

* update sounds.vue

* update

* fix type

* ✌️

* ;v;

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-12 10:39:57 +09:00
tamaina
f9720e0e6e update CHANGELOG.md 2023-04-12 01:32:25 +00:00
syuilo
77f91d67b4 perf(backend): ノート作成時のアンテナ追加パフォーマンスを改善 2023-04-12 10:07:14 +09:00
Namekuji
da83322200 feat: queueing bulk follow/unfollow and block/unblock (#10544)
* wrap follow/unfollow and block/unblock as job queue

* create import job to follow in each iteration

* make relationship jobs concurrent

* replace to job queue if called repeatedly

* use addBulk to import

* omit stream when importing

* fix job caller

* use ThinUser instead of User to reduce redis memory consumption

* createImportFollowingToDbJobの呼び出し方を変える, 型補強

* Force ThinUser

* オブジェクト操作のみのメソッド名はgenerate...Data

* Force ThinUser in generateRelationshipJobData

* silent bulk unfollow at admin api endpoint

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-12 09:13:58 +09:00
futchitwo
b463490d9f Update CHANGELOG.md (#10591) 2023-04-12 02:08:41 +09:00
okayurisotto
5002effd65 Refactor sw (#10579)
* refactor(sw): remove dead code

* refactor(sw): remove dead code

* refactor(sw): remove dead code

* refactor(sw): remove dead code

* refactor(sw): remove dead code

* refactor(sw): remove dead code

* refactor(sw): 冗長な部分を変更

* refactor(sw): 使われていない煩雑な機能を削除

* refactor(sw): remove dead code

* refactor(sw): URL文字列の作成に`URL`を使うように

* refactor(sw): 型アサーションの削除とそれに伴い露呈したエラーへの対処

* refactor(sw): `append` -> `set` in `URLSearchParams`

* refactor(sw): `any`の削除とそれに伴い露呈したエラーへの対処

* refactor(sw): 型アサーションの削除とそれに伴い露呈したエラーへの対処

対処と言っても`throw`するだけ。いままでもこの状況ではエラーが投げられていたはずなので、この対処により新たな問題が起きることはないはず。

* refactor(sw): i18n loading

* refactor(sw): 型推論がうまくできる書き方に変更

`codes`が`(string | undefined)[]`から`string[]`になった

* refactor(sw): クエリ文字列の作成に`URLSearchParams`を使うように

* refactor(sw): `findClient`

* refactor(sw): `openClient`における`any`や`as`の書き換え

* refactor(sw): `openPost`における`any`の書き換え

* refactor(sw): `let` -> `const`

* refactor(sw): `any` -> `unknown`

* cleanup(sw): import

* cleanup(sw)

* cleanup(sw): `?.`

* cleanup(sw/.eslintrc.js)

* refactor(sw): `@typescript-eslint/explicit-function-return-type`

* refactor(sw): `@typescript-eslint/no-unused-vars`

* refactor(sw): どうしようもないところに`eslint-disable-next-line`を

* refactor(sw): `import/no-default-export`

* update operations.ts

* throw new Error

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
2023-04-12 01:07:24 +09:00
tamaina
35613fd642 fix(client): noPaging: true with gallery/featured 2023-04-11 06:49:19 +00:00
syuilo
5cabbd0eef Update CHANGELOG.md 2023-04-11 14:59:56 +09:00
syuilo
8b509f6c36 13.11.2 2023-04-11 14:58:48 +09:00
たーびん
2612bcd738 Update CHANGELOG.md (#10577)
#10555 の更新分
2023-04-11 14:57:37 +09:00
syuilo
de0577bc38 enhance(frontend): tweak post form style 2023-04-11 14:57:06 +09:00
syuilo
b192dc0774 New Crowdin updates (#10566)
* 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 (Thai)

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

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Lao)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Indonesian)
2023-04-11 14:39:50 +09:00
syuilo
43eee54f2d enhance(backend): APIパラメータサイズ上限を128kbから1mbに緩和 2023-04-11 14:28:40 +09:00
syuilo
59ca0d21a1 enhance(backend): APIパラメータサイズ上限を32kbから128kbに緩和
Fix #10574
2023-04-11 14:27:09 +09:00
syuilo
92356d02b9 Update CHANGELOG.md 2023-04-11 14:22:12 +09:00
syuilo
c10d591bd0 perf(backend): cache swSubscriptions 2023-04-11 14:20:16 +09:00
tamaina
3a90bcc03c sw: なんかもうめっちゃ変えた (#10570)
* sw: なんかいろいろ

* remove debug code

* never renotify

* update changelog.md
2023-04-11 14:11:39 +09:00
たーびん
f6dc100748 fix #10554 チャンネルの検索用ページとAPIの追加 (#10555)
* add channel search

* move  channel search to channel list page

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com>
Co-authored-by: Masaya Suzuki <15100604+massongit@users.noreply.github.com>
Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com>
Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
Co-authored-by: xianon <xianon@hotmail.co.jp>
Co-authored-by: kabo2468 <28654659+kabo2468@users.noreply.github.com>
Co-authored-by: YS <47836716+yszkst@users.noreply.github.com>
Co-authored-by: Khsmty <me@khsmty.com>
Co-authored-by: Soni L <EnderMoneyMod@gmail.com>
Co-authored-by: mei23 <m@m544.net>
Co-authored-by: daima3629 <52790780+daima3629@users.noreply.github.com>
Co-authored-by: Windymelt <1113940+windymelt@users.noreply.github.com>
2023-04-11 07:42:27 +09:00
Chimorium
0702f9775a カスタム絵文字のキャッシュ時に"{}"が入ってしまう問題を修正 (#10573) 2023-04-11 07:39:46 +09:00
tamaina
838625edcd update CHANGELOG.md 2023-04-10 17:26:52 +00:00
tamaina
83bcdb8ede fix(client): Consider safe-area-inset-bottom on global widgets area
Fix #9052
2023-04-10 17:21:28 +00:00
tamaina
567c66567e fix(client): 🎨 fix MkEmojiPicker safe-area-inset-bottom
Fix https://github.com/misskey-dev/misskey/pull/10534
2023-04-10 16:46:58 +00:00
tamaina
da64273b43 chore(sw): use PascalCase 2023-04-10 12:10:06 +00:00
okayurisotto
6a23ffcce5 swのesbuildの更新とビルドスクリプトの更新 (#10549)
* cleanup(sw/build.js)

* fix(sw/build.js): `define`に真偽値を渡していた問題を修正

`define`では文字列を渡さなければならないので、`JSON.stringify`をするようにした。

* fix(sw/build.js): `string`が期待される`define`において`undefined`になる場合がある問題を修正

* update(sw): esbuild 0.17.15

* fixup! update(sw): esbuild 0.17.15

* fixup! fix(sw/build.js): `string`が期待される`define`において`undefined`になる場合がある問題を修正

コメントの文言を調整
2023-04-10 19:43:15 +09:00
syuilo
511dab0618 fix(frontend): webhook, 連携アプリ一覧でコンテンツが重複して表示される問題を修正
Fix #10564
2023-04-10 18:56:38 +09:00
syuilo
48e2523081 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-10 10:24:08 +09:00
e0431aed28 fix issue #10195 設定のバックアップ の「削除」の文字がない (#10559)
Co-authored-by: 藤 <nyaguri0417@gmail.com>
2023-04-10 10:23:56 +09:00
syuilo
29c9a7d71a enhance(frontend): 常に広告を見られるオプションを追加 2023-04-10 10:22:25 +09:00
syuilo
eba42230ee Update CHANGELOG.md 2023-04-10 10:12:05 +09:00
syuilo
f8315a40b4 Update CHANGELOG.md 2023-04-10 10:04:16 +09:00
syuilo
b5724d06b4 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-10 10:03:56 +09:00
syuilo
70a06e30d5 fix(backend): アンテナのノート、チャンネルのノート、通知が正常に作成できないことがある問題を修正
Fix #10482
2023-04-10 10:03:53 +09:00
YS
3ec060f0dc MkContainer.vue i18n をtemplateから見えるように (#10560) 2023-04-10 08:31:06 +09:00
syuilo
39cf80e19f fix(backend): イベント用redis分離が上手く動かない問題を修正 2023-04-09 17:09:27 +09:00
syuilo
b56f4b27ee fix(backend): ストリーミングのLTLチャンネルでサーバー側にエラーログが出るのを修正 2023-04-09 17:01:03 +09:00
Acid Chicken (硫酸鶏)
2b19e1f732 test: add /@:acct stories (#10517)
* test: add `/@:acct` stories

* test: add mocks
2023-04-09 04:16:56 +00:00
syuilo
59d0d507d5 fix(backend): 連合しているインスタンスについて予期せず配送が全て停止されることがある問題を修正
Fix #10499
2023-04-09 10:19:57 +09:00
syuilo
86de46debf Update CHANGELOG.md 2023-04-09 09:53:29 +09:00
syuilo
1057da1556 add note 2023-04-09 09:53:02 +09:00
tamaina
9feb6b0f5b fix(server): リアクションできない問題をとりあえず修正 (#10529)
* fix(server): リアクションできない問題をとりあえず修正
Fix #10502

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

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-09 09:52:19 +09:00
syuilo
269d1e72cb fix(frontend): ユーザープレビューが表示されない問題を修正
Fix #10540
2023-04-09 09:44:00 +09:00
syuilo
31ff3a22b7 13.11.1 2023-04-09 09:40:00 +09:00
syuilo
b9d022f164 New Crowdin updates (#10522)
* New translations ja-JP.yml (Swedish)

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

* New translations ja-JP.yml (Italian)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)
2023-04-09 09:18:18 +09:00
syuilo
087da7643e Update CHANGELOG.md 2023-04-09 08:37:55 +09:00
syuilo
cb3a84adb4 チャンネルを新規作成できない問題を修正 2023-04-09 08:22:49 +09:00
syuilo
004ace396d さがすのローカルユーザー(ピンどめ)が無限に生成される問題を修正 2023-04-09 08:12:50 +09:00
syuilo
9ae2e87a46 Update CHANGELOG.md 2023-04-09 08:10:12 +09:00
syuilo
ad4006738b チャンネルのお気に入りが無限に読み込まれる問題を修正 2023-04-09 08:09:25 +09:00
taichan
f794f3ad0a fix(client): リスト、クリップが無限ロードされる現象の解決 (#10538)
* fix my-list infinite items loading

* update CHANGELOG.md

* fix my-clip infinite items loading
2023-04-09 08:03:29 +09:00
syuilo
b7ed3ddfdd fix(backend): 通知読み込みでエラーが発生する場合がある問題を修正 2023-04-09 08:02:52 +09:00
futchitwo
038365bf2d fix: redis から取得できないチャンネル投稿はDBから取得 (#10539) 2023-04-09 07:56:27 +09:00
syuilo
64597a2dab Update CHANGELOG.md 2023-04-09 04:56:36 +09:00
tamaina
d76220cc80 fix(server): IdService.parseを全てのidタイプに対応させるように (#10533)
* wip fix-id

* ✌️

* fix import
2023-04-09 04:41:06 +09:00
tamaina
7a33c5d2ee update CHANGELOG.md 2023-04-08 16:44:49 +00:00
Takeshi Kishi
e58b357918 fix(client): PWA時の絵文字ピッカーの位置をホームバーに重ならないように調整 (#10534)
* fix emoji picker padding

emoji picker bottoms are hidden by iPhone Home Bar.
To fix this, add safe-area padding

* update CHANGELOG
2023-04-09 01:37:45 +09:00
syuilo
789689e759 [ci skip] Update CHANGELOG.md 2023-04-08 21:26:43 +09:00
syuilo
2321214c98 disable account migration 2023-04-08 20:51:36 +09:00
syuilo
50a9b29c48 [ci skip] 13.11.0 2023-04-08 20:29:55 +09:00
syuilo
c5ccd826c8 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-08 20:03:35 +09:00
syuilo
ad9f72291f use for of 2023-04-08 20:03:32 +09:00
tamaina
1c158dc510 fix(client): Fix antenna list infinite items loading
https://github.com/misskey-dev/misskey/pull/10506#issuecomment-1500859975
2023-04-08 10:37:35 +00:00
syuilo
cd915b6939 [ci skip] Update CHANGELOG.md 2023-04-08 18:44:03 +09:00
syuilo
a7455035c5 Update CHANGELOG.md 2023-04-08 17:42:34 +09:00
atsuchan
5cd93834d1 enhance(client): 広告・お知らせが新規登録時に増殖しないように (#10412) 2023-04-08 17:42:08 +09:00
okayurisotto
6892e9fbb7 fix(frontend): URLに他のクエリがあるときにもZen UIが適用されるように (#10477) 2023-04-08 17:41:23 +09:00
syuilo
abe73c9904 New Crowdin updates (#10512)
* New translations ja-JP.yml (Chinese Simplified)

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

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (Polish)

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

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Greek)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Swedish)

* New translations ja-JP.yml (Turkish)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Croatian)

* New translations ja-JP.yml (Uyghur)

* New translations ja-JP.yml (Lojban)

* New translations ja-JP.yml (Sinhala)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Lao)

* New translations ja-JP.yml (Haitian Creole)

* New translations ja-JP.yml (Kabyle)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Swedish)
2023-04-08 17:40:31 +09:00
syuilo
cc57fc041a Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-08 17:39:48 +09:00
syuilo
90df0d87ae disable flaky test 2023-04-08 17:39:44 +09:00
Acid Chicken (硫酸鶏)
95f670e3ec test: use M PLUS Rounded 1c on Storybook (#10520)
* test: use M PLUS Rounded 1c on Storybook

* test: fix hostname
2023-04-08 17:18:00 +09:00
omi
66342337ef chore: local test が redis version 起因で落ちる (#10519) 2023-04-08 17:06:42 +09:00
Acid Chicken (硫酸鶏)
59693bcb65 docs: thanks (#10518) 2023-04-08 17:02:18 +09:00
Acid Chicken (硫酸鶏)
a432f53d6c ci: avoid 0000000 2023-04-08 07:16:10 +00:00
syuilo
e72d6c9f2c 13.11.0-beta.8 2023-04-08 16:15:43 +09:00
Namekuji
e23cf77f86 fetch person again (#10514) 2023-04-08 16:13:50 +09:00
syuilo
0f328e8472 Update CHANGELOG.md 2023-04-08 16:09:43 +09:00
syuilo
7636536973 tweak locale 2023-04-08 16:06:08 +09:00
syuilo
975e79fa3b fix type 2023-04-08 15:59:41 +09:00
syuilo
eefa06622b Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-08 15:53:39 +09:00
syuilo
9114c8cb8e feat(backend): support replication of postgresql
Resolve #10205
2023-04-08 15:53:36 +09:00
syuilo
132feea1fa :art 2023-04-08 15:49:29 +09:00
Acid Chicken (硫酸鶏)
33e9428510 chore: disable sampling for cat ears (#10513)
* chore: sample 3px for cat ears

* fix: typo

* chore: disable avatar plot for cat ears
2023-04-08 06:22:39 +00:00
syuilo
1aa8f09b95 13.11.0-beta.7 2023-04-08 14:42:11 +09:00
syuilo
65584f21bb Update misskey-js.api.md 2023-04-08 14:39:54 +09:00
syuilo
b697f948df Update misskey-js.api.md 2023-04-08 14:32:52 +09:00
syuilo
551b00302b use css module 2023-04-08 14:32:21 +09:00
syuilo
05aa0fa11a 🎨 2023-04-08 14:31:09 +09:00
syuilo
008e3fb37e Create 1680931179228-account-move.js 2023-04-08 14:20:40 +09:00
syuilo
68e8892f61 chore: use for instead of forEach 2023-04-08 14:18:28 +09:00
Namekuji
25ebb73756 feat: account migration (#10507)
* add Move activity

* add endpoint to move from local to remote

* follow move activity coming to inbox

* fix move endpoint

* add known-as endpoint to create account alias

* add migration page

* add route to migration page

* add move and known-as endpoints

* fix dependnecies error

* fix new endpoints

* fix move activity id

* fix refollow

* add movedToUri and alsoKnownAs to api

* fix moveToUri indicator

* fix missing context

* add chengelog

* rename MkMoved to MkAccountMoved

* add missing semicolon

* fix targetUri

* fix followings query

* remove redundant null check
2023-04-08 14:16:26 +09:00
syuilo
fa67fb42b1 enhance(backend): Redisにチャンネル投稿がない場合はDBから持ってくるように
Fix #10473
2023-04-08 13:12:49 +09:00
syuilo
40b3041608 tweak MkWindow.vue 2023-04-08 13:01:28 +09:00
syuilo
e3b8e8746e lint fixes 2023-04-08 12:57:05 +09:00
syuilo
33bce49ee8 lint fix 2023-04-08 12:56:21 +09:00
remi
44a4faebc0 feat: add minimize/Fold button for windows (#10508)
* add window minimizing!

* Fix window being able to go offscreen

* Revert en-US.yml changes

---------

Co-authored-by: mothmoon <remilia@remilia.se>
2023-04-08 12:55:05 +09:00
Acid Chicken (硫酸鶏)
79f198e4f1 ci: ignore develop commits 2023-04-07 23:27:22 +09:00
syuilo
eb30976ae6 Update CHANGELOG.md 2023-04-07 20:35:58 +09:00
Acid Chicken (硫酸鶏)
abda3b6c8c ci(#10336): use TurboSnap (#10494)
* ci(#10336): use TurboSnap

* build: fix version

* ci(#10336): update build dir

* chore(#10336): fire changes

* chore: stabilize icon width on `PageHeader`

* chore: fire changes

* ci: invalid ignore

* ci: trace logs

* chore: debug

* revert: debug

This reverts commit 2329165e25.

* chore: do not reuse build dir

* build: scripts

* ci: tweak

* revert: re-revert debug

This reverts commit 596ef05d9e.

* chore: detect changes manually

* fix: syntax

* ci: do not use only-changed

* ci: fix command

* revert: re-re-revert debug

This reverts commit b027170d75.

* ci: use build dir

* revert: re-re-re-revert debug

This reverts commit 529ab126ed.

* ci: fix path

* revert: re-re-re-re-revert debug

This reverts commit 0b0c0b9ea4.

* ci: fix typo

* ci: only show story files

* revert: re-re-re-re-re-revert debug

This reverts commit 9f5b88df32.

* ci: skip when no stories found

* ci: use skip

* revert: re-re-re-re-re-re-revert debug

This reverts commit 0df4bdc30b.

* ci: fix micromatch version

* revert: re-re-re-re-re-re-re-revert debug

This reverts commit 63063b02bb.

* revert: re-re-re-re-re-re-re-re-revert debug

This reverts commit 01d9669e2a.

* chore: pin tabler icon width globally

* ci: notify when Chromatic skips

* ci: fix endpoint

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-07 20:34:23 +09:00
syuilo
191ed3c814 fix(test): disable remote user tests
#10495
2023-04-07 20:09:22 +09:00
syuilo
7da2ca1862 Revert "test"
This reverts commit cb39db100a.
2023-04-07 20:05:15 +09:00
syuilo
cb39db100a test 2023-04-07 19:45:08 +09:00
syuilo
e3f4c9bcf6 13.11.0-beta.6 2023-04-07 18:57:18 +09:00
syuilo
ed4a100e96 fix(backend): restore date properly of federated instance cache 2023-04-07 18:55:11 +09:00
syuilo
1377ea4178 perf(backend): improve cache of federated instances 2023-04-07 18:48:45 +09:00
syuilo
6e1ae7b242 enhance(backend): tweak cache of role
#10439
2023-04-07 18:15:47 +09:00
syuilo
930724f9de Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-07 18:12:27 +09:00
syuilo
3bbeec70ec perf(backend): improve cache of instance of user 2023-04-07 18:12:24 +09:00
okayurisotto
69828e8dac fix(sw): プッシュ通知の操作でZen UIなクライアントが開かれてしまう場合がある問題を修正 (#10497) (#10498) 2023-04-07 15:48:49 +09:00
syuilo
58c8d21348 13.11.0-beta.5 2023-04-07 12:01:52 +09:00
syuilo
1e3fb5531b typo 2023-04-07 11:33:15 +09:00
syuilo
239d3f2dbf feat(backend): ジョブキュー用Redisを別サーバーに分離できるように 2023-04-07 11:27:01 +09:00
syuilo
ff6d9d2860 feat(backend): イベント用Redisを別サーバーに分離できるように 2023-04-07 11:20:14 +09:00
syuilo
f4588f3907 perf(backend): reduce db query 2023-04-07 10:07:41 +09:00
syuilo
a561b83070 fix deps 2023-04-06 20:29:17 +09:00
syuilo
88f22da052 update deps 2023-04-06 20:22:26 +09:00
syuilo
89c12948a1 13.11.0-beta.4 2023-04-06 19:51:33 +09:00
syuilo
f8cdab6d02 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-04-06 19:48:28 +09:00
syuilo
521eb95dbc perf(backend): cache avatar and banner url to db 2023-04-06 19:48:24 +09:00
syuilo
c29f21ac56 perf(backend): prevent emit needless db queries 2023-04-06 19:45:12 +09:00
syuilo
de9d136a3f perf(backend): reduce db query 2023-04-06 18:27:42 +09:00
syuilo
a0d5e2469d Update about-misskey.vue 2023-04-06 18:08:35 +09:00
syuilo
b38811af7c fix(backend): fix pack of notification behaviour 2023-04-06 15:09:21 +09:00
tamaina
bc5be83e29 use pnpm@8.1.1 2023-04-06 01:56:58 +00:00
syuilo
52011610c6 13.11.0.beta-3 2023-04-06 11:45:47 +09:00
syuilo
a2e022631d New Crowdin updates (#10430)
* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Italian)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

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

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

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

* New translations ja-JP.yml (Italian)

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

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

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

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

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

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Russian)

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

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (Chinese Simplified)
2023-04-06 11:45:07 +09:00
syuilo
a574d0fbcb fix 2023-04-06 11:36:20 +09:00
462 changed files with 13251 additions and 6863 deletions

View File

@@ -51,6 +51,23 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -62,15 +79,29 @@ redis:
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#redisForPubsub:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
#redisForJobQueue:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────

View File

@@ -51,6 +51,23 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -62,15 +79,29 @@ redis:
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
#redisForPubsub:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForJobQueue:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: localhost
# port: 7700
# apiKey: ''
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@@ -100,16 +131,20 @@ id: 'aid'
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
#deliverJobConcurrency: 128
#inboxJobConcurrency: 16
#relashionshipJobConcurrency: 16
# What's relashionshipJob?:
# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 16
#deliverJobPerSec: 128
#inboxJobPerSec: 16
#relashionshipJobPerSec: 64
# Job attempts
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
#deliverJobMaxAttempts: 12
#inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4

View File

@@ -4,7 +4,10 @@
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers-contrib/features/pnpm:2": {}
"ghcr.io/devcontainers-contrib/features/pnpm:2": {},
"ghcr.io/devcontainers/features/node:1": {
"version": "18.16.0"
}
},
"forwardPorts": [3000],
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",

View File

@@ -51,6 +51,23 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -62,15 +79,29 @@ redis:
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#redisForPubsub:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
#redisForJobQueue:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────

View File

@@ -8,7 +8,6 @@ build/
built/
db/
docker-compose.yml
elasticsearch/
node_modules/
packages/*/node_modules
redis/

25
.github/labeler.yml vendored
View File

@@ -1,12 +1,21 @@
'Server':
'packages/backend':
- packages/backend/**/*
'🖥Client':
- packages/frontend/**/*
'🧪Test':
- cypress/**/*
'packages/backend:test':
- packages/backend/test/**/*
'‼️ wrong locales':
- any: ['locales/*.yml', '!locales/ja-JP.yml']
'packages/frontend':
- packages/frontend/**/*
'packages/frontend:test':
- cypress/**/*
'packages/sw':
- packages/sw/**/*
'packages/misskey-js':
- packages/misskey-js/**/*
'packages/misskey-js:test':
- packages/misskey-js/test/**/*
- packages/misskey-js/test-d/**/*

View File

@@ -19,5 +19,6 @@ https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
## Checklist
- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
- [ ] Test working in a local environment
- [ ] (If needed) Add story of storybook
- [ ] (If needed) Update CHANGELOG.md
- [ ] (If possible) Add tests

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
node-version-file: '.node-version'
cache: 'pnpm'
- name: Install dependencies

View File

@@ -17,11 +17,11 @@ jobs:
submodules: true
- uses: pnpm/action-setup@v2
with:
version: 7
version: 8
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -48,7 +48,7 @@ jobs:
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -74,7 +74,7 @@ jobs:
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile

View File

@@ -2,10 +2,6 @@ name: Storybook
on:
push:
branches:
- master
- develop
pull_request:
branches-ignore:
- l10n_develop
@@ -13,6 +9,9 @@ jobs:
build:
runs-on: ubuntu-latest
env:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- uses: actions/checkout@v3.3.0
with:
@@ -21,12 +20,12 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7
version: 8
run_install: false
- name: Use Node.js 18.x
uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -36,21 +35,38 @@ jobs:
run: pnpm --filter misskey-js build
- name: Build storybook
run: pnpm --filter frontend build-storybook
env:
NODE_OPTIONS: "--max_old_space_size=7168"
- name: Publish to Chromatic
id: chromatic
uses: chromaui/action@v1
with:
exitOnceUploaded: true
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
storybookBuildDir: storybook-static
workingDir: packages/frontend
- name: Compare on Chromatic
if: github.event_name == 'pull_request_target'
run: pnpm --filter frontend chromatic -d storybook-static --exit-once-uploaded --patch-build ${{ github.head_ref }}...${{ github.base_ref }}
if: github.ref == 'refs/heads/master'
run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
if: github.ref != 'refs/heads/master'
id: chromatic
run: |
DIFF="${{ github.event.before }} HEAD"
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
DIFF="HEAD"
fi
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
fi
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static $(echo "$CHROMATIC_PARAMETER")
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic will skip testing
uses: actions/github-script@v6.4.0
if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/develop' && steps.chromatic.outputs.skip == 'true'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: 'Chromatic will skip testing but you may still have to [review the changes on Chromatic](https://www.chromatic.com/pullrequests?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:

View File

@@ -35,7 +35,7 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7
version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0

View File

@@ -22,7 +22,7 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7
version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
@@ -106,7 +106,7 @@ jobs:
install: false
start: pnpm start:test
wait-on: 'http://localhost:61812'
headless: false
headed: true
browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v2
if: failure()

42
.github/workflows/test-production.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Test (production install and build)
on:
push:
branches:
- master
- develop
pull_request:
env:
NODE_ENV: production
jobs:
production:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
steps:
- uses: actions/checkout@v3.3.0
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.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
- name: Copy Configure
run: cp .github/misskey/test.yml .config/default.yml
- name: Build
run: pnpm build

2
.gitignore vendored
View File

@@ -44,7 +44,7 @@ built
/data
/.cache-loader
/db
/elasticsearch
/meili_data
npm-debug.log
*.pem
run.bat

View File

@@ -1 +1 @@
v18.13.0
18.16.0

View File

@@ -6,5 +6,6 @@
"files.associations": {
"*.test.ts": "typescript"
},
"jest.jestCommandLine": "pnpm run jest",
"jest.autoRun": "off"
}

View File

@@ -5,36 +5,163 @@
-
### Client
-
-
### Server
-
-->
## 13.x.x (unreleased)
### NOTE
- Redis 7.xが必要です
- Node.js 18.6.0以上が必要になりました
### General
- Meilisearchを全文検索に使用できるようになりました
- 新規登録前に簡潔なルールをユーザーに表示できる、サーバールール機能を追加
- ユーザーへの自分用メモ機能
* ユーザーに対して、自分だけが見られるメモを追加できるようになりました。
(自分自身に対してもメモを追加できます。)
* ユーザーメニューから追加できます。
デスクトップ表示ではusernameの右側のボタンからも追加可能
- アカウントの引っ越し(フォロワー引き継ぎ)に対応
* 一度引っ越したアカウントは利用に制限がかかります
- チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。
- ロールタイムラインをロールごとに表示するかどうかの選択できるようになりました。
* デフォルトがオフになるので、ロールタイムラインを表示する場合はオンにしてください。
- カスタム絵文字のライセンスを複数でセットできるようになりました。
- 管理者が予約ユーザー名を設定できるようになりました。
- Fix: フォローリクエストの通知が残る問題を修正
### Client
- チャンネル内検索ができるように
- チャンネル検索ですべてのチャンネルの取得/表示ができるように
- 通知の表示をカスタマイズできるように
- ドライブのファイル一覧から直接ノートを作成できるように
- ートメニューからRenoteしたユーザーの一覧を見れるように
- コントロールパネルのカスタム絵文字ページおよびaboutのカスタム絵文字の検索インプットで、`:emojiname1::emojiname2:`のように検索して絵文字を検索できるように
* 絵文字ピッカーから入力可能になります
- データセーバーモードを追加
* 画像が全て隠れた状態で表示されるようになります
- 1枚だけのメディアリストの画像のアスペクト比を画像に応じて縦長にするように
- 新しい実績を追加
- Fix: AiScript APIのMk:dialogで何も返していなかったのをNULLを返すように修正
- Fix: リアクションをホバーした時のユーザーリストで猫耳が切れてしまっていた問題を修正
### Server
- channel/searchのqueryが空の場合に全てのチャンネルを返すように変更
- 環境変数MISSKEY_CONFIG_YMLで設定ファイルをdefault.ymlから変更可能に
- Fix: 他のサーバーの情報が取得できないことがある問題を修正
- Fix: エクスポートデータの拡張子がunknownになる問題を修正
- Fix: Content-Dispositionのパースでエラーが発生した場合にダウンロードが完了しない問題を修正
- Fix: API: i/update avatarIdとbannerIdにnullを渡した時、画像がリセットされない問題を修正
- Fix: 1:1ではない画像のリアクション通知バッジが左や上に寄ってしまっていたのを中央に来るように修正
- Fix: .wav, .flacが再生できない問題を修正新しくアップロードされたファイルのみ修正が適用されます
- Fix: メモリの使用量を`used - buffers - cached`ではなく`total - available`で求めるように(環境によって正常に計測できていなかったため)
## 13.11.3
### General
- 指定したロールを持つユーザーのノートのみが流れるロールタイムラインを追加
- Deckのカラムとしても追加可能
- カスタム絵文字関連の改善
* ートなどに含まれるemojispopulateEmojiの結果プロキシされたURLではなくオリジナルのURLを指すように
* MFMでx3/x4もしくはscale.x/yが2.5以上に指定されていた場合にはオリジナル品質の絵文字を使用するように
- カスタム絵文字でリアクションできないことがある問題を修正
### Client
- チャンネルのピン留めされたノートの順番が正しくない問題を修正
### Server
- フォローインポートなどでの大量のフォロー等操作をキューイングするように #10544 @nmkj-io
- Misskey Webでのサーバーサイドエラー画面を改善
- Misskey Webでのサーバーサイドエラーのログが残るように
- ノート作成時のアンテナ追加パフォーマンスを改善
- アンテナとロールTLのuntil/sinceプロパティが動くように
## 13.11.2
### Note
- 13.11.0または13.11.1から13.11.2以降にアップデートする場合、Redisのカスタム絵文字のキャッシュを削除する必要があります(https://github.com/misskey-dev/misskey/issues/10502#issuecomment-1502790755 参照)
### General
- チャンネルの検索用ページの追加
### Client
- 常に広告を見られるオプションを追加
- ユーザーページの画像一覧が表示されない問題を修正
- webhook, 連携アプリ一覧でコンテンツが重複して表示される問題を修正
- iPhoneで絵文字ピッカーの表示が崩れる問題を修正
- iPhoneでウィジェットドロワーの「ウィジェットを編集」が押しにくい問題を修正
- 投稿フォームのデザインを調整
- ギャラリーの人気の投稿が無限にページングされる問題を修正
### Server
- channels/search Endpoint APIの追加
- APIパラメータサイズ上限を32kbから1mbに緩和
- プッシュ通知送信時のパフォーマンスを改善
- ローカルのカスタム絵文字のキャッシュが効いていなかった問題を修正
- アンテナのノート、チャンネルのノート、通知が正常に作成できないことがある問題を修正
- ストリーミングのLTLチャンネルでサーバー側にエラーログが出るのを修正
### Service Worker
- 「通知が既読になったらプッシュ通知を削除する」を復活
* 「プッシュ通知が更新されました」の挙動を変えた(ホストとバージョンを表示するようにし、一定時間後の削除は行わないように)
- プッシュ通知が実績を解除 (achievementEarned) に対応
- プッシュ通知のアクションから既存のクライアントの投稿フォームを開くことになった際の挙動を修正
- たくさんのプッシュ通知を閉じた際、その通知の数だけnotifications/mark-all-as-readを叩くのをやめるように
## 13.11.1
### General
- チャンネルの投稿を過去までさかのぼれるように
### Client
- PWA時の絵文字ピッカーの位置をホームバーに重ならないように調整
- リスト管理の画面でリストが無限に読み込まれる問題を修正
- 自分のクリップが無限に読み込まれる問題を修正
- チャンネルのお気に入りが無限に読み込まれる問題を修正
- さがすのローカルユーザー(ピンどめ)が無限に生成される問題を修正
- チャンネルを新規作成できない問題を修正
- ユーザープレビューが表示されない問題を修正
### Server
- 通知読み込みでエラーが発生する場合がある問題を修正
- リアクションできないことがある問題を修正
- IDをaid以外に設定している場合の問題を修正
- 連合しているインスタンスについて予期せず配送が全て停止されることがある問題を修正
## 13.11.0
### NOTE
- このバージョンからRedis 7.xが必要です。
- アップデートを行うと全ての通知およびアンテナのノートはリセットされます。
### General
- チャンネルをお気に入りに登録できるように
- タイムラインのアンテナ選択などでは、フォローしているアンテナの代わりにお気に入りしたアンテナが表示されるようになっています。チャンネルをお気に入りに登録するには、当該チャンネルのページ→概要→⭐️のボタンを押します。
- チャンネルにノートをピン留めできるように
### Client
- 投稿フォームのデザインを改善
- 検索ページでURLを入力した際に照会したときと同等の挙動をするように
- ノートのリアクションを大きく表示するオプションを追加
- ギャラリー一覧にメディア表示と同じように NSFW 設定を反映するように(ホバーで表示)
- オブジェクトストレージの設定画面を分かりやすく
- 広告・お知らせが新規登録時に増殖しないように
- 「にゃああああああああああああああ!!!!!!!!!!!!」 (`isCat`) 有効時にアバターに表示される猫耳について挙動を変更
- 「UIにぼかし効果を使用」 (`useBlurEffect`) で次の挙動が有効になります
- 猫耳のアバター内部部分をぼかしでマスク表示してより猫耳っぽく見えるように
- 猫耳の色がアバター上部のピクセルから決定されます(無効化時はアバター全体の平均色)
- 左耳は上からおよそ 10%, 左からおよそ 20% の位置で決定します
- 右耳は上からおよそ 10%, 左からおよそ 80% の位置で決定します
- 「UIのアニメーションを減らす」 (`reduceAnimation`) で猫耳を撫でられなくなります
- Add Minimizing ("folding") of windows
- 「データセーバー」モードを追加
- 非NSFWメディアが隠れている際にも「閲覧注意」が出てしまう問題を修正
### Server
- PostgreSQLのレプリケーション対応
- 設定ファイルの `dbReplications` および `dbSlaves` にて設定できます
- イベント用Redisを別サーバーに分離できるように
- ジョブキュー用Redisを別サーバーに分離できるように
- サーバーの全体的なパフォーマンスを向上
- ノート作成時のパフォーマンスを向上
- アンテナのタイムライン取得時のパフォーマンスを向上

View File

@@ -165,6 +165,11 @@ pnpm jest -- foo.ts
### e2e tests
TODO
## Environment Variable
- `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
## Continuous integration
Misskey uses GitHub Actions for executing automated tests.
Configuration files are located in [`/.github/workflows`](/.github/workflows).
@@ -245,7 +250,6 @@ You can override the default story by creating a impl story file (`MyComponent.s
```ts
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-duplicates */
import { StoryObj } from '@storybook/vue3';
import MyComponent from './MyComponent.vue';
export const Default = {

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.4
ARG NODE_VERSION=18.13.0-bullseye
ARG NODE_VERSION=18.16.0-bullseye
# build assets & compile TypeScript

View File

@@ -61,10 +61,18 @@ Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/), s
## Thanks
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" width="153" height="30" alt="Chromatic" /></a>
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" width="117" height="30" alt="Docker" /></a>
<a href="https://about.codecov.io/for/open-source/"><img src="https://about.codecov.io/wp-content/themes/codecov/assets/brand/sentry-cobranding/logos/codecov-by-sentry-logo.svg" height="30" alt="Codecov" /></a>
Thanks to [Codecov](https://about.codecov.io/for/open-source/) for providing the code coverage platform that helps us improve our test coverage.
<a href="https://crowdin.com/"><img src="https://user-images.githubusercontent.com/20679825/230709597-1299a011-171a-4294-a91e-355a9b37c672.svg" height="30" alt="Crowdin" /></a>
Thanks to [Crowdin](https://crowdin.com/) for providing the localization platform that helps us translate Misskey into many languages.
<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a>
Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production.

View File

@@ -72,25 +72,57 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: localhost
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
#redisForPubsub:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForJobQueue:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: localhost
# port: 7700
# apiKey: ''
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────

View File

@@ -52,6 +52,11 @@ describe('After setup instance', () => {
cy.intercept('POST', '/api/signup').as('signup');
cy.get('[data-cy-signup]').click();
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
cy.get('[data-cy-signup-rules-continue]').click();
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-submit]').should('be.disabled');
@@ -71,6 +76,11 @@ describe('After setup instance', () => {
// ユーザー名が重複している場合の挙動確認
cy.get('[data-cy-signup]').click();
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
cy.get('[data-cy-signup-rules-continue]').click();
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-password-retype] input').type('alice1234');

View File

@@ -7,7 +7,7 @@ services:
links:
- db
- redis
# - es
# - meilisearch
depends_on:
db:
condition: service_healthy
@@ -48,16 +48,18 @@ services:
interval: 5s
retries: 20
# es:
# meilisearch:
# restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
# image: getmeili/meilisearch:v1.1.1
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# - "TAKE_FILE_OWNERSHIP=111"
# - MEILI_NO_ANALYTICS=true
# - MEILI_ENV=production
# env_file:
# - .config/meilisearch.env
# networks:
# - internal_network
# volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data
# - ./meili_data:/meili_data
networks:
internal_network:

View File

@@ -45,7 +45,7 @@ gulp.task('build:backend:script', () => {
});
gulp.task('build:backend:style', () => {
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css'])
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css', './packages/backend/src/server/web/error.css'])
.pipe(cssnano({
zindex: false
}))

View File

@@ -1277,4 +1277,3 @@ _deck:
_webhookSettings:
name: "الإسم"
active: "مفعّل"

View File

@@ -1356,4 +1356,3 @@ _deck:
_webhookSettings:
name: "নাম"
active: "চালু"

View File

@@ -460,4 +460,3 @@ _deck:
list: "Llistes"
mentions: "Mencions"
direct: "Publicacions directes"

View File

@@ -779,4 +779,3 @@ _deck:
_webhookSettings:
name: "Jméno"
active: "Zapnuto"

View File

@@ -1,3 +1,2 @@
---
_lang_: "Dansk"

View File

@@ -20,6 +20,7 @@ noNotes: "Keine Notizen gefunden"
noNotifications: "Keine Benachrichtigungen gefunden"
instance: "Instanz"
settings: "Einstellungen"
notificationSettings: "Benachrichtigungseinstellungen"
basicSettings: "Allgemeine Einstellungen"
otherSettings: "Weitere Einstellungen"
openInWindow: "In einem Fenster öffnen"
@@ -67,7 +68,7 @@ import: "Import"
export: "Export"
files: "Dateien"
download: "Herunterladen"
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Notizen mit dieser Datei werden ebenso verschwinden."
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Sie wird in allen Inhalten, die sie verwenden, auch verschwinden."
unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
@@ -196,7 +197,7 @@ instanceInfo: "Instanzinformationen"
statistics: "Statistiken"
clearQueue: "Warteschlange leeren"
clearQueueConfirmTitle: "Möchtest du die Warteschlange wirklich leeren?"
clearQueueConfirmText: "Hierdurch werden jegliche noch nicht gesendete Notizen nicht förderiert. Normalerweise wird dies nicht benötigt."
clearQueueConfirmText: "Hierdurch werden jegliche noch nicht gesendete Notizen nicht föderiert. Normalerweise wird dies nicht benötigt."
clearCachedFiles: "Cache leeren"
clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?"
blockedInstances: "Blockierte Instanzen"
@@ -500,12 +501,13 @@ objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeich
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten Endpoint im Format „<host>“ oder „<host>:<port>“ angeben."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib „us-east-1“ an."
objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, gib „us-east-1“ an. Lasse es leer bei Verwendung von AWS Konfigurationsdateien oder Umgebungsvariablen."
objectStorageUseSSL: "SSL verwenden"
objectStorageUseSSLDesc: "Deaktiviere dies, falls du für API-Verbindungen kein HTTPS verwenden wirst"
objectStorageUseProxy: "Über Proxy verbinden"
objectStorageUseProxyDesc: "Deaktiviere dies, falls du für Verbindungen zur API keinen Proxy verwenden wirst"
objectStorageSetPublicRead: "Bei Upload auf \"public-read\" stellen"
s3ForcePathStyleDesc: "Ist s3ForcePathStyle aktiviert, so muss der Bucketname nicht im Hostnamen der URL, sondern im Pfad der URL angeben werden. Diese Option muss eventuell aktiviert werden, wenn Dienste wie z.B. eine selbstbetriebene Minio-Instanz verwendet werden."
serverLogs: "Serverprotokolle"
deleteAll: "Alle löschen"
showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen"
@@ -919,6 +921,7 @@ pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterst
sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald die relevanten Benachrichtigungen oder Nachrichten gelesen wurden"
sendPushNotificationReadMessageCaption: "Eine Push-Benachrichtigungen mit dem Inhalt \"{emptyPushNotificationMessage}\" wird kurz eingeblendet. Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
windowMaximize: "Maximieren"
windowMinimize: "Minimieren"
windowRestore: "Wiederherstellen"
caption: "Beschreibung"
loggedInAsBot: "Momentan als Bot angemeldet"
@@ -960,7 +963,9 @@ copyErrorInfo: "Fehlerdetails kopieren"
joinThisServer: "Bei dieser Instanz registrieren"
exploreOtherServers: "Eine andere Instanz finden"
letsLookAtTimeline: "Die Chronik durchstöbern"
disableFederationWarn: "Dies deaktiviert Föderation, aber alle Notizen bleiben, sofern nicht umgestellt, öffentlich. In den meisten Fällen wird diese Option nicht benötigt."
disableFederationConfirm: "Föderation wirklich deaktivieren?"
disableFederationConfirmWarn: "Auch mit deaktivierter Föderation bleiben Notizen, sofern nicht umgestellt, öffentlich. In den meisten Fällen wird dies nicht benötigt."
disableFederationOk: "Deaktivieren"
invitationRequiredToRegister: "Diese Instanz ist einladungsbasiert. Du musst einen validen Einladungscode eingeben, um dich zu registrieren."
emailNotSupported: "Diese Instanz unterstützt das Versenden von Emails nicht"
postToTheChannel: "In Kanal senden"
@@ -983,6 +988,19 @@ retryAllQueuesConfirmText: "Dies wird zu einer temporären Erhöhung der Serverl
enableChartsForRemoteUser: "Diagramme für Nutzer fremder Instanzen erstellen"
enableChartsForFederatedInstances: "Diagramme für fremde Instanzen erstellen"
showClipButtonInNoteFooter: "\"Clip\" zum Notizmenu hinzufügen"
largeNoteReactions: "Reaktionen vergrößert anzeigen"
noteIdOrUrl: "Notiz-ID oder URL"
accountMigration: "Konto-Umzug"
accountMoved: "Dieser Benutzer ist zu einem neuen Konto umgezogen:"
forceShowAds: "Werbung immer anzeigen"
_accountMigration:
moveTo: "Dieses Konto zu einem neuen umziehen"
moveToLabel: "Umzugsziel:"
moveAccountDescription: "Ein Umzug kann nicht rückgängig gemacht werden. Stelle zuerst sicher, dass du auf dem Umzugsziel einen Alias erstellt hast. Gib dann das Umzugsziel in folgendem Format ein: @person@instance.com"
moveFrom: "Von einem anderen Konto zu diesem umziehen"
moveFromLabel: "Umzugsursprung:"
moveFromDescription: "Stelle sicher, dass du auf dem Umzugsursprungskonto einen Alias zu diesem Konto erstellt hast, falls du die Follower des Ursprungskontos übertragen möchtest. Dies muss vor dem Umzug geschehen! Gib dann das Ursprungskonto in folgendem Format an: @person@instance.com"
migrationConfirm: "Dieses Konto wirklich zu {account} umziehen? Sobald der Umzug beginnt, kann er nicht rückgängig gemacht werden, und dieses Konto nicht wieder im ursprünglichen Zustand verwendet werden.\n\nÜberprüfe zusätzlich, dass du auf dem Umzugsziel einen Alias eingerichtet hast."
_achievements:
earnedAt: "Freigeschaltet am"
_types:
@@ -1255,7 +1273,7 @@ _role:
gtlAvailable: "Kann auf die globale Chronik zugreifen"
ltlAvailable: "Kann auf die lokale Chronik zugreifen"
canPublicNote: "Kann öffentliche Notizen erstellen"
canInvite: "Kann Einladungscodes für diese Instanz erstellen"
canInvite: "Erstellung von Einladungscodes für diese Instanz"
canManageCustomEmojis: "Benutzerdefinierte Emojis verwalten"
driveCapacity: "Drive-Kapazität"
pinMax: "Maximale Anzahl an angehefteten Notizen"
@@ -1390,6 +1408,8 @@ _channel:
following: "Gefolgt"
usersCount: "{n} Teilnehmer"
notesCount: "{n} Notizen"
nameAndDescription: "Name und Beschreibung"
nameOnly: "Nur Name"
_menuDisplay:
sideFull: "Seitlich"
sideIcon: "Seitlich (Icons)"
@@ -1408,7 +1428,7 @@ _instanceMute:
instanceMuteDescription: "Schaltet alle Notizen/Renotes stumm, die von den gelisteten Instanzen stammen, inklusive Antworten von Benutzern an einen Benutzer einer stummgeschalteten Instanz."
instanceMuteDescription2: "Instanzen getrennt durch Zeilenumbrüchen angeben"
title: "Blendet Notizen von stummgeschalteten Instanzen aus."
heading: "Liste der stummzuschaltenden Instanzen"
heading: "Stummzuschaltende Instanzen"
_theme:
explore: "Farbschemata erforschen"
install: "Farbschemata installieren"
@@ -1680,7 +1700,7 @@ _visibility:
followersDescription: "Nur für Follower sichtbar"
specified: "Direkt"
specifiedDescription: "Nur für bestimmte Benutzer sichtbar"
disableFederation: "Deförderiert"
disableFederation: "Deföderieren"
disableFederationDescription: "Nicht an andere Instanzen übertragen"
_postForm:
replyPlaceholder: "Dieser Notiz antworten …"
@@ -1870,6 +1890,7 @@ _deck:
channel: "Kanal"
mentions: "Erwähnungen"
direct: "Direktnachrichten"
roleTimeline: "Rollenchronik"
_dialog:
charactersExceeded: "Maximallänge überschritten! Momentan {current} von {max}"
charactersBelow: "Minimallänge unterschritten! Momentan {current} von {min}"
@@ -1893,4 +1914,3 @@ _webhookSettings:
renote: "Wenn du ein Renote erhältst"
reaction: "Wenn du eine Reaktion erhältst"
mention: "Wenn du erwähnt wirst"

View File

@@ -394,4 +394,3 @@ _deck:
mentions: "Επισημάνσεις"
_webhookSettings:
name: "Όνομα"

View File

@@ -20,6 +20,7 @@ noNotes: "No notes"
noNotifications: "No notifications"
instance: "Instance"
settings: "Settings"
notificationSettings: "Notification Settings"
basicSettings: "Basic Settings"
otherSettings: "Other Settings"
openInWindow: "Open in window"
@@ -67,7 +68,7 @@ import: "Import"
export: "Export"
files: "Files"
download: "Download"
driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted."
driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? It will also vanish from all contents that use it."
unfollowConfirm: "Are you sure you want to unfollow {name}?"
exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed."
importRequested: "You've requested an import. This may take a while."
@@ -500,12 +501,13 @@ objectStoragePrefixDesc: "Files will be stored under directories with this prefi
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '<host>' or '<host>:<port>', depending on the service you are using."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not distinguish between regions, leave this blank or enter 'us-east-1'."
objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not distinguish between regions, enter 'us-east-1'. Leave empty if using AWS configuration files or environment variables."
objectStorageUseSSL: "Use SSL"
objectStorageUseSSLDesc: "Turn this off if you are not going to use HTTPS for API connections"
objectStorageUseProxy: "Connect over Proxy"
objectStorageUseProxyDesc: "Turn this off if you are not going to use a Proxy for API connections"
objectStorageSetPublicRead: "Set \"public-read\" on upload"
s3ForcePathStyleDesc: "If s3ForcePathStyle is enabled, the bucket name has to included in the path of the URL as opposed to the hostname of the URL. You may need to enable this setting when using services such as a self-hosted Minio instance."
serverLogs: "Server logs"
deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline"
@@ -903,6 +905,7 @@ remoteOnly: "Remote only"
failedToUpload: "Upload failed"
cannotUploadBecauseInappropriate: "This file could not be uploaded because parts of it have been detected as potentially NSFW."
cannotUploadBecauseNoFreeSpace: "Upload failed due to lack of Drive capacity."
cannotUploadBecauseExceedsFileSizeLimit: "This file could not be uploaded because it exceeds the maximum allowed size."
beta: "Beta"
enableAutoSensitive: "Automatic NSFW-Marking"
enableAutoSensitiveDescription: "Allows automatic detection and marking of NSFW media through Machine Learning where possible. Even if this option is disabled, it may be enabled instance-wide."
@@ -917,8 +920,9 @@ unsubscribePushNotification: "Disable push notifications"
pushNotificationAlreadySubscribed: "Push notifications are already enabled"
pushNotificationNotSupported: "Your browser or instance does not support push notifications"
sendPushNotificationReadMessage: "Delete push notifications once the relevant notifications or messages have been read"
sendPushNotificationReadMessageCaption: "A notification containing the text \"{emptyPushNotificationMessage}\" will be displayed for a short time. This may increase the battery usage of your device, if applicable."
sendPushNotificationReadMessageCaption: "A notification containing the text \"{emptyPushNotificationMessage}\" will be displayed for a short time. This may increase the power consumption of your device."
windowMaximize: "Maximize"
windowMinimize: "Minimize"
windowRestore: "Restore"
caption: "Caption"
loggedInAsBot: "Currently logged in as bot"
@@ -960,7 +964,9 @@ copyErrorInfo: "Copy error details"
joinThisServer: "Sign up at this instance"
exploreOtherServers: "Look for another instance"
letsLookAtTimeline: "Have a look at the timeline"
disableFederationWarn: "This will disable federation, but posts will continue to be public unless set otherwise. You usually do not need to use this setting."
disableFederationConfirm: "Really disable federation?"
disableFederationConfirmWarn: "Even if defederated, posts will continue to be public unless set otherwise. You usually do not need to do this."
disableFederationOk: "Disable"
invitationRequiredToRegister: "This instance is invite-only. You must enter a valid invite code sign up."
emailNotSupported: "This instance does not support sending emails"
postToTheChannel: "Post to channel"
@@ -983,6 +989,19 @@ retryAllQueuesConfirmText: "This will temporarily increase the server load."
enableChartsForRemoteUser: "Generate remote user data charts"
enableChartsForFederatedInstances: "Generate remote instance data charts"
showClipButtonInNoteFooter: "Add \"Clip\" to note action menu"
largeNoteReactions: "Enlargen displayed reactions"
noteIdOrUrl: "Note ID or URL"
accountMigration: "Account Migration"
accountMoved: "This user has moved to a new account:"
forceShowAds: "Always show ads"
_accountMigration:
moveTo: "Migrate this account to a different one"
moveToLabel: "Account to move to:"
moveAccountDescription: "This action is irreversible. First, create an alias for this account on the account you wish to move to. Then, enter the account to move to in the following format: @person@instance.com"
moveFrom: "Migrate another account to this one"
moveFromLabel: "Account to move from:"
moveFromDescription: "Create an alias for the account to move from on this account if you wish to transfer its followers. This has to be done before the transfer! Then, enter the account to move to in the following format: @person@instance.com"
migrationConfirm: "Really migrate this account to {account}? Once started, this process cannot be stopped or taken back, and you will not be able to use this account in its original state anymore.\n\nAlso, confirm you've created an alias at the account to migrate to."
_achievements:
earnedAt: "Unlocked at"
_types:
@@ -1390,6 +1409,8 @@ _channel:
following: "Followed"
usersCount: "{n} Participants"
notesCount: "{n} Notes"
nameAndDescription: "Name and description"
nameOnly: "Name only"
_menuDisplay:
sideFull: "Side"
sideIcon: "Side (Icons)"
@@ -1680,7 +1701,7 @@ _visibility:
followersDescription: "Make visible to your followers only"
specified: "Direct"
specifiedDescription: "Make visible for specified users only"
disableFederation: "Unfederated"
disableFederation: "Defederate"
disableFederationDescription: "Don't transmit to other instances"
_postForm:
replyPlaceholder: "Reply to this note..."
@@ -1852,7 +1873,7 @@ _deck:
swapRight: "Swap with the right column"
swapUp: "Swap with the above column"
swapDown: "Swap with the below column"
stackLeft: "Stack with the left column"
stackLeft: "Stack on left column"
popRight: "Pop column to the right"
profile: "Profile"
newProfile: "New profile"
@@ -1870,6 +1891,7 @@ _deck:
channel: "Channel"
mentions: "Mentions"
direct: "Direct notes"
roleTimeline: "Role Timeline"
_dialog:
charactersExceeded: "You've exceeded the maximum character limit! Currently at {current} of {max}."
charactersBelow: "You're below the minimum character limit! Currently at {current} of {min}."
@@ -1893,4 +1915,3 @@ _webhookSettings:
renote: "When renoted"
reaction: "When receiving a reaction"
mention: "When being mentioned"

View File

@@ -506,6 +506,7 @@ objectStorageUseSSLDesc: "Desactive esto si no va a usar HTTPS para la conexión
objectStorageUseProxy: "Conectarse a través de Proxy"
objectStorageUseProxyDesc: "Desactive esto si no va a usar Proxy para la conexión de Almacenamiento de objetos"
objectStorageSetPublicRead: "Seleccionar \"public-read\" al subir "
s3ForcePathStyleDesc: "Si s3ForcePathStyle esta habilitado el nombre del bucket debe ser especificado como parte de la URL en lugar del nombre de host en la URL. Puede ser necesario activar esta opción cuando se utilice, por ejemplo, Minio en un servidor propio."
serverLogs: "Registros del servidor"
deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
@@ -919,6 +920,7 @@ pushNotificationNotSupported: "El navegador o la instancia no admiten notificaci
sendPushNotificationReadMessage: "Eliminar las notificaciones push después de leer las notificaciones y los mensajes"
sendPushNotificationReadMessageCaption: "La notificación \"{emptyPushNotificationMessage}\" aparecerá momentáneamente. Esto puede aumentar el consumo de batería del dispositivo."
windowMaximize: "Maximizar"
windowMinimize: "Minimizar"
windowRestore: "Regresar"
caption: "Pie de foto"
loggedInAsBot: "Inicio sesión como cuenta bot."
@@ -960,7 +962,9 @@ copyErrorInfo: "Copiar detalles del error"
joinThisServer: "Registrarse en esta instancia"
exploreOtherServers: "Buscar otra instancia"
letsLookAtTimeline: "Mirar la línea de tiempo local"
disableFederationWarn: "Esto desactivará la federación, pero las publicaciones segurán siendo públicas al menos que se configure diferente. Usualmente no necesitas usar esta configuración."
disableFederationConfirm: "¿Estas seguro que quieres desactivar la federación?"
disableFederationConfirmWarn: "Aunque no exista federación los posts no serán marcados como privados. En la mayoría de los casos, no es necesario hacer los posts no federar."
disableFederationOk: "Desactivar."
invitationRequiredToRegister: "Esta instancia está configurada sólo por invitación, tienes que ingresar un código de invitación válido."
emailNotSupported: "Esta instancia no soporta el envío de correo electrónico"
postToTheChannel: "Publicar en el canal"
@@ -980,6 +984,21 @@ drivecleaner: "Limpiador del Drive"
retryAllQueuesNow: "Reintentar inmediatamente todas las colas"
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"
showClipButtonInNoteFooter: "Añadir \"Clip\" al menú de notas"
largeNoteReactions: "Agrandar las reacciones de las notas"
noteIdOrUrl: "ID o URL de la nota"
accountMigration: "Migración de cuenta"
accountMoved: "Este usuario se ha mudado a una nueva cuenta:"
_accountMigration:
moveTo: "Mover esta cuenta a una nueva"
moveToLabel: "Cuenta destino:"
moveAccountDescription: "Esta operación no puede deshacerse. En primer lugar, asegúrese de haber creado un alias para esta cuenta en la cuenta a la que se va a trasladar. Después de crear el alias, introduzca la cuenta a la que se está trasladando de la siguiente manera: @person@instance.com"
moveFrom: "Trasladar de otra cuenta a ésta"
moveFromLabel: "Cuenta desde la que se realiza el traslado:"
moveFromDescription: "Si quieres transferir seguidores de otra cuenta a esta cuenta y trasladarlos, tendrás que crear un alias aquí. Asegúrate de crearlo antes de realizar el traslado. Introduce la cuenta desde la que estás moviendo los seguidores así: @person@instance.com"
migrationConfirm: "¿Estás seguro de que quieres mover esta cuenta a {account}? Una vez trasladada, no podrás deshacer el traslado y no podrás volver a utilizar la cuenta original.\n\nAdemás, compruebe que ha configurado un alias en el destino del traslado."
_achievements:
earnedAt: "Desbloqueado el"
_types:
@@ -1276,6 +1295,8 @@ _role:
followersMoreThanOrEq: "Tiene X o más seguidores"
followingLessThanOrEq: "Sigue X o menos cuentas"
followingMoreThanOrEq: "Sigue X o más cuentas"
notesLessThanOrEq: "El número de notas es inferior o igual a"
notesMoreThanOrEq: "El número de notas es superior o igual a"
and: "Condicional AND"
or: "Condicional OR"
not: "Condicional NOT"
@@ -1875,6 +1896,16 @@ _drivecleaner:
orderBySizeDesc: "Más grandes"
orderByCreatedAtAsc: "Más antiguos"
_webhookSettings:
createWebhook: "Crear Webhook"
name: "Nombre"
secret: "Secreto"
events: "Eventos de webhook"
active: "Activado"
_events:
follow: "Cuando se sigue a alguien"
followed: "Cuando se es seguido"
note: "Cuando se publica una nota"
reply: "Cuando se recibe una respuesta"
renote: "Cuando reciba un \"re-note\""
reaction: "Cuando se recibe una reacción"
mention: "Cuando hay una mención"

View File

@@ -1470,4 +1470,3 @@ _deck:
_webhookSettings:
name: "Nom"
active: "Activé"

View File

@@ -1,2 +1 @@
---

View File

@@ -1,2 +1 @@
---

View File

@@ -122,6 +122,8 @@ unmarkAsSensitive: "Hapus tanda konten sensitif"
enterFileName: "Masukkan nama berkas"
mute: "Bisukan"
unmute: "Hapus bisukan"
renoteMute: "Matikan renote"
renoteUnmute: "Batal mematikan renote"
block: "Blokir"
unblock: "Buka blokir"
suspend: "Bekukan"
@@ -393,11 +395,15 @@ about: "Informasi"
aboutMisskey: "Tentang Misskey"
administrator: "Admin"
token: "Token"
totp: "Aplikasi autentikator"
totpDescription: "Gunakan aplikasi autentikator untuk mendapatkan kata sandi sekali pakai"
moderator: "Moderator"
moderation: "Moderasi"
nUsersMentioned: "{n} pengguna disebut"
securityKeyAndPasskey: "Security key dan passkey"
securityKey: "Kunci keamanan"
lastUsed: "Terakhir digunakan"
lastUsedAt: "Penggunaan terakhir: {t}"
unregister: "Batalkan pendaftaran"
passwordLessLogin: "Setel login tanpa kata sandi"
resetPassword: "Atur ulang kata sandi"
@@ -844,6 +850,7 @@ tenMinutes: "10 Menit"
oneHour: "1 Jam"
oneDay: "1 Hari"
oneWeek: "1 Bulan"
oneMonth: "satu bulan"
reflectMayTakeTime: "Mungkin perlu beberapa saat untuk dicerminkan."
failedToFetchAccountInformation: "Gagal untuk mendapatkan informasi akun"
rateLimitExceeded: "Batas sudah terlampaui"
@@ -901,6 +908,7 @@ pushNotificationNotSupported: "Browser atau instansi kamu tidak mendukung pember
sendPushNotificationReadMessage: "Hapus pemberitahuan push ketika pemberitahuan relevan atau pesan telah dibaca"
sendPushNotificationReadMessageCaption: "Pemberitahuan berisi teks「{emptyPushNotificationMessage}」akan ditampilkan dalam waktu pendek. Ini mungkin dapat menambah pemakaian baterai pada perangkat kamu."
windowMaximize: "Maksimalkan"
windowMinimize: "Minimalkan"
windowRestore: "Kembalikan"
caption: "Keterangan"
loggedInAsBot: "Sedang login sebagai bot"
@@ -939,6 +947,12 @@ collapseRenotes: "Tutup renote yang sudah kamu lihat"
internalServerError: "Kesalahan internal peladen"
internalServerErrorDescription: "Peladen sedang mengalami galat tak terduga"
copyErrorInfo: "Salin detil galat"
joinThisServer: "Gabung server ini"
exploreOtherServers: "Cari server lain"
letsLookAtTimeline: "LIhat timeline"
disableFederationConfirm: "Matikan federasi?"
disableFederationConfirmWarn: "Mematikan federasi tidak membuat kiriman menjadi privat. Umumnya, mematikan federasi tidak diperlukan."
disableFederationOk: "Matikan federasi"
_achievements:
earnedAt: "Terbuka pada"
_types:
@@ -1806,4 +1820,3 @@ _deck:
_webhookSettings:
name: "Nama"
active: "Aktif"

View File

@@ -20,6 +20,7 @@ noNotes: "Nessuna nota!"
noNotifications: "Nessuna notifica"
instance: "Istanza"
settings: "Impostazioni"
notificationSettings: "Preferenze di notifica"
basicSettings: "Impostazioni generali"
otherSettings: "Altre impostazioni"
openInWindow: "Apri in una finestra"
@@ -170,7 +171,7 @@ proxyAccountDescription: "Un profilo proxy funziona come follower per i profili
host: "Server remoto"
selectUser: "Seleziona profilo"
recipient: "Destinatario"
annotation: "Descrizione"
annotation: "Annotazione preventiva"
federation: "Federazione"
instances: "Istanza"
registeredAt: "Registrato presso"
@@ -212,7 +213,7 @@ intro: "L'installazione di Misskey è terminata! Si prega di creare il profilo a
done: "Fine"
processing: "In elaborazione"
preview: "Anteprima"
default: "Medio"
default: "Predefinito"
defaultValueIs: "Predefinito: {value}"
noCustomEmojis: "Nessun emoji"
noJobs: "Nessun lavoro"
@@ -237,14 +238,14 @@ more: "Di più!"
featured: "Tendenze"
usernameOrUserId: "Nome utente o ID utente"
noSuchUser: "Nessun utente trovato"
lookup: "Cerca"
lookup: "Ricerca remota"
announcements: "Annunci"
imageUrl: "URL dell'immagine"
remove: "Elimina"
removed: "Eliminato con successo"
removeAreYouSure: "Vuoi davvero eliminare \"{x}\"?"
deleteAreYouSure: "Eliminare \"{x}\"?"
resetAreYouSure: "Reimposta"
resetAreYouSure: "Ripristinare?"
saved: "Salvato"
messaging: "Messaggi"
upload: "Carica"
@@ -409,7 +410,7 @@ lastUsedAt: "Uso più recente: {t}"
unregister: "Annulla l'iscrizione"
passwordLessLogin: "Accedi senza password"
passwordLessLoginDescription: "Accedi senza password, usando la chiave di sicurezza"
resetPassword: "Reimposta password"
resetPassword: "Ripristina la password"
newPasswordIs: "La tua nuova password è「{password}」"
reduceUiAnimation: "Ridurre le animazioni dell'interfaccia"
share: "Condividi"
@@ -506,6 +507,7 @@ objectStorageUseSSLDesc: "Disabilita quest'opzione se non utilizzi HTTPS per le
objectStorageUseProxy: "Usa proxy"
objectStorageUseProxyDesc: "Disabilita quest'opzione se non usi proxy per la connessione API."
objectStorageSetPublicRead: "Imposta \"visibilità pubblica\" al momento di caricare"
s3ForcePathStyleDesc: "L'attivazione di s3ForcePathStyle impone di specificare il nome del bucket come parte del percorso nell'URL anziché del nome host. Potrebbe tornare utile quando si utilizzano applicazioni come Minio."
serverLogs: "Log del server"
deleteAll: "Cancella cronologia"
showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline"
@@ -564,7 +566,7 @@ invisibleNote: "Nota invisibile"
enableInfiniteScroll: "Abilita scorrimento infinito"
visibility: "Visibilità"
poll: "Sondaggio"
useCw: "Nascondere media"
useCw: "Content Warning"
enablePlayer: "Visualizza"
disablePlayer: "Chiudi"
expandTweet: "Espandi tweet"
@@ -579,7 +581,7 @@ plugins: "Estensioni"
preferencesBackups: "Backup delle impostazioni"
deck: "Deck"
undeck: "Esci dal deck"
useBlurEffectForModal: "Utilizza effetto sfocatura per i modali"
useBlurEffectForModal: "Utilizza effetto sfocatura per le finestre modali"
useFullReactionPicker: "Usa la totalità del pannello di reazioni"
width: "Larghezza"
height: "Altezza"
@@ -596,7 +598,7 @@ notificationType: "Tipo di notifiche"
edit: "Modifica"
emailServer: "Server email"
enableEmail: "Abilita consegna email"
emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronica e per reimpostare la tua password"
emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronica e per ripristinare la password"
email: "Email"
emailAddress: "Indirizzo di posta elettronica"
smtpConfig: "Impostazioni del server SMTP"
@@ -785,7 +787,7 @@ gallery: "Galleria"
recentPosts: "Le più recenti"
popularPosts: "Le più visualizzate"
shareWithNote: "Condividere in nota"
ads: "Pubblicità"
ads: "Banner"
expiration: "Scadenza"
startingperiod: "Periodo di inizio"
memo: "Promemoria"
@@ -814,7 +816,7 @@ translatedFrom: "Tradotto 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"
keepCw: "Mantieni il CW"
keepCw: "Mantieni il Content Warning"
pubSub: "Publish/Subscribe del profilo"
lastCommunication: "La comunicazione più recente"
resolved: "Risolto"
@@ -919,6 +921,7 @@ pushNotificationNotSupported: "Il client o il server non supporta le notifiche p
sendPushNotificationReadMessage: "Elimina le notifiche push dopo la relativa lettura"
sendPushNotificationReadMessageCaption: "Se possibile, verrà mostrata brevemente una notifica con il testo \"{emptyPushNotificationMessage}\". Potrebbe influire negativamente sulla durata della batteria."
windowMaximize: "Ingrandisci"
windowMinimize: "Contrai finestra"
windowRestore: "Ripristina"
caption: "Didascalia"
loggedInAsBot: "Connessione come Bot"
@@ -960,16 +963,18 @@ copyErrorInfo: "Copia le informazioni sull'errore"
joinThisServer: "Registrati su questa istanza"
exploreOtherServers: "Trova altre istanze"
letsLookAtTimeline: "Sbircia la timeline"
disableFederationWarn: "Disabilita la federazione. Questo cambiamento non rende le pubblicazioni private. Di solito non è necessario abilitare questa opzione."
invitationRequiredToRegister: "L'accesso a questo nodo è solo ad invito. Devi inserire un codice d'invito valido. Puoi richiedere un codice all'amministratore."
disableFederationConfirm: "Vuoi davvero disattivare la federazione?"
disableFederationConfirmWarn: "Anche se defederate, le Note continueranno ad essere pubbliche, se non diversamente specificato. Di solito, non è necessario far questo."
disableFederationOk: "Disabilita federazione"
invitationRequiredToRegister: "L'accesso a questa istanza è solo ad invito. Può registrarsi solo chi ha un codice fornito dall'amministrazione."
emailNotSupported: "L'istanza non supporta l'invio di email"
postToTheChannel: "Pubblica sul canale"
postToTheChannel: "Pubblica nel canale"
cannotBeChangedLater: "Non sarà più modificabile"
reactionAcceptance: "Accettazione reazioni"
likeOnly: "Solo i Like"
likeOnlyForRemote: "Solo Like remoti"
rolesAssignedToMe: "I miei ruoli"
resetPasswordConfirm: "Vuoi reimpostare la password?"
resetPasswordConfirm: "Vuoi davvero ripristinare la password?"
sensitiveWords: "Parole sensibili"
sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga."
notesSearchNotAvailable: "Non è possibile cercare tra le Note."
@@ -980,6 +985,22 @@ drivecleaner: "Drive cleaner"
retryAllQueuesNow: "Ritenta di consumare tutte le code"
retryAllQueuesConfirmTitle: "Vuoi ritentare adesso?"
retryAllQueuesConfirmText: "Potrebbe sovraccaricare il server temporaneamente."
enableChartsForRemoteUser: "Abilita i grafici per i profili remoti"
enableChartsForFederatedInstances: "Abilita i grafici per le istanze federate"
showClipButtonInNoteFooter: "Aggiungi il bottone Clip tra le azioni delle Note"
largeNoteReactions: "Ingrandisci le reazioni"
noteIdOrUrl: "ID della Nota o URL"
accountMigration: "Migrazione del profilo"
accountMoved: "Questo profilo ha migrato altrove:"
forceShowAds: "Mostra sempre i banner"
_accountMigration:
moveTo: "Migrare questo profilo verso un un altro"
moveToLabel: "Profilo verso cui migrare"
moveAccountDescription: "Questa attività è irreversibile! Innanzitutto, assicurati di aver creato, nella istanza di destinazione, un alias con l'indirizzo di questo profilo. Successivamente, indica qui il profilo di destinazione in questo modo: @persona@istanza.it"
moveFrom: "Migra un altro profilo dentro a questo"
moveFromLabel: "Profilo da cui migrare:"
moveFromDescription: "Se desideri spostare i profili 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"
migrationConfirm: "Vuoi davvero migrare questo profilo su {account}? L'azione è irreversibile e non potrai più utilizzare questo profilo nel suo stato originale.\nInoltre, assicurati di aver già creato un alias sull'account a cui ti stai trasferendo."
_achievements:
earnedAt: "Data di conseguimento"
_types:
@@ -1276,6 +1297,8 @@ _role:
followersMoreThanOrEq: "Ha più di N follower"
followingLessThanOrEq: "Segue N profili o meno"
followingMoreThanOrEq: "Segue N profili o più"
notesLessThanOrEq: "Conteggio Note inferiore o uguale a"
notesMoreThanOrEq: "Conteggio Note maggiore o uguale a"
and: "E"
or: "O"
not: "NON"
@@ -1314,8 +1337,8 @@ _ad:
hide: "Nascondi"
_forgotPassword:
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo."
ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare l'amministratore·trice dell'istanza."
contactAdmin: "Poiché questa istanza non permette l'utilizzo di una mail, si prega di contattare l'amministratore·trice dell'istanza per poter ripristinare la password."
ifNoEmail: "Se il tuo indirizzo email non risulta registrato, contatta l'amministrazione dell'istanza."
contactAdmin: "Poiché questa istanza non permette di impostare l'indirizzo mail, contatta l'amministrazione per ripristinare la password.\n"
_gallery:
my: "Le mie pubblicazioni"
liked: "Pubblicazioni che mi piacciono"
@@ -1385,6 +1408,8 @@ _channel:
following: "Seguiti"
usersCount: "{n} partecipanti"
notesCount: "{n} note"
nameAndDescription: "Nome e descrizione"
nameOnly: "Solo il nome"
_menuDisplay:
sideFull: "Laterale"
sideIcon: "Laterale (solo icone)"
@@ -1670,12 +1695,12 @@ _visibility:
public: "Pubblica"
publicDescription: "Visibile per tutti sul Fediverso"
home: "Home"
homeDescription: "Visibile solo sulla timeline \"Home\""
homeDescription: "Visibile solo sulla timeline locale"
followers: "Follower"
followersDescription: "Visibile solo per i tuoi follower"
followersDescription: "Visibile solo ai tuoi follower"
specified: "Nota diretta"
specifiedDescription: "Visibile solo ai profili menzionati"
disableFederation: "Interrompi la federazione"
disableFederation: "Federazione disabilitata"
disableFederationDescription: "Non spedire attività alle altre istanze remote"
_postForm:
replyPlaceholder: "Rispondi a questa nota..."
@@ -1870,11 +1895,21 @@ _dialog:
charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({corrente})"
_disabledTimeline:
title: "Timeline disabilitata"
description: "Il tuo ruolo non ha i permessi per accedere a questa timeline"
description: "Il ruolo in cui sei non ti permette di leggere questa timeline"
_drivecleaner:
orderBySizeDesc: "Dal più grande al più piccolo"
orderByCreatedAtAsc: "Dal più vecchio al più recente"
_webhookSettings:
createWebhook: "Creazione Webhook"
name: "Nome"
secret: "Segreto"
events: "Quando eseguire il Webhook"
active: "Attivo"
_events:
follow: "Quando segui un profilo"
followed: "Quando ti segue un profilo"
note: "Quando pubblichi una Nota"
reply: "Quando rispondono ad una Nota"
renote: "Quando la Nota è Rinotata"
reaction: "Quando ricevo una reazione"
mention: "Quando mi menzionano"

View File

@@ -20,6 +20,7 @@ noNotes: "ノートはありません"
noNotifications: "通知はありません"
instance: "サーバー"
settings: "設定"
notificationSettings: "通知の設定"
basicSettings: "基本設定"
otherSettings: "その他の設定"
openInWindow: "ウィンドウで開く"
@@ -262,14 +263,16 @@ noMoreHistory: "これより過去の履歴はありません"
startMessaging: "チャットを開始"
nUsersRead: "{n}人が読みました"
agreeTo: "{0}に同意"
agree: "同意する"
agreeBelow: "下記に同意する"
basicNotesBeforeCreateAccount: "基本的な注意事項"
tos: "利用規約"
termsOfService: "利用規約"
start: "始める"
home: "ホーム"
remoteUserCaution: "リモートユーザーのため、情報が不完全です。"
activity: "アクティビティ"
images: "画像"
image: "画像"
birthday: "誕生日"
yearsOld: "{age}歳"
registeredDate: "登録日"
@@ -473,6 +476,8 @@ createAccount: "アカウントを作成"
existingAccount: "既存のアカウント"
regenerate: "再生成"
fontSize: "フォントサイズ"
mediaListWithOneImageAppearance: "画像が1枚のみのメディアリストの高さ"
limitTo: "{x}を上限に"
noFollowRequests: "フォロー申請はありません"
openImageInNewTab: "画像を新しいタブで開く"
dashboard: "ダッシュボード"
@@ -698,6 +703,8 @@ contact: "連絡先"
useSystemFont: "システムのデフォルトのフォントを使う"
clips: "クリップ"
experimentalFeatures: "実験的機能"
experimental: "実験的"
thisIsExperimentalFeature: "これは実験的な機能です。仕様が変更されたり、正常に動作しなかったりする可能性があります。"
developer: "開発者"
makeExplorable: "アカウントを見つけやすくする"
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らなくなります。"
@@ -904,6 +911,7 @@ remoteOnly: "リモートのみ"
failedToUpload: "アップロード失敗"
cannotUploadBecauseInappropriate: "不適切な内容を含む可能性があると判定されたためアップロードできません。"
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いためアップロードできません。"
cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズの制限を超えているためアップロードできません。"
beta: "ベータ"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにNSFWフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。"
@@ -917,9 +925,10 @@ subscribePushNotification: "プッシュ通知を有効化"
unsubscribePushNotification: "プッシュ通知を停止する"
pushNotificationAlreadySubscribed: "プッシュ通知は有効です"
pushNotificationNotSupported: "ブラウザかサーバーがプッシュ通知に非対応"
sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を削除する"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。"
sendPushNotificationReadMessage: "通知が既読になったらプッシュ通知を削除する"
sendPushNotificationReadMessageCaption: "端末の電池消費量が増加する可能性があります。"
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "元に戻す"
caption: "キャプション"
loggedInAsBot: "Botアカウントでログイン中"
@@ -936,6 +945,7 @@ didYouLikeMisskey: "Misskeyを気に入っていただけましたか"
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!"
roles: "ロール"
role: "ロール"
noRole: "ロールはありません"
normalUser: "一般ユーザー"
undefined: "未定義"
assign: "アサイン"
@@ -945,6 +955,10 @@ manageCustomEmojis: "カスタム絵文字の管理"
youCannotCreateAnymore: "これ以上作成することはできません。"
cannotPerformTemporary: "一時的に利用できません"
cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。"
invalidParamError: "パラメータエラー"
invalidParamErrorDescription: "リクエストパラメータに問題があります。通常これはバグですが、入力した文字数が多すぎる等の可能性もあります。"
permissionDeniedError: "操作が拒否されました"
permissionDeniedErrorDescription: "このアカウントにはこの操作を行うための権限がありません。"
preset: "プリセット"
selectFromPresets: "プリセットから選択"
achievements: "実績"
@@ -988,6 +1002,53 @@ enableChartsForFederatedInstances: "リモートサーバーのチャートを
showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
largeNoteReactions: "ノートのリアクションを大きく表示"
noteIdOrUrl: "ートIDまたはURL"
video: "動画"
videos: "動画"
dataSaver: "データセーバー"
accountMigration: "アカウントの移行"
accountMoved: "このユーザーは新しいアカウントに移行しました:"
accountMovedShort: "このアカウントは移行されています"
operationForbidden: "この操作はできません"
forceShowAds: "常に広告を表示する"
addMemo: "メモを追加"
editMemo: "メモを編集"
reactionsList: "リアクション一覧"
renotesList: "Renote一覧"
notificationDisplay: "通知の表示"
leftTop: "左上"
rightTop: "右上"
leftBottom: "左下"
rightBottom: "右下"
stackAxis: "スタック方向"
vertical: "縦"
horizontal: "横"
position: "位置"
serverRules: "サーバールール"
pleaseConfirmBelowBeforeSignup: "このサーバーに登録する前に、以下を確認してください。"
pleaseAgreeAllToContinue: "続けるには、全ての「同意する」にチェックが入っている必要があります。"
continue: "続ける"
preservedUsernames: "予約ユーザー名"
preservedUsernamesDescription: "予約するユーザー名を改行で列挙します。ここで指定されたユーザー名はアカウント作成時に使えなくなりますが、管理者によるアカウント作成時はこの制限を受けません。また、既に存在するアカウントも影響を受けません。"
createNoteFromTheFile: "このファイルからノートを作成"
_serverRules:
description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。"
_accountMigration:
moveFrom: "別のアカウントからこのアカウントに移行"
moveFromSub: "別のアカウントへエイリアスを作成"
moveFromLabel: "移行元のアカウント #{n}"
moveFromDescription: "別のアカウントからこのアカウントに移行したい場合、ここでエイリアスを作成しておく必要があります。\n移行元のアカウントをこのように入力してください: @username@server.example.com\n削除するには、入力欄を空にして保存します非推奨。"
moveTo: "このアカウントを新しいアカウントへ移行"
moveToLabel: "移行先のアカウント:"
moveCannotBeUndone: "アカウントを移行すると、取り消すことはできません。"
moveAccountDescription: "新しいアカウントへ移行します。\n ・フォロワーが新しいアカウントを自動でフォローします\n ・このアカウントからのフォローは全て解除されます\n ・このアカウントではートの作成などができなくなります\n\nフォロワーの移行は自動ですが、フォローの移行は手動で行う必要があります。移行前にこのアカウントでフォローエクスポートし、移行後すぐに移行先アカウントでインポートを行なってください。\nリスト・ミュート・ブロックについても同様ですので、手動で移行する必要があります。\n\nこの説明はこのサーバーMisskey v13.12.0以降の仕様です。Mastodonなどの他のActivityPubソフトウェアでは挙動が異なる場合があります。"
moveAccountHowTo: "アカウントの移行には、まずは移行先のアカウントでこのアカウントに対しエイリアスを作成します。\nエイリアス作成後、移行先のアカウントを次のように入力してください: @username@server.example.com"
startMigration: "移行する"
migrationConfirm: "本当にこのアカウントを {account} に移行しますか?一度移行すると取り消せず、二度とこのアカウントを元の状態で使用できなくなります。"
movedAndCannotBeUndone: "\nアカウントは移行されています。\n移行を取り消すことはできません。"
postMigrationNote: "このアカウントからのフォロー解除は移行操作から24時間後に実行されます。\nこのアカウントのフォロー・フォロワー数は0になっています。フォロワーの解除はされないため、あなたのフォロワーはこのアカウントのフォロワー向け投稿を引き続き閲覧できます。"
movedTo: "移行先のアカウント:"
_achievements:
earnedAt: "獲得日時"
@@ -1160,6 +1221,9 @@ _achievements:
_client30min:
title: "ひとやすみ"
description: "クライアントを起動してから30分以上経過した"
_client60min:
title: "Misskeyの見すぎ"
description: "クライアントを起動してから60分以上経過した"
_noteDeletedWithin1min:
title: "いまのなし"
description: "投稿してから1分以内にその投稿を削除した"
@@ -1249,6 +1313,8 @@ _role:
iconUrl: "アイコン画像のURL"
asBadge: "バッジとして表示"
descriptionOfAsBadge: "オンにすると、ユーザー名の横にロールのアイコンが表示されます。"
isExplorable: "ロールタイムラインを公開"
descriptionOfIsExplorable: "オンにすると、ロールのタイムラインを公開します。ロールの公開がオフの場合、タイムラインの公開はされません。"
displayOrder: "表示順"
descriptionOfDisplayOrder: "数値が大きいほどUI上で先頭に表示されます。"
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
@@ -1414,6 +1480,8 @@ _channel:
following: "フォロー中"
usersCount: "{n}人が参加中"
notesCount: "{n}投稿があります"
nameAndDescription: "名前と説明"
nameOnly: "名前のみ"
_menuDisplay:
sideFull: "横"
@@ -1927,6 +1995,7 @@ _deck:
channel: "チャンネル"
mentions: "あなた宛て"
direct: "ダイレクト"
roleTimeline: "ロールタイムライン"
_dialog:
charactersExceeded: "最大文字数を超えています! 現在 {current} / 制限 {max}"
@@ -1954,4 +2023,3 @@ _webhookSettings:
renote: "Renoteされたとき"
reaction: "リアクションがあったとき"
mention: "メンションされたとき"

View File

@@ -16,22 +16,23 @@ cancel: "やめとく"
noThankYou: "やめとく"
enterUsername: "ユーザー名を入れてや"
renotedBy: "{user}がRenoteしたで"
noNotes: "ノートなんてあらへん"
noNotifications: "通知なんてあらへん"
noNotes: "ノートあらへん"
noNotifications: "通知あらへん"
instance: "サーバー"
settings: "設定"
notificationSettings: "通知の設定"
basicSettings: "基本設定"
otherSettings: "ほかの設定"
openInWindow: "ウィンドウで開くで"
profile: "プロフィール"
timeline: "タイムライン"
noAccountDescription: "自己紹介食ってもた"
noAccountDescription: "自己紹介はあらへん"
login: "ログイン"
loggingIn: "ログインしよるで"
logout: "ログアウト"
signup: "新規登録"
uploading: "アップロードしとるで"
save: "保存"
save: "とっとく"
users: "ユーザー"
addUser: "ユーザーを追加や"
favorite: "お気に入り"
@@ -81,9 +82,9 @@ followsYou: "フォローされとるで"
createList: "リスト作る"
manageLists: "リストの管理"
error: "エラー"
somethingHappened: "なんかアカンことが起こったで"
somethingHappened: "なんかあかんわ"
retry: "もっぺんやる?"
pageLoadError: "ページ読み込みに失敗してもうたわ"
pageLoadError: "ページ読み込めんかったわ"
pageLoadErrorDescription: "これは普通ならネットワークかブラウザキャッシュが悪さしてるんよ。キャッシュをほかすか、もうちょっとだけ待ってくれへん?"
serverIsDead: "サーバーからの応答がないで。もうちょい待ってから試してみてな。"
youShouldUpgradeClient: "このページを表示するには、リロードして新しいバージョンのクライアントを使ってなー。"
@@ -108,8 +109,8 @@ inChannelQuote: "チャンネル内引用"
pinnedNote: "ピン留めされとるノート"
pinned: "ピン留めしとく"
you: "あんた"
clickToShow: "押したら見えるで"
sensitive: "ちょっとアカンやつやで"
clickToShow: "押したら出ら"
sensitive: "気いつけて見いや"
add: "増やす"
reaction: "リアクション"
reactions: "リアクション"
@@ -122,8 +123,8 @@ unmarkAsSensitive: "そこまでアカンことないやろ"
enterFileName: "ファイル名を入れてや"
mute: "ミュート"
unmute: "ミュートやめたる"
renoteMute: "リノートは見いひん"
renoteUnmute: "リノートもやっぱ見るわ"
renoteMute: "Renoteは見いひん"
renoteUnmute: "Renoteもやっぱ見るわ"
block: "ブロック"
unblock: "ブロックやめたる"
suspend: "凍結"
@@ -141,14 +142,14 @@ editWidgetsExit: "編集終ったで"
customEmojis: "カスタム絵文字"
emoji: "絵文字"
emojis: "絵文字"
emojiName: "絵文字"
emojiName: "絵文字はんの名前"
emojiUrl: "絵文字画像URL"
addEmoji: "絵文字を追加"
settingGuide: "ええ感じの設定"
cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定を切っとくと、リモートファイルをキャッシュせ直リンクするようになるで。サーバーの容量は節約できるけど、サムネイル作られんくなるから通信量が増えるで。"
cacheRemoteFilesDescription: "この設定を切っとったら、リモートファイルをキャッシュせんと直リンクするようになるで。サーバーの容量は節約できるけど、サムネイル作らなるから通信量が増えるで。"
flagAsBot: "Botにするで"
flagAsBotDescription: "もしこのアカウントをプログラム使うて運用するんやったら、このフラグをオンにしてや。オンにすれば、反応がバーッて連鎖するのを避けるために開発者が使うたり、Misskeyのシステム上での扱いがBotに合ったもんになるからな。"
flagAsBotDescription: "もしこのアカウントをプログラム使うて運用するんやったら、このフラグをオンにしてや。オンにすれば、反応がバーッて連鎖せんように開発者が使うたり、Misskeyのシステム上での扱いがBotに合ったもんになるからな。"
flagAsCat: "Catやで"
flagAsCatDescription: "ワレ、猫ちゃんならこのフラグをつけてみ?"
flagShowTimelineReplies: "タイムラインにノートへの返信を表示するで"
@@ -194,10 +195,10 @@ network: "ネットワーク"
disk: "ディスク"
instanceInfo: "サーバー情報"
statistics: "統計"
clearQueue: "キューにさいなら"
clearQueueConfirmTitle: "キューをクリアしまっか?"
clearQueueConfirmText: "未配達の投稿は配送されなくなるで。ふつうこの操作を行う必要は無いんやけどな。"
clearCachedFiles: "キャッシュにさいなら"
clearQueue: "キューをほかす"
clearQueueConfirmTitle: "キューをほかしとこか?"
clearQueueConfirmText: "未配達の投稿は配送されなるで。ふつうこの操作を行う必要は無いんやけどな。"
clearCachedFiles: "キャッシュをほかす"
clearCachedFilesConfirm: "キャッシュされとるリモートファイルをみんなほかしてええか?"
blockedInstances: "ブロックしたサーバー"
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできひんくなるで。ついでにそのサブドメインもブロックするで。"
@@ -206,7 +207,7 @@ mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー"
noUsers: "ユーザーはおらん"
editProfile: "プロフィールをいじる"
noteDeleteConfirm: "このノートを削除しまっか?"
noteDeleteConfirm: "このノートをほかしてええか?"
pinLimitExceeded: "これ以上ピン留めできひん"
intro: "Misskeyのインストールが完了したで管理者アカウントを作ってや。"
done: "でけた"
@@ -226,9 +227,9 @@ notResponding: "応答してへんで"
instanceFollowing: "サーバーのフォロー"
instanceFollowers: "サーバーのフォロワー\n"
instanceUsers: "サーバーのユーザー"
changePassword: "パスワード変える"
changePassword: "パスワードをいじる"
security: "セキュリティ"
retypedNotMatch: "入れたやつ同じになってないで。"
retypedNotMatch: "入れたやつ合うてへんわ。"
currentPassword: "今のパスワード"
newPassword: "次のパスワード"
newPasswordRetype: "今度のパスワード(もっぺん入れて)"
@@ -258,7 +259,7 @@ uploadFromUrlRequested: "アップロードしたい言うといたで"
uploadFromUrlMayTakeTime: "アップロード終わるんにちょい時間かかるかもしれへんわ。"
explore: "みつける"
messageRead: "もう読んだ"
noMoreHistory: "これより過去の履歴はあらへんで"
noMoreHistory: "これより昔のんはあらへんで"
startMessaging: "チャットやるで"
nUsersRead: "{n}人が読んでもうた"
agreeTo: "{0}に同意したで"
@@ -294,14 +295,14 @@ createFolder: "フォルダー作る"
renameFolder: "フォルダー名を変える"
deleteFolder: "フォルダーをほかす"
addFile: "ファイルを追加"
emptyDrive: "ドライブにはなんも残っとらん"
emptyDrive: "ドライブは空っぽや"
emptyFolder: "このフォルダーは空や"
unableToDelete: "消そうおもってんけどな、あかんかったわ"
unableToDelete: "消んかったわ"
inputNewFileName: "今度のファイル名は何にするん?"
inputNewDescription: "新しいキャプションを入れてや"
inputNewFolderName: "今度のフォルダ名は何にするん?"
circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーや。"
hasChildFilesOrFolders: "このフォルダ、まだなんか入っとるから消されへん"
hasChildFilesOrFolders: "このフォルダは空っぽちゃうから消されへん"
copyUrl: "URLをコピー"
rename: "名前を変えるで"
avatar: "アイコン"
@@ -506,6 +507,7 @@ objectStorageUseSSLDesc: "API接続にhttpsを使わん場合はオフにする
objectStorageUseProxy: "Proxyを使う"
objectStorageUseProxyDesc: "API接続にproxy使わんのやったら切ってくれへん"
objectStorageSetPublicRead: "アップロードした時に'public-read'を設定してや"
s3ForcePathStyleDesc: "s3ForcePathStyleを使たらバケット名をURLのホスト名やなくてパスの一部として必ず指定させるようになるで。セルフホストされたMinioとかを使うてるんやったら有効にせなあかん場合があるで。"
serverLogs: "サーバーログ"
deleteAll: "全部ほかす"
showFixedPostForm: "タイムラインの上の方で投稿できるようにやってくれへん?"
@@ -643,7 +645,7 @@ reporter: "通報者"
reporteeOrigin: "通報先"
reporterOrigin: "通報元"
forwardReport: "リモートサーバーに通報を転送するで"
forwardReportIsAnonymous: "リモートインスタンスからはあんたの情報は見れへんくって、匿名のシステムアカウントとして表示されるで。"
forwardReportIsAnonymous: "リモートサーバーからはあんたの情報は見えんなって、匿名のシステムアカウントとして表示されるで。"
send: "送信"
abuseMarkAsResolved: "対応したで"
openInNewTab: "新しいタブで開く"
@@ -739,7 +741,7 @@ capacity: "容量"
inUse: "使用中"
editCode: "コードを編集"
apply: "適用"
receiveAnnouncementFromInstance: "インスタンスからのお知らせを受け取る"
receiveAnnouncementFromInstance: "サーバーからのお知らせを受け取る"
emailNotification: "メール通知"
publish: "公開"
inChannelSearch: "チャンネル内検索"
@@ -767,7 +769,7 @@ active: "アクティブ"
offline: "オフライン"
notRecommended: "あんま推奨しやんで"
botProtection: "Botプロテクション"
instanceBlocking: "インスタンスブロック"
instanceBlocking: "サーバーブロック"
selectAccount: "アカウントを選んでなー"
switchAccount: "アカウントを変えるで"
enabled: "有効"
@@ -851,8 +853,8 @@ themeColor: "テーマカラー"
size: "大きさ"
numberOfColumn: "列の数"
searchByGoogle: "探す"
instanceDefaultLightTheme: "インスタンスの最初の明るいテーマ"
instanceDefaultDarkTheme: "インスタンスの最初の暗いテーマ"
instanceDefaultLightTheme: "サーバーおすすめの明るいテーマ"
instanceDefaultDarkTheme: "サーバーおすすめのの暗いテーマ"
instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入するで。"
mutePeriod: "ミュートする期間"
period: "期限"
@@ -866,7 +868,7 @@ reflectMayTakeTime: "反映されるまで時間がかかることがあるで"
failedToFetchAccountInformation: "アカウントの取得に失敗したみたいや…"
rateLimitExceeded: "レート制限が超えたみたいやで"
cropImage: "画像のクロップ"
cropImageAsk: "画像をクロップしたってええか?"
cropImageAsk: "画像をクロップしてええか?"
cropYes: "切り抜いたる"
cropNo: "切り抜かへん"
file: "ファイル"
@@ -901,11 +903,11 @@ sensitiveMediaDetection: "センシティブなメディアの検出"
localOnly: "ローカルのみ"
remoteOnly: "リモートのみ"
failedToUpload: "アップロードに失敗してもうたわ…"
cannotUploadBecauseInappropriate: "不適切な内容を含むかもしれへんって判定されたアップロードできへん。"
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いアップロードできへん。"
cannotUploadBecauseInappropriate: "不適切な内容を含むかもしれへんって判定されたからアップロードできへん。"
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いからアップロードできへん。"
beta: "ベータ"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、インスタンスによっては自動で設定されることがあるで。"
enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。"
activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかとかを判定して積極的に行うで。オフにすると単に文字列として正しいかどうかだけチェックするで。"
navbar: "ナビゲーションバー"
shuffle: "シャッフルするで"
@@ -915,10 +917,11 @@ pushNotification: "プッシュ通知"
subscribePushNotification: "プッシュ通知をオンにするで"
unsubscribePushNotification: "プッシュ通知を止めるで"
pushNotificationAlreadySubscribed: "プッシュ通知はオンになってるで"
pushNotificationNotSupported: "ブラウザかインスタンスがプッシュ通知に対応してないみたいやで。"
pushNotificationNotSupported: "ブラウザかサーバーがプッシュ通知に対応してないみたいやで。"
sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を消すで"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」っていう表示が一瞬表示されるようになるで。端末の電池使用量が増える可能性があるで。"
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "元に戻す"
caption: "キャプション"
loggedInAsBot: "Botアカウントでログイン中やで"
@@ -931,7 +934,7 @@ numberOfLikes: "いいね数"
show: "表示"
neverShow: "今後表示しない"
remindMeLater: "また後で"
didYouLikeMisskey: "Misskey気に入っとっただけましたん"
didYouLikeMisskey: "Misskey気に入ってくれた"
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
roles: "ロール"
role: "ロール"
@@ -941,7 +944,7 @@ assign: "アサイン"
unassign: "アサインを解除"
color: "色"
manageCustomEmojis: "カスタム絵文字の管理"
youCannotCreateAnymore: "これ以上作れなさそうや"
youCannotCreateAnymore: "これ以上作れなさそうや"
cannotPerformTemporary: "一時的に利用できへんで"
cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。"
preset: "プリセット"
@@ -960,7 +963,9 @@ copyErrorInfo: "エラー情報をコピー"
joinThisServer: "このサーバーに登録するわ"
exploreOtherServers: "他のサーバー見てみる"
letsLookAtTimeline: "タイムライン見てみーや"
disableFederationWarn: "連合が無効になっとるで。無効にしても投稿は非公開ってわけちゃうねん。大体の場合はこのオプションを有効にする必要は別にないで。"
disableFederationConfirm: "連合なしにしとくか?"
disableFederationConfirmWarn: "連合なしにしても投稿は非公開にはならへんで。大体の場合は連合なしにする必要はないで。"
disableFederationOk: "連合なしにしとく"
invitationRequiredToRegister: "今このサーバー招待制になってもうてんねん。招待コードを持っとるんやったら登録できるで。"
emailNotSupported: "このサーバーはメール配信がサポートされてへんみたいやわ"
postToTheChannel: "チャンネルに投稿"
@@ -983,6 +988,19 @@ retryAllQueuesConfirmText: "一時的にサーバー重なるかもしれへん
enableChartsForRemoteUser: "リモートユーザーのチャートを作る"
enableChartsForFederatedInstances: "リモートサーバーのチャートを作る"
showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
largeNoteReactions: "ノートのリアクションを大きする"
noteIdOrUrl: "ートIDかURL"
accountMigration: "アカウントのお引っ越し"
accountMoved: "このユーザーはさらのアカウントに引っ越したで:"
forceShowAds: "常に広告を表示しとく"
_accountMigration:
moveTo: "このアカウントをさらのアカウントに引っ越すで"
moveToLabel: "引っ越し先のアカウント:"
moveAccountDescription: "この操作は戻されへんで。まず引っ越し先のアカウントでこのアカウントへのエイリアスが作れたか確認してきなはれや。エイリアスができてたら、引っ越し先のアカウントをこんな風に入力してくれへんか?:@person@instance.com"
moveFrom: "別のアカウントからこのアカウントに引っ越す"
moveFromLabel: "引っ越し元のアカウント:"
moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したかったら、ここでエイリアスを作っとく必要があるで。必ずお引っ越しを実行する前に作っとかなあかんで!引っ越し元のアカウントをこんな風に入力してくれへんか?:@person@instance.com"
migrationConfirm: "ほんまにこのアカウントを {account} に引っ越すんか?一回引っ越してもうたら取り消されへんし、二度とこのアカウントを元に戻されへんくなるで。\nそれと、引っ越し先のアカウントでエイリアスが作れたかちゃんと確認しーや"
_achievements:
earnedAt: "貰った日ぃ"
_types:
@@ -1043,7 +1061,7 @@ _achievements:
_login7:
title: "ビギナーⅡ"
description: "通算7日ログインした"
flavor: "慣れてきたんちゃう?"
flavor: "慣れてきたんちゃう?"
_login15:
title: "ビギナーⅢ"
description: "通算15日ログインした"
@@ -1147,7 +1165,7 @@ _achievements:
_iLoveMisskey:
title: "Misskey好きやねん"
description: "\"I ❤ #Misskey\"を投稿した"
flavor: "Misskeyを使ってくれてありがとう by 開発チーム"
flavor: "Misskeyを使ってくれておおきに by 開発チーム"
_foundTreasure:
title: "なんでも鑑定団"
description: "隠されたお宝を発見した"
@@ -1173,7 +1191,7 @@ _achievements:
description: "ホームタイムラインの流速が20npmを超す"
_viewInstanceChart:
title: "アナリスト"
description: "インスタンスのチャートを表示した"
description: "サーバーのチャートを表示した"
_outputHelloWorldOnScratchpad:
title: "Hello, world!"
description: "スクラッチパッドで hello worldを出力した"
@@ -1210,7 +1228,7 @@ _achievements:
_loggedInOnNewYearsDay:
title: "あけましておめでとうございます!"
description: "元旦にログインした"
flavor: "今年も弊インスタンスをよろしくお願いします"
flavor: "今年も弊サーバーをよろしゅう頼みます"
_cookieClicked:
title: "クッキー叩くやつ"
description: "クッキー叩いてもうた"
@@ -1225,8 +1243,8 @@ _role:
name: "ロール名"
description: "ロールの説明"
permission: "ロールの権限"
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関わる操作を行えるで。\n<b>管理者</b>はインスタンスの全ての設定を変更できるで。"
assignTarget: "アサインターゲット"
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関わる操作を行えるで。\n<b>管理者</b>はサーバーの全ての設定を変更できるで。"
assignTarget: "アサイン"
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれてるかを手動で管理するで。\n<b>コンディショナル</b>は条件を設定して、それに合うユーザーが自動で含まれるようになるで。"
manual: "マニュアル"
conditional: "コンディショナル"
@@ -1255,7 +1273,7 @@ _role:
gtlAvailable: "グローバルタイムラインの閲覧"
ltlAvailable: "ローカルタイムラインの閲覧"
canPublicNote: "パブリック投稿の許可"
canInvite: "インスタンス招待コードの発行"
canInvite: "サーバー招待コードの発行"
canManageCustomEmojis: "カスタム絵文字の管理"
driveCapacity: "ドライブ容量"
pinMax: "ノートのピン留めの最大数"
@@ -1267,7 +1285,7 @@ _role:
userListMax: "ユーザーリストの作成可能数"
userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
rateLimitFactor: "レートリミット"
descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩なって、大きいほど制限されるで。"
descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩なって、大きいほど制限されるで。"
canHideAds: "広告を表示させへん"
canSearchNotes: "ノート検索を使わすかどうか"
_condition:
@@ -1320,7 +1338,7 @@ _ad:
_forgotPassword:
enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。"
ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。"
contactAdmin: "このインスタンスはメールに対応してへんから、パスワードリセットをしたいときは管理者まで教えてな~。"
contactAdmin: "このサーバーはメールに対応してへんから、パスワードリセットをしたいときは管理者まで教えてな~。"
_gallery:
my: "あんたの投稿"
liked: "いいねした投稿"
@@ -1390,6 +1408,8 @@ _channel:
following: "フォロー中やで"
usersCount: "{n}人が参加中やで"
notesCount: "{n}こ投稿があるで"
nameAndDescription: "名前と説明"
nameOnly: "名前だけ"
_menuDisplay:
sideFull: "横"
sideIcon: "横(アイコン)"
@@ -1405,10 +1425,10 @@ _wordMute:
hard: "ハード"
mutedNotes: "ミュートされたノート"
_instanceMute:
instanceMuteDescription: "ミュートしたインスタンスのユーザーへの返信を含めて、設定したインスタンスの全てのートとRenoteをミュートにするで。"
instanceMuteDescription: "ミュートしたサーバーのユーザーへの返信を含めて、設定したインスタンスの全てのートとRenoteをミュートにするで。"
instanceMuteDescription2: "改行で区切って設定するんやで"
title: "設定したインスタンスのノートを隠すで。"
heading: "ミュートするインスタンス"
title: "設定したサーバーのノートを隠すで。"
heading: "ミュートするサーバー"
_theme:
explore: "テーマを探す"
install: "テーマのインストール"
@@ -1630,7 +1650,7 @@ _widgets:
digitalClock: "デジタル時計"
unixClock: "UNIX時計"
federation: "連合"
instanceCloud: "インスタンスクラウド"
instanceCloud: "サーバークラウド"
postForm: "投稿フォーム"
slideshow: "スライドショー"
button: "ボタン"
@@ -1681,7 +1701,7 @@ _visibility:
specified: "ダイレクト"
specifiedDescription: "選んだユーザーのみに公開するで"
disableFederation: "連合なし"
disableFederationDescription: "他インスタンスへは送らんとくわ"
disableFederationDescription: "他サーバーへは送らんとくわ"
_postForm:
replyPlaceholder: "このノートに返信..."
quotePlaceholder: "このノートを引用..."
@@ -1870,6 +1890,7 @@ _deck:
channel: "チャンネル"
mentions: "あんた宛て"
direct: "ダイレクト"
roleTimeline: "ロールタイムライン"
_dialog:
charactersExceeded: "最大の文字数を上回っとるで!今は {current} / 最大でも {max}"
charactersBelow: "最小の文字数を下回っとるで!今は {current} / 最低でも {min}"
@@ -1893,4 +1914,3 @@ _webhookSettings:
renote: "Renoteされるとき"
reaction: "リアクションがあるとき~!"
mention: "メンションがあるとき~!"

View File

@@ -1,2 +1 @@
---

View File

@@ -103,4 +103,3 @@ _deck:
_columns:
notifications: "Ilɣuyen"
list: "Tibdarin"

View File

@@ -83,4 +83,3 @@ _deck:
notifications: "ಅಧಿಸೂಚನೆಗಳು"
tl: "ಸಮಯಸಾಲು"
mentions: "ಹೆಸರಿಸಿದ"

View File

@@ -958,7 +958,6 @@ copyErrorInfo: "오류 정보 복사"
joinThisServer: "이 서버에 가입"
exploreOtherServers: "다른 서버 둘러보기"
letsLookAtTimeline: "타임라인 구경하기"
disableFederationWarn: "연합이 비활성화됩니다. 비활성화해도 게시물이 비공개가 되지는 않습니다. 대부분의 경우 이 옵션을 활성화할 필요가 없습니다."
invitationRequiredToRegister: "현재 이 서버는 비공개입니다. 회원가입을 하시려면 초대 코드가 필요합니다."
emailNotSupported: "이 서버에서는 메일 전송을 지원하지 않습니다"
postToTheChannel: "채널에 게시하기"
@@ -1851,4 +1850,3 @@ _dialog:
_webhookSettings:
name: "이름"
active: "활성화"

View File

@@ -163,11 +163,15 @@ instanceInfo: "ອີນສະແຕນ"
statistics: "ສະຖິຕິ"
clearQueue: "ລ້າງຄິວ"
clearCachedFiles: "ລຶບລ້າງແຄສ"
noUsers: "ບໍ່ພົບຜູ້ໃຊ້"
editProfile: "ແກ້ໄຂໂປຣໄຟລ໌"
done: "ສຳເລັດ"
processing: "ກຳລັງປະມວນຜົນ"
preview: "ສະແດງເປັນຕົວຢ່າງ"
default: "ຄ່າເລີ່ມຕົ້ນ"
defaultValueIs: "ຄ່າເລີ່ມຕົ້ນ: {value}"
noCustomEmojis: "ບໍ່ມີອີໂມຈິ"
noJobs: "ບໍ່ມີຊິ້ນວຽກ"
federating: "ສະຫະພັນ"
blocked: "ບລັອກແລ້ວ "
suspended: "ໂຈະ"
@@ -182,6 +186,9 @@ changePassword: "ປ່ຽນ​ລະ​ຫັດ​ຜ່ານ"
security: "ຄວາມປອດໄພ"
retypedNotMatch: "ວັດສະດຸປ້ອນບໍ່ກົງກັນ"
currentPassword: "ລະຫັດຜ່ານປະຈຸບັນ"
newPassword: "ລະຫັດຜ່ານໃໝ່"
newPasswordRetype: "ໃສ່ລະຫັດຜ່ານໃໝ່ອີກເທື່ອໜຶ່ງ"
attachFile: "ແນບໄຟລ໌"
more: "ເພີ່ມເຕີມ!"
featured: "ໄຮໄລທ໌"
usernameOrUserId: "ຊື່ຜູ້ໃຊ້ ຫຼື id ຜູ້ໃຊ້"
@@ -196,25 +203,31 @@ saved: "ບັນທຶກແລ້ວ"
messaging: "ແຊ໋ດ"
upload: "ອັບໂຫຼດ"
keepOriginalUploading: "ຮັກສາຮູບພາບຕົ້ນສະບັບ"
fromDrive: "ຈາກ Drive"
fromUrl: "ຈາກ URL"
uploadFromUrl: "ອັບໂຫຼດຈາກ URL"
uploadFromUrlDescription: "URL ຂອງໄຟລ໌ທີ່ທ່ານຕ້ອງການອັບໂຫລດ"
uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດ"
messageRead: "ອ່ານແລ້ວ"
startMessaging: "ເລີ່ມການສົນທະນາໃໝ່"
nUsersRead: "ອ່ານໂດຍ {n}"
tos: "ເງື່ອນໄຂການໃຫ້ບໍລິການ"
start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີຍ"
home: "ໜ້າຫຼັກ"
activity: "ກິດຈະກຳ"
images: "ຮູບພາບ"
birthday: "ວັນເກີດ"
yearsOld: "{age} ປີ"
registeredDate: "ວັນທີ່ເປັນສະມາຊິກ"
location: "ທີ່ຕັ້ງ"
theme: "ແທ໋ມ"
themeForLightMode: "ຮູບແບບສີສັນເພື່ອໃຊ້ໃນໂໝດແສງ"
themeForDarkMode: "ຮູບແບບສີສັນທີ່ຈະໃຊ້ຢູ່ໃນໂໝດມືດ"
light: "ສະຫວ່າງ"
dark: "ມືດ"
lightThemes: "ຊຸດຮູບແບບສະຫວ່າງ"
darkThemes: "ຮູບແບບສີສັນມືດ"
syncDeviceDarkMode: "ຊິງຄ໌ໂໝດມືດກັບການຕັ້ງຄ່າທົ່ວອຸປະກອນ"
drive: "ຂັບ"
fileName: "ຊື່ໄຟລ໌"
selectFile: "ເລືອກໄຟລ໌"
@@ -265,6 +278,9 @@ invite: "ເຊີນ"
driveCapacityPerLocalAccount: "ຄວາມອາດສາມາດຂັບຕໍ່ຜູ້ໃຊ້ທ້ອງຖິ່ນ"
driveCapacityPerRemoteAccount: "ໄດຣຟ໌ຄວາມອາດສາມາດຕໍ່ຜູ້ໃຊ້ທາງໄກ"
pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
turnstileSiteKey: "ກະແຈໄຊທ໌"
turnstileSecretKey: "ກະແຈລັບ"
name: "ຊື່"
userList: "ລາຍການ"
about: "ກ່ຽວກັບ"
aboutMisskey: "ກ່ຽວກັບ Misskey"
@@ -326,6 +342,7 @@ _widgets:
instanceInfo: "ອີນສະແຕນ"
notifications: "ການແຈ້ງເຕືອນ"
timeline: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​"
activity: "ກິດຈະກຳ"
federation: "ສະຫະພັນ"
_userList:
chooseList: "ເລືອກບັນຊີລາຍການ"
@@ -335,6 +352,7 @@ _visibility:
home: "ໜ້າຫຼັກ"
followers: "ຜູ້ຕິດຕາມ"
_profile:
name: "ຊື່"
username: "ຊື່ຜູ້ໃຊ້"
_exportOrImport:
followingList: "ກຳລັງຕິດຕາມ"
@@ -368,4 +386,5 @@ _deck:
list: "ລາຍການ"
channel: "ຊ່ອງ"
mentions: "ກ່າວເຖິງ"
_webhookSettings:
name: "ຊື່"

View File

@@ -485,4 +485,3 @@ _deck:
mentions: "Vermeldingen"
_webhookSettings:
name: "Naam"

View File

@@ -1,3 +1,2 @@
---
_lang_: "Norsk Bokmål"

View File

@@ -1379,4 +1379,3 @@ _deck:
_webhookSettings:
name: "Nazwa"
active: "Właczono"

View File

@@ -557,4 +557,3 @@ _deck:
direct: "Notas diretas"
_webhookSettings:
name: "Nome"

View File

@@ -703,4 +703,3 @@ _deck:
mentions: "Mențiuni"
_webhookSettings:
name: "Nume"

View File

@@ -950,7 +950,6 @@ copyErrorInfo: "Скопировать код ошибки"
joinThisServer: "Присоединяйтесь к этому серверу"
exploreOtherServers: "Искать другие сервера"
letsLookAtTimeline: "Давайте посмотрим на ленту"
disableFederationWarn: "Объединение отключено. Если вы отключите это, сообщение не будет приватным. В большинстве случаев вам не нужно включать эту опцию."
_achievements:
earnedAt: "Разблокировано в"
_types:
@@ -1839,4 +1838,3 @@ _dialog:
_webhookSettings:
name: "Название"
active: "Вкл."

View File

@@ -1,2 +1 @@
---

View File

@@ -1477,4 +1477,3 @@ _deck:
_webhookSettings:
name: "Názov"
active: "Zapnuté"

View File

@@ -343,9 +343,16 @@ recentlyRegisteredUsers: "Nyligen registrerade användare"
userList: "Listor"
aboutMisskey: "Om Misskey"
administrator: "Administratör"
passwordLessLogin: "Lösenordsfri inloggning"
passwordLessLoginDescription: "Tillåter lösenordsfri inloggning med endast en säkerhetsnyckel eller en passkey."
resetPassword: "Återställ Lösenord"
newPasswordIs: "Det nya lösenordet är \"{password}\""
share: "Dela"
enable: "Aktivera"
weakPassword: "Svagt Lösenord"
normalPassword: "Medel Lösenord"
strongPassword: "Starkt Lösenord"
signinFailed: "Kan inte logga in. Det angivna användarnamnet eller lösenordet är felaktigt."
serviceworkerInfo: "Måste vara aktiverad för pushnotiser."
enableInfiniteScroll: "Ladda mer automatiskt"
enablePlayer: "Öppna videospelare"
@@ -354,11 +361,14 @@ enableEmail: "Aktivera epost-utskick"
smtpHost: "Värd"
smtpUser: "Användarnamn"
smtpPass: "Lösenord"
emptyToDisableSmtpAuth: "Lämna användarnamn och lösenord tomt för att avaktivera SMTP verifiering"
clearCache: "Rensa cache"
onlineUsersCount: "{n} användare är online"
enabled: "Aktiverad"
user: "Användare"
global: "Global"
squareAvatars: "Visa fyrkantiga profilbilder"
incorrectPassword: "Fel lösenord."
searchByGoogle: "Sök"
file: "Filer"
enableAutoSensitive: "Automatisk NSFW markering"
@@ -368,6 +378,15 @@ subscribePushNotification: "Aktivera pushnotiser"
unsubscribePushNotification: "Avaktivera pushnotiser"
pushNotificationAlreadySubscribed: "Pushnotiser är redan aktiverade"
pushNotificationNotSupported: "Din webbläsare eller instans har inte stöd för pushnotiser"
windowMaximize: "Maximera"
windowMinimize: "Minimera"
windowRestore: "Återställ"
resetPasswordConfirm: "Återställ verkligen ditt lösenord?"
_achievements:
_types:
_open3windows:
title: "Flera Fönster"
description: "Ha minst 3 fönster öppna samtidigt"
_email:
_follow:
title: "följde dig"
@@ -384,6 +403,7 @@ _sfx:
chat: "Chatt"
antenna: "Antenner"
_2fa:
passwordToTOTP: "Skriv in ditt lösenord"
renewTOTPCancel: "Nej tack"
_antennaSources:
all: "Alla noter"
@@ -444,4 +464,3 @@ _deck:
mentions: "Omnämningar"
_webhookSettings:
active: "Aktiverad"

View File

@@ -122,6 +122,8 @@ unmarkAsSensitive: "ยกเลิกทำเครื่องหมายเ
enterFileName: "พิมพ์ชื่อไฟล์"
mute: "ปิดเสียง"
unmute: "ยกเลิกการปิดเสียง"
renoteMute: "ปิดเสียงรีโน้ต"
renoteUnmute: "เปิดเสียง รีโน้ต"
block: "บล็อค"
unblock: "เลิกปิดกั้น"
suspend: "ถูกระงับ"
@@ -153,6 +155,7 @@ flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์
flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโน้ตของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้"
autoAcceptFollowed: "อนุมัติคำขอติดตามโดยอัตโนมัติทันที จากผู้ใช้งานที่คุณกำลังติดตาม"
addAccount: "เพิ่มบัญชี"
reloadAccountsList: "รีโหลดรายการบัญชีใหม่"
loginFailed: "การเข้าสู่ระบบไม่สำเร็จ"
showOnRemote: "ดูบนอินสแตนซ์ระยะไกล"
general: "ทั่วไป"
@@ -503,6 +506,7 @@ objectStorageUseSSLDesc: "ปิดการทำงานนี้ไว้
objectStorageUseProxy: "เชื่อมต่อผ่านพร็อกซี"
objectStorageUseProxyDesc: "ปิดสิ่งนี้ไว้ถ้าหากคุณจะไม่ใช้ Proxy สำหรับการเชื่อมต่อ API"
objectStorageSetPublicRead: "ตั้งค่า \"public-read\" ในการอัปโหลด"
s3ForcePathStyleDesc: "ถ้าหากเปิดใช้งาน s3ForcePathStyle ชื่อบัคเก็ตนั้นอาจจะต้องรวมอยู่ในเส้นทางของ URL ซึ่งตรงข้ามกับชื่อโฮสต์ของ URL คุณอาจจะต้องเปิดใช้งานการตั้งค่านี้เมื่อใช้บริการต่างๆ เช่น อินสแตนซ์ Minio ที่โฮสต์เองนะ"
serverLogs: "บันทึกของเซิร์ฟเวอร์"
deleteAll: "ลบทั้งหมด"
showFixedPostForm: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนสุดของไทม์ไลน์"
@@ -545,7 +549,9 @@ userSilenced: "ผู้ใช้รายนี้กำลังถูกป
yourAccountSuspendedTitle: "บัญชีนี้นั้นถูกระงับ"
yourAccountSuspendedDescription: "บัญชีนี้ถูกระงับ เนื่องจากละเมิดข้อกำหนดในการให้บริการของเซิร์ฟเวอร์หรืออาจจะละเมิดหลักเกณฑ์ชุมชน หรือ อาจจะโดนร้องเรียนเรื่องการละเมิดลิขสิทธิ์และอื่นๆอย่างต่อเนื่องซ้ำๆ หากคุณคิดว่าไม่ได้ทำผิดจริงๆหรือตัดสินผิดพลาด ได้โปรดกรุณาติดต่อผู้ดูแลระบบหากคุณต้องการทราบเหตุผลโดยละเอียดเพิ่มเติม และขอความกรุณาอย่าสร้างบัญชีใหม่"
tokenRevoked: "โทเค็นไม่ถูกต้อง"
tokenRevokedDescription: "โทเค็นนี้หมดอายุแล้วนะค่ะกรุณาเข้าสู่ระบบอีกครั้งนะ"
accountDeleted: "ลบบัญชีแล้ว"
accountDeletedDescription: "บัญชีนี้ถูกลบไปแล้วนะ"
menu: "เมนู"
divider: "ตัวแบ่ง"
addItem: "เพิ่มรายการ"
@@ -914,6 +920,7 @@ pushNotificationNotSupported: "เบราว์เซอร์หรืออ
sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
sendPushNotificationReadMessageCaption: "การแจ้งเตือนที่มีข้อความ \"{emptyPushNotificationMessage}\" จะแสดงขึ้นมาในช่วงระยะเวลาสั้นๆ การดำเนินการนี้อาจทำให้เพิ่มการใช้งานแบตเตอรี่ของอุปกรณ์ถ้าหากมีนะ"
windowMaximize: "ขยายใหญ่สุดแล้ว"
windowMinimize: "ย่อเล็กที่สุด"
windowRestore: "เลิกทำ"
caption: "รายละเอียด"
loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้"
@@ -955,12 +962,17 @@ copyErrorInfo: "คัดลอกรายละเอียดข้อผิ
joinThisServer: "ลงชื่อสมัครใช้ในอินสแตนซ์นี้"
exploreOtherServers: "มองหาอินสแตนซ์อื่น"
letsLookAtTimeline: "ลองดูที่ไทม์ไลน์"
disableFederationWarn: "การดำเนินการนี้ถ้าหากจะปิดใช้งานการรวมศูนย์ แต่โพสต์ดังกล่าวนั้นจะยังคงเป็นสาธารณะต่อไป ยกเว้นแต่ว่าจะตั้งค่าเป็นอย่างอื่น โดยปกติคุณไม่จำเป็นต้องใช้การตั้งค่านี้นะ"
disableFederationConfirm: "ปิดใช้งานสหพันธ์จริงๆหรอแน่ใจแล้วนะ?"
disableFederationConfirmWarn: "แม้ว่าจะถูกยกเลิกเอาไว้โพสต์ดังกล่าวนั้นจะยังคงเป็นสาธารณะต่อไป เว้นแต่ว่า...จะตั้งค่าเป็นอย่างอื่น โดยปกติคุณไม่จำเป็นต้องทำตรงนี้หรอกนะค่ะ"
disableFederationOk: "ปิดการใช้งาน"
invitationRequiredToRegister: "อินสแตนซ์นี้เป็นแบบรับเชิญเท่านั้น คุณต้องป้อนรหัสเชิญที่ถูกต้องถึงจะลงทะเบียนได้นะค่ะ"
emailNotSupported: "อินสแตนซ์นี้ไม่รองรับการส่งอีเมลนะค่ะ"
postToTheChannel: "โพสต์ลงช่อง"
cannotBeChangedLater: "สิ่งนี้ไม่สามารถเปลี่ยนแปลงได้ในภายหลังนะ"
reactionAcceptance: "การยอมรับรีแอคชั่น"
likeOnly: "ที่ชอบเท่านั้น"
likeOnlyForRemote: "ไลค์สำหรับอินสแตนซ์ระยะไกลเท่านั้น"
rolesAssignedToMe: "บทบาทที่ได้รับมอบหมายให้ฉัน"
resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุณจริงๆหรอ?"
sensitiveWords: "คำที่ละเอียดอ่อน"
sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
@@ -972,6 +984,22 @@ drivecleaner: "ทำความสะอาดไดรฟ์"
retryAllQueuesNow: "ลองเรียกใช้คิวทั้งหมดอีกครั้ง"
retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริงๆหรอแน่ใจนะ?"
retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ"
enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล"
enableChartsForFederatedInstances: "สร้างแผนภูมิข้อมูลอินสแตนซ์ระยะไกล"
showClipButtonInNoteFooter: "เพิ่ม \"คลิป\" เพื่อบันทึกเมนูการทำงาน"
largeNoteReactions: "ขยายรีแอคชั่นการแสดงผล"
noteIdOrUrl: "โน้ต ID หรือ URL"
accountMigration: "การโยกย้ายบัญชี"
accountMoved: "ผู้ใช้รายนี้ได้ย้ายไปยังบัญชีใหม่แล้ว:"
forceShowAds: "แสดงโฆษณาเสมอ"
_accountMigration:
moveTo: "ย้ายข้อมูลบัญชีนี้ไปยังบัญชีอีกหนึ่ง"
moveToLabel: "บัญชีที่จะย้ายไปที่:"
moveAccountDescription: "การกระทำนี้ไม่สามารถย้อนกลับได้นะ ขั้นตอนแรก ต้องสร้างนามแฝงสำหรับบัญชีนี้ในบัญชีที่คุณต้องการย้ายไป หลังจากนั้นแล้ว ป้อนบัญชีที่จะย้ายไปในรูปแบบดังต่อไปนี้: @person@instance.com"
moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง"
moveFromLabel: "บัญชีที่จะย้ายจาก:"
moveFromDescription: "สร้างนามแฝงสำหรับบัญชีที่จะย้ายจากบัญชีนี้ ถ้าหากคุณต้องการโอนผู้ติดตาม สิ่งนี้ต้องทำก่อนโอนก่อนนะค่ะ! หลังจากนั้น ป้อนบัญชีที่จะย้ายไปในรูปแบบต่อไปนี้: @person@instance.com"
migrationConfirm: "ย้ายข้อมูลบัญชีนี้ไปที่ {account} จริงๆนะ เมื่อมีการเริ่มต้นแล้ว กระบวนการนี้จะไม่สามารถหยุดหรือนำกลับคืนมาได้ และคุณจะไม่สามารถใช้บัญชีนี้ในสถานะดั้งเดิมได้อีกต่อไป\n\nนอกจากนี้ เพื่อให้แน่ใจยืนยันว่าคุณได้สร้างนามแฝงในบัญชีที่จะย้ายข้อมูลนะค่ะ"
_achievements:
earnedAt: "ได้รับเมื่อ"
_types:
@@ -1268,6 +1296,8 @@ _role:
followersMoreThanOrEq: "จำนวนผู้ติดตามมากกว่าหรือเท่ากับ\n"
followingLessThanOrEq: "จำนวนบัญชีต่อไปนี้คือ น้อยกว่าหรือเท่ากับ"
followingMoreThanOrEq: "จำนวนบัญชีต่อไปนี้คือ มากกว่าหรือเท่ากับ"
notesLessThanOrEq: "จำนวนโพสต์น้อยกว่าเท่ากับ"
notesMoreThanOrEq: "จำนวนโพสต์มากกว่าเท่ากับ"
and: "และ"
or: "หรือ"
not: "ไม่"
@@ -1867,6 +1897,16 @@ _drivecleaner:
orderBySizeDesc: "ขนาดไฟล์จากมากไปหาน้อย"
orderByCreatedAtAsc: "วันที่จากน้อยไปหามาก"
_webhookSettings:
createWebhook: "สร้าง Webhook"
name: "ชื่อ"
secret: "ความลับ"
events: "อีเว้นท์ Webhook"
active: "เปิดใช้งาน"
_events:
follow: "เมื่อกำลังติดตามผู้ใช้"
followed: "เมื่อกำลังติดตามแล้ว"
note: "เมื่อกำลังโพสต์โน้ต"
reply: "เมื่อได้รับการตอบกลับ"
renote: "รีโน้ตแล้วเมื่อ"
reaction: "เมื่อได้รับรีแอคชั่น"
mention: "เมื่อกำลังถูกกล่าวถึง"

View File

@@ -60,4 +60,3 @@ _deck:
_columns:
notifications: "Bildirim"
tl: "Zaman çizelgesi"

View File

@@ -2,4 +2,3 @@
_lang_: "ياپونچە"
search: "ئىزدەش"
searchByGoogle: "ئىزدەش"

View File

@@ -1641,4 +1641,3 @@ _deck:
_webhookSettings:
name: "Ім'я"
active: "Увімкнено"

View File

@@ -1707,4 +1707,3 @@ _dialog:
_webhookSettings:
name: "Tên"
active: "Đã bật"

View File

@@ -16,16 +16,17 @@ cancel: "取消"
noThankYou: "不用,谢谢"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转贴"
noNotes: "没有帖"
noNotes: "没有帖"
noNotifications: "无通知"
instance: "服务器"
settings: "设置"
notificationSettings: "通知设置"
basicSettings: "基本设置"
otherSettings: "其他设置"
openInWindow: "在新窗口中打开"
profile: "个人资料"
timeline: "时间线"
noAccountDescription: "这个人很懒,没有写自我介绍"
noAccountDescription: "此用户尚无自我介绍"
login: "登录"
loggingIn: "正在登录..."
logout: "登出"
@@ -60,7 +61,7 @@ youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "您收到了关注请求"
followRequestAccepted: "您的关注请求被通过了"
mention: "提及"
mentions: "提"
mentions: "提到我的"
directNotes: "私信"
importAndExport: "导入和导出"
import: "导入"
@@ -85,7 +86,7 @@ somethingHappened: "出现了一些问题!"
retry: "重试"
pageLoadError: "页面加载失败。"
pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。"
serverIsDead: "服务器没有响应。 请稍等片刻,然后重试。"
serverIsDead: "没有服务器响应。 请稍后再试。"
youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。"
enterListName: "输入列表名称"
privacy: "隐私"
@@ -95,7 +96,7 @@ follow: "关注"
followRequest: "关注申请"
followRequests: "关注申请"
unfollow: "取消关注"
followRequestPending: "发送关注请求"
followRequestPending: "关注请求批准中"
enterEmoji: "输入表情符号"
renote: "转发"
unrenote: "取消转发"
@@ -119,7 +120,7 @@ rememberNoteVisibility: "保存上次设置的可见性"
attachCancel: "删除附件"
markAsSensitive: "标记为敏感内容"
unmarkAsSensitive: "取消标记为敏感内容"
enterFileName: "输入文件名"
enterFileName: "输入文件名"
mute: "屏蔽"
unmute: "解除屏蔽"
renoteMute: "屏蔽转帖"
@@ -142,18 +143,18 @@ customEmojis: "自定义表情符号"
emoji: "表情符号"
emojis: "表情符号"
emojiName: "表情符号名称"
emojiUrl: "表情符号地址"
emojiUrl: "emoji 地址"
addEmoji: "添加表情符号"
settingGuide: "推荐配置"
cacheRemoteFiles: "远程文件缓存"
cacheRemoteFiles: "缓存远程文件"
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程服务器载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
flagAsBot: "这是一个机器人账号"
flagAsBotDescription: "如果此户由程序控制请启用此项。启用后此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为并让Misskey的内部系统将此户识别为机器人。"
flagAsBotDescription: "如果此户由程序控制请启用此项。启用后此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为并让Misskey的内部系统将此户识别为机器人。"
flagAsCat: "将这个账户设定为一只猫"
flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。\n开启后会在您的头像上出现猫耳朵并将你的帖子中的「na」替换为「nya」日文同理。"
flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许关注者的关注"
autoAcceptFollowed: "自动允许来自我关注的用户对我的关注请求"
addAccount: "添加账户"
reloadAccountsList: "更新账户列表"
loginFailed: "登录失败"
@@ -203,7 +204,7 @@ blockedInstances: "被阻拦的服务器"
blockedInstancesDescription: "设定要阻拦的服务器,以换行来进行分割。被阻拦的服务器将无法与本服务器进行交换通讯。"
muteAndBlock: "屏蔽/拉黑"
mutedUsers: "已屏蔽用户"
blockedUsers: "拉黑的用户"
blockedUsers: "拉黑的用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "要删除该帖子吗?"
@@ -336,7 +337,7 @@ enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
registration: "注册"
enableRegistration: "允许新用户注册"
enableRegistration: "允许任何人注册"
invite: "邀请"
driveCapacityPerLocalAccount: "每个用户的网盘空间"
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
@@ -354,7 +355,7 @@ pinnedNotes: "已置顶的帖子"
hcaptcha: "hCaptcha"
enableHcaptcha: "启用 hCaptcha"
hcaptchaSiteKey: "网站密钥"
hcaptchaSecretKey: "hCaptcha 密钥(SecretKey)"
hcaptchaSecretKey: "密钥"
recaptcha: "reCAPTCHA"
enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
recaptchaSiteKey: "网站密钥"
@@ -506,6 +507,7 @@ objectStorageUseSSLDesc: "如果不使用https进行API连接请关闭。"
objectStorageUseProxy: "使用代理"
objectStorageUseProxyDesc: "如果您不使用代理进行API连接请将其关闭。"
objectStorageSetPublicRead: "上传时设置为public-read"
s3ForcePathStyleDesc: "启用 s3ForcePathStyle 会强制将存储桶名称指定为 URL 中路径的一部分,而不是主机名。使用自托管 Minio 等时可能需要启用。"
serverLogs: "服务器日志"
deleteAll: "全部删除"
showFixedPostForm: "在时间线顶部显示发帖框"
@@ -919,6 +921,7 @@ pushNotificationNotSupported: "浏览器或服务器不支持推送通知消息"
sendPushNotificationReadMessage: "删除已读推送通知消息"
sendPushNotificationReadMessageCaption: "“{emptyPushNotificationMessage}”的通知消息将会显示。您终端设备的电池消耗可能会增加。"
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "还原"
caption: "标题"
loggedInAsBot: "以Bot账户登录"
@@ -960,7 +963,9 @@ copyErrorInfo: "复制错误信息"
joinThisServer: "在本服务器上注册"
exploreOtherServers: "探索其他服务器"
letsLookAtTimeline: "时间线"
disableFederationWarn: "联合被禁用。 禁用它并不能使帖子变成私人的。 在大多数情况下,这个选项不需要被启用。"
disableFederationConfirm: "确定要禁用联合?"
disableFederationConfirmWarn: "禁用联合不会将帖子设为私有。在大多数情况下,不需要禁用联合。"
disableFederationOk: "联合禁用"
invitationRequiredToRegister: "此服务器目前只允许拥有邀请码的人注册。"
emailNotSupported: "此服务器不支持发送邮件"
postToTheChannel: "发布到频道"
@@ -983,6 +988,19 @@ retryAllQueuesConfirmText: "可能会使服务器负荷在一定时间内增加"
enableChartsForRemoteUser: "生成远程用户的图表"
enableChartsForFederatedInstances: "生成远程服务器的图表"
showClipButtonInNoteFooter: "在贴文下方显示便签按钮"
largeNoteReactions: "使用大图标来显示回应"
noteIdOrUrl: "帖子ID或URL"
accountMigration: "账户迁移"
accountMoved: "此用户已迁移账户"
forceShowAds: "总是显示广告"
_accountMigration:
moveTo: "把这个账户迁移到新的账户"
moveToLabel: "迁移后的账户"
moveAccountDescription: "此操作无法取消。请先确认您已在迁移后的账户上,为此账户创造了别名。创造别名后,请如以下输入您的迁移后的账户:@person@instance.com"
moveFrom: "从别的账号迁移到此账户"
moveFromLabel: "迁移前的账户"
moveFromDescription: "如果迁移时需要继承其他账户的关注者,请在此创造别名。此操作需要在实行迁移之前完成!请如已下输入需要迁移的账户:@person@instance.com"
migrationConfirm: "确定要把此账户迁移到{account}吗?一旦确定后,此操作无法取消,此账户也无法以原来的状态使用。\n同时请确认迁移后的账户已创造别名。"
_achievements:
earnedAt: "达成时间"
_types:
@@ -1255,7 +1273,7 @@ _role:
gtlAvailable: "查看全局时间线"
ltlAvailable: "查看本地时间线"
canPublicNote: "允许公开发帖"
canInvite: "发放实例邀请码"
canInvite: "发放服务器邀请码"
canManageCustomEmojis: "管理自定义表情符号"
driveCapacity: "网盘容量"
pinMax: "帖子置顶数量限制"
@@ -1390,6 +1408,8 @@ _channel:
following: "正在关注"
usersCount: "有{n}人参与"
notesCount: "有{n}个帖子"
nameAndDescription: "名称与描述"
nameOnly: "仅名称"
_menuDisplay:
sideFull: "横向"
sideIcon: "横向(图标)"
@@ -1629,7 +1649,7 @@ _widgets:
photos: "照片"
digitalClock: "数字时钟"
unixClock: "UNIX时钟"
federation: "联邦宇宙"
federation: "联"
instanceCloud: "服务器云"
postForm: "投稿窗口"
slideshow: "幻灯片展示"
@@ -1870,6 +1890,7 @@ _deck:
channel: "频道"
mentions: "提及"
direct: "指定用户"
roleTimeline: "角色时间线"
_dialog:
charactersExceeded: "已经超过了最大字符数! 当前字符数 {current} / 限制字符数 {max}"
charactersBelow: "低于最小字符数!当前字符数 {current} / 限制字符数 {min}"
@@ -1893,4 +1914,3 @@ _webhookSettings:
renote: "被转发时"
reaction: "被回应时"
mention: "被提及时"

View File

@@ -20,6 +20,7 @@ noNotes: "無貼文。"
noNotifications: "沒有通知"
instance: "實例"
settings: "設定"
notificationSettings: "通知選項"
basicSettings: "基本設定"
otherSettings: "其他設定"
openInWindow: "在新視窗開啟"
@@ -506,6 +507,7 @@ objectStorageUseSSLDesc: "如果不使用https進行API連接請關閉"
objectStorageUseProxy: "使用網路代理"
objectStorageUseProxyDesc: "如果不使用代理進行API連接請關閉"
objectStorageSetPublicRead: "上傳時設定為\"public-read\""
s3ForcePathStyleDesc: "啟用 s3ForcePathStyle 會強制將儲存槽名稱指定為 URL 中路徑的一部分,而不是主機名。 使用自託管 Minio 之類的可能需要啟用。"
serverLogs: "伺服器日誌"
deleteAll: "刪除所有記錄"
showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框"
@@ -560,7 +562,7 @@ inboxUrl: "收件夾URL"
addedRelays: "已加入的中繼"
serviceworkerInfo: "您需要啟用推送通知"
deletedNote: "已删除的貼文"
invisibleNote: "隱藏的貼文"
invisibleNote: "私密的貼文"
enableInfiniteScroll: "啟用自動滾動頁面模式"
visibility: "可見性"
poll: "投票"
@@ -919,6 +921,7 @@ pushNotificationNotSupported: "瀏覽器或實例不支援推播通知"
sendPushNotificationReadMessage: "通知與訊息如果已讀的話,就將推播通知刪除"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」通知將立刻顯示。可能會增加設備的電池消耗。"
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "復原"
caption: "標題"
loggedInAsBot: "以機器人帳戶登入中"
@@ -960,7 +963,9 @@ copyErrorInfo: "複製錯誤資訊"
joinThisServer: "在此伺服器上註冊"
exploreOtherServers: "探索其他伺服器"
letsLookAtTimeline: "看看時間軸"
disableFederationWarn: "聯邦被停用了。即使停用也不會讓您的貼文不公開,在大多數情況下,不需要啟用這個選項。"
disableFederationConfirm: "要停止聯邦功能嗎?"
disableFederationConfirmWarn: "即使停止了聯邦功能,貼文也不會變成私密的。在大部分的情況下,沒有必要停止聯邦功能。"
disableFederationOk: "停止聯邦功能"
invitationRequiredToRegister: "目前這個伺服器為邀請制,必須擁有邀請碼才能註冊。"
emailNotSupported: "這個伺服器不支援寄送郵件"
postToTheChannel: "發布到頻道"
@@ -983,6 +988,19 @@ retryAllQueuesConfirmText: "伺服器的負荷可能會暫時增加。"
enableChartsForRemoteUser: "生成遠端用戶的圖表"
enableChartsForFederatedInstances: "生成遠端伺服器的圖表"
showClipButtonInNoteFooter: "將摘錄添加至貼文"
largeNoteReactions: "將貼文的反應放大顯示"
noteIdOrUrl: "貼文ID或URL"
accountMigration: "遷移帳戶"
accountMoved: "這個使用者已遷移至新的帳戶:"
forceShowAds: "總是顯示廣告"
_accountMigration:
moveTo: "將這個帳戶遷移至新的帳戶"
moveToLabel: "要遷移到的帳戶:"
moveAccountDescription: "這個操作不可撤銷。首先,請確認已在要遷移到的帳戶中為這個帳戶建立了一個別名。建立別名之後,像這樣輸入你要遷移到的帳戶:@person@instance.com"
moveFrom: "從其他帳戶遷移到這個帳戶"
moveFromLabel: "要遷移過來的帳戶:"
moveFromDescription: "如果你想把跟隨者從別的帳戶遷移過來,必須先在這裡建立別名。請務必在執行遷移之前建立別名!請像這樣輸入要遷移的帳戶:@person@instance.com"
migrationConfirm: "確定要將這個帳戶遷移至 {account} 嗎?一旦遷移就無法撤銷,也就無法以原來的狀態使用這個帳戶。\n另外請確認在要遷移到的帳戶已經建立了一個別名。"
_achievements:
earnedAt: "獲得日期"
_types:
@@ -1390,6 +1408,8 @@ _channel:
following: "關注中"
usersCount: "有{n}人參與"
notesCount: "有{n}個貼文"
nameAndDescription: "名稱與說明"
nameOnly: "僅名稱"
_menuDisplay:
sideFull: "側向"
sideIcon: "側向(圖示)"
@@ -1870,6 +1890,7 @@ _deck:
channel: "頻道"
mentions: "提及"
direct: "指定使用者"
roleTimeline: "角色時間軸"
_dialog:
charactersExceeded: "已超過最大字數!現在 {current} / 限制 {max}"
charactersBelow: "低於最少字數!現在 {current} / 限制 {max}"
@@ -1893,4 +1914,3 @@ _webhookSettings:
renote: "當被轉發時"
reaction: "當獲得反應時"
mention: "當被提到時"

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "13.11.0.beta-2",
"version": "13.12.0-beta.1",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@8.1.0",
"packageManager": "pnpm@8.3.1",
"workspaces": [
"packages/frontend",
"packages/backend",
@@ -16,6 +16,7 @@
"scripts": {
"build-pre": "node ./scripts/build-pre.js",
"build": "pnpm build-pre && pnpm -r build && pnpm gulp",
"build-storybook": "pnpm --filter frontend build-storybook",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js",
"init": "pnpm migrate",
@@ -50,19 +51,19 @@
"gulp-replace": "1.1.4",
"gulp-terser": "2.1.0",
"js-yaml": "4.1.0",
"typescript": "5.0.2"
"typescript": "5.0.4"
},
"devDependencies": {
"@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.1",
"@typescript-eslint/eslint-plugin": "5.57.0",
"@typescript-eslint/parser": "5.57.0",
"@typescript-eslint/eslint-plugin": "5.59.2",
"@typescript-eslint/parser": "5.59.2",
"cross-env": "7.0.3",
"cypress": "12.9.0",
"eslint": "8.37.0",
"cypress": "12.11.0",
"eslint": "8.39.0",
"start-server-and-test": "2.0.0"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.2.0"
"@tensorflow/tfjs-core": "4.4.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,8 +1,15 @@
import Redis from 'ioredis';
import { loadConfig } from './built/config.js';
import { createRedisConnection } from './built/redis.js';
const config = loadConfig();
const redis = createRedisConnection(config);
const redis = new Redis({
port: config.redis.port,
host: config.redis.host,
family: config.redis.family == null ? 0 : config.redis.family,
password: config.redis.pass,
keyPrefix: `${config.redis.prefix}:`,
db: config.redis.db ?? 0,
});
redis.on('connect', () => redis.disconnect());
redis.on('error', (e) => {

View File

@@ -0,0 +1,18 @@
export class UserMemo1680702787050 {
name = 'UserMemo1680702787050'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "user_memo" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "targetUserId" character varying(32) NOT NULL, "memo" character varying(2048) NOT NULL, CONSTRAINT "PK_e9aaa58f7d3699a84d79078f4d9" PRIMARY KEY ("id")); COMMENT ON COLUMN "user_memo"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "user_memo"."targetUserId" IS 'The ID of target user.'; COMMENT ON COLUMN "user_memo"."memo" IS 'Memo.'`);
await queryRunner.query(`CREATE INDEX "IDX_650b49c5639b5840ee6a2b8f83" ON "user_memo" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_66ac4a82894297fd09ba61f3d3" ON "user_memo" ("targetUserId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_faef300913c738265638ba3ebc" ON "user_memo" ("userId", "targetUserId") `);
await queryRunner.query(`ALTER TABLE "user_memo" ADD CONSTRAINT "FK_650b49c5639b5840ee6a2b8f83e" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_memo" ADD CONSTRAINT "FK_66ac4a82894297fd09ba61f3d35" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_memo" DROP CONSTRAINT "FK_66ac4a82894297fd09ba61f3d35"`);
await queryRunner.query(`ALTER TABLE "user_memo" DROP CONSTRAINT "FK_650b49c5639b5840ee6a2b8f83e"`);
await queryRunner.query(`DROP TABLE "user_memo"`);
}
}

View File

@@ -0,0 +1,17 @@
export class AvatarUrlAndBannerUrl1680775031481 {
name = 'AvatarUrlAndBannerUrl1680775031481'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" ADD "avatarUrl" character varying(512)`);
await queryRunner.query(`ALTER TABLE "user" ADD "bannerUrl" character varying(512)`);
await queryRunner.query(`ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`);
await queryRunner.query(`ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerUrl"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarUrl"`);
}
}

View File

@@ -0,0 +1,17 @@
export class AccountMove1680931179228 {
name = 'AccountMove1680931179228'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" ADD "movedToUri" character varying(512)`);
await queryRunner.query(`COMMENT ON COLUMN "user"."movedToUri" IS 'The URI of the new account of the User'`);
await queryRunner.query(`ALTER TABLE "user" ADD "alsoKnownAs" text`);
await queryRunner.query(`COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`);
}
async down(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAs"`);
await queryRunner.query(`COMMENT ON COLUMN "user"."movedToUri" IS 'The URI of the new account of the User'`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedToUri"`);
}
}

View File

@@ -0,0 +1,11 @@
export class ServerRules1681400427971 {
name = 'ServerRules1681400427971'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "serverRules" character varying(280) array NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "serverRules"`);
}
}

View File

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

View File

@@ -0,0 +1,13 @@
export class MovedAt1682190963894 {
name = 'MovedAt1682190963894'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" ADD "movedAt" TIMESTAMP WITH TIME ZONE`);
await queryRunner.query(`COMMENT ON COLUMN "user"."movedAt" IS 'When the user moved to another account'`);
}
async down(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "user"."movedAt" IS 'When the user moved to another account'`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedAt"`);
}
}

View File

@@ -0,0 +1,11 @@
export class PreservedUsernames1682754135458 {
name = 'PreservedUsernames1682754135458'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "preservedUsernames" character varying(1024) array NOT NULL DEFAULT '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "preservedUsernames"`);
}
}

View File

@@ -0,0 +1,11 @@
export class ChannelColor1682985520254 {
name = 'ChannelColor1682985520254'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "channel" ADD "color" character varying(16) NOT NULL DEFAULT '#86b300'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "color"`);
}
}

View File

@@ -22,42 +22,42 @@
"test-and-coverage": "pnpm jest-and-coverage"
},
"optionalDependencies": {
"@swc/core-android-arm64": "^1.3.11",
"@swc/core-darwin-arm64": "^1.3.42",
"@swc/core-darwin-x64": "^1.3.42",
"@swc/core-linux-arm-gnueabihf": "^1.3.42",
"@swc/core-linux-arm64-gnu": "^1.3.42",
"@swc/core-linux-arm64-musl": "^1.3.42",
"@swc/core-linux-x64-gnu": "^1.3.42",
"@swc/core-linux-x64-musl": "^1.3.42",
"@swc/core-win32-arm64-msvc": "^1.3.42",
"@swc/core-win32-ia32-msvc": "^1.3.42",
"@swc/core-win32-x64-msvc": "^1.3.42",
"@tensorflow/tfjs": "4.2.0",
"@tensorflow/tfjs-node": "4.2.0"
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.3.56",
"@swc/core-darwin-x64": "1.3.56",
"@swc/core-linux-arm-gnueabihf": "1.3.56",
"@swc/core-linux-arm64-gnu": "1.3.56",
"@swc/core-linux-arm64-musl": "1.3.56",
"@swc/core-linux-x64-gnu": "1.3.56",
"@swc/core-linux-x64-musl": "1.3.56",
"@swc/core-win32-arm64-msvc": "1.3.56",
"@swc/core-win32-ia32-msvc": "1.3.56",
"@swc/core-win32-x64-msvc": "1.3.56",
"@tensorflow/tfjs": "4.4.0",
"@tensorflow/tfjs-node": "4.4.0"
},
"dependencies": {
"@aws-sdk/client-s3": "3.301.0",
"@aws-sdk/lib-storage": "3.301.0",
"@aws-sdk/node-http-handler": "3.296.0",
"@bull-board/api": "5.0.0",
"@bull-board/fastify": "5.0.0",
"@bull-board/ui": "5.0.0",
"@aws-sdk/client-s3": "3.321.1",
"@aws-sdk/lib-storage": "3.321.1",
"@aws-sdk/node-http-handler": "3.321.1",
"@bull-board/api": "5.1.2",
"@bull-board/fastify": "5.1.2",
"@bull-board/ui": "5.1.2",
"@discordapp/twemoji": "14.1.2",
"@fastify/accepts": "4.1.0",
"@fastify/cookie": "8.3.0",
"@fastify/cors": "8.2.1",
"@fastify/http-proxy": "9.0.0",
"@fastify/multipart": "7.5.0",
"@fastify/static": "6.9.0",
"@fastify/multipart": "7.6.0",
"@fastify/static": "6.10.1",
"@fastify/view": "7.4.1",
"@nestjs/common": "9.3.12",
"@nestjs/core": "9.3.12",
"@nestjs/testing": "9.3.12",
"@nestjs/common": "9.4.0",
"@nestjs/core": "9.4.0",
"@nestjs/testing": "9.4.0",
"@peertube/http-signature": "1.7.0",
"@sinonjs/fake-timers": "10.0.2",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.42",
"@swc/core": "1.3.56",
"accepts": "1.3.8",
"ajv": "8.12.0",
"archiver": "5.3.1",
@@ -73,25 +73,26 @@
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.29.3",
"date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
"fastify": "4.15.0",
"fastify": "4.17.0",
"feed": "4.2.2",
"file-type": "18.2.1",
"file-type": "18.3.0",
"fluent-ffmpeg": "2.1.2",
"form-data": "4.0.0",
"got": "12.6.0",
"happy-dom": "8.9.0",
"happy-dom": "9.10.2",
"hpagent": "1.2.0",
"ioredis": "4.28.5",
"ioredis": "5.3.2",
"ip-cidr": "3.1.0",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
"jsdom": "21.1.1",
"json5": "2.2.3",
"jsonld": "8.1.1",
"jsrsasign": "10.7.0",
"meilisearch": "0.32.3",
"jsrsasign": "10.8.6",
"mfm-js": "0.23.3",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
@@ -111,7 +112,7 @@
"pug": "3.0.2",
"punycode": "2.3.0",
"pureimage": "0.3.17",
"qrcode": "1.5.1",
"qrcode": "1.5.3",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.18.0",
@@ -119,13 +120,13 @@
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"rndstr": "1.0.0",
"rss-parser": "3.12.0",
"rxjs": "7.8.0",
"rss-parser": "3.13.0",
"rxjs": "7.8.1",
"s-age": "1.1.2",
"sanitize-html": "2.10.0",
"seedrandom": "3.0.5",
"semver": "7.3.8",
"sharp": "0.32.0",
"semver": "7.5.0",
"sharp": "0.32.1",
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
@@ -133,23 +134,23 @@
"systeminformation": "5.17.12",
"tinycolor2": "1.6.0",
"tmp": "0.2.1",
"tsc-alias": "1.8.5",
"tsc-alias": "1.8.6",
"tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0",
"typeorm": "0.3.11",
"typescript": "5.0.2",
"typeorm": "0.3.15",
"typescript": "5.0.4",
"ulid": "2.3.0",
"unzipper": "0.10.11",
"uuid": "9.0.0",
"vary": "1.1.2",
"web-push": "3.5.0",
"web-push": "3.6.1",
"websocket": "1.0.34",
"ws": "8.13.0",
"xev": "3.0.2"
},
"devDependencies": {
"@jest/globals": "29.5.0",
"@swc/jest": "0.2.24",
"@swc/jest": "0.2.26",
"@types/accepts": "1.3.5",
"@types/archiver": "5.3.2",
"@types/bcryptjs": "2.4.2",
@@ -159,14 +160,13 @@
"@types/content-disposition": "0.5.5",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21",
"@types/ioredis": "4.28.10",
"@types/jest": "29.5.0",
"@types/jest": "29.5.1",
"@types/js-yaml": "4.0.5",
"@types/jsdom": "21.1.1",
"@types/jsonld": "1.5.8",
"@types/jsrsasign": "10.5.8",
"@types/mime-types": "2.1.1",
"@types/node": "18.15.11",
"@types/node": "18.16.3",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.7",
"@types/oauth": "0.9.1",
@@ -180,7 +180,7 @@
"@types/rename": "1.0.4",
"@types/sanitize-html": "2.9.0",
"@types/semver": "7.3.13",
"@types/sharp": "0.31.1",
"@types/sharp": "0.32.0",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
@@ -190,11 +190,11 @@
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "5.57.0",
"@typescript-eslint/parser": "5.57.0",
"@typescript-eslint/eslint-plugin": "5.59.2",
"@typescript-eslint/parser": "5.59.2",
"aws-sdk-client-mock": "^2.1.1",
"cross-env": "7.0.3",
"eslint": "8.37.0",
"eslint": "8.39.0",
"eslint-plugin-import": "2.27.5",
"execa": "6.1.0",
"jest": "29.5.0",

View File

@@ -1,19 +1,17 @@
import { setTimeout } from 'node:timers/promises';
import { Global, Inject, Module } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DataSource } from 'typeorm';
import { createRedisConnection } from '@/redis.js';
import { MeiliSearch } from 'meilisearch';
import { DI } from './di-symbols.js';
import { loadConfig } from './config.js';
import { createPostgresDataSource } from './postgres.js';
import { RepositoryModule } from './models/RepositoryModule.js';
import type { Provider, OnApplicationShutdown } from '@nestjs/common';
const config = loadConfig();
const $config: Provider = {
provide: DI.config,
useValue: config,
useValue: loadConfig(),
};
const $db: Provider = {
@@ -25,21 +23,65 @@ const $db: Provider = {
inject: [DI.config],
};
const $redis: Provider = {
provide: DI.redis,
const $meilisearch: Provider = {
provide: DI.meilisearch,
useFactory: (config) => {
const redisClient = createRedisConnection(config);
return redisClient;
if (config.meilisearch) {
return new MeiliSearch({
host: `http://${config.meilisearch.host}:${config.meilisearch.port}`,
apiKey: config.meilisearch.apiKey,
});
} else {
return null;
}
},
inject: [DI.config],
};
const $redisSubscriber: Provider = {
provide: DI.redisSubscriber,
const $redis: Provider = {
provide: DI.redis,
useFactory: (config) => {
const redisSubscriber = createRedisConnection(config);
redisSubscriber.subscribe(config.host);
return redisSubscriber;
return new Redis.Redis({
port: config.redis.port,
host: config.redis.host,
family: config.redis.family == null ? 0 : config.redis.family,
password: config.redis.pass,
keyPrefix: `${config.redis.prefix}:`,
db: config.redis.db ?? 0,
});
},
inject: [DI.config],
};
const $redisForPub: Provider = {
provide: DI.redisForPub,
useFactory: (config) => {
const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
password: config.redisForPubsub.pass,
keyPrefix: `${config.redisForPubsub.prefix}:`,
db: config.redisForPubsub.db ?? 0,
});
return redis;
},
inject: [DI.config],
};
const $redisForSub: Provider = {
provide: DI.redisForSub,
useFactory: (config) => {
const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
password: config.redisForPubsub.pass,
keyPrefix: `${config.redisForPubsub.prefix}:`,
db: config.redisForPubsub.db ?? 0,
});
redis.subscribe(config.host);
return redis;
},
inject: [DI.config],
};
@@ -47,14 +89,15 @@ const $redisSubscriber: Provider = {
@Global()
@Module({
imports: [RepositoryModule],
providers: [$config, $db, $redis, $redisSubscriber],
exports: [$config, $db, $redis, $redisSubscriber, RepositoryModule],
providers: [$config, $db, $meilisearch, $redis, $redisForPub, $redisForSub],
exports: [$config, $db, $meilisearch, $redis, $redisForPub, $redisForSub, RepositoryModule],
})
export class GlobalModule implements OnApplicationShutdown {
constructor(
@Inject(DI.db) private db: DataSource,
@Inject(DI.redis) private redisClient: Redis.Redis,
@Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis,
@Inject(DI.redisForPub) private redisForPub: Redis.Redis,
@Inject(DI.redisForSub) private redisForSub: Redis.Redis,
) {}
async onApplicationShutdown(signal: string): Promise<void> {
@@ -69,7 +112,8 @@ export class GlobalModule implements OnApplicationShutdown {
await Promise.all([
this.db.destroy(),
this.redisClient.disconnect(),
this.redisSubscriber.disconnect(),
this.redisForPub.disconnect(),
this.redisForSub.disconnect(),
]);
}
}

View File

@@ -4,7 +4,7 @@
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { dirname, resolve } from 'node:path';
import * as yaml from 'js-yaml';
/**
@@ -25,6 +25,14 @@ export type Source = {
disableCache?: boolean;
extra?: { [x: string]: string };
};
dbReplications?: boolean;
dbSlaves?: {
host: string;
port: number;
db: string;
user: string;
pass: string;
}[];
redis: {
host: string;
port: number;
@@ -33,13 +41,26 @@ export type Source = {
db?: number;
prefix?: string;
};
elasticsearch: {
redisForPubsub?: {
host: string;
port: number;
ssl?: boolean;
user?: string;
pass?: string;
index?: string;
family?: number;
pass: string;
db?: number;
prefix?: string;
};
redisForJobQueue?: {
host: string;
port: number;
family?: number;
pass: string;
db?: number;
prefix?: string;
};
meilisearch?: {
host: string;
port: string;
apiKey: string;
};
proxy?: string;
@@ -60,8 +81,10 @@ export type Source = {
deliverJobConcurrency?: number;
inboxJobConcurrency?: number;
relashionshipJobConcurrency?: number;
deliverJobPerSec?: number;
inboxJobPerSec?: number;
relashionshipJobPerSec?: number;
deliverJobMaxAttempts?: number;
inboxJobMaxAttempts?: number;
@@ -91,6 +114,8 @@ export type Mixin = {
mediaProxy: string;
externalMediaProxyEnabled: boolean;
videoThumbnailGenerator: string | null;
redisForPubsub: NonNullable<Source['redisForPubsub']>;
redisForJobQueue: NonNullable<Source['redisForJobQueue']>;
};
export type Config = Source & Mixin;
@@ -106,9 +131,11 @@ const dir = `${_dirname}/../../../.config`;
/**
* Path of configuration file
*/
const path = process.env.NODE_ENV === 'test'
? `${dir}/test.yml`
: `${dir}/default.yml`;
const path = process.env.MISSKEY_CONFIG_YML
? resolve(dir, process.env.MISSKEY_CONFIG_YML)
: process.env.NODE_ENV === 'test'
? resolve(dir, 'test.yml')
: resolve(dir, 'default.yml');
export function loadConfig() {
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
@@ -151,6 +178,8 @@ export function loadConfig() {
: null;
if (!config.redis.prefix) config.redis.prefix = mixin.host;
if (config.redisForPubsub == null) config.redisForPubsub = config.redis;
if (config.redisForJobQueue == null) config.redisForJobQueue = config.redis;
return Object.assign(config, mixin);
}

View File

@@ -56,6 +56,11 @@ export const FILE_TYPE_BROWSERSAFE = [
'audio/webm',
'audio/aac',
// see https://github.com/misskey-dev/misskey/pull/10686
'audio/flac',
'audio/wav',
// backward compatibility
'audio/x-flac',
'audio/vnd.wave',
];

View File

@@ -0,0 +1,347 @@
import { Inject, Injectable } from '@nestjs/common';
import { IsNull, In, MoreThan, Not } from 'typeorm';
import { bindThis } from '@/decorators.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
import type { User } from '@/models/entities/User.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { QueueService } from '@/core/QueueService.js';
import { RelayService } from '@/core/RelayService.js';
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 { CacheService } from '@/core/CacheService.js';
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { MetaService } from '@/core/MetaService.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
@Injectable()
export class AccountMoveService {
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
@Inject(DI.blockingsRepository)
private blockingsRepository: BlockingsRepository,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
@Inject(DI.userListJoiningsRepository)
private userListJoiningsRepository: UserListJoiningsRepository,
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
private userEntityService: UserEntityService,
private idService: IdService,
private apPersonService: ApPersonService,
private apRendererService: ApRendererService,
private apDeliverManagerService: ApDeliverManagerService,
private globalEventService: GlobalEventService,
private proxyAccountService: ProxyAccountService,
private perUserFollowingChart: PerUserFollowingChart,
private federatedInstanceService: FederatedInstanceService,
private instanceChart: InstanceChart,
private metaService: MetaService,
private relayService: RelayService,
private cacheService: CacheService,
private queueService: QueueService,
) {
}
/**
* Move a local account to a new account.
*
* After delivering Move activity, its local followers unfollow the old account and then follow the new one.
*/
@bindThis
public async moveFromLocal(src: LocalUser, dst: LocalUser | RemoteUser): Promise<unknown> {
const srcUri = this.userEntityService.getUserUri(src);
const dstUri = this.userEntityService.getUserUri(dst);
// add movedToUri to indicate that the user has moved
const update = {} as Partial<LocalUser>;
update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
update.movedToUri = dstUri;
update.movedAt = new Date();
await this.usersRepository.update(src.id, update);
Object.assign(src, update);
// Update cache
this.cacheService.uriPersonCache.set(srcUri, src);
const srcPerson = await this.apRendererService.renderPerson(src);
const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src));
await this.apDeliverManagerService.deliverToFollowers(src, updateAct);
this.relayService.deliverToRelays(src, updateAct);
// Deliver Move activity to the followers of the old account
const moveAct = this.apRendererService.addContext(this.apRendererService.renderMove(src, dst));
await this.apDeliverManagerService.deliverToFollowers(src, moveAct);
// Publish meUpdated event
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
// Unfollow after 24 hours
const followings = await this.followingsRepository.findBy({
followerId: src.id,
});
this.queueService.createDelayedUnfollowJob(followings.map(following => ({
from: { id: src.id },
to: { id: following.followeeId },
})), process.env.NODE_ENV === 'test' ? 10000 : 1000 * 60 * 60 * 24);
await this.postMoveProcess(src, dst);
return iObj;
}
@bindThis
public async postMoveProcess(src: User, dst: User): Promise<void> {
// Copy blockings and mutings, and update lists
try {
await Promise.all([
this.copyBlocking(src, dst),
this.copyMutings(src, dst),
this.updateLists(src, dst),
]);
} catch {
/* skip if any error happens */
}
// follow the new account
const proxy = await this.proxyAccountService.fetch();
const followings = await this.followingsRepository.findBy({
followeeId: src.id,
followerHost: IsNull(), // follower is local
followerId: proxy ? Not(proxy.id) : undefined,
});
const followJobs = followings.map(following => ({
from: { id: following.followerId },
to: { id: dst.id },
})) as RelationshipJobData[];
// Decrease following count instead of unfollowing.
try {
await this.adjustFollowingCounts(followJobs.map(job => job.from.id), src);
} catch {
/* skip if any error happens */
}
// Should be queued because this can cause a number of follow per one move.
this.queueService.createFollowJob(followJobs);
}
@bindThis
public async copyBlocking(src: ThinUser, dst: ThinUser): Promise<void> {
// Followers shouldn't overlap with blockers, but the destination account, different from the blockee (i.e., old account), may have followed the local user before moving.
// So block the destination account here.
const srcBlockings = await this.blockingsRepository.findBy({ blockeeId: src.id });
const dstBlockings = await this.blockingsRepository.findBy({ blockeeId: dst.id });
const blockerIds = dstBlockings.map(blocking => blocking.blockerId);
// reblock the destination account
const blockJobs: RelationshipJobData[] = [];
for (const blocking of srcBlockings) {
if (blockerIds.includes(blocking.blockerId)) continue; // skip if already blocked
blockJobs.push({ from: { id: blocking.blockerId }, to: { id: dst.id } });
}
// no need to unblock the old account because it may be still functional
this.queueService.createBlockJob(blockJobs);
}
@bindThis
public async copyMutings(src: ThinUser, dst: ThinUser): Promise<void> {
// Insert new mutings with the same values except mutee
const oldMutings = await this.mutingsRepository.findBy([
{ muteeId: src.id, expiresAt: IsNull() },
{ muteeId: src.id, expiresAt: MoreThan(new Date()) },
]);
if (oldMutings.length === 0) return;
// Check if the destination account is already indefinitely muted by the muter
const existingMutingsMuterUserIds = await this.mutingsRepository.findBy(
{ muteeId: dst.id, expiresAt: IsNull() },
).then(mutings => mutings.map(muting => muting.muterId));
const newMutings: Map<string, { muterId: string; muteeId: string; createdAt: Date; expiresAt: Date | null; }> = new Map();
// 重複しないようにIDを生成
const genId = (): string => {
let id: string;
do {
id = this.idService.genId();
} while (newMutings.has(id));
return id;
};
for (const muting of oldMutings) {
if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely
newMutings.set(genId(), {
...muting,
createdAt: new Date(),
muteeId: dst.id,
});
}
const arrayToInsert = Array.from(newMutings.entries()).map(entry => ({ ...entry[1], id: entry[0] }));
await this.mutingsRepository.insert(arrayToInsert);
}
/**
* Update lists while moving accounts.
* - No removal of the old account from the lists
* - Users number limit is not checked
*
* @param src ThinUser (old account)
* @param dst User (new account)
* @returns Promise<void>
*/
@bindThis
public async updateLists(src: ThinUser, dst: User): Promise<void> {
// Return if there is no list to be updated.
const oldJoinings = await this.userListJoiningsRepository.find({
where: {
userId: src.id,
},
});
if (oldJoinings.length === 0) return;
const existingUserListIds = await this.userListJoiningsRepository.find({
where: {
userId: dst.id,
},
}).then(joinings => joinings.map(joining => joining.userListId));
const newJoinings: Map<string, { createdAt: Date; userId: string; userListId: string; }> = new Map();
// 重複しないようにIDを生成
const genId = (): string => {
let id: string;
do {
id = this.idService.genId();
} while (newJoinings.has(id));
return id;
};
for (const joining of oldJoinings) {
if (existingUserListIds.includes(joining.userListId)) continue; // skip if dst exists in this user's list
newJoinings.set(genId(), {
createdAt: new Date(),
userId: dst.id,
userListId: joining.userListId,
});
}
const arrayToInsert = Array.from(newJoinings.entries()).map(entry => ({ ...entry[1], id: entry[0] }));
await this.userListJoiningsRepository.insert(arrayToInsert);
// 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 } }]);
}
}
}
@bindThis
private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: User): Promise<void> {
if (localFollowerIds.length === 0) return;
// Set the old account's following and followers counts to 0.
await this.usersRepository.update({ id: oldAccount.id }, { followersCount: 0, followingCount: 0 });
// Decrease following counts of local followers by 1.
await this.usersRepository.decrement({ id: In(localFollowerIds) }, 'followingCount', 1);
// Decrease follower counts of local followees by 1.
const oldFollowings = await this.followingsRepository.findBy({ followerId: oldAccount.id });
if (oldFollowings.length > 0) {
await this.usersRepository.decrement({ id: In(oldFollowings.map(following => following.followeeId)) }, 'followersCount', 1);
}
// Update instance stats by decreasing remote followers count by the number of local followers who were following the old account.
if (this.userEntityService.isRemoteUser(oldAccount)) {
this.federatedInstanceService.fetch(oldAccount.host).then(async i => {
this.instancesRepository.decrement({ id: i.id }, 'followersCount', localFollowerIds.length);
if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
this.instanceChart.updateFollowers(i.host, false);
}
});
}
// FIXME: expensive?
for (const followerId of localFollowerIds) {
this.perUserFollowingChart.update({ id: followerId, host: null }, oldAccount, false);
}
}
/**
* dstユーザーのalsoKnownAsをfetchPersonしていき、本当にmovedToUrlをdstに指定するユーザーが存在するのかを調べる
*
* @param dst movedToUrlを指定するユーザー
* @param check
* @param instant checkがtrueであるユーザーが最初に見つかったら即座にreturnするかどうか
* @returns Promise<LocalUser | RemoteUser | null>
*/
@bindThis
public async validateAlsoKnownAs(
dst: LocalUser | RemoteUser,
check: (oldUser: LocalUser | RemoteUser | null, newUser: LocalUser | RemoteUser) => boolean | Promise<boolean> = () => true,
instant = false,
): Promise<LocalUser | RemoteUser | null> {
let resultUser: LocalUser | RemoteUser | null = null;
if (this.userEntityService.isRemoteUser(dst)) {
if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
await this.apPersonService.updatePerson(dst.uri);
}
dst = await this.apPersonService.fetchPerson(dst.uri) ?? dst;
}
if (!dst.alsoKnownAs || dst.alsoKnownAs.length === 0) return null;
const dstUri = this.userEntityService.getUserUri(dst);
for (const srcUri of dst.alsoKnownAs) {
try {
let src = await this.apPersonService.fetchPerson(srcUri);
if (!src) continue; // oldAccountを探してもこのサーバーに存在しない場合はフォロー関係もないということなのでスルー
if (this.userEntityService.isRemoteUser(dst)) {
if ((new Date()).getTime() - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
await this.apPersonService.updatePerson(srcUri);
}
src = await this.apPersonService.fetchPerson(srcUri) ?? src;
}
if (src.movedToUri === dstUri) {
if (await check(resultUser, src)) {
resultUser = src;
}
if (instant && resultUser) return resultUser;
}
} catch {
/* skip if any error happens */
}
}
return resultUser;
}
}

View File

@@ -29,7 +29,7 @@ export class AccountUpdateService {
public async publishToFollowers(userId: User['id']) {
const user = await this.usersRepository.findOneBy({ id: userId });
if (user == null) throw new Error('user not found');
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
if (this.userEntityService.isLocalUser(user)) {
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));

View File

@@ -64,6 +64,7 @@ export const ACHIEVEMENT_TYPES = [
'iLoveMisskey',
'foundTreasure',
'client30min',
'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',

View File

@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { Antenna } from '@/models/entities/Antenna.js';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';
@@ -27,8 +27,8 @@ export class AntennaService implements OnApplicationShutdown {
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
@@ -52,12 +52,12 @@ export class AntennaService implements OnApplicationShutdown {
this.antennasFetched = false;
this.antennas = [];
this.redisSubscriber.on('message', this.onRedisMessage);
this.redisForSub.on('message', this.onRedisMessage);
}
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onRedisMessage);
this.redisForSub.off('message', this.onRedisMessage);
}
@bindThis
@@ -91,14 +91,24 @@ export class AntennaService implements OnApplicationShutdown {
}
@bindThis
public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise<void> {
this.redisClient.xadd(
`antennaTimeline:${antenna.id}`,
'MAXLEN', '~', '200',
`${this.idService.parse(note.id).date.getTime()}-*`,
'note', note.id);
this.globalEventService.publishAntennaStream(antenna.id, 'note', note);
public async addNoteToAntennas(note: Note, noteUser: { id: User['id']; username: string; host: string | null; }): Promise<void> {
const antennas = await this.getAntennas();
const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const)));
const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna);
const redisPipeline = this.redisClient.pipeline();
for (const antenna of matchedAntennas) {
redisPipeline.xadd(
`antennaTimeline:${antenna.id}`,
'MAXLEN', '~', '200',
'*',
'note', note.id);
this.globalEventService.publishAntennaStream(antenna.id, 'note', note);
}
redisPipeline.exec();
}
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている

View File

@@ -1,7 +1,7 @@
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';

View File

@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { LocalUser, User } from '@/models/entities/User.js';
@@ -27,8 +27,8 @@ export class CacheService implements OnApplicationShutdown {
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -116,7 +116,7 @@ export class CacheService implements OnApplicationShutdown {
fromRedisConverter: (value) => new Set(JSON.parse(value)),
});
this.redisSubscriber.on('message', this.onMessage);
this.redisForSub.on('message', this.onMessage);
}
@bindThis
@@ -167,6 +167,6 @@ export class CacheService implements OnApplicationShutdown {
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onMessage);
this.redisForSub.off('message', this.onMessage);
}
}

View File

@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { AccountMoveService } from './AccountMoveService.js';
import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
import { AntennaService } from './AntennaService.js';
@@ -49,6 +50,7 @@ import { WebhookService } from './WebhookService.js';
import { ProxyAccountService } from './ProxyAccountService.js';
import { UtilityService } from './UtilityService.js';
import { FileInfoService } from './FileInfoService.js';
import { SearchService } from './SearchService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js';
import NotesChart from './chart/charts/notes.js';
@@ -119,6 +121,7 @@ import type { Provider } from '@nestjs/common';
//#region 文字列ベースでのinjection用(循環参照対応のため)
const $LoggerService: Provider = { provide: 'LoggerService', useExisting: LoggerService };
const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
@@ -169,6 +172,8 @@ const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', u
const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
const $NotesChart: Provider = { provide: 'NotesChart', useExisting: NotesChart };
@@ -242,6 +247,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
],
providers: [
LoggerService,
AccountMoveService,
AccountUpdateService,
AiService,
AntennaService,
@@ -292,6 +298,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
WebhookService,
UtilityService,
FileInfoService,
SearchService,
ChartLoggerService,
FederationChart,
NotesChart,
@@ -359,6 +366,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
//#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService,
$AccountMoveService,
$AccountUpdateService,
$AiService,
$AntennaService,
@@ -409,6 +417,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$WebhookService,
$UtilityService,
$FileInfoService,
$SearchService,
$ChartLoggerService,
$FederationChart,
$NotesChart,
@@ -477,6 +486,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
exports: [
QueueModule,
LoggerService,
AccountMoveService,
AccountUpdateService,
AiService,
AntennaService,
@@ -527,6 +537,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
WebhookService,
UtilityService,
FileInfoService,
SearchService,
FederationChart,
NotesChart,
UsersChart,
@@ -593,6 +604,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
//#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService,
$AccountMoveService,
$AccountUpdateService,
$AiService,
$AntennaService,
@@ -643,6 +655,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$WebhookService,
$UtilityService,
$FileInfoService,
$SearchService,
$FederationChart,
$NotesChart,
$UsersChart,

View File

@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, In, IsNull } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
@@ -13,6 +13,7 @@ import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
import { UtilityService } from '@/core/UtilityService.js';
import type { Config } from '@/config.js';
import { query } from '@/misc/prelude/url.js';
import type { Serialized } from '@/server/api/stream/types.js';
@Injectable()
export class CustomEmojiService {
@@ -43,8 +44,14 @@ export class CustomEmojiService {
lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))),
toRedisConverter: (value) => JSON.stringify(value.values()),
fromRedisConverter: (value) => new Map(JSON.parse(value).map((x: Emoji) => [x.name, x])), // TODO: Date型の変換
toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
fromRedisConverter: (value) => {
if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
return new Map(JSON.parse(value).map((x: Serialized<Emoji>) => [x.name, {
...x,
updatedAt: x.updatedAt ? new Date(x.updatedAt) : null,
}]));
},
});
}
@@ -190,6 +197,22 @@ export class CustomEmojiService {
emojis: await this.emojiEntityService.packDetailedMany(ids),
});
}
@bindThis
public async setLicenseBulk(ids: Emoji['id'][], license: string | null) {
await this.emojisRepository.update({
id: In(ids),
}, {
updatedAt: new Date(),
license: license,
});
this.localEmojisCache.refresh();
this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: await this.emojiEntityService.packDetailedMany(ids),
});
}
@bindThis
public async delete(id: Emoji['id']) {
@@ -267,16 +290,7 @@ export class CustomEmojiService {
const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull);
if (emoji == null) return null;
const isLocal = emoji.host == null;
const emojiUrl = emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl してるのは後方互換性のためpublicUrlはstringなので??はだめ)
const url = isLocal
? emojiUrl
: this.config.proxyRemoteFiles
? `${this.config.mediaProxy}/emoji.webp?${query({ url: emojiUrl })}`
: emojiUrl;
return url;
return emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl してるのは後方互換性のためpublicUrlはstringなので??はだめ)
}
/**

View File

@@ -86,9 +86,13 @@ export class DownloadService {
const contentDisposition = res.headers['content-disposition'];
if (contentDisposition != null) {
const parsed = parse(contentDisposition);
if (parsed.parameters.filename) {
filename = parsed.parameters.filename;
try {
const parsed = parse(contentDisposition);
if (parsed.parameters.filename) {
filename = parsed.parameters.filename;
}
} catch (e) {
this.logger.warn(`Failed to parse content-disposition: ${contentDisposition}`, { stack: e });
}
}
}).on('downloadProgress', (progress: Got.Progress) => {

View File

@@ -59,6 +59,8 @@ type AddFileArgs = {
uri?: string | null;
/** Mark file as sensitive */
sensitive?: boolean | null;
/** Extension to force */
ext?: string | null;
requestIp?: string | null;
requestHeaders?: Record<string, string> | null;
@@ -125,7 +127,7 @@ export class DriveService {
/***
* Save file
* @param path Path for original
* @param name Name for original
* @param name Name for original (should be extention corrected)
* @param type Content-Type for original
* @param hash Hash for original
* @param size Size for original
@@ -151,7 +153,7 @@ export class DriveService {
}
// 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、
// 許可されているファイル形式でしか拡張子をつけない
// 許可されているファイル形式でしかURLに拡張子をつけない
if (!FILE_TYPE_BROWSERSAFE.includes(type)) {
ext = '';
}
@@ -173,7 +175,7 @@ export class DriveService {
//#region Uploads
this.registerLogger.info(`uploading original: ${key}`);
const uploads = [
this.upload(key, fs.createReadStream(path), type, ext, name),
this.upload(key, fs.createReadStream(path), type, null, name),
];
if (alts.webpublic) {
@@ -189,7 +191,7 @@ export class DriveService {
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext));
uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext, `${name}.thumbnail`));
}
await Promise.all(uploads);
@@ -396,8 +398,9 @@ export class DriveService {
);
}
// Expire oldest file (without avatar or banner) of remote user
@bindThis
private async deleteOldFile(user: RemoteUser) {
private async expireOldFile(user: RemoteUser, driveCapacity: number) {
const q = this.driveFilesRepository.createQueryBuilder('file')
.where('file.userId = :userId', { userId: user.id })
.andWhere('file.isLink = FALSE');
@@ -410,12 +413,17 @@ export class DriveService {
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
}
//This selete is hard coded, be careful if change database schema
q.addSelect('SUM("file"."size") OVER (ORDER BY "file"."id" DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)', 'acc_usage');
q.orderBy('file.id', 'ASC');
const oldFile = await q.getOne();
const fileList = await q.getRawMany();
const exceedFileIds = fileList.filter((x: any) => x.acc_usage > driveCapacity).map((x: any) => x.file_id);
if (oldFile) {
this.deleteFile(oldFile, true);
for (const fileId of exceedFileIds) {
const file = await this.driveFilesRepository.findOneBy({ id: fileId });
if (file == null) continue;
this.deleteFile(file, true);
}
}
@@ -437,6 +445,7 @@ export class DriveService {
sensitive = null,
requestIp = null,
requestHeaders = null,
ext = null,
}: AddFileArgs): Promise<DriveFile> {
let skipNsfwCheck = false;
const instance = await this.metaService.fetch();
@@ -468,7 +477,7 @@ export class DriveService {
// DriveFile.nameは256文字, validateFileNameは200文字制限であるため、
// extを付加してデータベースの文字数制限に当たることはまずない
(name && this.driveFileEntityService.validateFileName(name)) ? name : 'untitled',
info.type.ext,
ext ?? info.type.ext,
);
if (user && !force) {
@@ -489,22 +498,19 @@ export class DriveService {
//#region Check drive usage
if (user && !isLink) {
const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
const isLocalUser = this.userEntityService.isLocalUser(user);
const policies = await this.roleService.getUserPolicies(user.id);
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`);
// If usage limit exceeded
if (usage + info.size > driveCapacity) {
if (this.userEntityService.isLocalUser(user)) {
if (driveCapacity < usage + info.size) {
if (isLocalUser) {
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
} else {
// (アバターまたはバナーを含まず)最も古いファイルを削除する
this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser);
}
await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser, driveCapacity - info.size);
}
}
//#endregion

View File

@@ -1,7 +1,8 @@
import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import type { InstancesRepository } from '@/models/index.js';
import type { Instance } from '@/models/entities/Instance.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js';
@@ -9,23 +10,41 @@ import { bindThis } from '@/decorators.js';
@Injectable()
export class FederatedInstanceService {
private cache: MemoryKVCache<Instance>;
public federatedInstanceCache: RedisKVCache<Instance | null>;
constructor(
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
private utilityService: UtilityService,
private idService: IdService,
) {
this.cache = new MemoryKVCache<Instance>(1000 * 60 * 60);
this.federatedInstanceCache = new RedisKVCache<Instance | null>(this.redisClient, 'federatedInstance', {
lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: (key) => this.instancesRepository.findOneBy({ host: key }),
toRedisConverter: (value) => JSON.stringify(value),
fromRedisConverter: (value) => {
const parsed = JSON.parse(value);
if (parsed == null) return null;
return {
...parsed,
firstRetrievedAt: new Date(parsed.firstRetrievedAt),
latestRequestReceivedAt: parsed.latestRequestReceivedAt ? new Date(parsed.latestRequestReceivedAt) : null,
infoUpdatedAt: parsed.infoUpdatedAt ? new Date(parsed.infoUpdatedAt) : null,
};
},
});
}
@bindThis
public async fetch(host: string): Promise<Instance> {
host = this.utilityService.toPuny(host);
const cached = this.cache.get(host);
const cached = await this.federatedInstanceCache.get(host);
if (cached) return cached;
const index = await this.instancesRepository.findOneBy({ host });
@@ -37,24 +56,27 @@ export class FederatedInstanceService {
firstRetrievedAt: new Date(),
}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));
this.cache.set(host, i);
this.federatedInstanceCache.set(host, i);
return i;
} else {
this.cache.set(host, index);
this.federatedInstanceCache.set(host, index);
return index;
}
}
@bindThis
public async updateCachePartial(host: string, data: Partial<Instance>): Promise<void> {
host = this.utilityService.toPuny(host);
public async update(id: Instance['id'], data: Partial<Instance>): Promise<void> {
const result = await this.instancesRepository.createQueryBuilder().update()
.set(data)
.where('id = :id', { id })
.returning('*')
.execute()
.then((response) => {
return response.raw[0];
});
const updated = result.raw[0];
const cached = this.cache.get(host);
if (cached == null) return;
this.cache.set(host, {
...cached,
...data,
});
this.federatedInstanceCache.set(updated.host, updated);
}
}

View File

@@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
import { LoggerService } from '@/core/LoggerService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { DOMWindow } from 'jsdom';
type NodeInfo = {
@@ -42,6 +43,7 @@ export class FetchInstanceMetadataService {
private appLockService: AppLockService,
private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
private federatedInstanceService: FederatedInstanceService,
) {
this.logger = this.loggerService.getLogger('metadata', 'cyan');
}
@@ -96,7 +98,7 @@ export class FetchInstanceMetadataService {
if (favicon) updates.faviconUrl = favicon;
if (themeColor) updates.themeColor = themeColor;
await this.instancesRepository.update(instance.id, updates);
await this.federatedInstanceService.update(instance.id, updates);
this.logger.succ(`Successfuly updated metadata of ${instance.host}`);
} catch (e) {

View File

@@ -5,7 +5,7 @@ import * as stream from 'node:stream';
import * as util from 'node:util';
import { Injectable } from '@nestjs/common';
import { FSWatcher } from 'chokidar';
import { fileTypeFromFile } from 'file-type';
import * as fileType from 'file-type';
import FFmpeg from 'fluent-ffmpeg';
import isSvg from 'is-svg';
import probeImageSize from 'probe-image-size';
@@ -301,21 +301,34 @@ export class FileInfoService {
return fs.promises.access(path).then(() => true, () => false);
}
@bindThis
public fixMime(mime: string | fileType.MimeType): string {
// see https://github.com/misskey-dev/misskey/pull/10686
if (mime === "audio/x-flac") {
return "audio/flac";
}
if (mime === "audio/vnd.wave") {
return "audio/wav";
}
return mime;
}
/**
* Detect MIME Type and extension
*/
@bindThis
public async detectType(path: string): Promise<{
mime: string;
ext: string | null;
}> {
mime: string;
ext: string | null;
}> {
// Check 0 byte
const fileSize = await this.getFileSize(path);
if (fileSize === 0) {
return TYPE_OCTET_STREAM;
}
const type = await fileTypeFromFile(path);
const type = await fileType.fileTypeFromFile(path);
if (type) {
// XMLはSVGかもしれない
@@ -324,7 +337,7 @@ export class FileInfoService {
}
return {
mime: type.mime,
mime: this.fixMime(type.mime),
ext: type.ext,
};
}

View File

@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js';
import type { UserList } from '@/models/entities/UserList.js';
@@ -14,11 +14,13 @@ import type {
MainStreamTypes,
NoteStreamTypes,
UserListStreamTypes,
RoleTimelineStreamTypes,
} from '@/server/api/stream/types.js';
import type { Packed } from '@/misc/json-schema.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js';
import { Role } from '@/models';
@Injectable()
export class GlobalEventService {
@@ -26,8 +28,8 @@ export class GlobalEventService {
@Inject(DI.config)
private config: Config,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.redisForPub)
private redisForPub: Redis.Redis,
) {
}
@@ -37,7 +39,7 @@ export class GlobalEventService {
{ type: type, body: null } :
{ type: type, body: value };
this.redisClient.publish(this.config.host, JSON.stringify({
this.redisForPub.publish(this.config.host, JSON.stringify({
channel: channel,
message: message,
}));
@@ -81,6 +83,11 @@ export class GlobalEventService {
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
}
@bindThis
public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: Role['id'], type: K, value?: RoleTimelineStreamTypes[K]): void {
this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value);
}
@bindThis
public publishNotesStream(note: Packed<'Note'>): void {
this.publish('notesStream', null, note);

View File

@@ -3,10 +3,11 @@ import { ulid } from 'ulid';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { genAid, parseAid } from '@/misc/id/aid.js';
import { genMeid } from '@/misc/id/meid.js';
import { genMeidg } from '@/misc/id/meidg.js';
import { genMeid, parseMeid } from '@/misc/id/meid.js';
import { genMeidg, parseMeidg } from '@/misc/id/meidg.js';
import { genObjectId } from '@/misc/id/object-id.js';
import { bindThis } from '@/decorators.js';
import { parseUlid } from '@/misc/id/ulid.js';
@Injectable()
export class IdService {
@@ -37,11 +38,10 @@ export class IdService {
public parse(id: string): { date: Date; } {
switch (this.method) {
case 'aid': return parseAid(id);
// TODO
//case 'meid':
//case 'meidg':
//case 'ulid':
//case 'objectid':
case 'objectid':
case 'meid': return parseMeid(id);
case 'meidg': return parseMeidg(id);
case 'ulid': return parseUlid(id);
default: throw new Error('unrecognized id generation method');
}
}

View File

@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { Meta } from '@/models/entities/Meta.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
@@ -14,8 +14,8 @@ export class MetaService implements OnApplicationShutdown {
private intervalId: NodeJS.Timer;
constructor(
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,
@Inject(DI.db)
private db: DataSource,
@@ -33,7 +33,7 @@ export class MetaService implements OnApplicationShutdown {
}, 1000 * 60 * 5);
}
this.redisSubscriber.on('message', this.onMessage);
this.redisForSub.on('message', this.onMessage);
}
@bindThis
@@ -122,6 +122,6 @@ export class MetaService implements OnApplicationShutdown {
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
clearInterval(this.intervalId);
this.redisSubscriber.off('message', this.onMessage);
this.redisForSub.off('message', this.onMessage);
}
}

View File

@@ -1,7 +1,7 @@
import { setImmediate } from 'node:timers/promises';
import * as mfm from 'mfm-js';
import { In, DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { extractMentions } from '@/misc/extract-mentions.js';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
@@ -46,6 +46,7 @@ import { bindThis } from '@/decorators.js';
import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js';
import { SearchService } from '@/core/SearchService.js';
const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
@@ -198,6 +199,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private apRendererService: ApRendererService,
private roleService: RoleService,
private metaService: MetaService,
private searchService: SearchService,
private notesChart: NotesChart,
private perUserNotesChart: PerUserNotesChart,
private activeUsersChart: ActiveUsersChart,
@@ -329,7 +331,7 @@ export class NoteCreateService implements OnApplicationShutdown {
this.redisClient.xadd(
`channelTimeline:${data.channel.id}`,
'MAXLEN', '~', '1000',
`${this.idService.parse(note.id).date.getTime()}-*`,
'*',
'note', note.id);
}
@@ -493,14 +495,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}
});
// Antenna
for (const antenna of (await this.antennaService.getAntennas())) {
this.antennaService.checkHitAntenna(antenna, note, user).then(hit => {
if (hit) {
this.antennaService.addNoteToAntenna(antenna, note, user);
}
});
}
this.antennaService.addNoteToAntennas(note, user);
if (data.reply) {
this.saveReply(data.reply, note);
@@ -554,6 +549,8 @@ export class NoteCreateService implements OnApplicationShutdown {
this.globalEventService.publishNotesStream(noteObj);
this.roleService.addNoteToRoleTimeline(noteObj);
this.webhookService.getActiveWebhooks().then(webhooks => {
webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
for (const webhook of webhooks) {
@@ -733,17 +730,9 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis
private index(note: Note) {
if (note.text == null || this.config.elasticsearch == null) return;
/*
es!.index({
index: this.config.elasticsearch.index ?? 'misskey_note',
id: note.id.toString(),
body: {
text: normalizeForSearch(note.text),
userId: note.userId,
userHost: note.userHost,
},
});*/
if (note.text == null && note.cw == null) return;
this.searchService.indexNote(note);
}
@bindThis

View File

@@ -1,5 +1,5 @@
import { setTimeout } from 'node:timers/promises';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
@@ -66,6 +66,7 @@ export class NotificationService implements OnApplicationShutdown {
@bindThis
private postReadAllNotifications(userId: User['id']) {
this.globalEventService.publishMainStream(userId, 'readAllNotifications');
this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined);
}
@bindThis
@@ -99,7 +100,7 @@ export class NotificationService implements OnApplicationShutdown {
const redisIdPromise = this.redisClient.xadd(
`notificationTimeline:${notifieeId}`,
'MAXLEN', '~', '300',
`${this.idService.parse(notification.id).date.getTime()}-*`,
'*',
'data', JSON.stringify(notification));
const packed = await this.notificationEntityService.pack(notification, notifieeId, {});
@@ -110,7 +111,7 @@ export class NotificationService implements OnApplicationShutdown {
// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
setTimeout(2000, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
if (latestReadNotificationId && (latestReadNotificationId >= await redisIdPromise)) return;
if (latestReadNotificationId && (latestReadNotificationId >= (await redisIdPromise)!)) return;
this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed);
this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);

View File

@@ -1,12 +1,14 @@
import { Inject, Injectable } from '@nestjs/common';
import push from 'web-push';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { Packed } from '@/misc/json-schema';
import { getNoteSummary } from '@/misc/get-note-summary.js';
import type { SwSubscriptionsRepository } from '@/models/index.js';
import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
import { MetaService } from '@/core/MetaService.js';
import { bindThis } from '@/decorators.js';
import { RedisKVCache } from '@/misc/cache.js';
// Defined also packages/sw/types.ts#L13
type PushNotificationsTypes = {
@@ -15,6 +17,7 @@ type PushNotificationsTypes = {
antenna: { id: string, name: string };
note: Packed<'Note'>;
};
'readAllNotifications': undefined;
};
// Reduce length because push message servers have character limits
@@ -40,15 +43,27 @@ function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: Pus
@Injectable()
export class PushNotificationService {
private subscriptionsCache: RedisKVCache<SwSubscription[]>;
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.swSubscriptionsRepository)
private swSubscriptionsRepository: SwSubscriptionsRepository,
private metaService: MetaService,
) {
this.subscriptionsCache = new RedisKVCache<SwSubscription[]>(this.redisClient, 'userSwSubscriptions', {
lifetime: 1000 * 60 * 60 * 1, // 1h
memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }),
toRedisConverter: (value) => JSON.stringify(value),
fromRedisConverter: (value) => JSON.parse(value),
});
}
@bindThis
@@ -62,12 +77,13 @@ export class PushNotificationService {
meta.swPublicKey,
meta.swPrivateKey);
// Fetch
const subscriptions = await this.swSubscriptionsRepository.findBy({
userId: userId,
});
const subscriptions = await this.subscriptionsCache.fetch(userId);
for (const subscription of subscriptions) {
if ([
'readAllNotifications',
].includes(type) && !subscription.sendReadMessage) continue;
const pushSubscription = {
endpoint: subscription.endpoint,
keys: {

View File

@@ -3,18 +3,18 @@ import Bull from 'bull';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { Provider } from '@nestjs/common';
import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from '../queue/types.js';
import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData, DbJobMap } from '../queue/types.js';
function q<T>(config: Config, name: string, limitPerSec = -1) {
return new Bull<T>(name, {
redis: {
port: config.redis.port,
host: config.redis.host,
family: config.redis.family == null ? 0 : config.redis.family,
password: config.redis.pass,
db: config.redis.db ?? 0,
port: config.redisForJobQueue.port,
host: config.redisForJobQueue.host,
family: config.redisForJobQueue.family == null ? 0 : config.redisForJobQueue.family,
password: config.redisForJobQueue.pass,
db: config.redisForJobQueue.db ?? 0,
},
prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue',
prefix: config.redisForJobQueue.prefix ? `${config.redisForJobQueue.prefix}:queue` : 'queue',
limiter: limitPerSec > 0 ? {
max: limitPerSec,
duration: 1000,
@@ -41,7 +41,8 @@ export type SystemQueue = Bull.Queue<Record<string, unknown>>;
export type EndedPollNotificationQueue = Bull.Queue<EndedPollNotificationJobData>;
export type DeliverQueue = Bull.Queue<DeliverJobData>;
export type InboxQueue = Bull.Queue<InboxJobData>;
export type DbQueue = Bull.Queue<DbJobData>;
export type DbQueue = Bull.Queue<DbJobData<keyof DbJobMap>>;
export type RelationshipQueue = Bull.Queue<RelationshipJobData>;
export type ObjectStorageQueue = Bull.Queue<ObjectStorageJobData>;
export type WebhookDeliverQueue = Bull.Queue<WebhookDeliverJobData>;
@@ -75,6 +76,12 @@ const $db: Provider = {
inject: [DI.config],
};
const $relationship: Provider = {
provide: 'queue:relationship',
useFactory: (config: Config) => q(config, 'relationship', config.relashionshipJobPerSec ?? 64),
inject: [DI.config],
};
const $objectStorage: Provider = {
provide: 'queue:objectStorage',
useFactory: (config: Config) => q(config, 'objectStorage'),
@@ -96,6 +103,7 @@ const $webhookDeliver: Provider = {
$deliver,
$inbox,
$db,
$relationship,
$objectStorage,
$webhookDeliver,
],
@@ -105,6 +113,7 @@ const $webhookDeliver: Provider = {
$deliver,
$inbox,
$db,
$relationship,
$objectStorage,
$webhookDeliver,
],

View File

@@ -6,9 +6,10 @@ import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
import type { ThinUser } from '../queue/types.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
import type { DbJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
import type httpSignature from '@peertube/http-signature';
import Bull from 'bull';
@Injectable()
export class QueueService {
@@ -21,6 +22,7 @@ export class QueueService {
@Inject('queue:deliver') public deliverQueue: DeliverQueue,
@Inject('queue:inbox') public inboxQueue: InboxQueue,
@Inject('queue:db') public dbQueue: DbQueue,
@Inject('queue:relationship') public relationshipQueue: RelationshipQueue,
@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
) {}
@@ -56,7 +58,7 @@ export class QueueService {
activity: activity,
signature,
};
return this.inboxQueue.add(data, {
attempts: this.config.inboxJobMaxAttempts ?? 8,
timeout: 5 * 60 * 1000, // 5min
@@ -71,7 +73,7 @@ export class QueueService {
@bindThis
public createDeleteDriveFilesJob(user: ThinUser) {
return this.dbQueue.add('deleteDriveFiles', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -81,7 +83,7 @@ export class QueueService {
@bindThis
public createExportCustomEmojisJob(user: ThinUser) {
return this.dbQueue.add('exportCustomEmojis', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -91,7 +93,7 @@ export class QueueService {
@bindThis
public createExportNotesJob(user: ThinUser) {
return this.dbQueue.add('exportNotes', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -101,7 +103,7 @@ export class QueueService {
@bindThis
public createExportFavoritesJob(user: ThinUser) {
return this.dbQueue.add('exportFavorites', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -111,7 +113,7 @@ export class QueueService {
@bindThis
public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) {
return this.dbQueue.add('exportFollowing', {
user: user,
user: { id: user.id },
excludeMuting,
excludeInactive,
}, {
@@ -123,7 +125,7 @@ export class QueueService {
@bindThis
public createExportMuteJob(user: ThinUser) {
return this.dbQueue.add('exportMuting', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -133,7 +135,7 @@ export class QueueService {
@bindThis
public createExportBlockingJob(user: ThinUser) {
return this.dbQueue.add('exportBlocking', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -143,7 +145,7 @@ export class QueueService {
@bindThis
public createExportUserListsJob(user: ThinUser) {
return this.dbQueue.add('exportUserLists', {
user: user,
user: { id: user.id },
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -153,7 +155,7 @@ export class QueueService {
@bindThis
public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importFollowing', {
user: user,
user: { id: user.id },
fileId: fileId,
}, {
removeOnComplete: true,
@@ -161,10 +163,16 @@ export class QueueService {
});
}
@bindThis
public createImportFollowingToDbJob(user: ThinUser, targets: string[]) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel }));
return this.dbQueue.addBulk(jobs);
}
@bindThis
public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importMuting', {
user: user,
user: { id: user.id },
fileId: fileId,
}, {
removeOnComplete: true,
@@ -175,7 +183,7 @@ export class QueueService {
@bindThis
public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importBlocking', {
user: user,
user: { id: user.id },
fileId: fileId,
}, {
removeOnComplete: true,
@@ -183,10 +191,32 @@ export class QueueService {
});
}
@bindThis
public createImportBlockingToDbJob(user: ThinUser, targets: string[]) {
const jobs = targets.map(rel => this.generateToDbJobData('importBlockingToDb', { user, target: rel }));
return this.dbQueue.addBulk(jobs);
}
@bindThis
private generateToDbJobData<T extends 'importFollowingToDb' | 'importBlockingToDb', D extends DbJobData<T>>(name: T, data: D): {
name: string,
data: D,
opts: Bull.JobOptions,
} {
return {
name,
data,
opts: {
removeOnComplete: true,
removeOnFail: true,
},
};
}
@bindThis
public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importUserLists', {
user: user,
user: { id: user.id },
fileId: fileId,
}, {
removeOnComplete: true,
@@ -197,7 +227,7 @@ export class QueueService {
@bindThis
public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importCustomEmojis', {
user: user,
user: { id: user.id },
fileId: fileId,
}, {
removeOnComplete: true,
@@ -208,7 +238,7 @@ export class QueueService {
@bindThis
public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) {
return this.dbQueue.add('deleteAccount', {
user: user,
user: { id: user.id },
soft: opts.soft,
}, {
removeOnComplete: true,
@@ -216,6 +246,58 @@ export class QueueService {
});
}
@bindThis
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean }[]) {
const jobs = followings.map(rel => this.generateRelationshipJobData('follow', rel));
return this.relationshipQueue.addBulk(jobs);
}
@bindThis
public createUnfollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string }[]) {
const jobs = followings.map(rel => this.generateRelationshipJobData('unfollow', rel));
return this.relationshipQueue.addBulk(jobs);
}
@bindThis
public createDelayedUnfollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string }[], delay: number) {
const jobs = followings.map(rel => this.generateRelationshipJobData('unfollow', rel, { delay }));
return this.relationshipQueue.addBulk(jobs);
}
@bindThis
public createBlockJob(blockings: { from: ThinUser, to: ThinUser, silent?: boolean }[]) {
const jobs = blockings.map(rel => this.generateRelationshipJobData('block', rel));
return this.relationshipQueue.addBulk(jobs);
}
@bindThis
public createUnblockJob(blockings: { from: ThinUser, to: ThinUser, silent?: boolean }[]) {
const jobs = blockings.map(rel => this.generateRelationshipJobData('unblock', rel));
return this.relationshipQueue.addBulk(jobs);
}
@bindThis
private generateRelationshipJobData(name: 'follow' | 'unfollow' | 'block' | 'unblock', data: RelationshipJobData, opts: Bull.JobOptions = {}): {
name: string,
data: RelationshipJobData,
opts: Bull.JobOptions,
} {
return {
name,
data: {
from: { id: data.from.id },
to: { id: data.to.id },
silent: data.silent,
requestId: data.requestId,
},
opts: {
removeOnComplete: true,
removeOnFail: true,
...opts,
},
};
}
@bindThis
public createDeleteObjectStorageFileJob(key: string) {
return this.objectStorageQueue.add('deleteFile', {
@@ -246,7 +328,7 @@ export class QueueService {
createdAt: Date.now(),
eventId: uuid(),
};
return this.webhookDeliverQueue.add(data, {
attempts: 4,
timeout: 1 * 60 * 1000, // 1min
@@ -264,7 +346,7 @@ export class QueueService {
//deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});
this.deliverQueue.clean(0, 'delayed');
this.inboxQueue.once('cleaned', (jobs, status) => {
//inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});

View File

@@ -4,7 +4,7 @@ import chalk from 'chalk';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js';
import type { RemoteUser, User } from '@/models/entities/User.js';
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
import type { Config } from '@/config.js';
import type Logger from '@/logger.js';
import { UtilityService } from '@/core/UtilityService.js';
@@ -33,7 +33,7 @@ export class RemoteUserResolveService {
}
@bindThis
public async resolveUser(username: string, host: string | null): Promise<User> {
public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> {
const usernameLower = username.toLowerCase();
if (host == null) {
@@ -44,7 +44,7 @@ export class RemoteUserResolveService {
} else {
return u;
}
});
}) as LocalUser;
}
host = this.utilityService.toPuny(host);
@@ -57,7 +57,7 @@ export class RemoteUserResolveService {
} else {
return u;
}
});
}) as LocalUser;
}
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null;
@@ -109,7 +109,7 @@ export class RemoteUserResolveService {
if (u == null) {
throw new Error('user not found');
} else {
return u;
return u as LocalUser | RemoteUser;
}
});
}

View File

@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
@@ -13,6 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { StreamMessages } from '@/server/api/stream/types.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { Packed } from '@/misc/json-schema';
import type { OnApplicationShutdown } from '@nestjs/common';
export type RolePolicies = {
@@ -64,8 +65,11 @@ export class RoleService implements OnApplicationShutdown {
public static NotAssignedError = class extends Error {};
constructor(
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -84,10 +88,10 @@ export class RoleService implements OnApplicationShutdown {
) {
//this.onMessage = this.onMessage.bind(this);
this.rolesCache = new MemorySingleCache<Role[]>(Infinity);
this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(Infinity);
this.rolesCache = new MemorySingleCache<Role[]>(1000 * 60 * 60 * 1);
this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(1000 * 60 * 60 * 1);
this.redisSubscriber.on('message', this.onMessage);
this.redisForSub.on('message', this.onMessage);
}
@bindThis
@@ -398,8 +402,27 @@ export class RoleService implements OnApplicationShutdown {
this.globalEventService.publishInternalEvent('userRoleUnassigned', existing);
}
@bindThis
public async addNoteToRoleTimeline(note: Packed<'Note'>): Promise<void> {
const roles = await this.getUserRoles(note.userId);
const redisPipeline = this.redisClient.pipeline();
for (const role of roles) {
redisPipeline.xadd(
`roleTimeline:${role.id}`,
'MAXLEN', '~', '1000',
'*',
'note', note.id);
this.globalEventService.publishRoleTimelineStream(role.id, 'note', note);
}
redisPipeline.exec();
}
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onMessage);
this.redisForSub.off('message', this.onMessage);
}
}

View File

@@ -0,0 +1,166 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js';
import { Note } from '@/models/entities/Note.js';
import { User } from '@/models/index.js';
import type { NotesRepository } from '@/models/index.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import { QueryService } from '@/core/QueryService.js';
import { IdService } from '@/core/IdService.js';
import type { Index, MeiliSearch } from 'meilisearch';
type K = string;
type V = string | number | boolean;
type Q =
{ op: '=', k: K, v: V } |
{ op: '!=', k: K, v: V } |
{ op: '>', k: K, v: number } |
{ op: '<', k: K, v: number } |
{ op: '>=', k: K, v: number } |
{ op: '<=', k: K, v: number } |
{ op: 'and', qs: Q[] } |
{ op: 'or', qs: Q[] } |
{ op: 'not', q: Q };
function compileValue(value: V): string {
if (typeof value === 'string') {
return `'${value}'`; // TODO: escape
} else if (typeof value === 'number') {
return value.toString();
} else if (typeof value === 'boolean') {
return value.toString();
}
throw new Error('unrecognized value');
}
function compileQuery(q: Q): string {
switch (q.op) {
case '=': return `(${q.k} = ${compileValue(q.v)})`;
case '!=': return `(${q.k} != ${compileValue(q.v)})`;
case '>': return `(${q.k} > ${compileValue(q.v)})`;
case '<': return `(${q.k} < ${compileValue(q.v)})`;
case '>=': return `(${q.k} >= ${compileValue(q.v)})`;
case '<=': return `(${q.k} <= ${compileValue(q.v)})`;
case 'and': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' AND ') })`;
case 'or': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' OR ') })`;
case 'not': return `(NOT ${compileQuery(q.q)})`;
default: throw new Error('unrecognized query operator');
}
}
@Injectable()
export class SearchService {
private meilisearchNoteIndex: Index | null = null;
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.meilisearch)
private meilisearch: MeiliSearch | null,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private queryService: QueryService,
private idService: IdService,
) {
if (meilisearch) {
this.meilisearchNoteIndex = meilisearch.index('notes');
this.meilisearchNoteIndex.updateSettings({
searchableAttributes: [
'text',
'cw',
],
sortableAttributes: [
'createdAt',
],
filterableAttributes: [
'createdAt',
'userId',
'userHost',
'channelId',
],
typoTolerance: {
enabled: false,
},
pagination: {
maxTotalHits: 10000,
},
});
}
}
@bindThis
public async indexNote(note: Note): Promise<void> {
if (this.meilisearch) {
this.meilisearchNoteIndex!.addDocuments([{
id: note.id,
createdAt: note.createdAt.getTime(),
userId: note.userId,
userHost: note.userHost,
channelId: note.channelId,
cw: note.cw,
text: note.text,
}], {
primaryKey: 'id',
});
}
}
@bindThis
public async searchNote(q: string, me: User | null, opts: {
userId?: Note['userId'] | null;
channelId?: Note['channelId'] | null;
}, pagination: {
untilId?: Note['id'];
sinceId?: Note['id'];
limit?: number;
}): Promise<Note[]> {
if (this.meilisearch) {
const filter: Q = {
op: 'and',
qs: [],
};
if (pagination.untilId) filter.qs.push({ op: '<', k: 'createdAt', v: this.idService.parse(pagination.untilId).date.getTime() });
if (pagination.sinceId) filter.qs.push({ op: '>', k: 'createdAt', v: this.idService.parse(pagination.sinceId).date.getTime() });
if (opts.userId) filter.qs.push({ op: '=', k: 'userId', v: opts.userId });
if (opts.channelId) filter.qs.push({ op: '=', k: 'channelId', v: opts.channelId });
const res = await this.meilisearchNoteIndex!.search(q, {
sort: ['createdAt:desc'],
matchingStrategy: 'all',
attributesToRetrieve: ['id', 'createdAt'],
filter: compileQuery(filter),
limit: pagination.limit,
});
if (res.hits.length === 0) return [];
return await this.notesRepository.findBy({
id: In(res.hits.map(x => x.id)),
});
} else {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), pagination.sinceId, pagination.untilId);
if (opts.userId) {
query.andWhere('note.userId = :userId', { userId: opts.userId });
} else if (opts.channelId) {
query.andWhere('note.channelId = :channelId', { channelId: opts.channelId });
}
query
.andWhere('note.text ILIKE :q', { q: `%${ sqlLikeEscape(q) }%` })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
if (me) this.queryService.generateBlockedUserQuery(query, me);
return await query.take(pagination.limit).getMany();
}
}
}

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