feat(desktop): implement phase 1 tauri client
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
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]"]);
|
||||
|
||||
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 = window.localStorage.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 previous = getDesktopServerUrl();
|
||||
window.localStorage.setItem(DESKTOP_SERVER_URL_KEY, result.url);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(DESKTOP_SERVER_URL_CHANGED_EVENT, {
|
||||
detail: { previous, current: result.url },
|
||||
}),
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
export const clearDesktopServerUrl = (): void => {
|
||||
const previous = getDesktopServerUrl();
|
||||
window.localStorage.removeItem(DESKTOP_SERVER_URL_KEY);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(DESKTOP_SERVER_URL_CHANGED_EVENT, {
|
||||
detail: { previous, current: null },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const shouldRequireDesktopServerUrl = (): boolean => isTauriRuntime() && !hasDesktopServerUrl();
|
||||
Reference in New Issue
Block a user