diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aee51fe12..1914a518c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ (Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13) - Enhance: 照会に失敗した場合、その理由を表示するように - Enhance: AiScriptのセーブデータを明示的に削除する関数`Mk:remove`を追加 +- Enhance: AiScriptの拡張API関数において引数の型チェックをより厳格に - Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正 - Fix: サーバー情報メニューに区切り線が不足していたのを修正 - Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正 @@ -20,13 +21,17 @@ (Cherry-picked from https://github.com/TeamNijimiss/misskey/commit/800359623e41a662551d774de15b0437b6849bb4) - Fix: ノート作成画面でファイルの添付可能個数を超えてもノートボタンが押せていた問題を修正 - Fix: 「アカウントを管理」画面で、ユーザー情報の取得に失敗したアカウント(削除されたアカウントなど)が表示されない問題を修正 +- Fix: 言語データのキャッシュ状況によっては、埋め込みウィジェットが正しく起動しない問題を修正 ### Server - Enhance: pg_bigmが利用できるよう、ノートの検索をILIKE演算子でなくLIKE演算子でLOWER()をかけたテキストに対して行うように - Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 ) - Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/737) - +- Fix: ロックダウンされた期間指定のノートがStreaming経由でLTLに出現するのを修正 ( #15200 ) +- Fix: disableClustering設定時の初期化ロジックを調整( #15223 ) +- Fix: ActivityPubリクエストかどうかの判定が正しくない問題を修正 + (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/869) ## 2024.11.0 diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts index 13651a6193..2f94e49c11 100644 --- a/packages/backend/src/boot/entry.ts +++ b/packages/backend/src/boot/entry.ts @@ -91,16 +91,22 @@ process.on('warning', warning => { //#endregion -if (cluster.isPrimary || envOption.disableClustering) { - await masterMain(); - +if (!envOption.disableClustering) { if (cluster.isPrimary) { + logger.info(`Start main process... pid: ${process.pid}`); + await masterMain(); ev.mount(); + } else if (cluster.isWorker) { + logger.info(`Start worker process... pid: ${process.pid}`); + await workerMain(); + } else { + throw new Error('Unknown process type'); } -} - -if (cluster.isWorker || envOption.disableClustering) { - await workerMain(); +} else { + // 非clusterの場合はMasterのみが起動するため、Workerの処理は行わない(cluster.isWorker === trueの状態でこのブロックに来ることはない) + logger.info(`Start main process... pid: ${process.pid}`); + await masterMain(); + ev.mount(); } readyRef.value = true; diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 73c91c4623..f9d0a81991 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -94,25 +94,37 @@ export async function masterMain() { }); } - if (envOption.disableClustering) { + bootLogger.info( + `mode: [disableClustering: ${envOption.disableClustering}, onlyServer: ${envOption.onlyServer}, onlyQueue: ${envOption.onlyQueue}]` + ); + + if (!envOption.disableClustering) { + // clusterモジュール有効時 + if (envOption.onlyServer) { - await server(); + // onlyServer かつ enableCluster な場合、メインプロセスはforkのみに制限する(listenしない)。 + // ワーカープロセス側でlistenすると、メインプロセスでポートへの着信を受け入れてワーカープロセスへの分配を行う動作をする。 + // そのため、メインプロセスでも直接listenするとポートの競合が発生して起動に失敗してしまう。 + // see: https://nodejs.org/api/cluster.html#cluster } else if (envOption.onlyQueue) { await jobQueue(); } else { await server(); await jobQueue(); } + + await spawnWorkers(config.clusterLimit); } else { + // clusterモジュール無効時 + if (envOption.onlyServer) { - // nop + await server(); } else if (envOption.onlyQueue) { - // nop + await jobQueue(); } else { await server(); + await jobQueue(); } - - await spawnWorkers(config.clusterLimit); } if (envOption.onlyQueue) { diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 400783daa0..79584363bd 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -105,8 +105,7 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise { - // FIXME: このvisibility変更処理が当関数にあるのは若干不自然かもしれない(関数名を treatVisibility とかに変える手もある) + private treatVisibility(packedNote: Packed<'Note'>): Packed<'Note'>['visibility'] { if (packedNote.visibility === 'public' || packedNote.visibility === 'home') { const followersOnlyBefore = packedNote.user.makeNotesFollowersOnlyBefore; if ((followersOnlyBefore != null) @@ -118,7 +117,11 @@ export class NoteEntityService implements OnModuleInit { packedNote.visibility = 'followers'; } } + return packedNote.visibility; + } + @bindThis + private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise { if (meId === packedNote.userId) return; // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) @@ -479,6 +482,8 @@ export class NoteEntityService implements OnModuleInit { } : {}), }); + this.treatVisibility(packed); + if (!opts.skipHide) { await this.hideNote(packed, meId); } diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 44fdc04346..49d0ef41e8 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -525,8 +525,8 @@ export class ActivityPubServerService { }, deriveConstraint(request: IncomingMessage) { const accepted = accepts(request).type(['html', ACTIVITY_JSON, LD_JSON]); - const isAp = typeof accepted === 'string' && !accepted.match(/html/); - return isAp ? 'ap' : 'html'; + if (accepted === false) return null; + return accepted !== 'html' ? 'ap' : 'html'; }, }); diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index 229ce58ce5..3a5035a25c 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -6,8 +6,8 @@ import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; import { EventEmitter } from 'eventemitter3'; +import { Options } from 'reconnecting-websocket'; import type { PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types'; -import _ReconnectingWebsocket from 'reconnecting-websocket'; // Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts // @@ -3341,7 +3341,7 @@ export class Stream extends EventEmitter implements IStream { constructor(origin: string, user: { token: string; } | null, options?: { - WebSocket?: _ReconnectingWebsocket.Options['WebSocket']; + WebSocket?: Options['WebSocket']; }); // (undocumented) close(): void; diff --git a/packages/cherrypick-js/src/streaming.ts b/packages/cherrypick-js/src/streaming.ts index 42e448e17c..c6d5117d6f 100644 --- a/packages/cherrypick-js/src/streaming.ts +++ b/packages/cherrypick-js/src/streaming.ts @@ -1,8 +1,10 @@ import { EventEmitter } from 'eventemitter3'; -import _ReconnectingWebsocket from 'reconnecting-websocket'; +import _ReconnectingWebSocket, { Options } from 'reconnecting-websocket'; import type { BroadcastEvents, Channels } from './streaming.types.js'; -const ReconnectingWebsocket = _ReconnectingWebsocket as unknown as typeof _ReconnectingWebsocket['default']; +// コンストラクタとクラスそのものの定義が上手く解決出来ないため再定義 +const ReconnectingWebSocketConstructor = _ReconnectingWebSocket as unknown as typeof _ReconnectingWebSocket.default; +type ReconnectingWebSocket = _ReconnectingWebSocket.default; export function urlQuery(obj: Record): string { const params = Object.entries(obj) @@ -43,7 +45,7 @@ export interface IStream extends EventEmitter { */ // eslint-disable-next-line import/no-default-export export default class Stream extends EventEmitter implements IStream { - private stream: _ReconnectingWebsocket.default; + private stream: ReconnectingWebSocket; public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing'; private sharedConnectionPools: Pool[] = []; private sharedConnections: SharedConnection[] = []; @@ -51,7 +53,7 @@ export default class Stream extends EventEmitter implements IStrea private idCounter = 0; constructor(origin: string, user: { token: string; } | null, options?: { - WebSocket?: _ReconnectingWebsocket.Options['WebSocket']; + WebSocket?: Options['WebSocket']; }) { super(); @@ -80,7 +82,7 @@ export default class Stream extends EventEmitter implements IStrea const wsOrigin = origin.replace('http://', 'ws://').replace('https://', 'wss://'); - this.stream = new ReconnectingWebsocket(`${wsOrigin}/streaming?${query}`, '', { + this.stream = new ReconnectingWebSocketConstructor(`${wsOrigin}/streaming?${query}`, '', { minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91 WebSocket: options.WebSocket, }); diff --git a/packages/frontend-embed/src/boot.ts b/packages/frontend-embed/src/boot.ts index c56e68fc25..deb25d06af 100644 --- a/packages/frontend-embed/src/boot.ts +++ b/packages/frontend-embed/src/boot.ts @@ -13,7 +13,7 @@ import { createApp, defineAsyncComponent } from 'vue'; import defaultLightTheme from '@@/themes/l-light.json5'; import defaultDarkTheme from '@@/themes/d-dark.json5'; import { MediaProxy } from '@@/js/media-proxy.js'; -import { url } from '@@/js/config.js'; +import { url, version, basedMisskeyVersion, locale, lang, updateLocale } from '@@/js/config.js'; import { parseEmbedParams } from '@@/js/embed-page.js'; import type { Theme } from '@/theme.js'; import { applyTheme, assertIsTheme } from '@/theme.js'; @@ -22,7 +22,7 @@ import { DI } from '@/di.js'; import { serverMetadata } from '@/server-metadata.js'; import { postMessageToParentWindow, setIframeId } from '@/post-message.js'; import { serverContext } from '@/server-context.js'; -import { i18n } from '@/i18n.js'; +import { i18n, updateI18n } from '@/i18n.js'; console.log('CherryPick Embed'); @@ -70,6 +70,23 @@ if (embedParams.colorMode === 'dark') { } //#endregion +//#region Detect language & fetch translations +const localeVersion = localStorage.getItem('localeVersion'); +const lastBasedMisskeyVersion = localStorage.getItem('lastBasedMisskeyVersion'); +const localeOutdated = (localeVersion == null || localeVersion !== version || lastBasedMisskeyVersion !== basedMisskeyVersion || locale == null); +if (localeOutdated) { + const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); + if (res.status === 200) { + const newLocale = await res.text(); + const parsedNewLocale = JSON.parse(newLocale); + localStorage.setItem('locale', newLocale); + localStorage.setItem('localeVersion', version); + updateLocale(parsedNewLocale); + updateI18n(parsedNewLocale); + } +} +//#endregion + // サイズの制限 document.documentElement.style.maxWidth = '500px'; diff --git a/packages/frontend-embed/src/components/EmMfm.ts b/packages/frontend-embed/src/components/EmMfm.ts index 2ce43e030f..4b6e268d11 100644 --- a/packages/frontend-embed/src/components/EmMfm.ts +++ b/packages/frontend-embed/src/components/EmMfm.ts @@ -421,8 +421,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext
- +
@@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only