Files
ctms/frontend/src/runtime/desktopServerConfig.ts
T
2026-06-30 22:26:34 +08:00

81 lines
2.6 KiB
TypeScript

import { isTauriRuntime } from "./platform";
export const DESKTOP_SERVER_URL_KEY = "ctms_desktop_server_url";
export const DESKTOP_SERVER_URL_CHANGED_EVENT = "ctms:desktop-server-url-changed";
export type DesktopServerUrlValidationResult =
| { ok: true; url: string }
| { ok: false; reason: string };
const LOCAL_HTTP_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
const getStorage = (): Storage | null => {
if (typeof window === "undefined") return null;
try {
return window.localStorage;
} catch {
return null;
}
};
const emitServerUrlChanged = (previous: string | null, current: string | null): void => {
if (typeof window === "undefined") return;
window.dispatchEvent(
new CustomEvent(DESKTOP_SERVER_URL_CHANGED_EVENT, {
detail: { previous, current },
}),
);
};
export const normalizeDesktopServerUrl = (value: string): DesktopServerUrlValidationResult => {
const raw = value.trim();
if (!raw) return { ok: false, reason: "请输入服务器地址" };
let parsed: URL;
try {
parsed = new URL(raw);
} catch {
return { ok: false, reason: "服务器地址格式不正确" };
}
if (parsed.username || parsed.password) {
return { ok: false, reason: "服务器地址不能包含用户名或密码" };
}
const isHttps = parsed.protocol === "https:";
const isLocalHttp = parsed.protocol === "http:" && LOCAL_HTTP_HOSTS.has(parsed.hostname);
if (!isHttps && !isLocalHttp) {
return { ok: false, reason: "非本地服务必须使用 HTTPS" };
}
return { ok: true, url: `${parsed.origin}/` };
};
export const getDesktopServerUrl = (): string | null => {
const stored = getStorage()?.getItem(DESKTOP_SERVER_URL_KEY);
if (!stored) return null;
const result = normalizeDesktopServerUrl(stored);
return result.ok ? result.url : null;
};
export const hasDesktopServerUrl = (): boolean => Boolean(getDesktopServerUrl());
export const setDesktopServerUrl = (value: string): DesktopServerUrlValidationResult => {
const result = normalizeDesktopServerUrl(value);
if (!result.ok) return result;
const storage = getStorage();
if (!storage) return { ok: false, reason: "当前环境无法保存服务器地址" };
const previous = getDesktopServerUrl();
storage.setItem(DESKTOP_SERVER_URL_KEY, result.url);
emitServerUrlChanged(previous, result.url);
return result;
};
export const clearDesktopServerUrl = (): void => {
const previous = getDesktopServerUrl();
getStorage()?.removeItem(DESKTOP_SERVER_URL_KEY);
emitServerUrlChanged(previous, null);
};
export const shouldRequireDesktopServerUrl = (): boolean => isTauriRuntime() && !hasDesktopServerUrl();