81 lines
2.6 KiB
TypeScript
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();
|