From c3a6da19d79c69f7a60157f48398b76345ced31f Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 18 Jul 2024 08:53:45 +0900 Subject: [PATCH 01/27] =?UTF-8?q?chore:=20CHANGELOG=E3=81=AB=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=BF=BD=E8=A8=98=20(follo?= =?UTF-8?q?w-up=20of=20#13464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c44c4f8b83..ad0a88e949ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます - Feat: 連合に使うHTTP SignaturesがEd25519鍵に対応するように #13464 - Ed25519署名に対応するサーバーが増えると、deliverで要求されるサーバーリソースが削減されます + - ジョブキューのconfig設定のデフォルト値を変更しました。 + default.ymlでジョブキューの並列度を設定している場合は、従前よりもconcurrencyの値をより下げるとパフォーマンスが改善する可能性があります。 + * deliverJobConcurrency: 16 (←128) + * deliverJobPerSec: 1024 (←128) + * inboxJobConcurrency: 4 (←16) + * inboxJobPerSec: 64 (←32) - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正 From de166a8ed4b0e185e49c71825c92b4f94e2f350f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:55:36 +0900 Subject: [PATCH 02/27] =?UTF-8?q?fix(backend):=20=E3=83=AA=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=81=8C?= =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=81=8C=E5=88=87?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=81=BE=E3=81=A7=E5=8A=B9=E3=81=8B=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1424?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: RenoteMuteがキャッシュが切れるまで効かない問題を修正 (cherry picked from commit e9601029b52e0ad43d9131b555b614e56c84ebc1) * update changelog * :art: * remove unused import * 消したときもキャッシュを飛ばすように * lint --------- Co-authored-by: mattyatea --- CHANGELOG.md | 2 + packages/backend/src/core/CoreModule.ts | 6 +++ .../src/core/UserRenoteMutingService.ts | 52 +++++++++++++++++++ .../api/endpoints/renote-mute/create.ts | 23 ++++---- .../api/endpoints/renote-mute/delete.ts | 8 +-- 5 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 packages/backend/src/core/UserRenoteMutingService.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ad0a88e949ce..8728ebf733ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,8 @@ - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正 - Fix: エラーメッセージの誤字を修正 (#14213) - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正 +- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正 + (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1) ### Misskey.js - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応) diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 0208540afa7b..c9427bbeb7bd 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -61,6 +61,7 @@ import { UserFollowingService } from './UserFollowingService.js'; import { UserKeypairService } from './UserKeypairService.js'; import { UserListService } from './UserListService.js'; import { UserMutingService } from './UserMutingService.js'; +import { UserRenoteMutingService } from './UserRenoteMutingService.js'; import { UserSuspendService } from './UserSuspendService.js'; import { UserAuthService } from './UserAuthService.js'; import { VideoProcessingService } from './VideoProcessingService.js'; @@ -203,6 +204,7 @@ const $UserFollowingService: Provider = { provide: 'UserFollowingService', useEx const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisting: UserKeypairService }; const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService }; const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService }; +const $UserRenoteMutingService: Provider = { provide: 'UserRenoteMutingService', useExisting: UserRenoteMutingService }; const $UserSearchService: Provider = { provide: 'UserSearchService', useExisting: UserSearchService }; const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService }; const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService }; @@ -350,6 +352,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserKeypairService, UserListService, UserMutingService, + UserRenoteMutingService, UserSearchService, UserSuspendService, UserAuthService, @@ -493,6 +496,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserKeypairService, $UserListService, $UserMutingService, + $UserRenoteMutingService, $UserSearchService, $UserSuspendService, $UserAuthService, @@ -637,6 +641,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserKeypairService, UserListService, UserMutingService, + UserRenoteMutingService, UserSearchService, UserSuspendService, UserAuthService, @@ -779,6 +784,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserKeypairService, $UserListService, $UserMutingService, + $UserRenoteMutingService, $UserSearchService, $UserSuspendService, $UserAuthService, diff --git a/packages/backend/src/core/UserRenoteMutingService.ts b/packages/backend/src/core/UserRenoteMutingService.ts new file mode 100644 index 000000000000..bdc5e23f4bd6 --- /dev/null +++ b/packages/backend/src/core/UserRenoteMutingService.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import type { RenoteMutingsRepository } from '@/models/_.js'; +import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; + +import { IdService } from '@/core/IdService.js'; +import type { MiUser } from '@/models/User.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { CacheService } from '@/core/CacheService.js'; + +@Injectable() +export class UserRenoteMutingService { + constructor( + @Inject(DI.renoteMutingsRepository) + private renoteMutingsRepository: RenoteMutingsRepository, + + private idService: IdService, + private cacheService: CacheService, + ) { + } + + @bindThis + public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise { + await this.renoteMutingsRepository.insert({ + id: this.idService.gen(), + muterId: user.id, + muteeId: target.id, + }); + + await this.cacheService.renoteMutingsCache.refresh(user.id); + } + + @bindThis + public async unmute(mutings: MiRenoteMuting[]): Promise { + if (mutings.length === 0) return; + + await this.renoteMutingsRepository.delete({ + id: In(mutings.map(m => m.id)), + }); + + const muterIds = [...new Set(mutings.map(m => m.muterId))]; + for (const muterId of muterIds) { + await this.cacheService.renoteMutingsCache.refresh(muterId); + } + } +} diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index 39bf0cc428b5..84a1f010d437 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -6,12 +6,11 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { IdService } from '@/core/IdService.js'; -import type { RenoteMutingsRepository } from '@/models/_.js'; -import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; +import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js"; +import type { RenoteMutingsRepository } from '@/models/_.js'; export const meta = { tags: ['account'], @@ -62,7 +61,7 @@ export default class extends Endpoint { // eslint- private renoteMutingsRepository: RenoteMutingsRepository, private getterService: GetterService, - private idService: IdService, + private userRenoteMutingService: UserRenoteMutingService, ) { super(meta, paramDef, async (ps, me) => { const muter = me; @@ -79,21 +78,19 @@ export default class extends Endpoint { // eslint- }); // Check if already muting - const exist = await this.renoteMutingsRepository.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, + const exist = await this.renoteMutingsRepository.exists({ + where: { + muterId: muter.id, + muteeId: mutee.id, + }, }); - if (exist != null) { + if (exist === true) { throw new ApiError(meta.errors.alreadyMuting); } // Create mute - await this.renoteMutingsRepository.insert({ - id: this.idService.gen(), - muterId: muter.id, - muteeId: mutee.id, - } as MiRenoteMuting); + await this.userRenoteMutingService.mute(muter, mutee); }); } } diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts index 6e037cc07ef0..1a584b840480 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts @@ -5,10 +5,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RenoteMutingsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; +import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js"; +import type { RenoteMutingsRepository } from '@/models/_.js'; export const meta = { tags: ['account'], @@ -53,6 +54,7 @@ export default class extends Endpoint { // eslint- private renoteMutingsRepository: RenoteMutingsRepository, private getterService: GetterService, + private userRenoteMutingService: UserRenoteMutingService, ) { super(meta, paramDef, async (ps, me) => { const muter = me; @@ -79,9 +81,7 @@ export default class extends Endpoint { // eslint- } // Delete mute - await this.renoteMutingsRepository.delete({ - id: exist.id, - }); + await this.userRenoteMutingService.unmute([exist]); }); } } From e716c201c65128bb04299f509c5ccedbc7061a64 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:57:02 +0900 Subject: [PATCH 03/27] =?UTF-8?q?docs:=20=E9=96=8B=E7=99=BA=E7=92=B0?= =?UTF-8?q?=E5=A2=83=E3=81=AE=E3=82=BB=E3=83=83=E3=83=88=E3=82=A2=E3=83=83?= =?UTF-8?q?=E3=83=97=E6=89=8B=E9=A0=86=E3=82=92=E8=A9=B3=E7=B4=B0=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B=20(#14235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: mentioning Devcontainer fix #13753 * revise * revise 2 * Apply suggestions from code review per https://github.com/misskey-dev/misskey/pull/14235#discussion_r1680883942 Co-authored-by: anatawa12 * 下の方にあったDevcontainerのセクションをマージ * revise 3 * Update CONTRIBUTING.md https://github.com/misskey-dev/misskey/pull/14235#discussion_r1680928026 Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com> * mention Meilisearch * Update CONTRIBUTING.md --------- Co-authored-by: anatawa12 Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com> --- CONTRIBUTING.md | 52 ++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 532a2dc66f69..9a56345e6e4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -106,6 +106,38 @@ If your language is not listed in Crowdin, please open an issue. ![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg) ## Development +### Setup +Before developing, you have to set up environment. Misskey requires Redis, PostgreSQL, and FFmpeg. + +You would want to install Meilisearch to experiment related features. Technically, meilisearch is not strict requirement, but some features and tests require it. + +There are a few ways to proceed. + +#### Use system-wide software +You could install them in system-wide (such as from package manager). + +#### Use `docker compose` +You could obtain middleware container by typing `docker compose -f $PROJECT_ROOT/compose.local-db.yml up -d`. + +#### Use Devcontainer +Devcontainer also has necessary setting. This method can be done by connecting from VSCode. + +Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. +To use Dev Container, open the project directory on VSCode with Dev Containers installed. +**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled. + +It will run the following command automatically inside the container. +``` bash +git submodule update --init +pnpm install --frozen-lockfile +cp .devcontainer/devcontainer.yml .config/default.yml +pnpm build +pnpm migrate +``` + +After finishing the migration, you can proceed. + +### Start developing During development, it is useful to use the ``` @@ -135,26 +167,6 @@ MK_DEV_PREFER=backend pnpm dev - To change the port of Vite, specify with `VITE_PORT` environment variable. - HMR may not work in some environments such as Windows. -### Dev Container -Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. -To use Dev Container, open the project directory on VSCode with Dev Containers installed. -**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled. - -It will run the following command automatically inside the container. -``` bash -git submodule update --init -pnpm install --frozen-lockfile -cp .devcontainer/devcontainer.yml .config/default.yml -pnpm build -pnpm migrate -``` - -After finishing the migration, run the `pnpm dev` command to start the development server. - -``` bash -pnpm dev -``` - ## Testing - Test codes are located in [`/packages/backend/test`](/packages/backend/test). From cd95a6e9c9ec0661892d824f520a48416e96adef Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 18 Jul 2024 14:23:47 +0900 Subject: [PATCH 04/27] fix: remove unreleased section (#14246) --- CHANGELOG.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4ef23d272d..9f78ba677d6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,3 @@ -## Unreleased - -### General -- - -### Client -- - -### Server -- - - ## 2024.5.0 ### Note From 4f85b6aa9158190bbdd9246bb8565d5c80081706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:41:32 +0900 Subject: [PATCH 05/27] =?UTF-8?q?fix(frontend):=20Twitch=E3=81=AE=E5=9F=8B?= =?UTF-8?q?=E3=82=81=E8=BE=BC=E3=81=BF=E3=81=8C=E9=96=8B=E3=81=91=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1424?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): twitchの埋め込みが開けない問題を修正 * Update Changelog * fix test --- CHANGELOG.md | 1 + .../frontend/src/components/MkUrlPreview.vue | 3 ++- .../src/components/MkYouTubePlayer.vue | 3 ++- .../src/scripts/player-url-transform.ts | 26 +++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 packages/frontend/src/scripts/player-url-transform.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8728ebf733ba..60bc28e0773b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため) - Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574) +- Fix: Twitchの埋め込みが開けない問題を修正 ### Server - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949) diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 8df5e0fe4048..c868a220451e 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only scrolling="no" :allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')" :class="$style.playerIframe" - :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" + :src="transformPlayerUrl(player.url)" :style="{ border: 0 }" > invalid url @@ -91,6 +91,7 @@ import * as os from '@/os.js'; import { deviceKind } from '@/scripts/device-kind.js'; import MkButton from '@/components/MkButton.vue'; import { versatileLang } from '@/scripts/intl-const.js'; +import { transformPlayerUrl } from '@/scripts/player-url-transform.js'; import { defaultStore } from '@/store.js'; type SummalyResult = Awaited>; diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue index 1fad222fc5d5..e3711b3463f1 100644 --- a/packages/frontend/src/components/MkYouTubePlayer.vue +++ b/packages/frontend/src/components/MkYouTubePlayer.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
invalid url
@@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import MkWindow from '@/components/MkWindow.vue'; import { versatileLang } from '@/scripts/intl-const.js'; +import { transformPlayerUrl } from '@/scripts/player-url-transform.js'; import { defaultStore } from '@/store.js'; const props = defineProps<{ diff --git a/packages/frontend/src/scripts/player-url-transform.ts b/packages/frontend/src/scripts/player-url-transform.ts new file mode 100644 index 000000000000..53b2a9e44151 --- /dev/null +++ b/packages/frontend/src/scripts/player-url-transform.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { hostname } from '@/config.js'; + +export function transformPlayerUrl(url: string): string { + const urlObj = new URL(url); + if (!['https:', 'http:'].includes(urlObj.protocol)) throw new Error('Invalid protocol'); + + const urlParams = new URLSearchParams(urlObj.search); + + if (urlObj.hostname === 'player.twitch.tv') { + // TwitchはCSPの制約あり + // https://dev.twitch.tv/docs/embed/video-and-clips/ + urlParams.set('parent', hostname); + urlParams.set('allowfullscreen', ''); + urlParams.set('autoplay', 'true'); + } else { + urlParams.set('autoplay', '1'); + urlParams.set('auto_play', '1'); + } + urlObj.search = urlParams.toString(); + + return urlObj.toString(); +} From ec1c392f1e409e707ba81dfcd45112efb418c7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:44:18 +0900 Subject: [PATCH 06/27] =?UTF-8?q?fix(frontend):=20=E5=AD=90=E3=83=A1?= =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AE=E6=9C=80=E5=A4=A7=E9=95=B7?= =?UTF-8?q?=E8=AA=BF=E6=95=B4=E3=81=8C=E8=A1=8C=E3=82=8F=E3=82=8C=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(#14003)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): 子メニューの最大長調整が行われていない問題を修正 * Update Changelog * fix * changelog * Revert "fix" This reverts commit 39fb326d49eedf484342c78a61c0dba8e223e596. * Revert "fix(frontend): 子メニューの最大長調整が行われていない問題を修正" This reverts commit ea58bf7a53fc8a254b7fbdf222a676e23527358c. * use css * maxHeightをchildから定義するように * use css min --- CHANGELOG.md | 1 + packages/frontend/src/components/MkMenu.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bc28e0773b..8ed34ab0500b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574) - Fix: Twitchの埋め込みが開けない問題を修正 +- Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正 ### Server - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949) diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 2276da1d21af..c0728d56fa0b 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only }" :style="{ width: (width && !asDrawer) ? `${width}px` : '', - maxHeight: maxHeight ? `${maxHeight}px` : '', + maxHeight: maxHeight ? `min(${maxHeight}px, calc(100dvh - 32px))` : 'calc(100dvh - 32px)', }" @keydown.stop="() => {}" @contextmenu.self.prevent="() => {}" From 10ce7bf3c45c3e09dc86f1b9c3a0d7e79c23f5ee Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 18 Jul 2024 20:04:23 +0900 Subject: [PATCH 07/27] kill any from streaming API Implementation (#14251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add JsonValue type * refactor: kill any from Connection.ts * refactor: fix StreamEventEmitter contains undefined instead of null * refactor: kill any from channels * docs(changelog): Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 * fix license header * fix lints --- CHANGELOG.md | 2 + .../backend/src/core/GlobalEventService.ts | 28 +++++---- packages/backend/src/misc/json-value.ts | 8 +++ .../src/server/api/stream/Connection.ts | 59 +++++++++++-------- .../backend/src/server/api/stream/channel.ts | 13 ++-- .../src/server/api/stream/channels/admin.ts | 3 +- .../src/server/api/stream/channels/antenna.ts | 6 +- .../src/server/api/stream/channels/channel.ts | 6 +- .../src/server/api/stream/channels/drive.ts | 3 +- .../api/stream/channels/global-timeline.ts | 7 ++- .../src/server/api/stream/channels/hashtag.ts | 7 ++- .../api/stream/channels/home-timeline.ts | 7 ++- .../api/stream/channels/hybrid-timeline.ts | 9 +-- .../api/stream/channels/local-timeline.ts | 9 +-- .../src/server/api/stream/channels/main.ts | 3 +- .../server/api/stream/channels/queue-stats.ts | 10 +++- .../api/stream/channels/reversi-game.ts | 33 ++++++++--- .../src/server/api/stream/channels/reversi.ts | 3 +- .../api/stream/channels/role-timeline.ts | 6 +- .../api/stream/channels/server-stats.ts | 8 ++- .../server/api/stream/channels/user-list.ts | 10 ++-- 21 files changed, 156 insertions(+), 84 deletions(-) create mode 100644 packages/backend/src/misc/json-value.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed34ab0500b..14d638ebe14e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Note - デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。 +- Streaming APIにて入力が不正な場合にはそのメッセージを無視するようになりました。 #14251 ### General - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705 @@ -76,6 +77,7 @@ - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正 - Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正 (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1) +- Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251 ### Misskey.js - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 2a7d8d4bbe91..312bcfb3b5fc 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -209,6 +209,10 @@ type SerializedAll = { [K in keyof T]: Serialized; }; +type UndefinedAsNullAll = { + [K in keyof T]: T[K] extends undefined ? null : T[K]; +} + export interface InternalEventTypes { userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; @@ -248,43 +252,45 @@ export interface InternalEventTypes { userKeypairUpdated: { userId: MiUser['id']; }; } +type EventTypesToEventPayload = EventUnionFromDictionary>>; + // name/messages(spec) pairs dictionary export type GlobalEvents = { internal: { name: 'internal'; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; broadcast: { name: 'broadcast'; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; main: { name: `mainStream:${MiUser['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; drive: { name: `driveStream:${MiUser['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; note: { name: `noteStream:${MiNote['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; userList: { name: `userListStream:${MiUserList['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; roleTimeline: { name: `roleTimelineStream:${MiRole['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; antenna: { name: `antennaStream:${MiAntenna['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; admin: { name: `adminStream:${MiUser['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; notes: { name: 'notesStream'; @@ -292,11 +298,11 @@ export type GlobalEvents = { }; reversi: { name: `reversiStream:${MiUser['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; reversiGame: { name: `reversiGameStream:${MiReversiGame['id']}`; - payload: EventUnionFromDictionary>; + payload: EventTypesToEventPayload; }; }; diff --git a/packages/backend/src/misc/json-value.ts b/packages/backend/src/misc/json-value.ts new file mode 100644 index 000000000000..79944417915e --- /dev/null +++ b/packages/backend/src/misc/json-value.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export type JsonValue = JsonArray | JsonObject | string | number | boolean | null; +export type JsonObject = {[K in string]?: JsonValue}; +export type JsonArray = JsonValue[]; diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index 41c0feccc7e3..96082827f81f 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -14,6 +14,7 @@ import { CacheService } from '@/core/CacheService.js'; import { MiFollowing, MiUserProfile } from '@/models/_.js'; import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; +import type { JsonObject } from '@/misc/json-value.js'; import type { ChannelsService } from './ChannelsService.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; @@ -28,7 +29,7 @@ export default class Connection { private wsConnection: WebSocket.WebSocket; public subscriber: StreamEventEmitter; private channels: Channel[] = []; - private subscribingNotes: any = {}; + private subscribingNotes: Partial> = {}; private cachedNotes: Packed<'Note'>[] = []; public userProfile: MiUserProfile | null = null; public following: Record | undefined> = {}; @@ -101,7 +102,7 @@ export default class Connection { */ @bindThis private async onWsConnectionMessage(data: WebSocket.RawData) { - let obj: Record; + let obj: JsonObject; try { obj = JSON.parse(data.toString()); @@ -111,6 +112,8 @@ export default class Connection { const { type, body } = obj; + if (typeof body !== 'object' || body === null || Array.isArray(body)) return; + switch (type) { case 'readNotification': this.onReadNotification(body); break; case 'subNote': this.onSubscribeNote(body); break; @@ -151,7 +154,7 @@ export default class Connection { } @bindThis - private readNote(body: any) { + private readNote(body: JsonObject) { const id = body.id; const note = this.cachedNotes.find(n => n.id === id); @@ -163,7 +166,7 @@ export default class Connection { } @bindThis - private onReadNotification(payload: any) { + private onReadNotification(payload: JsonObject) { this.notificationService.readAllNotification(this.user!.id); } @@ -171,16 +174,14 @@ export default class Connection { * 投稿購読要求時 */ @bindThis - private onSubscribeNote(payload: any) { - if (!payload.id) return; - - if (this.subscribingNotes[payload.id] == null) { - this.subscribingNotes[payload.id] = 0; - } + private onSubscribeNote(payload: JsonObject) { + if (!payload.id || typeof payload.id !== 'string') return; - this.subscribingNotes[payload.id]++; + const current = this.subscribingNotes[payload.id] ?? 0; + const updated = current + 1; + this.subscribingNotes[payload.id] = updated; - if (this.subscribingNotes[payload.id] === 1) { + if (updated === 1) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } } @@ -189,11 +190,14 @@ export default class Connection { * 投稿購読解除要求時 */ @bindThis - private onUnsubscribeNote(payload: any) { - if (!payload.id) return; - - this.subscribingNotes[payload.id]--; - if (this.subscribingNotes[payload.id] <= 0) { + private onUnsubscribeNote(payload: JsonObject) { + if (!payload.id || typeof payload.id !== 'string') return; + + const current = this.subscribingNotes[payload.id]; + if (current == null) return; + const updated = current - 1; + this.subscribingNotes[payload.id] = updated; + if (updated <= 0) { delete this.subscribingNotes[payload.id]; this.subscriber.off(`noteStream:${payload.id}`, this.onNoteStreamMessage); } @@ -212,17 +216,22 @@ export default class Connection { * チャンネル接続要求時 */ @bindThis - private onChannelConnectRequested(payload: any) { + private onChannelConnectRequested(payload: JsonObject) { const { channel, id, params, pong } = payload; - this.connectChannel(id, params, channel, pong); + if (typeof id !== 'string') return; + if (typeof channel !== 'string') return; + if (typeof pong !== 'boolean' && typeof pong !== 'undefined' && pong !== null) return; + if (typeof params !== 'undefined' && (typeof params !== 'object' || params === null || Array.isArray(params))) return; + this.connectChannel(id, params, channel, pong ?? undefined); } /** * チャンネル切断要求時 */ @bindThis - private onChannelDisconnectRequested(payload: any) { + private onChannelDisconnectRequested(payload: JsonObject) { const { id } = payload; + if (typeof id !== 'string') return; this.disconnectChannel(id); } @@ -230,7 +239,7 @@ export default class Connection { * クライアントにメッセージ送信 */ @bindThis - public sendMessageToWs(type: string, payload: any) { + public sendMessageToWs(type: string, payload: JsonObject) { this.wsConnection.send(JSON.stringify({ type: type, body: payload, @@ -241,7 +250,7 @@ export default class Connection { * チャンネルに接続 */ @bindThis - public connectChannel(id: string, params: any, channel: string, pong = false) { + public connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) { const channelService = this.channelsService.getChannelService(channel); if (channelService.requireCredential && this.user == null) { @@ -288,7 +297,11 @@ export default class Connection { * @param data メッセージ */ @bindThis - private onChannelMessageRequested(data: any) { + private onChannelMessageRequested(data: JsonObject) { + if (typeof data.id !== 'string') return; + if (typeof data.type !== 'string') return; + if (typeof data.body === 'undefined') return; + const channel = this.channels.find(c => c.id === data.id); if (channel != null && channel.onMessage != null) { channel.onMessage(data.type, data.body); diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index a267d27fba70..84cb552369fe 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -8,6 +8,7 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { Packed } from '@/misc/json-schema.js'; +import type { JsonObject, JsonValue } from '@/misc/json-value.js'; import type Connection from './Connection.js'; /** @@ -81,10 +82,12 @@ export default abstract class Channel { this.connection = connection; } + public send(payload: { type: string, body: JsonValue }): void + public send(type: string, payload: JsonValue): void @bindThis - public send(typeOrPayload: any, payload?: any) { - const type = payload === undefined ? typeOrPayload.type : typeOrPayload; - const body = payload === undefined ? typeOrPayload.body : payload; + public send(typeOrPayload: { type: string, body: JsonValue } | string, payload?: JsonValue) { + const type = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).type : (typeOrPayload as string); + const body = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).body : payload; this.connection.sendMessageToWs('channel', { id: this.id, @@ -93,11 +96,11 @@ export default abstract class Channel { }); } - public abstract init(params: any): void; + public abstract init(params: JsonObject): void; public dispose?(): void; - public onMessage?(type: string, body: any): void; + public onMessage?(type: string, body: JsonValue): void; } export type MiChannelService = { diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 92b6d2ac040f..355d5dba21a6 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -5,6 +5,7 @@ import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class AdminChannel extends Channel { @@ -14,7 +15,7 @@ class AdminChannel extends Channel { public static kind = 'read:admin:stream'; @bindThis - public async init(params: any) { + public async init(params: JsonObject) { // Subscribe admin stream this.subscriber.on(`adminStream:${this.user!.id}`, data => { this.send(data); diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 4a1d2dd109d2..53dc7f18b63b 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class AntennaChannel extends Channel { @@ -27,8 +28,9 @@ class AntennaChannel extends Channel { } @bindThis - public async init(params: any) { - this.antennaId = params.antennaId as string; + public async init(params: JsonObject) { + if (typeof params.antennaId !== 'string') return; + this.antennaId = params.antennaId; // Subscribe stream this.subscriber.on(`antennaStream:${this.antennaId}`, this.onEvent); diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 140dd3dd9bcd..7108e0cd6e3a 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class ChannelChannel extends Channel { @@ -27,8 +28,9 @@ class ChannelChannel extends Channel { } @bindThis - public async init(params: any) { - this.channelId = params.channelId as string; + public async init(params: JsonObject) { + if (typeof params.channelId !== 'string') return; + this.channelId = params.channelId; // Subscribe stream this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 0d9b48630511..03768f3d23ef 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -5,6 +5,7 @@ import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class DriveChannel extends Channel { @@ -14,7 +15,7 @@ class DriveChannel extends Channel { public static kind = 'read:account'; @bindThis - public async init(params: any) { + public async init(params: JsonObject) { // Subscribe drive stream this.subscriber.on(`driveStream:${this.user!.id}`, data => { this.send(data); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 17116258d89b..ed56fe0d4059 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class GlobalTimelineChannel extends Channel { @@ -32,12 +33,12 @@ class GlobalTimelineChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.gtlAvailable) return; - this.withRenotes = params.withRenotes ?? true; - this.withFiles = params.withFiles ?? false; + this.withRenotes = !!(params.withRenotes ?? true); + this.withFiles = !!(params.withFiles ?? false); // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 57bada5d9cfb..8105f15cb17a 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -9,6 +9,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class HashtagChannel extends Channel { @@ -28,11 +29,11 @@ class HashtagChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { + if (!Array.isArray(params.q)) return; + if (!params.q.every(x => Array.isArray(x) && x.every(y => typeof y === 'string'))) return; this.q = params.q; - if (this.q == null) return; - // Subscribe stream this.subscriber.on('notesStream', this.onNote); } diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 878a3180cb62..1f440732a65e 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class HomeTimelineChannel extends Channel { @@ -29,9 +30,9 @@ class HomeTimelineChannel extends Channel { } @bindThis - public async init(params: any) { - this.withRenotes = params.withRenotes ?? true; - this.withFiles = params.withFiles ?? false; + public async init(params: JsonObject) { + this.withRenotes = !!(params.withRenotes ?? true); + this.withFiles = !!(params.withFiles ?? false); this.subscriber.on('notesStream', this.onNote); } diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 575d23d53c18..6938b6e3ead1 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class HybridTimelineChannel extends Channel { @@ -34,13 +35,13 @@ class HybridTimelineChannel extends Channel { } @bindThis - public async init(params: any): Promise { + public async init(params: JsonObject): Promise { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withRenotes = params.withRenotes ?? true; - this.withReplies = params.withReplies ?? false; - this.withFiles = params.withFiles ?? false; + this.withRenotes = !!(params.withRenotes ?? true); + this.withReplies = !!(params.withReplies ?? false); + this.withFiles = !!(params.withFiles ?? false); // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 442d08ae51a5..491029f5dea1 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class LocalTimelineChannel extends Channel { @@ -33,13 +34,13 @@ class LocalTimelineChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withRenotes = params.withRenotes ?? true; - this.withReplies = params.withReplies ?? false; - this.withFiles = params.withFiles ?? false; + this.withRenotes = !!(params.withRenotes ?? true); + this.withReplies = !!(params.withReplies ?? false); + this.withFiles = !!(params.withFiles ?? false); // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index a12976d69d1a..863d7f4c4e45 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class MainChannel extends Channel { @@ -25,7 +26,7 @@ class MainChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { // Subscribe main stream channel this.subscriber.on(`mainStream:${this.user!.id}`, async data => { switch (data.type) { diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index 061aa76904a4..ff7e7402261b 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -6,6 +6,7 @@ import Xev from 'xev'; import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { JsonObject, JsonValue } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; const ev = new Xev(); @@ -22,19 +23,22 @@ class QueueStatsChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { ev.addListener('queueStats', this.onStats); } @bindThis - private onStats(stats: any) { + private onStats(stats: JsonObject) { this.send('stats', stats); } @bindThis - public onMessage(type: string, body: any) { + public onMessage(type: string, body: JsonValue) { switch (type) { case 'requestLog': + if (typeof body !== 'object' || body === null || Array.isArray(body)) return; + if (typeof body.id !== 'string') return; + if (typeof body.length !== 'number') return; ev.once(`queueStatsLog:${body.id}`, statsLog => { this.send('statsLog', statsLog); }); diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts index f4a3a09367e3..17823a164a2d 100644 --- a/packages/backend/src/server/api/stream/channels/reversi-game.ts +++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { ReversiService } from '@/core/ReversiService.js'; import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import type { JsonObject, JsonValue } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class ReversiGameChannel extends Channel { @@ -28,25 +29,41 @@ class ReversiGameChannel extends Channel { } @bindThis - public async init(params: any) { - this.gameId = params.gameId as string; + public async init(params: JsonObject) { + if (typeof params.gameId !== 'string') return; + this.gameId = params.gameId; this.subscriber.on(`reversiGameStream:${this.gameId}`, this.send); } @bindThis - public onMessage(type: string, body: any) { + public onMessage(type: string, body: JsonValue) { switch (type) { - case 'ready': this.ready(body); break; - case 'updateSettings': this.updateSettings(body.key, body.value); break; - case 'cancel': this.cancelGame(); break; - case 'putStone': this.putStone(body.pos, body.id); break; + case 'ready': + if (typeof body !== 'boolean') return; + this.ready(body); + break; + case 'updateSettings': + if (typeof body !== 'object' || body === null || Array.isArray(body)) return; + if (typeof body.key !== 'string') return; + if (typeof body.value !== 'object' || body.value === null || Array.isArray(body.value)) return; + this.updateSettings(body.key, body.value); + break; + case 'cancel': + this.cancelGame(); + break; + case 'putStone': + if (typeof body !== 'object' || body === null || Array.isArray(body)) return; + if (typeof body.pos !== 'number') return; + if (typeof body.id !== 'string') return; + this.putStone(body.pos, body.id); + break; case 'claimTimeIsUp': this.claimTimeIsUp(); break; } } @bindThis - private async updateSettings(key: string, value: any) { + private async updateSettings(key: string, value: JsonObject) { if (this.user == null) return; this.reversiService.updateSettings(this.gameId!, this.user, key, value); diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts index 3998a0fd363e..6e889397245d 100644 --- a/packages/backend/src/server/api/stream/channels/reversi.ts +++ b/packages/backend/src/server/api/stream/channels/reversi.ts @@ -5,6 +5,7 @@ import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class ReversiChannel extends Channel { @@ -21,7 +22,7 @@ class ReversiChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { this.subscriber.on(`reversiStream:${this.user!.id}`, this.send); } diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index 6a4ad224607b..fcfa26c38b39 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -8,6 +8,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class RoleTimelineChannel extends Channel { @@ -28,8 +29,9 @@ class RoleTimelineChannel extends Channel { } @bindThis - public async init(params: any) { - this.roleId = params.roleId as string; + public async init(params: JsonObject) { + if (typeof params.roleId !== 'string') return; + this.roleId = params.roleId; this.subscriber.on(`roleTimelineStream:${this.roleId}`, this.onEvent); } diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index eb4d8c9992cb..6258afba35c3 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -6,6 +6,7 @@ import Xev from 'xev'; import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { JsonObject, JsonValue } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; const ev = new Xev(); @@ -22,19 +23,20 @@ class ServerStatsChannel extends Channel { } @bindThis - public async init(params: any) { + public async init(params: JsonObject) { ev.addListener('serverStats', this.onStats); } @bindThis - private onStats(stats: any) { + private onStats(stats: JsonObject) { this.send('stats', stats); } @bindThis - public onMessage(type: string, body: any) { + public onMessage(type: string, body: JsonValue) { switch (type) { case 'requestLog': + if (typeof body !== 'object' || body === null || Array.isArray(body)) return; ev.once(`serverStatsLog:${body.id}`, statsLog => { this.send('statsLog', statsLog); }); diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 14b30a157cd0..4f38351e94ed 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; +import type { JsonObject } from '@/misc/json-value.js'; import Channel, { type MiChannelService } from '../channel.js'; class UserListChannel extends Channel { @@ -36,10 +37,11 @@ class UserListChannel extends Channel { } @bindThis - public async init(params: any) { - this.listId = params.listId as string; - this.withFiles = params.withFiles ?? false; - this.withRenotes = params.withRenotes ?? true; + public async init(params: JsonObject) { + if (typeof params.listId !== 'string') return; + this.listId = params.listId; + this.withFiles = !!(params.withFiles ?? false); + this.withRenotes = !!(params.withRenotes ?? true); // Check existence and owner const listExist = await this.userListsRepository.exists({ From 615e60f25cde9115f0e044200e8adcab5eb5004d Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:52:39 +0900 Subject: [PATCH 08/27] chore: modernize issue template (#14263) --- .github/ISSUE_TEMPLATE/01_bug-report.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.yml b/.github/ISSUE_TEMPLATE/01_bug-report.yml index ac2b39cc12c6..315e712c30e4 100644 --- a/.github/ISSUE_TEMPLATE/01_bug-report.yml +++ b/.github/ISSUE_TEMPLATE/01_bug-report.yml @@ -53,8 +53,8 @@ body: Examples: * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4 * Browser: Chrome 113.0.5672.126 - * Server URL: misskey.io - * Misskey: 13.x.x + * Server URL: misskey.example.com + * Misskey: 2024.x.x value: | * Model and OS of the device(s): * Browser: @@ -74,11 +74,11 @@ body: Examples: * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment - * Misskey: 13.x.x + * Misskey: 2024.x.x * Node: 20.x.x * PostgreSQL: 15.x.x * Redis: 7.x.x - * OS and Architecture: Ubuntu 22.04.2 LTS aarch64 + * OS and Architecture: Ubuntu 24.04.2 LTS aarch64 value: | * Installation Method or Hosting Service: * Misskey: From 54d0a4637847aa13700b40ecb1d5edabd048cc2c Mon Sep 17 00:00:00 2001 From: taichan <40626578+tai-cha@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:53:49 +0900 Subject: [PATCH 09/27] =?UTF-8?q?fix(frontend):=20=E5=80=8B=E4=BA=BA?= =?UTF-8?q?=E5=AE=9B=E3=81=A6=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=81=8A=E7=9F=A5=E3=82=89=E3=81=9B=E3=81=8C=E5=8D=B3=E6=99=82?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=20(#14260)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): 個人向けお知らせが即時ダイアログで出ない問題 * Update CHANGELOG --- CHANGELOG.md | 1 + packages/frontend/src/boot/main-boot.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d638ebe14e..1e06f16bdfcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574) - Fix: Twitchの埋め込みが開けない問題を修正 - Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正 +- Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正 ### Server - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949) diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index d32701631726..2a549d1e8bc9 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -5,6 +5,7 @@ import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { common } from './common.js'; +import type * as Misskey from 'misskey-js'; import { ui } from '@/config.js'; import { i18n } from '@/i18n.js'; import { alert, confirm, popup, post, toast } from '@/os.js'; @@ -113,7 +114,7 @@ export async function mainBoot() { }); } - stream.on('announcementCreated', (ev) => { + function onAnnouncementCreated (ev: { announcement: Misskey.entities.Announcement }) { const announcement = ev.announcement; if (announcement.display === 'dialog') { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), { @@ -122,7 +123,9 @@ export async function mainBoot() { closed: () => dispose(), }); } - }); + } + + stream.on('announcementCreated', onAnnouncementCreated); if ($i.isDeleted) { alert({ @@ -315,6 +318,9 @@ export async function mainBoot() { updateAccount({ hasUnreadAnnouncement: false }); }); + // 個人宛てお知らせが発行されたとき + main.on('announcementCreated', onAnnouncementCreated); + // トークンが再生成されたとき // このままではMisskeyが利用できないので強制的にサインアウトさせる main.on('myTokenRegenerated', () => { From 1f24a8cb5a307c3ff621577189a2a618b9dcfdc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:57:01 +0900 Subject: [PATCH 10/27] =?UTF-8?q?enhance(frontend):=20=E3=82=BB=E3=83=B3?= =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E3=83=A1=E3=83=87?= =?UTF-8?q?=E3=82=A3=E3=82=A2=E3=82=92=E9=96=8B=E3=81=8F=E9=9A=9B=E3=81=AB?= =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=82=92=E5=87=BA=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#14115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように * Update Changelog --- CHANGELOG.md | 1 + locales/index.d.ts | 8 ++++++ locales/ja-JP.yml | 2 ++ .../frontend/src/components/MkMediaAudio.vue | 14 +++++++++- .../frontend/src/components/MkMediaBanner.vue | 26 ++++++++++++------- .../frontend/src/components/MkMediaImage.vue | 12 ++++++++- .../frontend/src/components/MkMediaList.vue | 8 +++--- .../frontend/src/components/MkMediaVideo.vue | 14 +++++++++- .../frontend/src/pages/settings/general.vue | 3 +++ packages/frontend/src/store.ts | 4 +++ 10 files changed, 75 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e06f16bdfcb..7cf77d60837b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ (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`) +- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968) - Fix: リバーシの対局を正しく共有できないことがある問題を修正 diff --git a/locales/index.d.ts b/locales/index.d.ts index 694ee53a1f0e..55c65f2aedf3 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5008,6 +5008,14 @@ export interface Locale extends ILocale { * もう一度お試しください。 */ "tryAgain": string; + /** + * センシティブなメディアを表示するとき確認する + */ + "confirmWhenRevealingSensitiveMedia": string; + /** + * センシティブなメディアです。表示しますか? + */ + "sensitiveMediaRevealConfirm": string; "_delivery": { /** * 配信状態 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index bb3999f0e3b1..3ca4b46682d7 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1248,6 +1248,8 @@ noDescription: "説明文はありません" alwaysConfirmFollow: "フォローの際常に確認する" inquiry: "お問い合わせ" tryAgain: "もう一度お試しください。" +confirmWhenRevealingSensitiveMedia: "センシティブなメディアを表示するとき確認する" +sensitiveMediaRevealConfirm: "センシティブなメディアです。表示しますか?" _delivery: status: "配信状態" diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 582cf238c0b8..a080550ddfaf 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only @contextmenu.stop @keydown.stop > -