From a387ea124a205744c99b962aa17aeda229534800 Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Sun, 25 Oct 2026 05:38:06 +0100 Subject: [PATCH] enforce client server compatibility --- src/App.svelte | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/App.svelte b/src/App.svelte index 9072ad1..2b40cb7 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -4,6 +4,7 @@ let serverAddress = 'http://127.0.0.1:8787'; let connectionState = 'OFFLINE'; let serverVersion = '—'; + let serverCompatibility = ''; let diagnostics: { status: string; database: string; twitch_identity_count: number; unseen_notification_count: number; realtime: string } | undefined; let backups: Array<{ filename: string; size_bytes: number; modified_at: string | null }> = []; let backupError = ''; @@ -101,6 +102,24 @@ bot: { connected: false, missing_scopes: [], reauthorization_required: false }, }; + const CLIENT_VERSION = '0.1.0'; + + function versionParts(value: string) { + const match = value.match(/^(\d+)\.(\d+)\.(\d+)/); + return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : undefined; + } + + function isClientCompatible(minimum: string, maximum: string) { + const client = versionParts(CLIENT_VERSION); + const minimumParts = versionParts(minimum); + const maximumParts = versionParts(maximum); + if (!client || !minimumParts) return false; + const compare = (left: number[], right: number[]) => left[0] - right[0] || left[1] - right[1] || left[2] - right[2]; + if (compare(client, minimumParts) < 0) return false; + if (maximum === '0.x' || !maximumParts) return true; + return compare(client, maximumParts) <= 0; + } + $: if (authenticated && selectedOverlayId && overlayName && !overlayBusy) { const signature = overlayDraftSignature(); cacheOverlayDraft(); @@ -129,6 +148,13 @@ if (!response.ok) throw new Error(`HTTP ${response.status}`); const info: { server_version: string; minimum_client_version?: string; maximum_client_version?: string } = await response.json(); serverVersion = info.server_version; + if (info.minimum_client_version && info.maximum_client_version && !isClientCompatible(info.minimum_client_version, info.maximum_client_version)) { + serverCompatibility = `Requires client ${info.minimum_client_version}–${info.maximum_client_version}`; + connectionState = 'INCOMPATIBLE'; + connectionNotice = 'Client update required'; + return; + } + serverCompatibility = info.minimum_client_version && info.maximum_client_version ? `${info.minimum_client_version}–${info.maximum_client_version}` : ''; connectionState = 'CONNECTED'; connectionNotice = ''; return; @@ -141,6 +167,10 @@ } async function login() { + if (connectionState === 'INCOMPATIBLE') { + authError = 'This client version is not compatible with the server.'; + return; + } authBusy = true; authError = ''; try {