diff --git a/CHANGELOG_YOJO.md b/CHANGELOG_YOJO.md index 0e52f14901..727c739fa4 100644 --- a/CHANGELOG_YOJO.md +++ b/CHANGELOG_YOJO.md @@ -34,12 +34,14 @@ - ユーザー検索画面で照会しますか?のダイアログが2つ出る問題 - Fix: 更新情報を確認のCherryPickの項目へのリンクを修正 - Feat: お気に入りのタグリストを作成できるように +- Enhance: リバーシ連合の対応状況をサーバー一覧に表示するように ### Server - Fix: ユーザーnull(System)の場合forceがfalseでも新規追加されるのを修正 - Fix: Outboxから投稿を所得する際にタイムラインに投稿が流れないように - Fix: 翻訳にdeepl以外を利用していると翻訳できない問題を修正 [#355](https://github.com/yojo-art/cherrypick/pull/355) - Fix: 絵文字インポート時にすでにファイルがあるならそれを使うように +- Enhance: リバーシ連合の対応状況をnodeinfoに追加 ### Misc diff --git a/locales/index.d.ts b/locales/index.d.ts index ecef123d81..d4c01cef7e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -11575,6 +11575,18 @@ export interface Locale extends ILocale { * 石をアイコンにする */ "useAvatarAsStone": string; + /** + * リモートサーバーのバージョンが不明です + */ + "remoteVersionUnknown": string; + /** + * 対応していない可能性があります + */ + "remoteVersionUnknownCaption": string; + /** + * リモートサーバーのバージョンが非互換です + */ + "remoteVersionBad": string; }; "_offlineScreen": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0fc3f38434..e8bacaf1af 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3078,6 +3078,9 @@ _reversi: disallowIrregularRules: "変則なし" showBoardLabels: "盤面に行・列番号を表示" useAvatarAsStone: "石をアイコンにする" + remoteVersionUnknown: "リモートサーバーのバージョンが不明です" + remoteVersionUnknownCaption: "対応していない可能性があります" + remoteVersionBad: "リモートサーバーのバージョンが非互換です" _offlineScreen: title: "オフライン - サーバーに接続できません" diff --git a/packages/backend/migration/1724921022768-AddNodeinfoReversi.js b/packages/backend/migration/1724921022768-AddNodeinfoReversi.js new file mode 100644 index 0000000000..bb06a86aa4 --- /dev/null +++ b/packages/backend/migration/1724921022768-AddNodeinfoReversi.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project, yojo-art team + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AddNodeinfoReversi1724921022768 { + name = 'AddNodeinfoReversi1724921022768' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" ADD "reversiVersion" character varying(32)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "reversiVersion"`); + } +} diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 8848c109c2..fe2e33e091 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -120,6 +120,7 @@ export class FetchInstanceMetadataService { updates.openRegistrations = info.openRegistrations; updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name ?? null) : null : null; updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email ?? null) : null : null; + updates.reversiVersion = info.metadata ? info.metadata.reversiVersion ?? null : null; } if (name) updates.name = name; diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts index 35c8b95cdf..40df0a004e 100644 --- a/packages/backend/src/core/ReversiService.ts +++ b/packages/backend/src/core/ReversiService.ts @@ -117,10 +117,10 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit { if (typeof(reversiVersion) === 'string') { //0.0.0-foo => 0.0.0 const version = reversiVersion.split('-')[0]; - await this.redisClient.setex(`reversi:federation:version:${host}`, version, 5 * 60); + await this.redisClient.setex(`reversi:federation:version:${host}`, 5 * 60, version); return version; } - await this.redisClient.setex(`reversi:federation:version:${host}`, '', 5 * 60); + await this.redisClient.setex(`reversi:federation:version:${host}`, 5 * 60, ''); return null; } @bindThis @@ -132,7 +132,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit { } const versionElements = version.split('.'); if (versionElements.length === 3) { - if (versionElements[0] !== NodeinfoServerService.reversiVersion.split('-')[0].split('.')[0]) { + if (versionElements[0] !== NodeinfoServerService.reversiVersion.split('.')[0]) { //メジャーバージョン不一致 return false; } diff --git a/packages/backend/src/core/activitypub/models/ApGameService.ts b/packages/backend/src/core/activitypub/models/ApGameService.ts index a89be674ca..ecbf6b3ba0 100644 --- a/packages/backend/src/core/activitypub/models/ApGameService.ts +++ b/packages/backend/src/core/activitypub/models/ApGameService.ts @@ -94,7 +94,7 @@ export class ApGameService { const targetUser = local_user; const fromUser = remote_user; if (!game.game_state.game_session_id) throw Error('bad session' + JSON.stringify(game)); - if (await this.reversiService.federationAvailable(remote_user.host) === false) { + if ((await this.reversiService.federationAvailable(remote_user.host)) === false) { //確実に利用できない時 return; } diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 4c45c13167..bdb39f3d71 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -57,6 +57,7 @@ export class InstanceEntityService { infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, moderationNote: iAmModerator ? instance.moderationNote : null, + reversiVersion: instance.reversiVersion, }; } diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 46ff323adb..9c26956d81 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -17,6 +17,7 @@ import { InstanceActorService } from '@/core/InstanceActorService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { NodeinfoServerService } from '@/server/NodeinfoServerService.js'; @Injectable() export class MetaEntityService { @@ -132,6 +133,7 @@ export class MetaEntityService { enableUrlPreview: instance.urlPreviewEnabled, noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local', urlPreviewEndpoint: instance.directSummalyProxy ? (instance.urlPreviewSummaryProxyUrl || `${this.config.url}/url` ) : `${this.config.url}/url`, + reversiVersion: NodeinfoServerService.reversiVersion, }; return packed; diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts index 17cd5c6665..0287b2f010 100644 --- a/packages/backend/src/models/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -158,4 +158,9 @@ export class MiInstance { length: 16384, default: '', }) public moderationNote: string; + + @Column('varchar', { + length: 64, nullable: true, + }) + public reversiVersion: string | null; } diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 3488ce250d..8f4ead0b14 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -120,5 +120,9 @@ export const packedFederationInstanceSchema = { type: 'string', optional: true, nullable: true, }, + reversiVersion: { + type: 'string', + optional: true, nullable: true, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts index 1b64967bd2..334983bffc 100644 --- a/packages/backend/src/models/json-schema/meta.ts +++ b/packages/backend/src/models/json-schema/meta.ts @@ -265,6 +265,10 @@ export const packedMetaLiteSchema = { optional: false, nullable: false, default: 'local', }, + reversiVersion: { + type: 'string', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index 984a40229f..a6c47bd3c1 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -4939,6 +4939,7 @@ export type components = { /** Format: date-time */ latestRequestReceivedAt: string | null; moderationNote?: string | null; + reversiVersion?: string | null; }; GalleryPost: { /** @@ -5282,6 +5283,7 @@ export type components = { * @enum {string} */ noteSearchableScope: 'local' | 'global'; + reversiVersion: string; }; MetaDetailedOnly: { features?: { diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index 1a22dd54fd..5b43684201 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ instance.name ?? instance.host }} {{ instance.host }} / {{ instance.softwareName || '?' }} {{ instance.softwareVersion }} + {{ i18n.ts._reversi.reversi }} / {{ instance.reversiVersion || `(${i18n.ts.unknown})` }}
@@ -20,6 +21,7 @@ import * as Misskey from 'cherrypick-js'; import MkMiniChart from '@/components/MkMiniChart.vue'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; +import { i18n } from '@/i18n.js'; const props = defineProps<{ instance: Misskey.entities.FederationInstance; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 90c4687c60..d31db8b4d4 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -22,6 +22,10 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue index 774fc4fe41..d509437916 100644 --- a/packages/frontend/src/pages/reversi/index.vue +++ b/packages/frontend/src/pages/reversi/index.vue @@ -120,6 +120,7 @@ import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; import { pleaseLogin } from '@/scripts/please-login.js'; import * as sound from '@/scripts/sound.js'; +import { instance } from '@/instance.js'; const myGamesPagination = { endpoint: 'reversi/games' as const, @@ -198,6 +199,34 @@ async function matchUser() { const user = await os.selectUser({ includeSelf: false, localOnly: false }); if (user == null) return; + if (user.host) { + let remote_instance = await misskeyApi('federation/show-instance', { + host: user.host, + }); + //初期のバージョン番号を持たない実装は1.0.0扱いにする + const reversiVersion = remote_instance?.reversiVersion ?? '1.0.0'; + + const versionElements = reversiVersion.split('.'); + if (versionElements.length === 3) { + if (versionElements[0] !== instance.reversiVersion.split('.')[0]) { + //メジャーバージョン不一致 + await os.alert({ + type: 'error', + text: i18n.ts._reversi.remoteVersionBad, + }); + return; + } + } + //バージョン番号不明の場合は警告だけ出す + if (!remote_instance?.reversiVersion) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts._reversi.remoteVersionUnknown, + caption: i18n.ts._reversi.remoteVersionUnknownCaption, + }); + if (canceled) return; + } + } matchingUser.value = user;