1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-07 02:55:39 +02:00

Compare commits

..

10 Commits

Author SHA1 Message Date
syuilo
293b26c37a refactor utility/storage.ts 2026-03-05 19:07:15 +09:00
かっこかり
2d3262a7c6 Update MkForm.vue 2026-03-04 12:22:01 +09:00
kakkokari-gtyih
368531a930 fix: fix types 2026-03-03 19:22:50 +09:00
kakkokari-gtyih
93a6737e69 fix: rename users/get-following-users-by-birthday 2026-03-03 19:18:18 +09:00
kakkokari-gtyih
2ea630e40c fix 2026-03-03 19:15:35 +09:00
kakkokari-gtyih
bc3f02cb57 fix(frontend): add comment
Co-Authored-By: anatawa12 <anatawa12@icloud.com>
2026-03-03 19:14:03 +09:00
kakkokari-gtyih
b03e6cbf8d fix 2026-03-03 19:12:51 +09:00
kakkokari-gtyih
f22d5ad683 fix: storagePersistenceのtop-level awaitを解消 2026-03-03 19:11:36 +09:00
kakkokari-gtyih
4d9f6e758c fix(frontend): fix outdated comments 2026-03-03 19:06:55 +09:00
kakkokari-gtyih
3fb80bc9b4 fix: OAuthのContent-Typeを正しく判定するように 2026-03-03 19:05:15 +09:00
950 changed files with 9838 additions and 26708 deletions

View File

@@ -19,6 +19,7 @@
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Orta.vscode-jest",
"dbaeumer.vscode-eslint",
"mrmlnc.vscode-json5"
]

View File

@@ -34,6 +34,9 @@ updates:
patterns:
- "storybook*"
- "@storybook/*"
swc-core:
patterns:
- "@swc/core*"
typescript-eslint:
patterns:
- "@typescript-eslint/*"

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false
@@ -29,7 +29,7 @@ jobs:
- name: setup node
id: setup-node
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: pnpm
@@ -66,7 +66,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub

View File

@@ -32,7 +32,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta

View File

@@ -17,7 +17,7 @@ jobs:
DOCKLE_VERSION: 0.4.15
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |

View File

@@ -25,14 +25,14 @@ jobs:
ref: refs/pull/${{ github.event.number }}/merge
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -40,14 +40,14 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

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

View File

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

View File

@@ -16,21 +16,18 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
# see https://docs.github.com/actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry
registry-url: 'https://registry.npmjs.org'
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: Publish package
run: |
pnpm i --frozen-lockfile

View File

@@ -22,12 +22,12 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 0
submodules: true
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
if: github.event_name == 'pull_request_target'
with:
fetch-depth: 0
@@ -37,9 +37,9 @@ jobs:
if: github.event_name == 'pull_request_target'
run: git checkout "$(git rev-list --parents -n1 HEAD | cut -d" " -f3)"
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

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

View File

@@ -36,7 +36,7 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
@@ -68,7 +68,7 @@ jobs:
fi
done
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'

View File

@@ -28,13 +28,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -76,7 +76,7 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v6.0.1
with:
submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150
@@ -86,9 +86,9 @@ jobs:
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Setup pnpm
uses: pnpm/action-setup@v4.4.0
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.3.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

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

View File

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

View File

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

3
.gitignore vendored
View File

@@ -81,6 +81,3 @@ vite.config.local-dev.ts.timestamp-*
# VSCode addon
.favorites.json
# Affinity
*.af~lock~

View File

