Files
hi-frontend/apps/admin/src/stores/global.tsx
T
2026-03-10 19:03:39 +08:00

232 lines
6.8 KiB
TypeScript

import { currentUser } from "@workspace/ui/services/admin/user";
import { isBrowser } from "@workspace/ui/utils/index";
import { create } from "zustand";
export interface GlobalStore {
common: API.GetGlobalConfigResponse;
user?: API.User;
setCommon: (common: Partial<API.GetGlobalConfigResponse>) => void;
setUser: (user?: API.User) => void;
getUserInfo: () => Promise<void>;
getUserSubscribe: (short: string, token: string, type?: string) => string[];
getAppSubLink: (url: string, schema?: string) => string;
}
/**
* Extracts the full domain or root domain from a URL.
*
* @param url - The URL to extract the domain from.
* @param extractRoot - If true, extracts the root domain (e.g., example.com). If false, extracts the full domain (e.g., sub.example.com).
* @returns The extracted domain or root domain, or null if the URL is invalid.
*/
export function extractDomain(url: string, extractRoot = true): string | null {
try {
const { hostname } = new URL(url);
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
return hostname;
}
const domainParts = hostname.split(".").filter(Boolean);
if (extractRoot && domainParts.length > 2) {
return domainParts.slice(-2).join(".");
}
return hostname;
} catch (error) {
console.error("Invalid URL:", error);
return null;
}
}
export const useGlobalStore = create<GlobalStore>((set, get) => ({
common: {
site: {
host: "",
site_name: "",
site_desc: "",
site_logo: "",
keywords: "",
custom_html: "",
custom_data: "",
},
verify: {
turnstile_site_key: "",
captcha_type: "turnstile",
enable_login_verify: false,
enable_register_verify: false,
enable_reset_password_verify: false,
enable_user_login_captcha: false,
enable_user_register_captcha: false,
enable_user_reset_password_captcha: false,
enable_admin_login_captcha: false,
},
auth: {
mobile: {
enable: false,
enable_whitelist: false,
whitelist: [],
},
email: {
enable: false,
enable_verify: false,
enable_domain_suffix: false,
domain_suffix_list: "",
},
register: {
stop_register: false,
enable_ip_register_limit: false,
ip_register_limit: 0,
ip_register_limit_duration: 0,
},
device: {
enable: false,
show_ads: false,
enable_security: false,
only_real_device: false,
},
},
invite: {
forced_invite: false,
referral_percentage: 0,
only_first_purchase: false,
},
currency: {
currency_unit: "USD",
currency_symbol: "$",
},
subscribe: {
single_model: false,
subscribe_path: "",
subscribe_domain: "",
pan_domain: false,
user_agent_limit: false,
user_agent_list: "",
},
verify_code: {
verify_code_expire_time: 5,
verify_code_limit: 15,
verify_code_interval: 60,
},
oauth_methods: [],
web_ad: false,
},
user: undefined,
setCommon: (common) =>
set((state) => ({
common: {
...state.common,
...common,
},
})),
setUser: (user) => set({ user }),
getUserInfo: async () => {
try {
const { data } = await currentUser();
set({ user: data.data });
} catch (error) {
console.error("Failed to refresh user:", error);
}
},
getUserSubscribe: (short: string, token: string, type?: string) => {
const { pan_domain, subscribe_domain, subscribe_path } =
get().common.subscribe || {};
const domains = subscribe_domain
? subscribe_domain.split("\n")
: [extractDomain(window.location.origin, pan_domain)];
return domains.map((domain) => {
if (pan_domain) {
if (type)
return `https://${short}.${type}.${domain}${subscribe_path}?token=${token}&type=${type}`;
return `https://${short}.${domain}${subscribe_path}?token=${token}`;
}
if (type)
return `https://${domain}${subscribe_path}?token=${token}&type=${type}`;
return `https://${domain}${subscribe_path}?token=${token}`;
});
},
getAppSubLink: (url: string, schema?: string) => {
const name = get().common?.site?.site_name || "";
if (!schema) return "url";
try {
let result = schema.replace(/\${url}/g, url).replace(/\${name}/g, name);
const maxLoop = 10;
let prev: string;
let loop = 0;
do {
prev = result;
result = result.replace(
/\${encodeURIComponent\(JSON\.stringify\(([^)]+)\)\)}/g,
(match, expr) => {
try {
const processedExpr = expr
.replace(/url/g, `"${url}"`)
.replace(/name/g, `"${name}"`);
if (processedExpr.includes("server_remote")) {
const serverRemoteValue = `${url}, tag=${name}`;
return encodeURIComponent(
JSON.stringify({ server_remote: [serverRemoteValue] })
);
}
const obj = eval(`(${processedExpr})`);
return encodeURIComponent(JSON.stringify(obj));
} catch {
return match;
}
}
);
result = result.replace(
/\${encodeURIComponent\(([^)]+)\)}/g,
(match, expr) => {
if (expr === "url") return encodeURIComponent(url);
if (expr === "name") return encodeURIComponent(name);
try {
return encodeURIComponent(expr);
} catch {
return match;
}
}
);
result = result.replace(
/\${window\.btoa\(([^)]+)\)}/g,
(match, expr) => {
const btoa = isBrowser() ? window.btoa : (str: string) => str;
if (expr === "url") return btoa(url);
if (expr === "name") return btoa(name);
try {
return btoa(expr);
} catch {
return match;
}
}
);
result = result.replace(
/\${JSON\.stringify\(([^}]+)\)}/g,
(match, expr) => {
try {
const processedExpr = expr
.replace(/url/g, `"${url}"`)
.replace(/name/g, `"${name}"`);
if (processedExpr.includes("server_remote")) {
const serverRemoteValue = `${url}, tag=${name}`;
return JSON.stringify({ server_remote: [serverRemoteValue] });
}
const result = eval(`(${processedExpr})`);
return JSON.stringify(result);
} catch {
return match;
}
}
);
loop++;
} while (result !== prev && loop < maxLoop);
return result;
} catch (_error) {
return "";
}
},
}));