From e400890eee63acc2f4bee9c459990ea3fd26ff40 Mon Sep 17 00:00:00 2001 From: StroepWafel Date: Thu, 25 Jun 2026 19:32:53 +0930 Subject: [PATCH] fix(accounts): login with account give device name --- src/background.ts | 31 ++++++++----- src/seqta/utils/CloudAuth.ts | 3 ++ src/seqta/utils/bsplusDeviceName.ts | 70 +++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 src/seqta/utils/bsplusDeviceName.ts diff --git a/src/background.ts b/src/background.ts index 6a6b5970..fd7aeb16 100644 --- a/src/background.ts +++ b/src/background.ts @@ -11,6 +11,7 @@ import { requestCloudSettingsDebouncedUpload, runCloudSettingsPoll, } from "./background/cloudSettingsAutoSync"; +import { getBsplusDeviceName } from "@/seqta/utils/bsplusDeviceName"; /** * Session-only dev-mode override of the content API base. @@ -178,25 +179,33 @@ function handleCloudReserveClient(request: any, sendResponse: MessageSender): bo } function handleCloudLogin(request: any, sendResponse: MessageSender): boolean { - const { client_id, redirect_uri, login, password } = request; + const { client_id, redirect_uri, login, password, device_name } = request; if (!client_id || !redirect_uri || !login || !password) { sendResponse({ error: "Missing client_id, redirect_uri, login, or password" }); return false; } - fetch("https://accounts.betterseqta.org/api/bsplus/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ client_id, redirect_uri, login, password }), - }) - .then(async (r) => { + void (async () => { + const loginBody: Record = { + client_id, + redirect_uri, + login, + password, + device_name: device_name ?? await getBsplusDeviceName(), + }; + try { + const r = await fetch("https://accounts.betterseqta.org/api/bsplus/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(loginBody), + }); const data = await parseJsonResponse(r); if (!r.ok) sendResponse({ error: data?.error ?? "Login failed" }); else sendResponse(data); - }) - .catch((err) => { + } catch (err) { console.error("[Background] cloudLogin error:", err); - sendResponse({ error: err?.message ?? "Network error" }); - }); + sendResponse({ error: (err as Error)?.message ?? "Network error" }); + } + })(); return true; } diff --git a/src/seqta/utils/CloudAuth.ts b/src/seqta/utils/CloudAuth.ts index 0a178c94..baa7a52e 100644 --- a/src/seqta/utils/CloudAuth.ts +++ b/src/seqta/utils/CloudAuth.ts @@ -1,4 +1,5 @@ import browser from "webextension-polyfill"; +import { getBsplusDeviceName } from "@/seqta/utils/bsplusDeviceName"; import { clearCloudPfpCache } from "@/seqta/utils/cloudPfpCache"; import { clearLastUploadedSnapshot } from "@/seqta/utils/cloudSettingsSync"; import { settingsState } from "@/seqta/utils/listeners/SettingsState"; @@ -167,12 +168,14 @@ class CloudAuthService { ): Promise<{ success: boolean; error?: string }> { try { const clientId = await this.getClientId(); + const device_name = await getBsplusDeviceName(); const result = (await browser.runtime.sendMessage({ type: "cloudLogin", client_id: clientId, redirect_uri: REDIRECT_URI, login: login.trim(), password, + device_name, })) as { access_token?: string; refresh_token?: string; diff --git a/src/seqta/utils/bsplusDeviceName.ts b/src/seqta/utils/bsplusDeviceName.ts new file mode 100644 index 00000000..8edaf005 --- /dev/null +++ b/src/seqta/utils/bsplusDeviceName.ts @@ -0,0 +1,70 @@ +import browser from "webextension-polyfill"; + +function detectOsNameFromNavigator(): string { + const ua = navigator.userAgent; + const platform = navigator.platform ?? ""; + + const userAgentData = (navigator as Navigator & { userAgentData?: { platform?: string } }) + .userAgentData; + if (userAgentData?.platform) { + const mapped: Record = { + Windows: "Windows", + macOS: "macOS", + Linux: "Linux", + Android: "Android", + iOS: "iOS", + "Chrome OS": "ChromeOS", + }; + return mapped[userAgentData.platform] ?? userAgentData.platform; + } + + if (/Win/i.test(platform) || ua.includes("Windows")) return "Windows"; + if (/Mac/i.test(platform) || ua.includes("Mac OS X") || ua.includes("Macintosh")) return "macOS"; + if (/Linux/i.test(platform) || ua.includes("Linux")) return "Linux"; + if (/Android/i.test(ua)) return "Android"; + if (/iPhone|iPad|iPod/i.test(ua)) return "iOS"; + if (/CrOS/i.test(ua)) return "ChromeOS"; + + return platform || "Unknown OS"; +} + +function detectBrowserNameFromUserAgent(ua: string): string { + if (ua.includes("Edg/")) return "Edge"; + if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera"; + if (ua.includes("Firefox/")) return "Firefox"; + if (ua.includes("Chrome/") && !ua.includes("Edg/")) return "Chrome"; + if (ua.includes("Safari/") && !ua.includes("Chrome/")) return "Safari"; + return "Browser"; +} + +async function detectBrowserName(): Promise { + try { + const runtime = browser.runtime as typeof browser.runtime & { + getBrowserInfo?: () => Promise<{ name?: string }>; + }; + if (typeof runtime.getBrowserInfo === "function") { + const info = await runtime.getBrowserInfo(); + if (info.name === "Firefox") return "Firefox"; + if (info.name) return info.name; + } + } catch { + // Fall back to user-agent parsing below. + } + + if (typeof navigator !== "undefined") { + return detectBrowserNameFromUserAgent(navigator.userAgent); + } + + return "Browser"; +} + +/** + * Friendly device label for BetterSEQTA+ cloud login (`device_name` on POST /api/bsplus/login). + * Format: "Chrome on Windows", "Firefox on macOS", etc. + */ +export async function getBsplusDeviceName(): Promise { + const browserName = await detectBrowserName(); + const osName = + typeof navigator !== "undefined" ? detectOsNameFromNavigator() : "Unknown OS"; + return `${browserName} on ${osName}`; +}