@@ -1,85 +1,10 @@
## Unreleased
### General
-
### Client
-
### Server
-
## 2026.5.0
### General
- Enhance: アバターデコレーションにカテゴリを設定できるように
### Client
- Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように
- Enhance: ベータ版でのアップデート時のダイアログの更新情報リンクをGitHubのReleasesページに遷移するようにし、正しく閲覧できるように
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
- Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正
- Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正
- Fix: オーディオ、動画の再生速度メニューが開けない問題を修正
### Server
- Enhance: メモリ使用量を削減
- Enhance: 起動の高速化
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1410)
- Enhance: バックエンドの開発モード時の安定性向上
- Enhance: バックエンドビルド・テスト時に使用する依存関係の整理swc/esbuild→Rolldown, Jest→Vitest
- Fix: ファイルシステムを用いる処理におけるパスの取り扱いを改善
- Fix: `/api-doc` にアクセスできない問題を修正
- Fix: support `alsoKnownAs` from remote actors as either array or unwrapped singleton
- Fix: ローカルに存在しないリモートアカウントに対するアカウント削除リクエストを受信した際に、そのユーザーを新規作成して削除する挙動を修正
- Fix: Inboxでの特定のエラーによる失敗はDelayedにしない
- Fix: ID生成アルゴリズムにULIDを使用している場合にMisskeyが正しく動作しない問題を修正
- Fix: リレー経由で届いたノートがリノートとして表示される問題を修正
- Fix: robots.txtの内容を調整
- Fix: 特定のユーザーに管理者権限を持つロールが複数ついている際に、取得できるユーザーIDが重複する問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/17ed4108cec4b6bd2fd989db5a9091db91fa37a7)
- Fix: ブロックしたサーバーからのInboxジョブが蓄積し続ける問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/3f0f4bfe923f2b3a7837017b54841598f421c6ef)
- Fix: support activity with `actor` as an id string or embedded object in inbox processor and ActivityPub inbox service
- Fix: コンフィグファイルに `meilisearch` の設定がある状態でほかの検索プロバイダを利用すると、UI上からリモートのートの検索ができない問題を修正
- Fix: ノートに関する通知で公開範囲が考慮されていない問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/cbce96c520a138b8bcd16890ff6f2952830fa166 originally presented in https://github.com/yojo-art/cherrypick/pull/743)
## 2026.3.2
### General
- 依存関係の更新
### Client
- Enhance: アプリ内ウィンドウの初期サイズを画面サイズに応じて自動で調整するように
- Fix: 絵文字パレットが空の状態でMisskeyについてのページが閲覧できない問題を修正
- Fix: ウィンドウのタイトルをクリックしても最前面に出ないことがある問題を修正
### Server
- Fix: 自分の行ったフォロワー限定投稿または指名投稿に自分自身でリアクションなどを行った場合のイベントが流れない問題を修正
- Fix: 署名付きGETリクエストにおいてAcceptヘッダを署名の対象から除外Acceptヘッダを正規化するCDNやリバースプロキシを使用している際に挙動がおかしくなる問題を修正
- Fix: WebSocket接続におけるートの非表示ロジックを修正
- Fix: チャンネルミュートを有効にしている際に、一部のタイムラインやノート一覧が空になる問題を修正
- Fix: 初期読込時に必要なフロントエンドのアセットがすべて読み込まれていない問題を修正
## 2026.3.1
### General
- 依存関係の更新
### Server
- Fix: セキュリティに関する修正
## 2026.3.0
## 2026.2.0
### Note
- `users/following``birthday` プロパティは非推奨になりました。代わりに `users/get-following-users-by-birthday` をご利用ください。
### General
- Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
- Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey)
- 「今日誕生日のユーザー」は「もうすぐ誕生日のユーザー」に名称変更されました
- Fix: ユーザーハッシュタグページでユーザーの読み込みが重複する問題を修正
@@ -135,9 +60,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Client
- Enhance: デッキのUI説明を追加
- Enhance: 設定がブラウザによって消去されないようにするオプションを追加
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
- Fix: 一部のUnicode絵文字のリアクションがボタンにならない問題を修正
### Server
@@ -182,11 +107,11 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: ページの内容がはみ出ることがある問題を修正
- Fix: ナビゲーションバーを下に表示しているときに、項目数が多いと表示が崩れる問題を修正
- Fix: ヘッダーメニューのチャンネルの新規作成の項目でチャンネル作成ページに飛べない問題を修正 #16816
- Fix: ラジオボタンに空白の選択肢が表示される問題を修正
- Fix: ラジオボタンに空白の選択肢が表示される問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1105)
- Fix: 一部のシチュエーションで投稿フォームのツアーが正しく表示されない問題を修正
- Fix: 投稿フォームのリセットボタンで注釈がリセットされない問題を修正
- Fix: PlayのAiScriptバージョン判定v0.x系・v1.x系の判定が正しく動作しない問題を修正
- Fix: PlayのAiScriptバージョン判定v0.x系・v1.x系の判定が正しく動作しない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1129)
- Fix: フォロー申請をキャンセルする際の確認ダイアログの文言が不正確な問題を修正
- Fix: 初回読み込み時にエラーになることがある問題を修正
@@ -196,12 +121,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Server
- Enhance: メモリ使用量を削減しました
- Enhance: 依存関係の更新
- Fix: ワードミュートの文字数計算を修正
- Fix: ワードミュートの文字数計算を修正
- Fix: チャンネルのリアルタイム更新時に、ロックダウン設定にて非ログイン時にノートを表示しない設定にしている場合でもノートが表示されてしまう問題を修正
- Fix: DeepL APIのAPIキー指定方式変更に対応
- Fix: DeepL APIのAPIキー指定方式変更に対応
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1096)
- 内部実装の変更にて対応可能な更新です。Misskey側の設定方法に変更はありません。
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1123)
## 2025.11.0
@@ -244,7 +169,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
## 2025.10.1
### General
- Enhance: リモートユーザーに付与したロールバッジを表示できるように(オプトイン)
- Enhance: リモートユーザーに付与したロールバッジを表示できるように(オプトイン)
パフォーマンス上の問題からデフォルトで無効化されています。「コントロールパネル > パフォーマンス」から有効化できます。
- 依存関係の更新
@@ -371,7 +296,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: レンダリングパフォーマンスの向上
- Enhance: 依存ソフトウェアの更新
- Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
- Fix: テーマエディタが動作しない問題を修正
- Fix: チャンネルのハイライトページにノートが表示されない問題を修正
@@ -531,7 +456,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: 画像の高品質なプレースホルダを無効化してパフォーマンスを向上させるオプションを追加
- Enhance: 招待されているが参加していないルームを開いたときに、招待を承認するかどうか尋ねるように
- Enhance: リプライ元にアンケートがあることが表示されるように
- Enhance: ノートのサーバー情報のデザインを改善・パフォーマンス向上
- Enhance: ノートのサーバー情報のデザインを改善・パフォーマンス向上
(Based on https://github.com/taiyme/misskey/pull/198, https://github.com/taiyme/misskey/pull/211, https://github.com/taiyme/misskey/pull/283)
- Enhance: ユーザー設定でURLプレビューを無効化できるように
- Enhance: ヒントとコツを追加
@@ -620,7 +545,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Server
- Enhance: ジョブキューの成功/失敗したジョブも一定数・一定期間保存するようにし、後から問題を調査することを容易に
- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように
- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように
(Cherry-picked from https://github.com/yojo-art/cherrypick/pull/568 and https://github.com/team-shahu/misskey/pull/38)
- Enhance: ユーザーごとにノートの表示が高速化するように
- Fix: システムアカウントの名前がサーバー名と同期されない問題を修正
@@ -726,7 +651,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### General
- Enhance: プロキシアカウントをシステムアカウントとして作成するように
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
書式は https://indieauth.spec.indieweb.org/20220212/#example-2 に準じます。
- Fix: システムアカウントが削除できる問題を修正
@@ -740,7 +665,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Server
- Fix: 特定のケースでActivityPubの処理がデッドロックになることがあるのを修正
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/895)
@@ -761,7 +686,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: リアクションする際に確認ダイアログを表示できるように
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
- Enhance: CWの注釈で入力済みの文字数を表示
- Enhance: ノート検索ページのデザイン調整
- Enhance: ノート検索ページのデザイン調整
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
@@ -778,7 +703,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように
- Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正
- Fix: HTTPプロキシとその除外設定を行った状態でカスタム絵文字の一括インポートをしたとき、除外設定が効かないのを修正( #8766 )
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
- Fix: `update-meta`でobjectStoragePrefixにS3_SAFEかつURL-safeでない文字列を使えないように
@@ -788,12 +713,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
## 2025.2.0
### General
- Fix: Docker のビルドに失敗する問題を修正
- Fix: Docker のビルドに失敗する問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/883)
### Client
- Fix: パスキーでパスワードレスログインが出来ない問題を修正
- Fix: 一部環境でセンシティブなファイルを含むノートの非表示が効かない問題
- Fix: 一部環境でセンシティブなファイルを含むノートの非表示が効かない問題
- Fix: データセーバー有効時にもユーザーページの「ファイル」タブで画像が読み込まれてしまう問題を修正
- Fix: MFMの `sparkle` エフェクトが正しく表示されない問題を修正
- Fix: ページのURLにスラッシュが含まれている場合にページが正しく表示されない問題を修正
@@ -820,14 +745,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
* β版として公開のため、旧画面も引き続き利用可能です
### Client
- Enhance: PC画面でチャンネルが複数列で表示されるように
- Enhance: PC画面でチャンネルが複数列で表示されるように
(Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13)
- Enhance: 照会に失敗した場合、その理由を表示するように
- Enhance: ワードミュートで検知されたワードを表示できるように
- Enhance: リモートのノートのリンクをコピーできるように
- Enhance: 連合がホワイトリスト化・無効化されているサーバー向けのデザイン修正
- Enhance: AiScriptのセーブデータを明示的に削除する関数`Mk:remove`を追加
- Enhance: ノートの添付ファイルを一覧で遡れる「ファイル」タブを追加
- Enhance: ノートの添付ファイルを一覧で遡れる「ファイル」タブを追加
(Based on https://github.com/Otaku-Social/maniakey/pull/14)
- Enhance: AiScriptの拡張API関数において引数の型チェックをより厳格に
- Enhance: クエリパラメータでuiを一時的に変更できるように #15240
@@ -835,26 +760,26 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正
- Fix: サーバー情報メニューに区切り線が不足していたのを修正
- Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正
- Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正
- Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/803)
- Fix: 絵文字管理画面で一部の絵文字が表示されない問題を修正
- Fix: プラグイン `register_note_view_interruptor` でノートのサーバー情報の書き換えができない問題を修正
- Fix: Botプロテクションの設定変更時は実際に検証を通過しないと保存できないように( #15137 )
- Fix: ノート検索が使用できない場合でもチャンネルのノート検索欄がでていた問題を修正
- Fix: `Ui:C:select`で値の変更が画面に反映されない問題を修正
- Fix: MiAuth認可画面で、認可処理に失敗した場合でもコールバックURLに遷移してしまう問題を修正
- Fix: MiAuth認可画面で、認可処理に失敗した場合でもコールバックURLに遷移してしまう問題を修正
(Cherry-picked from https://github.com/TeamNijimiss/misskey/commit/800359623e41a662551d774de15b0437b6849bb4)
- Fix: ノート作成画面でファイルの添付可能個数を超えてもノートボタンが押せていた問題を修正
- Fix: 「アカウントを管理」画面で、ユーザー情報の取得に失敗したアカウント(削除されたアカウントなど)が表示されない問題を修正
- Fix: MacOSでChrome系ブラウザを使用している場合に、Misskeyを閉じた際に他のタブのオーディオ機能と干渉する問題を修正
- Fix: 言語データのキャッシュ状況によっては、埋め込みウィジェットが正しく起動しない問題を修正
- Fix: 「削除して編集」でノートの引用を解除出来なかった問題を修正( #14476 )
- Fix: RSSウィジェットが正しく表示されない問題を修正
- Fix: RSSウィジェットが正しく表示されない問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/857)
- Fix: ワードミュートの保存失敗時にAPIエラーが握りつぶされる事があるのを修正
- Fix: アンケートでリモートの絵文字が正しく描画できない問題の修正
(Cherry-picked from https://github.com/yojo-art/cherrypick/pull/153)
- Fix: 非ログイン時のサーバー概要画面のメニューボタンが押せないことがあるのを修正
- Fix: 非ログイン時のサーバー概要画面のメニューボタンが押せないことがあるのを修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/656)
- Fix: URLにはじめから`#pswp`が含まれている場合に画像ビューワーがブラウザの戻るボタンで閉じられない問題を修正
- Fix: ロール作成画面で設定できるアイコンデコレーションの最大取付個数を16に制限
@@ -863,18 +788,18 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Server
- Enhance: pg_bigmが利用できるよう、ートの検索をILIKE演算子でなくLIKE演算子でLOWER()をかけたテキストに対して行うように
- Enhance: ート検索の選択肢としてpgroongaに対応 ( #14730 )
- Enhance: チャート更新時にDBに同時接続しないように
- Enhance: チャート更新時にDBに同時接続しないように
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/830)
- Enhance: config(default.yml)からSQLログ全文を出力するか否かを設定可能に ( #15266 )
- Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 )
- Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正
- Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/737)
- Fix: ートの閲覧にログイン必須にしてもFeedでートが表示されてしまう問題を修正
- Fix: 絵文字の連合でライセンス欄を相互にやり取りするように ( #10859, #14109 )
- Fix: ロックダウンされた期間指定のートがStreaming経由でLTLに出現するのを修正 ( #15200 )
- Fix: disableClustering設定時の初期化ロジックを調整( #15223 )
- Fix: URLとURIが異なるエンティティの照会に失敗する問題を修正( #15039 )
- Fix: ActivityPubリクエストかどうかの判定が正しくない問題を修正
- Fix: ActivityPubリクエストかどうかの判定が正しくない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/869)
- Fix: `/api/pages/update`にて`name`を指定せずにリクエストするとエラーが発生する問題を修正
- Fix: AIセンシティブ判定が arm64 環境で動作しない問題を修正
@@ -900,12 +825,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: お知らせ作成時に画像URL入力欄を空欄に変更できないのを修正 ( #14976 )
### Client
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/751)
- Enhance: ドライブでソートができるように
- Enhance: アイコンデコレーション管理画面の改善
- Enhance: 「単なるラッキー」の取得条件を変更
- Enhance: 投稿フォームでEscキーを押したときIME入力中ならフォームを閉じないように #10866
- Enhance: 投稿フォームでEscキーを押したときIME入力中ならフォームを閉じないように #10866
- Enhance: MiAuth, OAuthの認可画面の改善
- どのアカウントで認証しようとしているのかがわかるように
- 認証するアカウントを切り替えられるように
@@ -913,29 +838,29 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: カタルーニャ語 (ca-ES) に対応
- Enhance: 個別お知らせページではMetaタグを出力するように
- Enhance: ノート詳細画面にロールのバッジを表示
- Enhance: 過去に送信したフォローリクエストを確認できるように
- Enhance: 過去に送信したフォローリクエストを確認できるように
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663)
- Enhance: サイドバーを簡単に展開・折りたたみできるように ( #14981 )
- Enhance: リノートメニューに「リノートの詳細」を追加
- Enhance: 非ログイン状態でMisskeyを開いた際のパフォーマンスを向上
- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768)
- Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正
- Fix: Encode RSS urls with escape sequences before fetching allowing query parameters to be used
- Fix: リンク切れを修正
- Fix: ノート投稿ボタンにホバー時のスタイルが適用されていないのを修正
- Fix: ノート投稿ボタンにホバー時のスタイルが適用されていないのを修正
(Cherry-picked from https://github.com/taiyme/misskey/pull/305)
- Fix: メールアドレス登録有効化時の「完了」ダイアログボックスの表示条件を修正
- Fix: 画面幅が狭い環境でデザインが崩れる問題を修正
- Fix: 画面幅が狭い環境でデザインが崩れる問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/815)
- Fix: TypeScriptの型チェック対象ファイルを限定してビルドを高速化するように
- Fix: TypeScriptの型チェック対象ファイルを限定してビルドを高速化するように
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/725)
### Server
- Enhance: DockerのNode.jsを22.11.0に更新
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/715)
- Enhance: リモートユーザーの照会をオリジナルにリダイレクトするように
- Fix: sharedInboxが無いActorに紐づくリモートユーザーを照会できない
@@ -943,18 +868,18 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: フォロワーへのメッセージの絵文字をemojisに含めるように
- Fix: Nested proxy requestsを検出した際にブロックするように
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)
- Fix: 招待コードの発行可能な残り数算出に使用すべきロールポリシーの値が違う問題を修正
- Fix: 招待コードの発行可能な残り数算出に使用すべきロールポリシーの値が違う問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/706)
- Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正
- Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/711)
- Fix: ローカルユーザーへのメンションを含むートが連合される際に正しいURLに変換されないことがある問題を修正
- Fix: ローカルユーザーへのメンションを含むートが連合される際に正しいURLに変換されないことがある問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/712)
- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正
- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/709)
- Fix: User Webhookテスト機能のMock Payloadを修正
- Fix: アカウント削除のモデレーションログが動作していないのを修正 (#14996)
- Fix: User Webhookテスト機能のMock Payloadを修正
- Fix: アカウント削除のモデレーションログが動作していないのを修正 (#14996)
- Fix: リノートミュートが新規投稿通知に対して作用していなかった問題を修正
- Fix: Inboxの処理で生じるエラーを誤ってActivityとして処理することがある問題を修正
- Fix: Inboxの処理で生じるエラーを誤ってActivityとして処理することがある問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/730)
- Fix: セキュリティに関する修正
@@ -981,13 +906,13 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: 個人宛のお知らせは「わかった」を押すと自動的にアーカイブされるように
- Fix: `admin/emoji/update`エンドポイントのidのみ指定した時不正なエラーが発生するバグを修正
- Fix: RBT有効時、リートのリアクションが反映されない問題を修正
- Fix: キューのエラーログを簡略化するように
- Fix: キューのエラーログを簡略化するように
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/649)
## 2024.10.0
### Note
- セキュリティ向上のため、サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
- セキュリティ向上のため、サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
- ホスティングサービスを運営している場合は、コンフィグファイルを構築する際に`setupPassword`をランダムな値に設定し、ユーザーに通知するようにシステムを更新することをおすすめします。
- なお、初期パスワードが設定されていない場合でも初期設定を行うことが可能ですUI上で初期パスワードの入力欄を空欄にすると続行できます
- ユーザーデータを読み込む際の型が一部変更されました。
@@ -1007,7 +932,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Client
- Enhance: デザインの調整
- Enhance: ログイン画面の認証フローを改善
- Fix: クライアント上での時間ベースの実績獲得動作が実績獲得後も発動していた問題を修正
- Fix: クライアント上での時間ベースの実績獲得動作が実績獲得後も発動していた問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/657)
### Server
@@ -1025,7 +950,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Feat: フォローされた際のメッセージを設定できるように
- Feat: 連合をホワイトリスト制にできるように
- Feat: UserWebhookとSystemWebhookのテスト送信機能を追加 (#14445)
- Feat: モデレーターはユーザーにかかわらずファイルが添付されているノートを検索できるように
- Feat: モデレーターはユーザーにかかわらずファイルが添付されているノートを検索できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/680)
- Feat: データエクスポートが完了した際に通知を発行するように
- Enhance: ユーザーによるコンテンツインポートの可否をロールポリシーで制御できるように
@@ -1044,12 +969,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
- Fix: コントロールパネル内のAp requests内のチャートの表示がおかしかった問題を修正
- Fix: 月の違う同じ日はセパレータが表示されないのを修正
- Fix: タッチ画面でレンジスライダーを操作するとツールチップが複数表示される問題を修正
- Fix: タッチ画面でレンジスライダーを操作するとツールチップが複数表示される問題を修正
(Cherry-picked from https://github.com/taiyme/misskey/pull/265)
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725)
- Fix: 設定変更時のリロード確認ダイアログが複数個表示されることがある問題を修正
- Fix: ファイルの詳細ページのファイルの説明で改行が正しく表示されない問題を修正
- Fix: ファイルの詳細ページのファイルの説明で改行が正しく表示されない問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/bde6bb0bd2e8b0d027e724d2acdb8ae0585a8110)
- Fix: 一部画面のページネーションが動作しにくくなっていたのを修正 ( #12766 , #11449 )
@@ -1058,14 +983,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように
- この変更により、公式フロントエンドでは入力の不備が内部エラーとして報告される代わりに一般的なエラーダイアログで報告されます
- Fix: ファイルがサイズの制限を超えてアップロードされた際にエラーを返さなかった問題を修正
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/26e0412fbb91447c37e8fb06ffb0487346063bb8)
- Fix: Continue importing from file if single emoji import fails
- Fix: `Retry-After`ヘッダーが送信されなかった問題を修正
- Fix: `Retry-After`ヘッダーが送信されなかった問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/8a982c61c01909e7540ff1be9f019df07c3f0624)
- Fix: サーバーサイドのDOM解析完了時にリソースを開放するように
- Fix: サーバーサイドのDOM解析完了時にリソースを開放するように
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/634)
- Fix: `<link rel="alternate">`を追って照会するのはOKレスポンスが返却された場合のみに
- Fix: `<link rel="alternate">`を追って照会するのはOKレスポンスが返却された場合のみに
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/633)
- Fix: メールにスタイルが適用されていなかった問題を修正
@@ -1094,15 +1019,15 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。
- これにより、プッシュ通知が有効な同条件下の環境において、プッシュ通知が常に発生してしまう問題も修正されます。
- Fix: Play各種エンドポイントの返り値に`visibility`が含まれていない問題を修正
- Fix: サーバー情報取得の際にモデレーター限定の情報が取得できないことがあるのを修正
- Fix: サーバー情報取得の際にモデレーター限定の情報が取得できないことがあるのを修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/582)
- Fix: 公開範囲がダイレクトのノートをユーザーアクティビティのチャート生成に使用しないように
- Fix: 公開範囲がダイレクトのノートをユーザーアクティビティのチャート生成に使用しないように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/679)
- Fix: ActivityPubのエンティティタイプ判定で不明なタイプを受け取った場合でも処理を継続するように
- キュー処理のつまりが改善される可能性があります
- Fix: リバーシの対局設定の変更が反映されないのを修正
- Fix: 無制限にストリーミングのチャンネルに接続できる問題を修正
- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正
- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/700)
- Fix: Prevent memory leak from memory caches (#14310)
- Fix: More reliable memory cache eviction (#14311)
@@ -1134,9 +1059,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
- Enhance: 非ログイン時に他サーバーに遷移するアクションを追加
- Enhance: 非ログイン時のハイライトTLのデザインを改善
- Enhance: フロントエンドのアクセシビリティ改善
- Enhance: フロントエンドのアクセシビリティ改善
(Based on https://github.com/taiyme/misskey/pull/226)
- Enhance: サーバー情報ページ・お問い合わせページを改善
- Enhance: サーバー情報ページ・お問い合わせページを改善
(Cherry-picked from https://github.com/taiyme/misskey/pull/238)
- Enhance: AiScriptを0.19.0にアップデート
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
@@ -1145,7 +1070,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: 検索(ノート/ユーザー)において、入力に空白が含まれている場合は照会を行わないように
- Enhance: 検索(ノート/ユーザー)において、照会を行うかどうか、ハッシュタグのノート/ユーザー一覧ページを表示するかどうかの確認ダイアログを出すように
- Enhance: 検索(ノート/ユーザー)で `@` から始まる文字列(`@user@host`など)を入力すると、そのユーザーを照会できるように
- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように
- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように
(Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
- Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
- Enhance: ブラウザのコンテキストメニューを使用できるように
@@ -1153,19 +1078,19 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
- Fix: アンテナの編集画面のボタンに隙間を追加
- Fix: テーマプレビューが見れない問題を修正
- Fix: ショートカットキーが連打できる問題を修正
- Fix: ショートカットキーが連打できる問題を修正
(Cherry-picked from https://github.com/taiyme/misskey/pull/234)
- Fix: MkSignin.vueのcredentialRequestからReactivityを削除ProxyがPasskey認証処理に渡ることを避けるため
- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正
- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
- Fix: Twitchの埋め込みが開けない問題を修正
- Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
- Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
- Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正
- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正
- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672)
- Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
- Fix: deck uiの通知音が重なる問題 (#14029)
@@ -1208,14 +1133,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
4. フォローしていない非アクティブなユーザ
また、自分自身のアカウントもサジェストされるようになりました。
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
- Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
- Fix: FTT有効時にリモートユーザーのートがHTLにキャッシュされる問題を修正
- Fix: 一部の通知がローカル上のリモートユーザーに対して行われていた問題を修正
- Fix: エラーメッセージの誤字を修正 (#14213)
- Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正
- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正
(Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
- Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251
- Fix: `users/search`において `@` から始まる文字列が与えられた際の処理が正しくなかった問題を修正
@@ -1242,7 +1167,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### General
- Feat: エラートラッキングにSentryを使用できるようになりました
- Enhance: URLプレビューの有効化・無効化を設定できるように #13569
- Enhance: アンテナでBotによるートを除外できるように
- Enhance: アンテナでBotによるートを除外できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
- Enhance: クリップのノート数を表示するように
- Enhance: コンディショナルロールの条件として以下を新たに追加 (#13667)
@@ -1261,7 +1186,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Client
- Feat: アップロードするファイルの名前をランダム文字列にできるように
- Feat: 個別のお知らせにリンクで飛べるように
- Feat: 個別のお知らせにリンクで飛べるように
(Based on https://github.com/MisskeyIO/misskey/pull/639)
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
@@ -1291,9 +1216,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
- Fix: 周年の実績が閏年を考慮しない問題を修正
- Fix: ローカルURLのプレビューポップアップが左上に表示される
- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正
- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
- Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
- CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。
@@ -1316,13 +1241,13 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: ドライブのファイルがNSFWかどうか個別に連合されるように (#13756)
- 可能な場合、ノートの添付ファイルのセンシティブ判定がファイル単位になります
- Fix: リモートから配送されたアクティビティにJSON-LD compactionをかける
- Fix: フォローリクエストを作成する際に既存のものは削除するように
- Fix: フォローリクエストを作成する際に既存のものは削除するように
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
- Fix: エンドポイント`notes/translate`のエラーを改善
- Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
- Fix: 一部の音声ファイルが映像ファイルとして扱われる問題を修正
- Fix: リプライのみの引用リートと、CWのみの引用リートが純粋なリートとして誤って扱われてしまう問題を修正
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
- Fix: Add Cache-Control to Bull Board
- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
@@ -1515,10 +1440,10 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Note
- 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました
- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
**影響:**
それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。
**影響:**
それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。
投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。
1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。
@@ -1565,7 +1490,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- Enhance: Unicode 15.0のサポート
- Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように
- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります
- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります
(例: ` ```js ` → Javascript, ` ```ais ` → AiScript
- Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように
- Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
@@ -1972,9 +1897,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### General
- 招待機能を改善しました
* 過去に発行した招待コードを確認できるようになりました
* ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました
* 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました
* 過去に発行した招待コードを確認できるようになりました
* ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました
* 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました
- ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
@@ -2137,9 +2062,9 @@ Meilisearchの設定に`index`が必要になりました。値はMisskeyサー
* 「フォロワーのみ」の投稿は検索結果に表示されません。
- 新規登録前に簡潔なルールをユーザーに表示できる、サーバールール機能を追加
- ユーザーへの自分用メモ機能
* ユーザーに対して、自分だけが見られるメモを追加できるようになりました。
* ユーザーに対して、自分だけが見られるメモを追加できるようになりました。
(自分自身に対してもメモを追加できます。)
* ユーザーメニューから追加できます。
* ユーザーメニューから追加できます。
デスクトップ表示ではusernameの右側のボタンからも追加可能
- チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。
- チャンネルをアーカイブできるようになりました。

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.21
# syntax = docker/dockerfile:1.20
ARG NODE_VERSION=22.22.0-bookworm
ARG NODE_VERSION=22.21.1-bookworm
# build assets & compile TypeScript
@@ -102,6 +102,7 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/src-js ./packages/backend/src-js
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"lib": ["dom"],
"target": "esnext",
"lib": ["dom", "es5"],
"target": "es5",
"types": ["cypress", "node"]
},
"include": ["./**/*.ts"]

View File

@@ -264,7 +264,7 @@ noJobs: "No hi ha feines"
federating: "Federant"
blocked: "Bloquejat"
suspended: "Anul·lar subscripció "
all: "Tot"
all: "tot"
subscribing: "Subscrit a"
publishing: "S'està publicant"
notResponding: "Sense resposta"
@@ -1073,8 +1073,8 @@ thisPostMayBeAnnoying: "Aquesta nota pot ser molesta per algú."
thisPostMayBeAnnoyingHome: "Publicar a la línia de temps d'Inici"
thisPostMayBeAnnoyingCancel: "Cancel·lar "
thisPostMayBeAnnoyingIgnore: "Publicar de totes maneres"
collapseRenotes: "Col·lapsar els impulsos que ja has vist"
collapseRenotesDescription: "Col·lapse les notes a les quals ja has reaccionat o que ja has impulsat."
collapseRenotes: "Col·lapsar les renotes que ja has vist"
collapseRenotesDescription: "Col·lapse les notes a les quals ja has reaccionat o que ja has renotat"
internalServerError: "Error intern del servidor"
internalServerErrorDescription: "El servidor ha fallat de manera inexplicable."
copyErrorInfo: "Copiar la informació de l'error "
@@ -1408,7 +1408,6 @@ frame: "Marc"
presets: "Predefinit"
zeroPadding: "Sense omplir"
nothingToConfigure: "No hi ha res a configurar"
viewRenotedChannel: "Mirar el canal d'impulsos "
_imageEditing:
_vars:
caption: "Títol de l'arxiu"
@@ -1688,7 +1687,7 @@ _initialTutorial:
description: "Pots limitar qui pot veure les teves notes."
public: "La teva nota serà visible per a tots els usuaris."
home: "Publicar només a línia de temps d'Inici. La gent que visiti el teu perfil o mitjançant les remotes també la podran veure."
followers: "Només visible per a seguidors. Només els teus seguidors la podran veure i ningú més. Ningú més podrà fer impulsos."
followers: "Només visible per a seguidors. Només els teus seguidors la podran veure i ningú més. Ningú més podrà fer renotes."
direct: "Només visible per a alguns seguidors, el destinatari rebre una notificació. Es pot fer servir com una alternativa als missatges directes."
doNotSendConfidencialOnDirect1: "Tingues cura quan enviïs informació sensible."
doNotSendConfidencialOnDirect2: "Els administradors del servidor poden veure tot el que escrius. Ves compte quan enviïs informació sensible en enviar notes directes a altres usuaris en servidors de poca confiança."
@@ -3402,9 +3401,11 @@ _imageEffector:
threshold: "Llindar"
centerX: "Centre de X"
centerY: "Centre de Y"
density: "Densitat"
zoomLinesOutlineThickness: "Amplada de les vores exteriors"
zoomLinesSmoothing: "Suavitzat"
zoomLinesSmoothingDescription: "Els paràmetres de suavitzat i amplada de línia en augmentar no es poden fer servir junts."
zoomLinesThreshold: "Amplada de línia a l'augmentar "
zoomLinesMaskSize: "Diàmetre del centre"
zoomLinesBlack: "Obscurir"
circle: "Cercle"
drafts: "Esborrany "
_drafts:

View File

@@ -130,7 +130,6 @@ reactions: "Reakce"
reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání"
rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky"
attachCancel: "Odstranit přílohu"
deleteFile: "Smazat soubor"
markAsSensitive: "Označit jako NSFW"
unmarkAsSensitive: "Odznačit jako NSFW"
enterFileName: "Zadejte název souboru"
@@ -206,7 +205,6 @@ blockThisInstance: "Blokovat tuto instanci"
silenceThisInstance: "Utišit tuto instanci"
operations: "Operace"
software: "Software"
softwareName: "Software"
version: "Verze"
metadata: "Metadata"
withNFiles: "{n} soubor(ů)"
@@ -233,7 +231,6 @@ noteDeleteConfirm: "Jste si jistí že chcete smazat tuhle poznámku?"
pinLimitExceeded: "Nemůžete připnout další poznámky."
done: "Hotovo"
processing: "Zpracovávám"
preprocessing: "Připravuji..."
preview: "Náhled"
default: "Výchozí"
defaultValueIs: "Základní hodnota: {value}"
@@ -268,7 +265,6 @@ removed: "Smazáno"
removeAreYouSure: "Jste si jistí že chcete smazat \"{x}\"?"
deleteAreYouSure: "Jste si jistí že chcete smazat \"{x}\"?"
resetAreYouSure: "Opravdu resetovat?"
areYouSure: "Jste si jistí?"
saved: "Uloženo"
upload: "Nahrát soubory"
keepOriginalUploading: "Ponechat originální obrázek"
@@ -279,12 +275,9 @@ uploadFromUrl: "Nahrát z URL adresy"
uploadFromUrlDescription: "URL adresa souboru, který chcete nahrát"
uploadFromUrlRequested: "Upload zažádán"
uploadFromUrlMayTakeTime: "Může trvat nějakou dobu, dokud nebude dokončeno nahrávání."
uploadNFiles: "Uploadovat {n} souborů"
explore: "Objevovat"
messageRead: "Přečtené"
readAllChatMessages: "Označit všechny zprávy za přečtené"
noMoreHistory: "To je vše"
startChat: "Začít chat"
nUsersRead: "přečteno {n} uživateli"
agreeTo: "Souhlasím s {0}"
agree: "Souhlasím"
@@ -315,15 +308,12 @@ selectFile: "Vybrat soubor"
selectFiles: "Vybrat soubory"
selectFolder: "Vyberte složku"
selectFolders: "Vyberte složky"
fileNotSelected: "Nebyl vybrán žádný soubor"
renameFile: "Přejmenovat soubor"
folderName: "Název složky"
createFolder: "Vytvořit složku"
renameFolder: "Přejmenovat složku"
deleteFolder: "Odstranit složku"
folder: "Složka "
addFile: "Přidat soubor"
showFile: "Procházet soubory"
emptyDrive: "Váš disk je prázdný"
emptyFolder: "Tato složka je prázdná"
unableToDelete: "Nelze smazat"
@@ -434,7 +424,6 @@ totp: "Ověřovací aplikace"
totpDescription: "Použít ověřovací aplikaci pro použití jednorázových hesel"
moderator: "Moderátor"
moderation: "Moderování"
moderationNote: "Poznámka moderátora"
nUsersMentioned: "{n} uživatelů zmínilo"
securityKeyAndPasskey: "Bezpečnostní klíče a tokeny"
securityKey: "Bezpečnostní klíč"
@@ -490,9 +479,7 @@ uiLanguage: "Jazyk uživatelského rozhraní"
aboutX: "O {x}"
emojiStyle: "Styl emoji"
native: "Výchozí"
menuStyle: "Styl nabídky"
style: "Vzhled"
drawer: "Boční menu"
popup: "Vyskakovací okno"
showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši"
noHistory: "Žádná historie"
@@ -548,7 +535,6 @@ deleteAll: "Smazat vše"
showFixedPostForm: "Zobrazit formulář pro nové příspěvky nad časovou osou"
showFixedPostFormInChannel: "Zobrazit vkládací formulář na vrcholu časové osy (Kanály)"
newNoteRecived: "Jsou k dispozici nové poznámky"
newNote: "Nová poznámka"
sounds: "Zvuky"
sound: "Zvuky"
listen: "Poslouchat"
@@ -628,7 +614,6 @@ medium: "Střední"
small: "Malé"
generateAccessToken: "Vygenerovat přístupový token"
permission: "Oprávnění"
adminPermission: "Administrátorská práva"
enableAll: "Povolit vše"
disableAll: "Vypnout vše"
tokenRequested: "Povolit přístup k účtu"
@@ -904,9 +889,6 @@ oneHour: "1 hodina"
oneDay: "1 den"
oneWeek: "1 týden"
oneMonth: "1 měsíc"
threeMonths: "3 měsíce"
oneYear: "1 rok"
threeDays: "3 dny"
reflectMayTakeTime: "Může trvat nějakou dobu, než se projeví změny."
failedToFetchAccountInformation: "Nepodařily se načíst informace o účtě"
rateLimitExceeded: "Překročení rychlostního limitu"
@@ -1044,8 +1026,6 @@ showClipButtonInNoteFooter: "Přidat \"Připnout\" do akčního menu poznámky"
noteIdOrUrl: "ID nebo URL poznámky"
video: "Video"
videos: "Videa"
audio: "Zvuk"
audioFiles: "Zvuk"
dataSaver: "Spořič dat"
accountMigration: "Migrace účtu"
accountMoved: "Tenhle uživatel se přesunul na nový účet:"
@@ -1073,8 +1053,6 @@ preservedUsernames: "Rezervované uživatelské jména"
preservedUsernamesDescription: "Seznam uživatelských jmén na rezervaci oddělené mezerama. Tyhle jména se potom nebudou moc použít při normálním procesu vytvoření účtu ale můžou být použiti manuálně administratorém. Existujících účtů se to nedotkne."
createNoteFromTheFile: "Vytvořit poznámku z tohodle souboru"
archive: "Archiv"
archived: "Archivované"
unarchive: "Obnovit"
channelArchiveConfirmTitle: "Opravdu chcete archivovat {name}?"
channelArchiveConfirmDescription: "Archivovaný kanál se objeví v seznamu kanálů nebo ve výsledcích hledání. Nové poznámky se nedají vložit do seznamu."
thisChannelArchived: "Tenhle kanál je archivovaný"
@@ -1121,7 +1099,6 @@ doYouAgree: "Souhlasíte?"
beSureToReadThisAsItIsImportant: "Přečtěte si prosím tyto důležité informace."
iHaveReadXCarefullyAndAgree: "Přečetl jsem si text \"{x}\" a souhlasím s ním."
icon: "Avatar"
forYou: "Pro vás"
replies: "Odpovědět"
renotes: "Přeposlat"
sourceCode: "Zdrojový kód"

View File

@@ -3401,7 +3401,11 @@ _imageEffector:
threshold: "Schwellenwert"
centerX: "Zentrum X"
centerY: "Zentrum Y"
zoomLinesSmoothing: "Glättung"
zoomLinesSmoothingDescription: "Die Einstellungen für die Glättung und für die Breite der Konzentrationslinien können nicht gleichzeitig verwendet werden."
zoomLinesThreshold: "Breite der Konzentrationslinien"
zoomLinesMaskSize: "Mitteldurchmesser"
zoomLinesBlack: "Schwarz machen"
circle: "Kreisförmig"
drafts: "Entwurf"
_drafts:

View File

@@ -3401,7 +3401,11 @@ _imageEffector:
threshold: "Threshold"
centerX: "Center X"
centerY: "Center Y"
zoomLinesSmoothing: "Smoothing"
zoomLinesSmoothingDescription: "Smoothing and zoom line width cannot be used together."
zoomLinesThreshold: "Zoom line width"
zoomLinesMaskSize: "Center diameter"
zoomLinesBlack: "Make black"
circle: "Circular"
drafts: "Drafts"
_drafts:

View File

@@ -3312,7 +3312,7 @@ _clientPerformanceIssueTip:
_clip:
tip: "Clip es una función que permite organizar varias notas."
_userLists:
tip: "Puedes crear listas que incluyan a cualquier usuario. Las listas creadas se pueden visualizar en forma de cronología."
tip: "Las listas pueden contener cualquier usuario que especifiques al crearlas, la lista creada puede mostrarse entonces como una línea de tiempo mostrando solo los usuarios especificados."
watermark: "Marca de Agua"
defaultPreset: "Por defecto"
_watermarkEditor:
@@ -3401,9 +3401,11 @@ _imageEffector:
threshold: "Umbral"
centerX: "Centrar X"
centerY: "Centrar Y"
density: "Densidad"
zoomLinesOutlineThickness: "Grosor del borde"
zoomLinesSmoothing: "Suavizado"
zoomLinesSmoothingDescription: "El suavizado y el ancho de línea de zoom no se pueden utilizar juntos."
zoomLinesThreshold: "Ancho de línea del zoom"
zoomLinesMaskSize: "Diámetro del centro"
zoomLinesBlack: "Cambiar color de las líneas de impacto a negro."
circle: "Círculo"
drafts: "Borrador"
_drafts:

View File

@@ -1,7 +1,7 @@
---
_lang_: "Italiano"
headlineMisskey: "Rete collegata tramite Note"
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n🚀 Esplora un nuovo mondo insieme a noi!"
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n\n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n\n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n\n🚀 Esplora un nuovo mondo insieme a noi!"
poweredByMisskeyDescription: "{name} è uno dei servizi (chiamati istanze) che utilizzano la piattaforma open source <b>Misskey</b>."
monthAndDay: "{day}/{month}"
search: "Cerca"
@@ -1408,7 +1408,6 @@ frame: "Cornice"
presets: "Preimpostato"
zeroPadding: "Al vivo"
nothingToConfigure: "Niente da configurare"
viewRenotedChannel: "Visualizza il canale del Rinota"
_imageEditing:
_vars:
caption: "Didascalia dell'immagine"
@@ -3339,7 +3338,7 @@ _watermarkEditor:
stripeWidth: "Larghezza della linea"
stripeFrequency: "Il numero di linee"
polkadot: "A pallini"
checker: "Scacchiera"
checker: "revisore"
polkadotMainDotOpacity: "Opacità del punto principale"
polkadotMainDotRadius: "Dimensione del punto principale"
polkadotSubDotOpacity: "Opacità del punto secondario"
@@ -3368,7 +3367,7 @@ _imageEffector:
zoomLines: "Linea di saturazione"
stripe: "Strisce"
polkadot: "A pallini"
checker: "Scacchiera"
checker: "revisore"
blockNoise: "Attenua rumore"
tearing: "Strappa immagine"
fill: "Riempimento"
@@ -3402,9 +3401,11 @@ _imageEffector:
threshold: "Soglia"
centerX: "Centro orizzontale"
centerY: "Centro verticale"
density: "Densità"
zoomLinesOutlineThickness: "Spessore del bordo"
zoomLinesSmoothing: "Levigatura"
zoomLinesSmoothingDescription: "Non si possono usare insieme la levigatura e la larghezza della linea centrale."
zoomLinesThreshold: "Limite delle linee zoom"
zoomLinesMaskSize: "Ampiezza del diametro"
zoomLinesBlack: "Bande nere"
circle: "Circolare"
drafts: "Bozze"
_drafts:

View File

@@ -1408,7 +1408,6 @@ frame: "フレーム"
presets: "プリセット"
zeroPadding: "ゼロ埋め"
nothingToConfigure: "設定項目はありません"
viewRenotedChannel: "リノート先のチャンネルを見る"
_imageEditing:
_vars:
@@ -3513,9 +3512,11 @@ _imageEffector:
threshold: "しきい値"
centerX: "中心X"
centerY: "中心Y"
density: "密度"
zoomLinesOutlineThickness: "線の影の太さ"
zoomLinesSmoothing: "スムージング"
zoomLinesSmoothingDescription: "スムージングと集中線の幅の設定は併用できません。"
zoomLinesThreshold: "集中線の幅"
zoomLinesMaskSize: "中心径"
zoomLinesBlack: "黒色にする"
circle: "円形"
drafts: "下書き"
@@ -3553,17 +3554,3 @@ _qr:
scanFile: "端末の画像をスキャン"
raw: "テキスト"
mfm: "MFM"
_room:
snapToGrid: "グリッドにスナップ"
gridScale: "グリッドサイズ"
thereAreUnsavedChanges: "未保存の変更があります"
revertAllChangesConfirmation: "全ての変更を取り消し、部屋を最後に保存した状態まで戻しますか?"
graphicsQuality: "グラフィックの品質"
frameRate: "フレームレート"
resolution: "解像度"
yourDeviceNotSupported_title: "お使いのデバイスはMisskeyRoomをサポートしていません。"
yourDeviceNotSupported_description: "MisskeyRoomを動作させるには、WebGPUをサポートするデバイスが必要です。"
failedToInitialize: "初期化に失敗しました"
crushed_description: "バグ、またはデバイスのリソース不足の可能性が考えられます。"
antialiasing: "アンチエイリアス"

View File

@@ -1408,7 +1408,6 @@ frame: "프레임"
presets: "프리셋"
zeroPadding: "0으로 채우기"
nothingToConfigure: "설정 항목이 없습니다."
viewRenotedChannel: "리노트된 채널 보기"
_imageEditing:
_vars:
caption: "파일 설명"
@@ -1467,17 +1466,17 @@ _chat:
newMessage: "새로운 메시지"
individualChat: "개인 대화"
individualChat_description: "특정 유저와 일대일 채팅을 할 수 있습니다."
roomChat: "그룹 채팅"
roomChat: " 채팅"
roomChat_description: "여러 명이 함께 채팅할 수 있습니다.\n또한, 개인 채팅을 허용하지 않은 유저와도 상대방이 수락하면 채팅을 할 수 있습니다."
createRoom: "방 만들기"
createRoom: "룸을 생성"
inviteUserToChat: "유저를 초대하여 채팅을 시작하세요"
yourRooms: "만들어진 방"
joiningRooms: "참가 중인 "
yourRooms: "생성한 룸"
joiningRooms: "참가 중인 "
invitations: "초대"
noInvitations: "초대장이 없습니다"
history: "이력"
noHistory: "기록이 없습니다"
noRooms: "이 없습니다"
noRooms: "이 없습니다"
inviteUser: "유저를 초대"
sentInvitations: "초대를 보내기"
join: "참여"
@@ -1488,14 +1487,14 @@ _chat:
home: "홈"
send: "전송"
newline: "줄바꿈"
muteThisRoom: "이 을 뮤트하기"
deleteRoom: "을 삭제하기"
muteThisRoom: "이 을 뮤트"
deleteRoom: "을 삭제"
chatNotAvailableForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅이 활성화되어 있지 않습니다."
chatIsReadOnlyForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅은 읽기 전용입니다. 새로 쓰거나 채팅 룸을 만들거나 참가할 수 없습니다."
chatNotAvailableInOtherAccount: "상대방 계정에서 채팅 기능을 사용할 수 없는 상태입니다."
cannotChatWithTheUser: "이 유저와 채팅을 시작할 수 없습니다"
cannotChatWithTheUser_description: "채팅을 사용할 수 없는 상태이거나 상대방이 채팅을 열지 않은 상태입니다."
youAreNotAMemberOfThisRoomButInvited: "이 의 참가자가 아니지만 초대를 받았습니다. 참가하려면 초대를 수락하세요."
youAreNotAMemberOfThisRoomButInvited: "당신은 의 참가자가 아닙니다만 초대 신청을 받으셨습니다. 참가하려면 초대를 수락해주십시오."
doYouAcceptInvitation: "초대를 수락하시겠습니까?"
chatWithThisUser: "채팅하기"
thisUserAllowsChatOnlyFromFollowers: "이 유저는 팔로워만 채팅을 할 수 있습니다."
@@ -2545,7 +2544,7 @@ _widgets:
_userList:
chooseList: "리스트 선택"
clicker: "클리커"
birthdayFollowings: " 생일인 사용자"
birthdayFollowings: "오늘이 생일인 유저"
chat: "채팅하기"
_widgetOptions:
showHeader: "해더를 표시"
@@ -2793,7 +2792,7 @@ _notification:
newNote: "새 게시물"
unreadAntennaNote: "안테나 {name}"
roleAssigned: "역할이 부여 되었습니다."
chatRoomInvitationReceived: "채팅에 초대되었습니다"
chatRoomInvitationReceived: "채팅에 초대받았습니다"
emptyPushNotificationMessage: "푸시 알림이 갱신되었습니다"
achievementEarned: "도전 과제를 달성했습니다"
testNotification: "알림 테스트"
@@ -2824,7 +2823,7 @@ _notification:
receiveFollowRequest: "팔로우 요청을 받았을 때"
followRequestAccepted: "팔로우 요청이 승인되었을 때"
roleAssigned: "역할이 부여됨"
chatRoomInvitationReceived: "채팅에 초대"
chatRoomInvitationReceived: "채팅에 초대받음"
achievementEarned: "도전 과제 획득"
exportCompleted: "추출을 성공함"
login: "로그인"
@@ -2978,7 +2977,7 @@ _moderationLogTypes:
deletePage: "페이지를 삭제"
deleteFlash: "Play를 삭제"
deleteGalleryPost: "갤러리 게시물을 삭제"
deleteChatRoom: "채팅 삭제하기"
deleteChatRoom: "채팅 삭제"
updateProxyAccountDescription: "프록시 계정의 설명 업데이트"
_fileViewer:
title: "파일 상세"
@@ -3402,9 +3401,11 @@ _imageEffector:
threshold: "한계 값"
centerX: "X축 중심"
centerY: "Y축 중심"
density: "밀도"
zoomLinesOutlineThickness: "선 그림자의 굵기"
zoomLinesSmoothing: "다듬기"
zoomLinesSmoothingDescription: "다듬기와 집중선 폭 설정은 같이 쓸 수 없습니다."
zoomLinesThreshold: "집중선 폭"
zoomLinesMaskSize: "중앙 값"
zoomLinesBlack: "검은색으로 하기"
circle: "원형"
drafts: "초안"
_drafts:

View File

@@ -5,7 +5,6 @@ introMisskey: "ຍິນດີຕ້ອນຮັບ! Misskey ເປັນຊອ
poweredByMisskeyDescription: "{name} ແມ່ນສ່ວນໜຶ່ງຂອງການບໍລິການທີ່ຂັບເຄື່ອນໂດຍແພລດຟອມ open source. <b>Misskey</b> (ເອີ້ນວ່າ \"Misskey instance\")"
monthAndDay: "ເດືອນ{month} / ວັນ{day}"
search: "ຄົ້ນຫາ"
reset: "ຣີເຊັດ"
notifications: "ການແຈ້ງເຕືອນ"
username: "ຊື່ຜູ້ໃຊ້"
password: "ລະຫັດຜ່ານ"

View File

@@ -3289,7 +3289,11 @@ _imageEffector:
threshold: "Limiar"
centerX: "Centralizar X"
centerY: "Centralizar Y"
zoomLinesSmoothing: "Suavização"
zoomLinesSmoothingDescription: "Suavização e largura das linhas de zoom não podem ser utilizados simultaneamente."
zoomLinesThreshold: "Largura das linhas de zoom"
zoomLinesMaskSize: "Diâmetro do centro"
zoomLinesBlack: "Linhas pretas"
circle: "Circular"
drafts: "Rascunhos"
_drafts:

View File

@@ -3401,9 +3401,11 @@ _imageEffector:
threshold: "เทรชโฮลด์"
centerX: "กลาง X"
centerY: "กลาง Y"
density: "ความหนาทึบ"
zoomLinesOutlineThickness: "ความหนาของเงาเส้น"
zoomLinesSmoothing: "ทำให้สมูธ"
zoomLinesSmoothingDescription: "ตั้งให้สมูธไม่สามารถใช้ร่วมกับตั้งความกว้างเส้นรวมศูนย์ได้"
zoomLinesThreshold: "ความกว้างเส้นรวมศูนย์"
zoomLinesMaskSize: "ขนาดพื้นที่ตรงกลาง"
zoomLinesBlack: "ทำให้ดำ"
circle: "ทรงกลม"
drafts: "ร่าง"
_drafts:

View File

@@ -83,8 +83,6 @@ files: "Dosyalar"
download: "İndir"
driveFileDeleteConfirm: "“{name}” dosyasını silmek istediğinden emin misin? Bu dosyaya ekli tüm notlar da silinecek."
unfollowConfirm: "{name} kullanıcısını cidden takipden çıkmak istiyor musun?"
cancelFollowRequestConfirm: "{name} adlı kişiye gönderdiğiniz takip isteğini iptal etmek ister misiniz?"
rejectFollowRequestConfirm: "{name} adlı kullanıcının takip isteğini reddetmek istiyor musunuz?"
exportRequested: "Dışa aktarma işlemi talep ettin. Bu işlem biraz zaman alabilir. İşlem tamamlandığında Drive'ına eklenecek."
importRequested: "İçe aktarma talebinde bulundun. Bu işlem biraz zaman alabilir."
lists: "Listeler"
@@ -255,7 +253,6 @@ noteDeleteConfirm: "Bu notu silmek istediğinden emin misin?"
pinLimitExceeded: "Artık daha fazla not sabitleyemezsin"
done: "Tamam"
processing: "İşleniyor..."
preprocessing: "Hazırlık aşamasında"
preview: "Önizleme"
default: "Varsayılan"
defaultValueIs: "Varsayılan: {value}"
@@ -304,7 +301,6 @@ uploadFromUrlMayTakeTime: "Yükleme işleminin tamamlanması biraz zaman alabili
uploadNFiles: "{n} dosya yükle"
explore: "Keşfet"
messageRead: "Oku"
readAllChatMessages: "Tüm mesajları okundu olarak işaretle"
noMoreHistory: "Daha fazla geçmiş bilgisi yok."
startChat: "Sohbete başla"
nUsersRead: "{n} tarafından okundu"
@@ -337,7 +333,6 @@ fileName: "Dosya adı"
selectFile: "Dosya seçin"
selectFiles: "Dosyaları seçin"
selectFolder: "Klasör seçin"
unselectFolder: "Klasör seçimini kaldır"
selectFolders: "Klasörleri seçin"
fileNotSelected: "Hiç dosya seçilmedi"
renameFile: "Dosyayı yeniden adlandır"
@@ -350,7 +345,6 @@ addFile: "Bir dosya ekle"
showFile: "Dosyaları göster"
emptyDrive: "Drive boş"
emptyFolder: "Bu klasör boş"
dropHereToUpload: "Yüklemek için dosyalarınızı buraya sürükleyin."
unableToDelete: "Silinemiyor"
inputNewFileName: "Yeni bir dosya adı girin"
inputNewDescription: "Yeni alternatif metin girin"
@@ -543,7 +537,6 @@ regenerate: "Yeniden oluştur"
fontSize: "Yazı tipi boyutu"
mediaListWithOneImageAppearance: "Tek bir resim içeren medya listelerinin yüksekliği"
limitTo: "{x} ile sınırlandır"
showMediaListByGridInWideArea: "Ekran genişliği geniş olduğunda, medya listesi yatay olarak görüntülenecektir."
noFollowRequests: "Bekleyen takip istekleri yok."
openImageInNewTab: "Görüntüleri yeni sekmede aç"
dashboard: "Gösterge paneli"
@@ -779,7 +772,6 @@ lockedAccountInfo: "Notunuzun görünürlüğünü “Yalnızca takipçiler” o
alwaysMarkSensitive: "Varsayılan olarak hassas olarak işaretle"
loadRawImages: "Küçük resimleri göstermek yerine orijinal resimleri yükle"
disableShowingAnimatedImages: "Animasyonlu görüntüleri oynatmayın"
disableShowingAnimatedImages_caption: "Bu ayara rağmen animasyonlu görüntüler oynatılmıyorsa, bunun nedeni tarayıcınızın veya işletim sisteminizin erişilebilirlik ayarları veya güç tasarrufu ayarlarından kaynaklanan parazit olabilir."
highlightSensitiveMedia: "Hassas medyayı vurgulayın"
verificationEmailSent: "Doğrulama e-postası gönderildi. Doğrulamayı tamamlamak için e-postadaki bağlantıyı takip edin."
notSet: "Ayarlı değil"
@@ -1026,9 +1018,6 @@ pushNotificationAlreadySubscribed: "Push bildirimleri zaten açık"
pushNotificationNotSupported: "Push bildirimleri sunucu veya tarayıcı tarafından desteklenmiyor"
sendPushNotificationReadMessage: "Okunduktan sonra push bildirimlerini silin"
sendPushNotificationReadMessageCaption: "Bu, cihazınızın güç tüketimini artırabilir."
pleaseAllowPushNotification: "Lütfen tarayıcı ayarlarınızdan bildirimlere izin verin."
browserPushNotificationDisabled: "Bildirim gönderme izni alınamadı."
browserPushNotificationDisabledDescription: "{serverName} sunucusundan bildirim gönderme izniniz yok. Lütfen tarayıcı ayarlarınızdan bildirimlere izin verin ve tekrar deneyin."
windowMaximize: "Maksimize et"
windowMinimize: "Minimize et"
windowRestore: "Geri yükle"
@@ -1179,7 +1168,6 @@ installed: "Yüklendi"
branding: "Markalaşma"
enableServerMachineStats: "Sunucu donanım istatistiklerini yayınla"
enableIdenticonGeneration: "Kullanıcı identicon oluşturmayı etkinleştir"
showRoleBadgesOfRemoteUsers: "Uzaktan kullanıcılara verilen rol rozetlerini görüntüle"
turnOffToImprovePerformance: "Devre dışı bırakma, daha yüksek performansa yol açabilir."
createInviteCode: "Davet Kodu oluştur"
createWithOptions: "Seçeneklerle oluştur"
@@ -1328,7 +1316,6 @@ acknowledgeNotesAndEnable: "Önlemleri anladıktan sonra açın."
federationSpecified: "Bu sunucu, beyaz liste federasyonunda çalıştırılmaktadır. Yönetici tarafından belirlenen sunucular dışında diğer sunucularla etkileşim kurmak yasaktır."
federationDisabled: "Bu sunucuda federasyon devre dışıdır. Diğer sunuculardaki kullanıcılarla etkileşim kuramazsınız."
draft: "Taslaklar"
draftsAndScheduledNotes: "Taslaklar ve planlanmış gönderiler"
confirmOnReact: "Tepki verirken onaylayın"
reactAreYouSure: "“{emoji}” tepkisini eklemek ister misin?"
markAsSensitiveConfirm: "Bu medyayı hassas olarak ayarlamak ister misin?"
@@ -1357,7 +1344,6 @@ textCount: "Karakter sayısı"
information: "Hakkında"
chat: "Sohbet"
directMessage: "Kullanıcıyla sohbet et"
directMessage_short: "Mesaj"
migrateOldSettings: "Eski istemci ayarlarını taşıma"
migrateOldSettings_description: "Bu işlem otomatik olarak yapılmalıdır, ancak herhangi bir nedenle geçiş başarısız olursa, geçiş işlemini manuel olarak kendin başlatabilirsin. Mevcut yapılandırma bilgileri üzerine yazılacaktır."
compress: "Sıkıştır"
@@ -1385,8 +1371,6 @@ redisplayAllTips: "Tüm “İpucu & Püf Nokta” tekrar göster"
hideAllTips: "Tüm “İpucu & Püf Nokta” gizle"
defaultImageCompressionLevel: "Varsayılan görüntü sıkıştırma düzeyi"
defaultImageCompressionLevel_description: "Düşük seviye görüntü kalitesini korur ancak dosya boyutunu artırır.<br>Yüksek seviye dosya boyutunu azaltır ancak görüntü kalitesini düşürür."
defaultCompressionLevel: "Varsayılan sıkıştırma seviyesi"
defaultCompressionLevel_description: "Ayarı düşürmek kaliteyi koruyacak ancak dosya boyutunu artıracaktır. <br> Ayarı yükseltmek dosya boyutunu küçültecek ancak kaliteyi düşürecektir."
inMinutes: "Dakika(lar)"
inDays: "Gün(ler)"
safeModeEnabled: "Güvenli mod etkinleştirildi"
@@ -1394,74 +1378,21 @@ pluginsAreDisabledBecauseSafeMode: "Güvenli mod etkinleştirildiği için tüm
customCssIsDisabledBecauseSafeMode: "Güvenli mod etkin olduğu için özel CSS uygulanmıyor."
themeIsDefaultBecauseSafeMode: "Güvenli mod etkinken, varsayılan tema kullanılır. Güvenli modu devre dışı bırakmak bu değişiklikleri geri alır."
thankYouForTestingBeta: "Beta sürümünü test ettiğin için teşekkür ederiz!"
createUserSpecifiedNote: "Kullanıcı tarafından belirtilen notlar oluşturun"
schedulePost: "Bir gönderi planla"
scheduleToPostOnX: "{x} için bir gönderi planla"
scheduledToPostOnX: "{x} için bir gönderi planlandı."
schedule: "rezervasyon"
scheduled: "rezervasyon"
widgets: "Widget'lar"
deviceInfo: "Cihaz Bilgileri"
deviceInfoDescription: "Teknik bir sorunuz olduğunda, aşağıdaki bilgileri eklemeniz sorunun çözülmesine yardımcı olabilir."
youAreAdmin: "Siz yöneticisiniz."
frame: "Çerçeve"
presets: "Ön ayar"
zeroPadding: "Sıfır doldurma"
nothingToConfigure: "Ayarlar seçeneği bulunmamaktadır."
_imageEditing:
_vars:
caption: "Dosya başlığı"
filename: "Dosya adı"
filename_without_ext: "Uzantısız dosya adları"
year: "Çekim yılı"
month: "Çekim ayı"
day: "Çekim tarihi"
hour: "Fotoğrafın çekildiği zaman (saat)"
minute: "Çekim süresi (dakika)"
second: "Çekim süresi (saniye)"
camera_model: "Kamera Adı"
camera_lens_model: "Lens adı"
camera_mm: "Odak uzaklığı"
camera_mm_35: "Genişlik (35mm)"
camera_f: "açıklık"
camera_s: "Enstantane hızı"
camera_iso: "ISO hassasiyeti"
gps_lat: "Enlem"
gps_long: "Boylam"
_imageFrameEditor:
title: "Düzenleme kareleri"
tip: "Görselleri, meta verileri içeren çerçeveler ve etiketler ekleyerek süsleyebilirsiniz."
header: "Başlık"
footer: "Alt bilgi"
borderThickness: "jantın genişliği"
labelThickness: "Etiket genişliği"
labelScale: "Etiket ölçeği"
centered: "Merkezlenmiş"
captionMain: "Altyazı (büyük)"
captionSub: "Altyazı (küçük)"
availableVariables: "Mevcut değişkenler"
withQrCode: "2 boyutlu kod"
backgroundColor: "Arka Plan Rengi "
textColor: "Metin Rengi "
font: "Yazı tipi"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
quitWithoutSaveConfirm: "Kaydedilmemiş değişiklikleri silmek ister misin?"
failedToLoadImage: "Görüntü yükleme başarısız oldu "
_compression:
_quality:
high: "Yüksek Kalite "
medium: "Orta Kalite"
low: "Düşük Kalite "
_size:
large: "Büyük Boyut"
medium: "Orta Boyut"
small: "Küçük Boyut"
_order:
newest: "Önce yeni"
oldest: "Önce eski"
_chat:
messages: "Mesaj"
noMessagesYet: "Henüz mesaj yok"
newMessage: "Yeni mesaj"
individualChat: "Özel Sohbet"
@@ -1550,11 +1481,6 @@ _settings:
showUrlPreview: "URL önizlemesi"
showAvailableReactionsFirstInNote: "Mevcut tepkileri en üstte göster."
showPageTabBarBottom: "Sayfa sekme çubuğunu aşağıda göster"
emojiPaletteBanner: "Emoji seçiciye kalıcı olarak bir palet olarak görüntülenecek ön ayarları kaydedebilir veya seçicinin nasıl görüntüleneceğini özelleştirebilirsiniz."
enableAnimatedImages: "Hareketli görüntüleri etkinleştirin"
settingsPersistence_title: "Ayarların kalıcılığı"
settingsPersistence_description1: "Ayarların kalıcı olarak saklanmasını etkinleştirmek, yapılandırma bilgilerinin kaybolmasını önler."
settingsPersistence_description2: "Ortamınıza bağlı olarak bu özelliği etkinleştirmek mümkün olmayabilir."
_chat:
showSenderName: "Gönderenin adını göster"
sendOnEnter: "Enter tuşuna basarak gönderin"
@@ -1563,8 +1489,6 @@ _preferencesProfile:
profileNameDescription: "Bu cihazı tanımlayan bir ad belirle."
profileNameDescription2: "Örnek: “Ana bilgisayar”, “Akıllı telefon”"
manageProfiles: "Profilleri Yönet"
shareSameProfileBetweenDevicesIsNotRecommended: "Aynı profili birden fazla cihazda kullanmak önerilmez."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "Birden fazla cihazda senkronize etmek istediğiniz ayarlarınız varsa, lütfen her bir ayar için \"Birden fazla cihazda senkronize et\" seçeneğini etkinleştirin."
_preferencesBackup:
autoBackup: "Otomatik yedekleme"
restoreFromBackup: "Yedeklemeden geri yükle"
@@ -1574,7 +1498,6 @@ _preferencesBackup:
youNeedToNameYourProfileToEnableAutoBackup: "Otomatik yedeklemeyi etkinleştirmek için bir profil adı ayarlanmalıdır."
autoPreferencesBackupIsNotEnabledForThisDevice: "Bu cihazda ayarların otomatik yedeklemesi etkinleştirilmemiş."
backupFound: "Ayarların yedeği bulundu"
forceBackup: "Ayarların zorunlu yedeklenmesi"
_accountSettings:
requireSigninToViewContents: "İçeriği görüntülemek için oturum açmanız gerekir."
requireSigninToViewContentsDescription1: "Oluşturduğun tüm notları ve diğer içeriği görüntülemek için oturum açman gerekir. Bu, tarayıcıların bilgilerini toplamasına engel olacaktır."
@@ -2080,7 +2003,6 @@ _role:
canManageAvatarDecorations: "Avatar süslerini yönet"
driveCapacity: "Drive kapasitesi"
maxFileSize: "Yükleyebileceğin maksimum dosya boyutu"
maxFileSize_caption: "Önceki aşamada ters proxy veya CDN gibi başka yapılandırma ayarları da olabilir."
alwaysMarkNsfw: "Dosyaları her zaman NSFW olarak işaretle"
canUpdateBioMedia: "Bir simge veya banner görüntüsünü düzenleyebilir"
pinMax: "Sabitlenmiş notların maksimum sayısı"
@@ -2108,7 +2030,6 @@ _role:
uploadableFileTypes_caption: "İzin verilen MIME/dosya türlerini belirtir. Birden fazla MIME türü, yeni bir satırla ayırarak belirtilebilir ve joker karakterler yıldız işareti (*) ile belirtilebilir. (örneğin, image/*)"
uploadableFileTypes_caption2: "Bazı dosya türleri algılanamayabilir. Bu tür dosyalara izin vermek için, spesifikasyona {x} ekle."
noteDraftLimit: "Sunucu notlarının olası taslak sayısı"
scheduledNoteLimit: "Aynı anda oluşturulabilecek planlanmış gönderi sayısı"
watermarkAvailable: "Filigran işlevinin kullanılabilirliği"
_condition:
roleAssignedTo: "Manuel rollere atanmış"
@@ -2499,7 +2420,6 @@ _auth:
scopeUser: "Aşağıdaki kullanıcı olarak çalıştırın"
pleaseLogin: "Uygulamaları yetkilendirmek için lütfen giriş yapın."
byClickingYouWillBeRedirectedToThisUrl: "Erişim izni verildiğinde, otomatik olarak aşağıdaki URL'ye yönlendirileceksin."
alreadyAuthorized: "Bu uygulamaya zaten erişim izinleri verilmiş durumda."
_antennaSources:
all: "Tüm notlar"
homeTimeline: "Takip edilen kullanıcıların notları"
@@ -2548,40 +2468,11 @@ _widgets:
chat: "Sohbet"
_widgetOptions:
showHeader: "Başlığı göster"
transparent: "Arka planı şeffaf yapın"
height: "Yükseklik"
_button:
colored: "Renkli"
_clock:
size: "Boyut"
thickness: "İğne kalınlığı"
thicknessThin: "İnce"
thicknessMedium: "Normal"
thicknessThick: "Kalın"
graduations: "Kadran ölçeği"
graduationDots: "Nokta"
graduationArabic: "Arap rakamları"
fadeGraduations: "ölçeği soluklaştır"
sAnimation: "İkinci el animasyon"
sAnimationElastic: "Gerçek"
sAnimationEaseOut: "Düz"
twentyFour: "24 saat ekran"
labelTime: "Zaman"
labelTz: "Zaman Dilimi"
labelTimeAndTz: "Zaman ve Saat Dilimi"
timezone: "Zaman Dilimi "
showMs: "Milisaniye cinsinden göster"
showLabel: "Etiketi Göster"
_jobQueue:
sound: "Sesleri Çal"
_rss:
url: "RSS beslemesi URL'si"
refreshIntervalSec: "Güncelleme aralığı (saniye)"
maxEntries: "Görüntülenecek maksimum öğe sayısı"
_rssTicker:
shuffle: "Görüntüleme sırasını karıştır"
duration: "Kaydırma yazısı hızı (saniye)"
reverse: "Geriye doğru kaydır"
_birthdayFollowings:
period: "Süre"
_cw:
@@ -2628,20 +2519,9 @@ _postForm:
replyPlaceholder: "Bu notu yanıtla..."
quotePlaceholder: "Bu notu alıntı yap..."
channelPlaceholder: "Bir kanala gönder..."
showHowToUse: "Form açıklamasını göster"
_howToUse:
content_title: "Metin"
content_description: "Yayınlamak istediğiniz içeriği girin."
toolbar_title: "Araç Çubuğu"
toolbar_description: "Dosya ve anket ekleyebilir, açıklamalar ve etiketler ekleyebilir, emoji ve bahsetme mesajları ekleyebilirsiniz."
account_title: "Hesap Menüsü"
account_description: "Paylaşım yaptığınız hesabı değiştirebilir ve hesabınıza kaydedilmiş taslak ve planlanmış paylaşımların listesini görüntüleyebilirsiniz."
visibility_title: "Görünürlük"
visibility_description: "Notlarınıza kimlerin erişebileceğinin kapsamını belirleyebilirsiniz."
menu_title: "Menü"
menu_description: "Taslak olarak kaydetme, gönderi planlama ve tepki ayarlama gibi diğer işlemleri de gerçekleştirebilirsiniz."
submit_title: "Gönder düğmesi"
submit_description: "Bir not paylaşacağım. Ctrl + Enter / Cmd + Enter tuşlarını kullanarak da paylaşım yapabilirsiniz."
_placeholders:
a: "Ne yapıyorsun?"
b: "Çevrende neler oluyor?"
@@ -2787,8 +2667,6 @@ _notification:
youReceivedFollowRequest: "Bir takip isteği aldınız."
yourFollowRequestAccepted: "Takip isteğin kabul edildi."
pollEnded: "Anket sonuçlarııklandı."
scheduledNotePosted: "Rezervasyon defteri yayınlandı."
scheduledNotePostFailed: "Rezervasyon defterine gönderilemedi"
newNote: "Yeni not"
unreadAntennaNote: "{name} anteni"
roleAssigned: "Verilen rol"
@@ -2818,8 +2696,6 @@ _notification:
quote: "Alıntılar"
reaction: "Tepki"
pollEnded: "Anketler sona eriyor"
scheduledNotePosted: "Planlanan gönderi başarılı"
scheduledNotePostFailed: "Planlanan gönderi başarısız oldu"
receiveFollowRequest: "Takip istekleri alındı"
followRequestAccepted: "Kabul edilen takip istekleri"
roleAssigned: "Verilen rol"
@@ -2859,14 +2735,6 @@ _deck:
usedAsMinWidthWhenFlexible: "“Otomatik genişlik ayarı” seçeneği etkinleştirildiğinde, bunun için minimum genişlik kullanılacak."
flexible: "Otomatik genişlik ayarı"
enableSyncBetweenDevicesForProfiles: "Cihazlar arasında profil bilgilerinin senkronizasyonunu etkinleştir"
showHowToUse: "Kullanıcı arayüzü açıklamasını görüntüle"
_howToUse:
addColumn_title: "Sütun ekle"
addColumn_description: "Sütun türlerini seçip ekleyebilirsiniz."
settings_title: "Arayüz Yapılandırması"
settings_description: "Sekme kullanıcı arayüzünü ayrıntılı olarak yapılandırabilirsiniz."
switchProfile_title: "Profili Değiştir"
switchProfile_description: "Kullanıcı arayüzü düzenlerini profil olarak kaydedebilir ve istediğiniz zaman bunlar arasında geçiş yapabilirsiniz."
_columns:
main: "Ana"
widgets: "Widget'lar"
@@ -2927,8 +2795,6 @@ _abuseReport:
notifiedWebhook: "Kullanılacak webhook"
deleteConfirm: "Bildirim alıcısını silmek istediğinden emin misin?"
_moderationLogTypes:
clearQueue: "Kuyruğu temizle"
promoteQueue: "Sıraya alınmış işi yeniden deneyin."
createRole: "Rol oluşturuldu"
deleteRole: "Rol silindi"
updateRole: "Rol güncellendi"
@@ -3323,13 +3189,10 @@ _watermarkEditor:
title: "Filigranı Düzenle"
cover: "Her şeyi örtün"
repeat: "her yere yayılmış"
preserveBoundingRect: "Döndürme sırasında dışarı çıkmayacak şekilde ayarlayın."
opacity: "Opaklık"
scale: "Boyut"
text: "Metin"
qr: "2 boyutlu kod"
position: "Pozisyon"
margin: "Kenar"
type: "Tür"
image: "Görseller"
advanced: "Gelişmiş"
@@ -3344,21 +3207,16 @@ _watermarkEditor:
polkadotSubDotOpacity: "İkincil noktanın opaklığı"
polkadotSubDotRadius: "İkincil noktanın boyutu"
polkadotSubDotDivisions: "Alt nokta sayısı."
leaveBlankToAccountUrl: "Boş bırakılması durumunda hesap URL'si görüntülenecektir."
failedToLoadImage: "Görüntü yükleme başarısız oldu "
_imageEffector:
title: "Effektler"
addEffect: "Efektler Ekle"
discardChangesConfirm: "Cidden çıkmak istiyor musun? Kaydedilmemiş değişikliklerin var."
failedToLoadImage: "Görüntü yükleme başarısız oldu "
_fxs:
chromaticAberration: "Renk Sapması"
glitch: "Bozulma"
mirror: "Ayna"
invert: "Renkleri Ters Çevir"
grayscale: "Gri tonlama"
blur: "Bulanıklık"
pixelate: "Mozaik"
colorAdjust: "Renk Düzeltme"
colorClamp: "Renk Sıkıştırma"
colorClampAdvanced: "Renk Sıkıştırma (Gelişmiş)"
@@ -3370,13 +3228,10 @@ _imageEffector:
checker: "Denetleyici"
blockNoise: "Gürültüyü Engelle"
tearing: "Yırtılma"
fill: "Doldur"
_fxProps:
angle: "Açı"
scale: "Boyut"
size: "Boyut"
radius: "Yarıçap"
samples: "Örnek sayısı"
offset: "Pozisyon"
color: "Renk"
opacity: "Opaklık"
@@ -3401,10 +3256,11 @@ _imageEffector:
threshold: "Eşik"
centerX: "Merkez X"
centerY: "Merkez Y"
density: "Yoğunluk"
zoomLinesOutlineThickness: "çizgi gölge kalınlığı"
zoomLinesSmoothing: "Düzeltme"
zoomLinesSmoothingDescription: "Düzeltme ve yakınlaştırma çizgi genişliği birlikte kullanılamaz."
zoomLinesThreshold: "Zoom çizgi genişliği"
zoomLinesMaskSize: "Merkez çapı"
circle: "Dairesel"
zoomLinesBlack: "Siyah yap"
drafts: "Taslaklar"
_drafts:
select: "Taslak Seç"
@@ -3420,22 +3276,6 @@ _drafts:
restoreFromDraft: "Taslaktan geri yükle"
restore: "Geri yükle"
listDrafts: "Taslaklar Listesi"
schedule: "Planlanmış Gönderi"
listScheduledNotes: "Planlanmış gönderilerin listesi"
cancelSchedule: "Rezervasyonu iptal et"
qr: "2 boyutlu kod"
_qr:
showTabTitle: "Ekran"
readTabTitle: "Okumak"
shareTitle: "{name}{acct}"
shareText: "Beni Fediverse'te takip edin!"
chooseCamera: "Kamera Seç"
cannotToggleFlash: "Işık seçeneği mevcut değil."
turnOnFlash: "Işığıın"
turnOffFlash: "Işığı kapatın"
startQr: "Özgeçmiş Kodu Okuyucu"
stopQr: "Kod okuyucuyu durdurun"
noQrCodeFound: "QR kodu bulunamadı"
scanFile: "Cihazdaki görüntüyü tarayın"
raw: "Metin"
mfm: "MFM"

View File

@@ -1,5 +1,5 @@
---
_lang_: "Tiếng Việt"
_lang_: "Tiếng Việt "
headlineMisskey: "Mạng xã hội liên hợp"
introMisskey: "Xin chào! Misskey là một nền tảng tiểu blog phi tập trung mã nguồn mở.\nViết \"tút\" để chia sẻ những suy nghĩ của bạn 📡\nBằng \"biểu cảm\", bạn có thể bày tỏ nhanh chóng cảm xúc của bạn với các tút 👍\nHãy khám phá một thế giới mới! 🚀"
poweredByMisskeyDescription: "{name} là một trong những chủ máy của <b>Misskey</b> là nền tảng mã nguồn mở"
@@ -576,7 +576,6 @@ showFixedPostForm: "Hiện khung soạn tút ở phía trên bảng tin"
showFixedPostFormInChannel: "Hiển thị mẫu bài đăng ở phía trên bản tin"
withRepliesByDefaultForNewlyFollowed: "Mặc định hiển thị trả lời từ những người dùng mới theo dõi trong dòng thời gian"
newNoteRecived: "Đã nhận tút mới"
newNote: "Ghi chú mới"
sounds: "Âm thanh"
sound: "Âm thanh"
notificationSoundSettings: "Cài đặt âm thanh thông báo"
@@ -849,7 +848,7 @@ hideOnlineStatus: "Ẩn trạng thái online"
hideOnlineStatusDescription: "Ẩn trạng thái online của bạn làm giảm sự tiện lợi của một số tính năng như tìm kiếm."
online: "Online"
active: "Hoạt động"
offline: "Ngoại tuyến"
offline: "Offline"
notRecommended: "Không đề xuất"
botProtection: "Bảo vệ Bot"
instanceBlocking: "Máy chủ đã chặn"
@@ -1221,7 +1220,6 @@ information: "Giới thiệu"
chat: "Trò chuyện"
migrateOldSettings: "Di chuyển cài đặt cũ"
migrateOldSettings_description: "Thông thường, quá trình này diễn ra tự động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thủ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại của bạn."
driveAboutTip: "Trong Drive, danh sách các tệp bạn đã tải lên trước đây sẽ được hiển thị.<br>\nBạn có thể sử dụng lại chúng khi đính kèm vào ghi chú, hoặc tải lên trước các tệp để đăng sau.<br>\n<b>Lưu ý rằng nếu bạn xóa một tệp, tệp đó cũng sẽ biến mất khỏi tất cả những nơi đã sử dụng tệp đó (ghi chú, trang, ảnh đại diện, biểu ngữ, v.v.).</b><br>\nBạn cũng có thể tạo các thư mục để sắp xếp chúng."
inMinutes: "phút"
inDays: "ngày"
widgets: "Tiện ích"

View File

@@ -1408,7 +1408,6 @@ frame: "边框"
presets: "预设值"
zeroPadding: "填充 0"
nothingToConfigure: "没有项目"
viewRenotedChannel: "查看转帖所属频道"
_imageEditing:
_vars:
caption: "文件标题"
@@ -3402,9 +3401,11 @@ _imageEffector:
threshold: "阈值"
centerX: "中心 X "
centerY: "中心 Y"
density: "密度"
zoomLinesOutlineThickness: "线条阴影粗细"
zoomLinesSmoothing: "平滑"
zoomLinesSmoothingDescription: "平滑和集中线宽度设置不能同时使用。"
zoomLinesThreshold: "集中线宽度"
zoomLinesMaskSize: "中心直径"
zoomLinesBlack: "变成黑色"
circle: "圆形"
drafts: "草稿"
_drafts:

View File

@@ -10,7 +10,7 @@ notifications: "通知"
username: "使用者名稱"
password: "密碼"
initialPasswordForSetup: "啟動初始設定的密碼"
initialPasswordIsIncorrect: "啟動初始設定密碼錯誤。"
initialPasswordIsIncorrect: "啟動初始設定密碼錯誤。"
initialPasswordForSetupDescription: "如果您自己安裝了 Misskey請使用您在設定檔中輸入的密碼。\n如果您使用 Misskey 的託管服務之類的服務,請使用提供的密碼。\n如果您尚未設定密碼請將其留空並繼續。"
forgotPassword: "忘記密碼"
fetchingAsApObject: "從聯邦宇宙取得中..."
@@ -1408,7 +1408,6 @@ frame: "邊框"
presets: "預設值"
zeroPadding: "補零"
nothingToConfigure: "無可設定的項目"
viewRenotedChannel: "顯示轉發貼文者的頻道"
_imageEditing:
_vars:
caption: "檔案標題"
@@ -3402,9 +3401,11 @@ _imageEffector:
threshold: "閾值"
centerX: "X中心座標"
centerY: "Y中心座標"
density: "密度"
zoomLinesOutlineThickness: "線條陰影的粗細"
zoomLinesSmoothing: "平滑化"
zoomLinesSmoothingDescription: "平滑化與集中線寬度設定不能同時使用。"
zoomLinesThreshold: "集中線的寬度"
zoomLinesMaskSize: "中心直徑"
zoomLinesBlack: "變成黑色"
circle: "圓形"
drafts: "草稿\n"
_drafts:

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2026.5.0",
"version": "2026.2.0-beta.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@10.33.0",
"packageManager": "pnpm@10.30.1",
"workspaces": [
"packages/misskey-js",
"packages/i18n",
@@ -28,9 +28,9 @@
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "cd packages/backend && pnpm compile-config && node ./built/entry.js",
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/entry.js",
"start": "cd packages/backend && pnpm compile-config && node ./built/boot/entry.js",
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"cli": "cd packages/backend && pnpm cli",
"init": "pnpm migrate",
"migrate": "cd packages/backend && pnpm migrate",
@@ -44,8 +44,8 @@
"cy:run": "pnpm cypress run",
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"e2e-dev-container": "ncp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run",
"backend-unit-test": "cd packages/backend && pnpm test",
"backend-unit-test-and-coverage": "cd packages/backend && pnpm test-and-coverage",
"jest": "cd packages/backend && pnpm jest",
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
"test": "pnpm -r test",
"test-and-coverage": "pnpm -r test-and-coverage",
"clean": "node scripts/clean.mjs",
@@ -53,30 +53,30 @@
"cleanall": "pnpm clean-all"
},
"dependencies": {
"cssnano": "7.1.5",
"esbuild": "0.28.0",
"cssnano": "7.1.2",
"esbuild": "0.27.3",
"execa": "9.6.1",
"ignore-walk": "8.0.0",
"js-yaml": "4.1.1",
"postcss": "8.5.9",
"tar": "7.5.13",
"terser": "5.46.1"
"postcss": "8.5.6",
"tar": "7.5.9",
"terser": "5.46.0"
},
"devDependencies": {
"@eslint/js": "9.39.4",
"@eslint/js": "9.39.3",
"@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9",
"@types/node": "24.12.2",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@typescript/native-preview": "7.0.0-dev.20260421.2",
"@types/node": "24.10.13",
"@typescript-eslint/eslint-plugin": "8.56.0",
"@typescript-eslint/parser": "8.56.0",
"@typescript/native-preview": "7.0.0-dev.20260116.1",
"cross-env": "10.1.0",
"cypress": "15.13.1",
"eslint": "9.39.4",
"globals": "17.5.0",
"cypress": "15.10.0",
"eslint": "9.39.3",
"globals": "17.3.0",
"ncp": "2.0.0",
"pnpm": "10.33.0",
"start-server-and-test": "3.0.2",
"pnpm": "10.30.1",
"start-server-and-test": "2.1.3",
"typescript": "5.9.3"
},
"optionalDependencies": {
@@ -86,7 +86,7 @@
"overrides": {
"@aiscript-dev/aiscript-languageserver": "-",
"chokidar": "5.0.0",
"lodash": "4.18.1"
"lodash": "4.17.23"
},
"ignoredBuiltDependencies": [
"@sentry-internal/node-cpu-profiler",

29
packages/backend/.swcrc Normal file
View File

@@ -0,0 +1,29 @@
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"jsx": true,
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true,
"react": {
"runtime": "automatic",
"importSource": "@kitajs/html"
}
},
"experimental": {
"keepImportAssertions": true
},
"baseUrl": "src",
"paths": {
"@/*": ["*"]
},
"target": "es2022"
},
"minify": false,
"sourceMaps": "inline"
}

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Misskey API</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script
id="api-reference"
data-url="/api.json"></script>
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,4 @@
user-agent: *
allow: /
# todo: sitemap

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

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

View File

@@ -0,0 +1,220 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
// clearMocks: false,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts'],
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
globals: {
},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
// Do not resolve .wasm.js to .wasm by the rule below
'^(.+)\\.wasm\\.js$': '$1.wasm.js',
// SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule
// converts it again to `../../src/foo/bar` which then can be resolved to
// `.ts` files.
// See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225
// TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can
// directly import `.ts` files without this hack.
'^((?:\\.{1,2}|[A-Z:])*/.*)\\.js$': '$1',
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
//preset: "ts-jest/presets/js-with-ts-esm",
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: './jest-resolver.cjs',
// Automatically restore mock state between every test
restoreMocks: true,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
roots: [
"<rootDir>"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"<rootDir>/test/unit/**/*.ts",
"<rootDir>/src/**/*.test.ts",
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.(t|j)sx?$": ["@swc/jest"],
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\",
// "\\.pnp\\.[^\\\\]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
extensionsToTreatAsEsm: ['.ts', '.tsx'],
testTimeout: 60000,
// Let Jest kill the test worker whenever it grows too much
// (It seems there's a known memory leak issue in Node.js' vm.Script used by Jest)
// https://github.com/facebook/jest/issues/11956
maxWorkers: 1, // Make it use worker (that can be killed and restarted)
logHeapUsage: true, // To debug when out-of-memory happens on CI
workerIdleMemoryLimit: '1GiB', // Limit the worker to 1GB (GitHub Workflows dies at 2GB)
maxConcurrency: 32,
};

View File

@@ -0,0 +1,15 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
const base = require('./jest.config.cjs')
module.exports = {
...base,
globalSetup: "<rootDir>/built-test/entry.js",
setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
testMatch: [
"<rootDir>/test/e2e/**/*.ts",
],
};

View File

@@ -0,0 +1,13 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
const base = require('./jest.config.cjs');
module.exports = {
...base,
testMatch: [
'<rootDir>/test-federation/test/**/*.test.ts',
],
};

View File

@@ -0,0 +1,15 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
const base = require('./jest.config.cjs')
module.exports = {
...base,
globalSetup: "<rootDir>/test/jest.setup.unit.cjs",
testMatch: [
"<rootDir>/test/unit/**/*.ts",
"<rootDir>/src/**/*.test.ts",
],
};

31
packages/backend/jest.js Normal file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env node
import child_process from 'node:child_process';
import path from 'node:path';
import url from 'node:url';
import semver from 'semver';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const args = [];
args.push(...[
...semver.satisfies(process.version, '^20.17.0 || ^22.0.0 || ^24.10.0') ? ['--no-experimental-require-module'] : [],
'--experimental-vm-modules',
'--experimental-import-meta-resolve',
path.join(__dirname, 'node_modules/jest/bin/jest.js'),
...process.argv.slice(2),
]);
const child = child_process.spawn(process.execPath, args, { stdio: 'inherit' });
child.on('error', (err) => {
console.error('Failed to start Jest:', err);
process.exit(1);
});
child.on('exit', (code, signal) => {
if (code === null) {
process.exit(128 + signal);
} else {
process.exit(code);
}
});

View File

@@ -1,22 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class AddCategoryToAvatarDecorations1766652173085 {
name = 'AddCategoryToAvatarDecorations1766652173085';
/**
* @param {QueryRunner} queryRunner
*/
async up(queryRunner) {
await queryRunner.query('ALTER TABLE "avatar_decoration" ADD "category" character varying(128)');
}
/**
* @param {QueryRunner} queryRunner
*/
async down(queryRunner) {
await queryRunner.query('ALTER TABLE "avatar_decoration" DROP COLUMN "category"');
}
};

View File

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

View File

@@ -7,33 +7,51 @@
"node": "^22.15.0 || ^24.10.0"
},
"scripts": {
"start": "pnpm compile-config && node ./built/entry.js",
"start:inspect": "pnpm compile-config && node --inspect ./built/entry.js",
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/entry.js",
"start": "pnpm compile-config && node ./built/boot/entry.js",
"start:inspect": "pnpm compile-config && node --inspect ./built/boot/entry.js",
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"migrate": "pnpm compile-config && pnpm typeorm migration:run -d ormconfig.js",
"revert": "pnpm compile-config && pnpm typeorm migration:revert -d ormconfig.js",
"cli": "pnpm compile-config && node ./built/cli.js",
"cli": "pnpm compile-config && node ./src-js/boot/cli.js",
"check:connect": "pnpm compile-config && node ./scripts/check_connect.js",
"compile-config": "node ./scripts/compile_config.js",
"build": "rolldown -c",
"build:unit": "rolldown -c --sourcemap",
"build:e2e": "rolldown -c --e2e",
"build": "swc src -d src-js -D --strip-leading-paths && node ./build.js",
"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
"watch:swc": "swc src -d built -D -w --strip-leading-paths",
"build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
"watch": "pnpm compile-config && node ./scripts/watch.mjs",
"restart": "pnpm build && pnpm start",
"dev": "pnpm compile-config && rolldown -c --watch",
"dev": "pnpm compile-config && node ./scripts/dev.mjs",
"typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
"lint": "pnpm typecheck && pnpm eslint",
"test": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.unit.ts",
"test:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.e2e.ts",
"test:fed": "cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.fed.ts",
"test-and-coverage": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.unit.ts",
"test-and-coverage:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.e2e.ts",
"jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs",
"jest:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.e2e.cjs",
"jest:fed": "pnpm compile-config && node ./jest.js --forceExit --config jest.config.fed.cjs",
"jest-and-coverage": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.unit.cjs",
"jest-and-coverage:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.e2e.cjs",
"jest-clear": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --clearCache",
"test": "pnpm jest",
"test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e",
"test:fed": "pnpm jest:fed",
"test-and-coverage": "pnpm jest-and-coverage",
"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
"check-migrations": "node scripts/check_migrations_clean.js",
"generate-api-json": "pnpm compile-config && node ./scripts/generate_api_json.js"
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.15.11",
"@swc/core-darwin-x64": "1.15.11",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.15.11",
"@swc/core-linux-arm64-gnu": "1.15.11",
"@swc/core-linux-arm64-musl": "1.15.11",
"@swc/core-linux-x64-gnu": "1.15.11",
"@swc/core-linux-x64-musl": "1.15.11",
"@swc/core-win32-arm64-msvc": "1.15.11",
"@swc/core-win32-ia32-msvc": "1.15.11",
"@swc/core-win32-x64-msvc": "1.15.11",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.1.0",
@@ -53,29 +71,30 @@
"utf-8-validate": "6.0.6"
},
"dependencies": {
"@aws-sdk/client-s3": "3.1030.0",
"@aws-sdk/lib-storage": "3.1030.0",
"@aws-sdk/client-s3": "3.995.0",
"@aws-sdk/lib-storage": "3.995.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.4",
"@fastify/cors": "11.2.0",
"@fastify/express": "4.0.5",
"@fastify/http-proxy": "11.4.4",
"@fastify/multipart": "10.0.0",
"@fastify/static": "9.1.3",
"@fastify/express": "4.0.4",
"@fastify/http-proxy": "11.4.1",
"@fastify/multipart": "9.4.0",
"@fastify/static": "9.0.0",
"@kitajs/html": "4.2.13",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.97",
"@nestjs/common": "11.1.19",
"@nestjs/core": "11.1.19",
"@nestjs/testing": "11.1.19",
"@oxc-project/runtime": "0.125.0",
"@napi-rs/canvas": "0.1.94",
"@nestjs/common": "11.1.14",
"@nestjs/core": "11.1.14",
"@nestjs/testing": "11.1.14",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.48.0",
"@sentry/profiling-node": "10.48.0",
"@simplewebauthn/server": "13.3.0",
"@sinonjs/fake-timers": "15.3.2",
"@smithy/node-http-handler": "4.5.2",
"@sentry/node": "10.39.0",
"@sentry/profiling-node": "10.39.0",
"@simplewebauthn/server": "13.2.2",
"@sinonjs/fake-timers": "15.1.0",
"@smithy/node-http-handler": "4.4.10",
"@swc/cli": "0.8.0",
"@swc/core": "1.15.11",
"@twemoji/parser": "16.0.0",
"accepts": "1.3.8",
"ajv": "8.18.0",
@@ -84,68 +103,68 @@
"bcryptjs": "3.0.3",
"blurhash": "2.0.5",
"body-parser": "2.2.2",
"bullmq": "5.73.5",
"bullmq": "5.69.4",
"cacheable-lookup": "7.0.0",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "5.0.0",
"color-convert": "3.1.3",
"content-disposition": "1.1.0",
"content-disposition": "1.0.1",
"date-fns": "4.1.0",
"deep-email-validator": "0.1.21",
"fastify": "5.8.5",
"fastify": "5.7.4",
"fastify-raw-body": "5.0.0",
"feed": "5.2.0",
"file-type": "22.0.1",
"file-type": "21.3.0",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5",
"got": "14.6.6",
"hpagent": "1.2.0",
"http-link-header": "1.1.3",
"i18n": "workspace:*",
"ioredis": "5.10.1",
"ioredis": "5.9.3",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.3.0",
"is-svg": "6.1.0",
"json5": "2.2.3",
"jsonld": "9.0.0",
"juice": "11.1.1",
"meilisearch": "0.57.0",
"meilisearch": "0.55.0",
"mfm-js": "0.25.0",
"mime-types": "3.0.2",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828",
"nanoid": "5.1.7",
"nanoid": "5.1.6",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"node-html-parser": "7.1.0",
"nodemailer": "8.0.5",
"nsfwjs": "4.3.0",
"node-html-parser": "7.0.2",
"nodemailer": "8.0.1",
"nsfwjs": "4.2.0",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
"otpauth": "9.5.0",
"pg": "8.20.0",
"pg": "8.18.0",
"pkce-challenge": "6.0.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"qrcode": "1.5.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.24.0",
"re2": "1.23.3",
"reflect-metadata": "0.2.2",
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.2",
"sanitize-html": "2.17.3",
"sanitize-html": "2.17.1",
"secure-json-parse": "4.1.0",
"semver": "7.7.4",
"sharp": "0.33.5",
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"systeminformation": "5.31.5",
"systeminformation": "5.31.1",
"tinycolor2": "1.6.0",
"tmp": "0.2.5",
"tsc-alias": "1.8.16",
@@ -153,61 +172,62 @@
"ulid": "3.0.2",
"vary": "1.1.2",
"web-push": "3.6.7",
"ws": "8.20.0",
"ws": "8.19.0",
"xev": "3.0.2"
},
"devDependencies": {
"@jest/globals": "29.7.0",
"@kitajs/ts-html-plugin": "4.1.4",
"@nestjs/platform-express": "11.1.19",
"@rollup/plugin-esm-shim": "0.1.8",
"@sentry/vue": "10.48.0",
"@nestjs/platform-express": "11.1.14",
"@sentry/vue": "10.39.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
"@types/archiver": "7.0.0",
"@types/body-parser": "1.19.6",
"@types/color-convert": "3.0.1",
"@types/color-convert": "2.0.4",
"@types/content-disposition": "0.5.9",
"@types/fluent-ffmpeg": "2.1.28",
"@types/http-link-header": "1.0.7",
"@types/js-yaml": "4.0.9",
"@types/jest": "29.5.14",
"@types/jsonld": "1.5.15",
"@types/mime-types": "3.0.1",
"@types/ms": "2.1.0",
"@types/node": "24.12.2",
"@types/nodemailer": "8.0.0",
"@types/node": "24.10.13",
"@types/nodemailer": "7.0.11",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
"@types/pg": "8.20.0",
"@types/pg": "8.16.0",
"@types/qrcode": "1.5.6",
"@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6",
"@types/rename": "1.0.7",
"@types/sanitize-html": "2.16.1",
"@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1",
"@types/simple-oauth2": "5.0.8",
"@types/sinonjs__fake-timers": "15.0.1",
"@types/supertest": "7.2.0",
"@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@vitest/coverage-v8": "4.1.4",
"@typescript-eslint/eslint-plugin": "8.56.0",
"@typescript-eslint/parser": "8.56.0",
"aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.12",
"cbor": "10.0.11",
"cross-env": "10.1.0",
"esbuild-plugin-swc": "1.0.1",
"eslint-plugin-import": "2.32.0",
"execa": "9.6.1",
"fkill": "10.0.3",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"js-yaml": "4.1.1",
"pid-port": "2.1.1",
"rolldown": "1.0.0-rc.15",
"nodemon": "3.1.14",
"pid-port": "2.0.1",
"simple-oauth2": "5.1.0",
"supertest": "7.2.2",
"vite": "8.0.8",
"vitest": "4.1.4",
"vitest-mock-extended": "4.0.0"
"vite": "7.3.1"
}
}

View File

@@ -1,128 +0,0 @@
import { defineConfig } from 'rolldown';
import type { Plugin, ExternalOption } from 'rolldown';
import { execa, execaNode } from 'execa';
import type { ResultPromise } from 'execa';
import esmShim from '@rollup/plugin-esm-shim';
/**
* Watchモード時にバックエンドの起動・停止制御を行うプラグイン
*/
function backendDevServerPlugin(): Plugin {
let backendProcess: ResultPromise | null = null;
async function runBuildAssets() {
await execa('pnpm', ['run', 'build-assets'], {
cwd: '../../',
stdout: process.stdout,
stderr: process.stderr,
});
}
async function killBackendProcess() {
if (backendProcess) {
backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
backendProcess.kill();
await new Promise(resolve => backendProcess!.on('exit', resolve));
backendProcess = null;
}
}
return {
name: 'backend-dev-server',
async closeBundle() {
await runBuildAssets();
if (backendProcess) {
await killBackendProcess();
}
backendProcess = execaNode('./built/entry.js', [], {
stdout: process.stdout,
stderr: process.stderr,
env: {
NODE_ENV: 'development',
},
});
},
async watchChange() {
if (backendProcess) {
await killBackendProcess();
await runBuildAssets();
}
},
};
}
export default defineConfig((args) => {
const isWatchMode = args.watch != null && args.watch !== 'false';
const isE2E = args.e2e != null && args.e2e !== 'false';
// 通常のビルド時にexternalとするモジュール
const externalModules: ExternalOption = [
/^slacc-.*/,
'class-transformer',
'class-validator',
/^@sentry\/.*/,
/^@sentry-internal\/.*/,
'@nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module',
'@nestjs/microservices',
/^@napi-rs\/.*/,
'mock-aws-s3',
'aws-sdk',
'nock',
'sharp',
'jsdom',
're2',
'ipaddr.js',
'oauth2orize',
'file-type',
];
if (isE2E) {
return {
input: './test-server/entry.ts',
platform: 'node',
tsconfig: './test-server/tsconfig.json',
plugins: [
esmShim(),
],
output: {
keepNames: true,
sourcemap: true,
dir: './built-test',
cleanDir: true,
format: 'esm',
},
external: externalModules,
};
} else {
return {
input: [
'./src/boot/entry.ts',
'./src/boot/cli.ts',
'./src/config.ts',
'./src/postgres.ts',
'./src/server/api/openapi/gen-spec.ts',
],
platform: 'node',
tsconfig: true,
plugins: [
esmShim(),
(isWatchMode ? backendDevServerPlugin() : undefined),
],
output: {
keepNames: true,
minify: !isWatchMode,
sourcemap: isWatchMode,
dir: './built',
cleanDir: !isWatchMode,
format: 'esm',
},
watch: {
include: ['src/**/*.{ts,js,mjs,cjs,tsx,json}'],
clearScreen: false,
},
// ビルドの高速化のために、watchモードのときは外部モジュールは全てバンドルしないようにする
external: isWatchMode ? /^(?!@\/)[^.\/](?!:[\/\\])/ : externalModules,
};
}
});

View File

@@ -4,8 +4,8 @@
*/
import Redis from 'ioredis';
import { loadConfig } from '../built/config.js';
import { createPostgresDataSource } from '../built/postgres.js';
import { loadConfig } from '../src-js/config.js';
import { createPostgresDataSource } from '../src-js/postgres.js';
const config = loadConfig();

View File

@@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { execa, execaNode } from 'execa';
/** @type {import('execa').ExecaChildProcess | undefined} */
let backendProcess;
async function execBuildAssets() {
await execa('pnpm', ['run', 'build-assets'], {
cwd: '../../',
stdout: process.stdout,
stderr: process.stderr,
})
}
function execStart() {
// pnpm run start を呼び出したいが、windowsだとプロセスグループ単位でのkillが出来ずゾンビプロセス化するので
// 上記と同等の動きをするコマンドで子・孫プロセスを作らないようにしたい
backendProcess = execaNode('./built/boot/entry.js', [], {
stdout: process.stdout,
stderr: process.stderr,
env: {
'NODE_ENV': 'development',
},
});
}
async function killProc() {
if (backendProcess) {
backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
backendProcess.kill();
await new Promise(resolve => backendProcess.on('exit', resolve));
backendProcess = undefined;
}
}
(async () => {
execaNode(
'./node_modules/nodemon/bin/nodemon.js',
[
'-w', 'src',
'-e', 'ts,js,mjs,cjs,tsx,json,pug',
'--exec', 'pnpm', 'run', 'build',
],
{
stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
serialization: "json",
})
.on('message', async (message) => {
if (message.type === 'exit') {
// かならずbuild->build-assetsの順番で呼び出したいので、
// 少々トリッキーだがnodemonからのexitイベントを利用してbuild-assets->startを行う。
// pnpm restartをbuildが終わる前にbuild-assetsが動いてしまうので、バラバラに呼び出す必要がある
await killProc();
await execBuildAssets();
execStart();
}
})
})();

View File

@@ -19,10 +19,10 @@ async function main() {
}
/** @type {import('../src/config.js')} */
const { loadConfig } = await import('../built/config.js');
const { loadConfig } = await import('../src-js/config.js');
/** @type {import('../src/server/api/openapi/gen-spec.js')} */
const { genOpenapiSpec } = await import('../built/gen-spec.js');
const { genOpenapiSpec } = await import('../src-js/server/api/openapi/gen-spec.js');
const config = loadConfig();
const spec = genOpenapiSpec(config, true);

View File

@@ -55,7 +55,7 @@ async function getMemoryUsage(pid) {
async function measureMemory() {
// Start the Misskey backend server using fork to enable IPC
const serverProcess = fork(join(__dirname, '../built/entry.js'), ['expose-gc'], {
const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), ['expose-gc'], {
cwd: join(__dirname, '..'),
env: {
...process.env,

View File

@@ -6,7 +6,7 @@
import { Global, Inject, Module } from '@nestjs/common';
import * as Redis from 'ioredis';
import { DataSource } from 'typeorm';
import { Meilisearch } from 'meilisearch';
import { MeiliSearch } from 'meilisearch';
import { MiMeta } from '@/models/Meta.js';
import { DI } from './di-symbols.js';
import { Config, loadConfig } from './config.js';
@@ -40,10 +40,10 @@ const $meilisearch: Provider = {
useFactory: (config: Config) => {
if (config.fulltextSearch?.provider === 'meilisearch') {
if (!config.meilisearch) {
throw new Error('Meilisearch is enabled but no configuration is provided');
throw new Error('MeiliSearch is enabled but no configuration is provided');
}
return new Meilisearch({
return new MeiliSearch({
host: `${config.meilisearch.ssl ? 'https' : 'http'}://${config.meilisearch.host}:${config.meilisearch.port}`,
apiKey: config.meilisearch.apiKey,
});

View File

@@ -4,12 +4,16 @@
*/
import { NestFactory } from '@nestjs/core';
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
import { QueueProcessorService } from '@/queue/QueueProcessorService.js';
import { NestLogger } from '@/NestLogger.js';
import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js';
import { QueueStatsService } from '@/daemons/QueueStatsService.js';
import { ServerStatsService } from '@/daemons/ServerStatsService.js';
import { ServerService } from '@/server/ServerService.js';
import { MainModule } from '@/MainModule.js';
export async function server() {
const { MainModule } = await import('../MainModule.js');
const { ServerService } = await import('../server/ServerService.js');
const app = await NestFactory.createApplicationContext(MainModule, {
logger: new NestLogger(),
});
@@ -18,10 +22,6 @@ export async function server() {
await serverService.launch();
if (process.env.NODE_ENV !== 'test') {
const { ChartManagementService } = await import('../core/chart/ChartManagementService.js');
const { QueueStatsService } = await import('../daemons/QueueStatsService.js');
const { ServerStatsService } = await import('../daemons/ServerStatsService.js');
app.get(ChartManagementService).start();
app.get(QueueStatsService).start();
app.get(ServerStatsService).start();
@@ -31,10 +31,6 @@ export async function server() {
}
export async function jobQueue() {
const { QueueProcessorModule } = await import('../queue/QueueProcessorModule.js');
const { QueueProcessorService } = await import('../queue/QueueProcessorService.js');
const { ChartManagementService } = await import('../core/chart/ChartManagementService.js');
const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, {
logger: new NestLogger(),
});

View File

@@ -13,6 +13,8 @@ import chalk from 'chalk';
import Xev from 'xev';
import Logger from '@/logger.js';
import { envOption } from '../env.js';
import { masterMain } from './master.js';
import { workerMain } from './worker.js';
import { readyRef } from './ready.js';
import 'reflect-metadata';
@@ -69,12 +71,10 @@ process.on('exit', code => {
if (!envOption.disableClustering) {
if (cluster.isPrimary) {
logger.info(`Start main process... pid: ${process.pid}`);
const { masterMain } = await import('./master.js');
await masterMain();
ev.mount();
} else if (cluster.isWorker) {
logger.info(`Start worker process... pid: ${process.pid}`);
const { workerMain } = await import('./worker.js');
await workerMain();
} else {
throw new Error('Unknown process type');
@@ -82,7 +82,6 @@ if (!envOption.disableClustering) {
} else {
// 非clusterの場合はMasterのみが起動するため、Workerの処理は行わない(cluster.isWorker === trueの状態でこのブロックに来ることはない)
logger.info(`Start main process... pid: ${process.pid}`);
const { masterMain } = await import('./master.js');
await masterMain();
ev.mount();
}

View File

@@ -10,6 +10,7 @@ import { type FastifyServerOptions } from 'fastify';
import type * as Sentry from '@sentry/node';
import type * as SentryVue from '@sentry/vue';
import type { RedisOptions } from 'ioredis';
import type { ManifestChunk } from 'vite';
type RedisOptionsSource = Partial<RedisOptions> & {
host: string;
@@ -188,9 +189,10 @@ export type Config = {
authUrl: string;
driveUrl: string;
userAgent: string;
frontendEntry: ManifestChunk;
frontendManifestExists: boolean;
frontendEmbedEntry: ManifestChunk;
frontendEmbedManifestExists: boolean;
rootDir: string;
mediaProxy: string;
externalMediaProxyEnabled: boolean;
videoThumbnailGenerator: string | null;
@@ -248,6 +250,12 @@ export function loadConfig(): Config {
const frontendManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'));
const frontendEmbedManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'));
const frontendManifest = frontendManifestExists ?
JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'), 'utf-8'))
: { 'src/_boot_.ts': { file: null } };
const frontendEmbedManifest = frontendEmbedManifestExists ?
JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'), 'utf-8'))
: { 'src/boot.ts': { file: null } };
const config = JSON.parse(fs.readFileSync(compiledConfigFilePath, 'utf-8')) as Source;
@@ -329,9 +337,10 @@ export function loadConfig(): Config {
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
: null,
userAgent: `Misskey/${version} (${config.url})`,
frontendEntry: frontendManifest['src/_boot_.ts'],
frontendManifestExists: frontendManifestExists,
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],
frontendEmbedManifestExists: frontendEmbedManifestExists,
rootDir,
perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000,
perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 500,
deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7),

View File

@@ -4,31 +4,27 @@
*/
import * as fs from 'node:fs';
import { pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
import { Injectable, Inject } from '@nestjs/common';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Injectable } from '@nestjs/common';
import { Mutex } from 'async-mutex';
import fetch from 'node-fetch';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { Config } from '@/config.js';
import type { NSFWJS, PredictionType } from 'nsfwjs/core';
import type { NSFWJS, PredictionType } from 'nsfwjs';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const REQUIRED_CPU_FLAGS_X64 = ['avx2', 'fma'];
let isSupportedCpu: undefined | boolean = undefined;
@Injectable()
export class AiService {
private readonly modelDir: string;
private model: NSFWJS;
private modelLoadMutex: Mutex = new Mutex();
constructor(
@Inject(DI.config)
private config: Config,
) {
const md = resolve(this.config.rootDir, 'packages/backend/nsfw-model');
this.modelDir = md.endsWith('/') ? md : md + '/';
}
@bindThis
@@ -47,10 +43,10 @@ export class AiService {
tf.env().global.fetch = fetch;
if (this.model == null) {
const nsfw = await import('nsfwjs/core');
const nsfw = await import('nsfwjs');
await this.modelLoadMutex.runExclusive(async () => {
if (this.model == null) {
this.model = await nsfw.load(pathToFileURL(this.modelDir).toString(), { size: 299 });
this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
}
});
}

View File

@@ -129,9 +129,6 @@ export interface NoteEventTypes {
type NoteStreamEventTypes = {
[key in keyof NoteEventTypes]: {
id: MiNote['id'];
userId: MiNote['userId'];
visibility: MiNote['visibility'];
visibleUserIds: MiNote['visibleUserIds'];
body: NoteEventTypes[key];
};
};
@@ -381,12 +378,9 @@ export class GlobalEventService {
}
@bindThis
public publishNoteStream<K extends keyof NoteEventTypes>(note: MiNote, type: K, value?: NoteEventTypes[K]): void {
this.publish(`noteStream:${note.id}`, type, {
id: note.id,
userId: note.userId,
visibility: note.visibility,
visibleUserIds: note.visibleUserIds,
public publishNoteStream<K extends keyof NoteEventTypes>(noteId: MiNote['id'], type: K, value?: NoteEventTypes[K]): void {
this.publish(`noteStream:${noteId}`, type, {
id: noteId,
body: value,
});
}

View File

@@ -5,25 +5,29 @@
import * as fs from 'node:fs';
import * as Path from 'node:path';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const path = Path.resolve(_dirname, '../../../../files');
@Injectable()
export class InternalStorageService {
private readonly path: string;
constructor(
@Inject(DI.config)
private config: Config,
) {
this.path = Path.resolve(this.config.rootDir, 'files');
}
@bindThis
public resolvePath(key: string) {
return Path.resolve(this.path, key);
return Path.resolve(path, key);
}
@bindThis
@@ -33,14 +37,14 @@ export class InternalStorageService {
@bindThis
public saveFromPath(key: string, srcPath: string) {
fs.mkdirSync(this.path, { recursive: true });
fs.mkdirSync(path, { recursive: true });
fs.copyFileSync(srcPath, this.resolvePath(key));
return `${this.config.url}/files/${key}`;
}
@bindThis
public saveFromBuffer(key: string, data: Buffer) {
fs.mkdirSync(this.path, { recursive: true });
fs.mkdirSync(path, { recursive: true });
fs.writeFileSync(this.resolvePath(key), data);
return `${this.config.url}/files/${key}`;
}

View File

@@ -6,7 +6,7 @@
import { Injectable } from '@nestjs/common';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import type { Keyword } from 'color-convert';
import type { KEYWORD } from 'color-convert/conversions.js';
@Injectable()
export class LoggerService {
@@ -15,7 +15,7 @@ export class LoggerService {
}
@bindThis
public getLogger(domain: string, color?: Keyword | undefined) {
public getLogger(domain: string, color?: KEYWORD | undefined) {
return new Logger(domain, color);
}
}

View File

@@ -63,21 +63,20 @@ type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
class NotificationManager {
private notifier: { id: MiUser['id']; };
private note: MiNote;
private queue: Map<MiLocalUser['id'], {
private queue: {
target: MiLocalUser['id'];
reason: NotificationType;
}>;
}[];
constructor(
private mutingsRepository: MutingsRepository,
private notificationService: NotificationService,
private followingsRepository: FollowingsRepository,
notifier: { id: MiUser['id']; },
note: MiNote,
) {
this.notifier = notifier;
this.note = note;
this.queue = new Map();
this.queue = [];
}
@bindThis
@@ -85,7 +84,7 @@ class NotificationManager {
// 自分自身へは通知しない
if (this.notifier.id === notifiee) return;
const exist = this.queue.get(notifiee);
const exist = this.queue.find(x => x.target === notifiee);
if (exist) {
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
@@ -93,7 +92,7 @@ class NotificationManager {
exist.reason = reason;
}
} else {
this.queue.set(notifiee, {
this.queue.push({
reason: reason,
target: notifiee,
});
@@ -102,49 +101,7 @@ class NotificationManager {
@bindThis
public async notify() {
if (this.queue.size === 0) {
return;
}
let visibleUserIds: Set<MiUser['id']> | null;
switch (this.note.visibility) {
case 'public':
case 'home':
visibleUserIds = null;
break;
case 'specified':
visibleUserIds = new Set(this.note.visibleUserIds);
break;
// TODO: フォロワー限定ノートにフォロワーではない人がメンションされた場合通知されるのが正しい挙動なのか確認(一部に挙動の不一致がありそう)。現状は通知されるためフィルタしない
// case 'followers': {
// const targetUserIds = this.queue.map(x => x.target);
// const followers = await this.followingsRepository.find({
// where: {
// followeeId: this.note.userId,
// followerId: In(targetUserIds),
// isFollowerHibernated: false,
// },
// select: ['followerId'],
// });
// visibleUserIds = new Set(followers.map(f => f.followerId));
// break;
// }
default:
visibleUserIds = new Set();
break;
}
for (const x of this.queue.values()) {
const isVisibleToTarget = visibleUserIds === null || visibleUserIds.has(x.target);
if (!isVisibleToTarget) {
continue;
}
for (const x of this.queue) {
if (x.reason === 'renote') {
this.notificationService.createNotification(x.target, 'renote', {
noteId: this.note.id,
@@ -815,7 +772,7 @@ export class NoteCreateService implements OnApplicationShutdown {
this.webhookService.enqueueUserWebhook(user.id, 'note', { note: noteObj });
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, this.followingsRepository, user, note);
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, user, note);
await this.createMentionedEvents(mentionedUsers, note, nm);

View File

@@ -68,7 +68,7 @@ export class NoteDeleteService {
}
if (!quiet) {
this.globalEventService.publishNoteStream(note, 'deleted', {
this.globalEventService.publishNoteStream(note.id, 'deleted', {
deletedAt: deletedAt,
});

View File

@@ -83,7 +83,7 @@ export class PollService {
const index = choice + 1; // In SQL, array index is 1 based
await this.pollsRepository.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`);
this.globalEventService.publishNoteStream(note, 'pollVoted', {
this.globalEventService.publishNoteStream(note.id, 'pollVoted', {
choice: choice,
userId: user.id,
});

View File

@@ -259,7 +259,7 @@ export class QueryService {
@bindThis
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
// This code must always be synchronized with the checks in NoteEntityService.isVisibleForMe and Stream abstract class Channel.isNoteVisibleForMe.
// This code must always be synchronized with the checks in Notes.isVisibleForMe.
if (me == null) {
q.andWhere(new Brackets(qb => {
qb

View File

@@ -244,7 +244,7 @@ export class ReactionService {
},
});
this.globalEventService.publishNoteStream(note, 'reacted', {
this.globalEventService.publishNoteStream(note.id, 'reacted', {
reaction: decodedReaction.reaction,
emoji: customEmoji != null ? {
name: customEmoji.host ? `${customEmoji.name}@${customEmoji.host}` : `${customEmoji.name}@.`,
@@ -318,7 +318,7 @@ export class ReactionService {
.execute();
}
this.globalEventService.publishNoteStream(note, 'unreacted', {
this.globalEventService.publishNoteStream(note.id, 'unreacted', {
reaction: this.decodeReaction(exist.reaction).reaction,
userId: user.id,
});

View File

@@ -91,27 +91,13 @@ export class RelayService {
return JSON.stringify(result);
}
@bindThis
private getAcceptedRelays(): Promise<MiRelay[]> {
return this.relaysCache.fetch(() => this.relaysRepository.findBy({
status: 'accepted',
}));
}
@bindThis
public async isRelayActor(actor: { inbox: string | null; sharedInbox: string | null }): Promise<boolean> {
const relays = await this.getAcceptedRelays();
return relays.some(relay =>
(actor.inbox != null && relay.inbox === actor.inbox)
|| (actor.sharedInbox != null && relay.inbox === actor.sharedInbox),
);
}
@bindThis
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
if (activity == null) return;
const relays = await this.getAcceptedRelays();
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
status: 'accepted',
}));
if (relays.length === 0) return;
const copy = deepClone(activity);

View File

@@ -533,8 +533,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
roleId: In(administratorRoles.map(r => r.id)),
}) : [];
// TODO: isRootなアカウントも含める
// Setを経由して重複を除去ユーザIDは重複する可能性があるので
return [...new Set(assigns.map(a => a.userId))].sort((x, y) => x.localeCompare(y));
return assigns.map(a => a.userId);
}
@bindThis

View File

@@ -17,7 +17,7 @@ import { CacheService } from '@/core/CacheService.js';
import { QueryService } from '@/core/QueryService.js';
import { IdService } from '@/core/IdService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { Index, Meilisearch } from 'meilisearch';
import type { Index, MeiliSearch } from 'meilisearch';
type K = string;
type V = string | number | boolean;
@@ -85,7 +85,7 @@ export class SearchService {
private config: Config,
@Inject(DI.meilisearch)
private meilisearch: Meilisearch | null,
private meilisearch: MeiliSearch | null,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@@ -187,7 +187,7 @@ export class SearchService {
return this.searchNoteByLike(q, me, opts, pagination);
}
case 'meilisearch': {
return this.searchNoteByMeilisearch(q, me, opts, pagination);
return this.searchNoteByMeiliSearch(q, me, opts, pagination);
}
default: {
const _: never = this.provider;
@@ -239,14 +239,14 @@ export class SearchService {
}
@bindThis
private async searchNoteByMeilisearch(
private async searchNoteByMeiliSearch(
q: string,
me: MiUser | null,
opts: SearchOpts,
pagination: SearchPagination,
): Promise<MiNote[]> {
if (!this.meilisearch || !this.meilisearchNoteIndex) {
throw new Error('Meilisearch is not available');
throw new Error('MeiliSearch is not available');
}
const filter: Q = {

View File

@@ -164,3 +164,4 @@ export class SignupService {
return { account, secret };
}
}

View File

@@ -259,7 +259,7 @@ export class ApInboxService {
@bindThis
private async add(actor: MiRemoteUser, activity: IAdd, resolver?: Resolver): Promise<string | void> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -302,14 +302,12 @@ export class ApInboxService {
@bindThis
private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost, resolver?: Resolver): Promise<string | void> {
const uri = getApId(activity);
if (actor.isSuspended) {
return;
}
// リレーからのAnnounceかチェック
const fromRelay = await this.relayService.isRelayActor(actor);
const uri = getApId(fromRelay ? target : activity);
// アナウンス先が許可されているかチェック
if (!this.utilityService.isFederationAllowedUri(uri)) return;
@@ -338,14 +336,6 @@ export class ApInboxService {
throw err;
}
// リレーからのAnnounceはリートを作成せず、ートを直接公開する
if (fromRelay) {
this.logger.info(`Publishing relay-delivered note: ${uri}`);
const noteObj = await this.noteEntityService.pack(renote, null, { skipHide: true, withReactionAndUserPairCache: true });
this.globalEventService.publishNotesStream(noteObj);
return;
}
if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
return 'skip: invalid actor for this activity';
}
@@ -469,7 +459,7 @@ export class ApInboxService {
@bindThis
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -623,7 +613,7 @@ export class ApInboxService {
@bindThis
private async remove(actor: MiRemoteUser, activity: IRemove, resolver?: Resolver): Promise<string | void> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -643,7 +633,7 @@ export class ApInboxService {
@bindThis
private async undo(actor: MiRemoteUser, activity: IUndo, resolver?: Resolver): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -777,7 +767,7 @@ export class ApInboxService {
@bindThis
private async update(actor: MiRemoteUser, activity: IUpdate, resolver?: Resolver): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'skip: invalid actor';
}

View File

@@ -81,7 +81,7 @@ export class ApRequestCreator {
}, args.additionalHeaders),
};
const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host']);
const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']);
return {
request,

View File

@@ -376,7 +376,7 @@ export class ApPersonService implements OnModuleInit {
isLocked: person.manuallyApprovesFollowers,
movedToUri: person.movedTo,
movedAt: person.movedTo ? new Date() : null,
alsoKnownAs: toArray(person.alsoKnownAs),
alsoKnownAs: person.alsoKnownAs,
isExplorable: person.discoverable,
username: person.preferredUsername,
usernameLower: person.preferredUsername?.toLowerCase(),
@@ -568,7 +568,7 @@ export class ApPersonService implements OnModuleInit {
isCat: (person as any).isCat === true,
isLocked: person.manuallyApprovesFollowers,
movedToUri: person.movedTo ?? null,
alsoKnownAs: person.alsoKnownAs ? toArray(person.alsoKnownAs) : null,
alsoKnownAs: person.alsoKnownAs ?? null,
isExplorable: person.discoverable,
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
} as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;

View File

@@ -132,7 +132,7 @@ export class MetaEntityService {
sentryForFrontend: this.config.sentryForFrontend ?? null,
mediaProxy: this.config.mediaProxy,
enableUrlPreview: instance.urlPreviewEnabled,
noteSearchableScope: (this.config.fulltextSearch?.provider === 'meilisearch' && this.config.meilisearch?.scope === 'local') ? 'local' : 'global',
noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
maxFileSize: this.config.maxFileSize,
federation: this.meta.federation,
};

View File

@@ -17,7 +17,6 @@ import { DebounceLoader } from '@/misc/loader.js';
import { IdService } from '@/core/IdService.js';
import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js';
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
import { CacheService } from '@/core/CacheService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { ReactionService } from '../ReactionService.js';
@@ -67,7 +66,6 @@ export class NoteEntityService implements OnModuleInit {
private reactionService: ReactionService;
private reactionsBufferingService: ReactionsBufferingService;
private idService: IdService;
private cacheService: CacheService;
private noteLoader = new DebounceLoader(this.findNoteOrFail);
constructor(
@@ -103,7 +101,6 @@ export class NoteEntityService implements OnModuleInit {
//private reactionService: ReactionService,
//private reactionsBufferingService: ReactionsBufferingService,
//private idService: IdService,
//private cacheService: CacheService,
) {
}
@@ -114,7 +111,6 @@ export class NoteEntityService implements OnModuleInit {
this.reactionService = this.moduleRef.get('ReactionService');
this.reactionsBufferingService = this.moduleRef.get('ReactionsBufferingService');
this.idService = this.moduleRef.get('IdService');
this.cacheService = this.moduleRef.get('CacheService');
}
@bindThis
@@ -129,65 +125,75 @@ export class NoteEntityService implements OnModuleInit {
}
@bindThis
public async shouldHideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<boolean> {
if (meId === packedNote.userId) return false;
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<void> {
if (meId === packedNote.userId) return;
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false;
if (packedNote.user.requireSigninToViewContents && meId == null) {
return true;
hide = true;
}
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
return true;
if (!hide) {
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
hide = true;
}
}
// visibility が specified かつ自分が指定されていなかったら非表示
if (packedNote.visibility === 'specified') {
if (meId == null) {
return true;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (!hide) {
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (!specified) {
return true;
if (!specified) {
hide = true;
}
}
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (packedNote.visibility === 'followers') {
if (meId == null) {
return true;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
return false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
return false;
} else {
// フォロワーかどうか
const followings = await this.cacheService.userFollowingsCache.fetch(meId);
if (!Object.hasOwn(followings, packedNote.userId)) {
return true;
if (!hide) {
if (packedNote.visibility === 'followers') {
if (meId == null) {
hide = true;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
hide = false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
hide = false;
} else {
// フォロワーかどうか
// TODO: 当関数呼び出しごとにクエリが走るのは重そうだからなんとかする
const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: packedNote.userId,
followerId: meId,
},
});
hide = !isFollowing;
}
}
}
return false;
}
@bindThis
public hideNote(packedNote: Packed<'Note'>): void {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
// TODO: hiddenReason みたいなのを提供しても良さそう
if (hide) {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
// TODO: hiddenReason みたいなのを提供しても良さそう
}
}
@bindThis
@@ -272,7 +278,7 @@ export class NoteEntityService implements OnModuleInit {
@bindThis
public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null): Promise<boolean> {
// This code must always be synchronized with the checks in QueryService.generateVisibilityQuery.
// This code must always be synchronized with the checks in generateVisibilityQuery.
// visibility が specified かつ自分が指定されていなかったら非表示
if (note.visibility === 'specified') {
if (meId == null) {
@@ -462,8 +468,8 @@ export class NoteEntityService implements OnModuleInit {
this.treatVisibility(packed);
if (!opts.skipHide && await this.shouldHideNote(packed, meId)) {
this.hideNote(packed);
if (!opts.skipHide) {
await this.hideNote(packed, meId);
}
return packed;

View File

@@ -51,7 +51,6 @@ import { ChatService } from '@/core/ChatService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { NoteEntityService } from './NoteEntityService.js';
import type { PageEntityService } from './PageEntityService.js';
import { toArray } from '@/misc/prelude/array.js';
const Ajv = _Ajv.default;
const ajv = new Ajv();
@@ -528,10 +527,10 @@ export class UserEntityService implements OnModuleInit {
url: profile!.url,
uri: user.uri,
movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
alsoKnownAs: user.alsoKnownAs ?
Promise.all(toArray(user.alsoKnownAs).map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
.then(xs => xs.length === 0 ? null : xs.filter(x => x != null))
: null,
alsoKnownAs: user.alsoKnownAs
? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
.then(xs => xs.length === 0 ? null : xs.filter(x => x != null))
: null,
createdAt: this.idService.parse(user.id).date.toISOString(),
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,

View File

@@ -9,11 +9,11 @@ import { default as convertColor } from 'color-convert';
import { format as dateFormat } from 'date-fns';
import { bindThis } from '@/decorators.js';
import { envOption } from './env.js';
import type { Keyword } from 'color-convert';
import type { KEYWORD } from 'color-convert/conversions.js';
type Context = {
name: string;
color?: Keyword;
color?: KEYWORD;
};
type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';
@@ -23,7 +23,7 @@ export default class Logger {
private context: Context;
private parentLogger: Logger | null = null;
constructor(context: string, color?: Keyword) {
constructor(context: string, color?: KEYWORD) {
this.context = {
name: context,
color: color,
@@ -31,7 +31,7 @@ export default class Logger {
}
@bindThis
public createSubLogger(context: string, color?: Keyword): Logger {
public createSubLogger(context: string, color?: KEYWORD): Logger {
const logger = new Logger(context, color);
logger.parentLogger = this;
return logger;

View File

@@ -5,19 +5,12 @@
// Crockford's Base32
// https://github.com/ulid/spec#encoding
import { parseBigInt32 } from '@/misc/bigint.js';
const CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
export const ulidRegExp = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
function parseBigIntCrockford(str: string): bigint {
let result = 0n;
for (let i = 0; i < str.length; i++) {
result = result * 32n + BigInt(CHARS.indexOf(str[i]));
}
return result;
}
function parseBase32(timestamp: string) {
let time = 0;
for (let i = 0; i < timestamp.length; i++) {
@@ -33,6 +26,6 @@ export function parseUlid(id: string): { date: Date; } {
export function parseUlidFull(id: string): { date: number; additional: bigint; } {
return {
date: parseBase32(id.slice(0, 10)),
additional: parseBigIntCrockford(id.slice(10, 26)),
additional: parseBigInt32(id.slice(10, 26)),
};
}

View File

@@ -36,9 +36,4 @@ export class MiAvatarDecoration {
array: true, length: 128, default: '{}',
})
public roleIdsThatCanBeUsedThisDecoration: string[];
@Column('varchar', {
length: 128, nullable: true,
})
public category: string | null;
}

View File

@@ -13,7 +13,7 @@ import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataServic
import InstanceChart from '@/core/chart/charts/instance.js';
import ApRequestChart from '@/core/chart/charts/ap-request.js';
import FederationChart from '@/core/chart/charts/federation.js';
import { getApId, isActor, isDelete } from '@/core/activitypub/type.js';
import { getApId } from '@/core/activitypub/type.js';
import type { IActivity } from '@/core/activitypub/type.js';
import type { MiRemoteUser } from '@/models/User.js';
import type { MiUserPublickey } from '@/models/UserPublickey.js';
@@ -84,23 +84,6 @@ export class InboxProcessorService implements OnApplicationShutdown {
return `Old keyId is no longer supported. ${keyIdLower}`;
}
{
let userExistenceCheckApId: string | null = null;
// 存在しないActorに対するActorのDeleteアクティビティは無視する。
// actorとobjectが同じならばそれはActorに違いない
if (isDelete(activity) && typeof activity.object === 'object' && (isActor(activity.object) || getApId(activity.actor) === getApId(activity.object))) {
userExistenceCheckApId = getApId(activity.object);
}
if (userExistenceCheckApId != null) {
const user = await this.apDbResolverService.getUserFromApId(userExistenceCheckApId);
if (user == null) {
return `skip: user not found for delete activity. ${getApId(userExistenceCheckApId)}`;
}
}
}
// HTTP-Signature keyIdを元にDBから取得
let authUser: {
user: MiRemoteUser;
@@ -115,9 +98,9 @@ export class InboxProcessorService implements OnApplicationShutdown {
// 対象が4xxならスキップ
if (err instanceof StatusError) {
if (!err.isRetryable) {
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${getApId(activity.actor)} - ${err.statusCode}`);
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
}
throw new Error(`Error in actor ${getApId(activity.actor)} - ${err.statusCode}`);
throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
}
}
}
@@ -136,7 +119,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
// また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== getApId(activity.actor)) {
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
const ldSignature = activity.signature;
if (ldSignature) {
@@ -187,8 +170,8 @@ export class InboxProcessorService implements OnApplicationShutdown {
//#endregion
// もう一度actorチェック
if (authUser.user.uri !== getApId(activity.actor)) {
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${getApId(activity.actor)})`);
if (authUser.user.uri !== activity.actor) {
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
}
const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
@@ -243,17 +226,14 @@ export class InboxProcessorService implements OnApplicationShutdown {
}
} catch (e) {
if (e instanceof IdentifiableError) {
switch (e.id) {
case '689ee33f-f97c-479a-ac49-1b9f8140af99':
return 'blocked notes with prohibited words';
case '85ab9bd7-3a41-4530-959d-f07073900109':
return 'actor has been suspended';
case 'd450b8a9-48e4-4dab-ae36-f4db763fda7c': // invalid Note
return e.message;
case '9f466dab-c856-48cd-9e65-ff90ff750580':
return 'note contains too many mentions';
case '09d79f9e-64f1-4316-9cfa-e75c4d091574': // Instance is blocked
return 'skip: blocked instance';
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
return 'blocked notes with prohibited words';
}
if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') {
return 'actor has been suspended';
}
if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note
return e.message;
}
}
throw e;

View File

@@ -30,9 +30,9 @@ import { bindThis } from '@/decorators.js';
import { IActivity } from '@/core/activitypub/type.js';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import * as Acct from '@/misc/acct.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
import type { FindOptionsWhere } from 'typeorm';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
@@ -131,7 +131,6 @@ export class ActivityPubServerService {
if (signature.params.headers.indexOf('digest') === -1) {
// Digest not found.
reply.code(401);
return;
} else {
const digest = request.headers.digest;

View File

@@ -4,7 +4,8 @@
*/
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
import type { Config } from '@/config.js';
import type { DriveFilesRepository } from '@/models/_.js';
@@ -24,6 +25,11 @@ import { FileServerFileResolver } from './file/FileServerFileResolver.js';
import { FileServerProxyHandler } from './file/FileServerProxyHandler.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const assets = `${_dirname}/../../server/file/assets/`;
@Injectable()
export class FileServerService {
private logger: Logger;
@@ -31,8 +37,6 @@ export class FileServerService {
private proxyHandler: FileServerProxyHandler;
private fileResolver: FileServerFileResolver;
private readonly assets: string;
constructor(
@Inject(DI.config)
private config: Config,
@@ -48,7 +52,6 @@ export class FileServerService {
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('server', 'gray');
this.assets = resolve(this.config.rootDir, 'packages/backend/src/server/file/assets');
this.fileResolver = new FileServerFileResolver(
this.driveFilesRepository,
this.fileInfoService,
@@ -58,13 +61,13 @@ export class FileServerService {
this.driveHandler = new FileServerDriveHandler(
this.config,
this.fileResolver,
this.assets,
assets,
this.videoProcessingService,
);
this.proxyHandler = new FileServerProxyHandler(
this.config,
this.fileResolver,
this.assets,
assets,
this.imageProcessingService,
);
@@ -84,7 +87,7 @@ export class FileServerService {
fastify.register((fastify, options, done) => {
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
fastify.get('/files/app-default.jpg', (request, reply) => {
const file = fs.createReadStream(`${this.assets}/dummy.png`);
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
reply.header('Content-Type', 'image/jpeg');
reply.header('Cache-Control', 'max-age=31536000, immutable');
return reply.send(file);
@@ -118,7 +121,7 @@ export class FileServerService {
reply.header('Cache-Control', 'max-age=300');
if (request.query && 'fallback' in request.query) {
return reply.sendFile('/dummy.png', this.assets);
return reply.sendFile('/dummy.png', assets);
}
if (err instanceof StatusError && (err.statusCode === 302 || err.isClientError)) {

View File

@@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
import { DI } from '@/di-symbols.js';
import { readyRef } from '@/boot/ready.js';
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
import type { Meilisearch } from 'meilisearch';
import type { MeiliSearch } from 'meilisearch';
@Injectable()
export class HealthServerService {
@@ -34,7 +34,7 @@ export class HealthServerService {
private db: DataSource,
@Inject(DI.meilisearch)
private meilisearch: Meilisearch | null,
private meilisearch: MeiliSearch | null,
) {}
@bindThis

View File

@@ -49,7 +49,6 @@ import { ChatUserChannel } from './api/stream/channels/chat-user.js';
import { ChatRoomChannel } from './api/stream/channels/chat-room.js';
import { ReversiChannel } from './api/stream/channels/reversi.js';
import { ReversiGameChannel } from './api/stream/channels/reversi-game.js';
import { NoteStreamingHidingService } from './api/stream/NoteStreamingHidingService.js';
import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.js';
@Module({
@@ -99,7 +98,6 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j
QueueStatsChannel,
ServerStatsChannel,
UserListChannel,
NoteStreamingHidingService,
OpenApiServerService,
OAuth2ProviderService,
],

View File

@@ -55,10 +55,6 @@ export const meta = {
format: 'id',
},
},
category: {
type: 'string',
optional: false, nullable: true,
},
},
},
} as const;
@@ -72,7 +68,6 @@ export const paramDef = {
roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
type: 'string',
} },
category: { type: 'string', nullable: true },
},
required: ['name', 'description', 'url'],
} as const;
@@ -89,7 +84,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
description: ps.description,
url: ps.url,
roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
category: ps.category,
}, me);
return {
@@ -100,7 +94,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
description: created.description,
url: created.url,
roleIdsThatCanBeUsedThisDecoration: created.roleIdsThatCanBeUsedThisDecoration,
category: created.category,
};
});
}

View File

@@ -60,10 +60,6 @@ export const meta = {
format: 'id',
},
},
category: {
type: 'string',
optional: true, nullable: true,
},
},
},
},
@@ -99,7 +95,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
description: avatarDecoration.description,
url: avatarDecoration.url,
roleIdsThatCanBeUsedThisDecoration: avatarDecoration.roleIdsThatCanBeUsedThisDecoration,
category: avatarDecoration.category,
}));
});
}

View File

@@ -30,7 +30,6 @@ export const paramDef = {
roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
type: 'string',
} },
category: { type: 'string', nullable: true },
},
required: ['id'],
} as const;
@@ -46,7 +45,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
description: ps.description,
url: ps.url,
roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
category: ps.category,
}, me);
});
}

View File

@@ -15,7 +15,6 @@ import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointServ
import { MiLocalUser } from '@/models/User.js';
import { ChannelMutingService } from '@/core/ChannelMutingService.js';
import { ApiError } from '../../error.js';
import { Brackets } from 'typeorm';
export const meta = {
tags: ['notes', 'channels'],
@@ -133,10 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.then(x => x.map(x => x.id).filter(x => x !== ps.channelId));
if (mutingChannelIds.length > 0) {
query.andWhere('note.channelId NOT IN (:...mutingChannelIds)', { mutingChannelIds });
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteChannelId IS NULL');
qb.orWhere('note.renoteChannelId NOT IN (:...mutingChannelIds)', { mutingChannelIds });
}));
query.andWhere('note.renoteChannelId NOT IN (:...mutingChannelIds)', { mutingChannelIds });
}
}
//#endregion

View File

@@ -119,7 +119,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// Update
await this.driveFoldersRepository.update(folder.id, {
this.driveFoldersRepository.update(folder.id, {
name: folder.name,
parentId: folder.parentId,
});

View File

@@ -5,13 +5,7 @@
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
// 循環参照を回避
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
function getEndpoints() {
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
}
import endpoints from '../endpoints.js';
export const meta = {
requireCredential: false,
@@ -49,7 +43,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
) {
super(meta, paramDef, async (ps) => {
const endpoints = await getEndpoints();
const ep = endpoints.find(x => x.name === ps.endpoint);
if (ep == null) return null;
return {

View File

@@ -5,13 +5,7 @@
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
// 循環参照を回避
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
function getEndpoints() {
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
}
import endpoints from '../endpoints.js';
export const meta = {
requireCredential: false,
@@ -45,7 +39,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
) {
super(meta, paramDef, async () => {
const endpoints = await getEndpoints();
return endpoints.map(x => x.name);
});
}

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