mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-30 18:52:12 +08:00
chore: i18n (#5276)
* chore: notice i18n * feat: add script to clean up unused i18n keys * chore: cleanup i18n keys * refactor(i18n/proxies): migrate proxies UI to structured locale keys * chore: i18n for rule module * chore: i18n for profile module * chore: i18n for connections module * chore: i18n for settings module * chore: i18n for verge settings * chore: i18n for theme settings * chore: i18n for theme * chore(i18n): components.home.* * chore(i18n): remove unused i18n keys * chore(i18n): components.profile.* * chore(i18n): components.connection * chore(i18n): pages.logs.* * chore(i18n): pages.*.provider * chore(i18n): components.settings.externalCors.* * chore(i18n): components.settings.clash.* * chore(i18n): components.settings.liteMode.* * chore(i18n): components.settings.backup.* * chore(i18n): components.settings.clash.port.* * chore(i18n): components.settings.misc.* * chore(i18n): components.settings.update.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): pages.profiles.notices/components.providers.notices * refactor(notice): unify showNotice usage * refactor(notice): add typed showNotice shortcuts, centralize defaults, and simplify subscriptions * refactor: unify showNotice usage * refactor(notice): unify showNotice API * refactor(notice): unify showNotice usage * chore(i18n): components.test.* * chore(i18n): components.settings.dns.* * chore(i18n): components.home.clashInfo.* * chore(i18n): components.home.systemInfo.* * chore(i18n): components.home.ipInfo/traffic.* * chore(i18n): navigation.* * refactor(i18n): remove pages.* namespace and migrate route texts under module-level page keys * chore(i18n): common.* * chore(i18n): common.* * fix: change error handling in patch_profiles_config to return false when a switch is in progress * fix: improve error handling in patch_profiles_config to prevent requests during profile switching * fix: change error handling in patch_profiles_config to return false when a switch is in progress fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config * chore(i18n): restructure root-level locale keys into namespaces * chore(i18n): add missing i18n keys * docs: i18n guide * chore: adjust i18n * refactor(i18n): align UI actions and status labels with common keys * refactor(i18n): unify two-name locale namespaces * refactor(i18n/components): unify locale keys and update component references * chore(i18n): add shared and entities namespaces to all locale files * refactor(i18n): consolidate shared and entity namespaces across features * chore(deps): update npm dependencies to ^7.3.5 (#5310) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(i18n): migrate shared editor modes and consolidate entities namespaces * tmp * refactor(i18n): flatten locales and move theme/validation strings * docs: CONTRIBUTING_i18n.md * refactor(i18n): restructure feedback and profile namespaces for better organization * refactor(i18n): unify settings locale structure and update references * refactor(i18n): reorganize locale keys for home, proxies, rules, connections, logs, unlock, and tests * refactor(i18n/feedback/layout): unify shared toasts & normalize layout namespace * refactor(i18n): centralize common UI strings in shared * refactor(i18n): flatten headers and unify locale schema * refactor(i18n): consolidate duplicate per-feature translations into shared namespace * refactor(i18n): split locales into per-namespace files * style: lint * refactor(i18n): unify unlock UI translations under tests namespace * feat(i18n): add type-checked translation keys * style: eslint import order * feat(i18n): replace ad-hoc loader with rust-i18n backend bundles * chore(prebuild): remove locale-copy step * fix(i18n, notice): propagate runtime params and update cleanup script path * fix(i18n,notice): make locale formatting idempotent and guard early notice translations * fix(i18n): resolve locale aliases and match OS codes correctly * fix(unlock): use i18next-compatible double-brace interpolation in failure notice * fix(i18n): route unlock error notices through translation keys * fix(i18n): i18n types * feat(i18n): localize upgrade notice for Clash core viewer * fix(notice): ensure runtime overrides apply to prefix translations * chore(i18n): replace literal notices with translation keys * chore(i18n): types * chore(i18n): regen typings before formatting to keep keys in sync * chore(i18n): simply labels * chore(i18n): adjust translation * chore: remove eslint-plugin-i18next * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend (#5341) * chore(i18n): translate settings.json (missed in previous pass) (#5343) * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * fix(i18n-tauri): quote placeholder-leading value in ko.yml to prevent rust_i18n parse panic * chore(i18n): translate settings.json (forgot to include previously) --------- Co-authored-by: rozan <34974262+thelojan@users.noreply.github.com>
This commit is contained in:
co-authored by
renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
rozan
parent
c1a2e99f63
commit
c8aa72186e
+5
-11
@@ -314,28 +314,22 @@ export async function getAppDir() {
|
||||
}
|
||||
|
||||
export async function openAppDir() {
|
||||
return invoke<void>("open_app_dir").catch((err) =>
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
return invoke<void>("open_app_dir").catch((err) => showNotice.error(err));
|
||||
}
|
||||
|
||||
export async function openCoreDir() {
|
||||
return invoke<void>("open_core_dir").catch((err) =>
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
return invoke<void>("open_core_dir").catch((err) => showNotice.error(err));
|
||||
}
|
||||
|
||||
export async function openLogsDir() {
|
||||
return invoke<void>("open_logs_dir").catch((err) =>
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
return invoke<void>("open_logs_dir").catch((err) => showNotice.error(err));
|
||||
}
|
||||
|
||||
export const openWebUrl = async (url: string) => {
|
||||
try {
|
||||
await invoke("open_web_url", { url });
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -377,7 +371,7 @@ export async function cmdTestDelay(url: string) {
|
||||
|
||||
export async function invoke_uwp_tool() {
|
||||
return invoke<void>("invoke_uwp_tool").catch((err) =>
|
||||
showNotice("error", err?.message || err.toString(), 1500),
|
||||
showNotice.error(err, 1500),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+37
-6
@@ -17,6 +17,24 @@ export const supportedLanguages = [
|
||||
"zhtw",
|
||||
];
|
||||
|
||||
const FALLBACK_LANGUAGE = "zh";
|
||||
|
||||
type LocaleModule = {
|
||||
default: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const localeModules = import.meta.glob<LocaleModule>("@/locales/*/index.ts");
|
||||
|
||||
const localeLoaders = Object.entries(localeModules).reduce<
|
||||
Record<string, () => Promise<LocaleModule>>
|
||||
>((acc, [path, loader]) => {
|
||||
const match = path.match(/[/\\]locales[/\\]([^/\\]+)[/\\]index\.ts$/);
|
||||
if (match) {
|
||||
acc[match[1]] = loader;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const languages: Record<string, any> = supportedLanguages.reduce(
|
||||
(acc, lang) => {
|
||||
acc[lang] = {};
|
||||
@@ -27,14 +45,27 @@ export const languages: Record<string, any> = supportedLanguages.reduce(
|
||||
|
||||
export const loadLanguage = async (language: string) => {
|
||||
try {
|
||||
const module = await import(`@/locales/${language}.json`);
|
||||
const loader = localeLoaders[language];
|
||||
if (!loader) {
|
||||
throw new Error(`Locale loader not found for language "${language}"`);
|
||||
}
|
||||
const module = await loader();
|
||||
return module.default;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Failed to load language ${language}, fallback to zh, ${error}`,
|
||||
);
|
||||
const fallback = await import("@/locales/zh.json");
|
||||
return fallback.default;
|
||||
if (language !== FALLBACK_LANGUAGE) {
|
||||
console.warn(
|
||||
`Failed to load language ${language}, fallback to ${FALLBACK_LANGUAGE}, ${error}`,
|
||||
);
|
||||
const fallbackLoader = localeLoaders[FALLBACK_LANGUAGE];
|
||||
if (!fallbackLoader) {
|
||||
throw new Error(
|
||||
`Fallback language "${FALLBACK_LANGUAGE}" resources are missing.`,
|
||||
);
|
||||
}
|
||||
const fallback = await fallbackLoader();
|
||||
return fallback.default;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+311
-51
@@ -1,81 +1,341 @@
|
||||
import { ReactNode } from "react";
|
||||
import i18n from "i18next";
|
||||
import { ReactNode, isValidElement } from "react";
|
||||
|
||||
type NoticeType = "success" | "error" | "info";
|
||||
|
||||
export interface NoticeTranslationDescriptor {
|
||||
key: string;
|
||||
params?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface NoticeItem {
|
||||
id: number;
|
||||
type: "success" | "error" | "info";
|
||||
message: ReactNode;
|
||||
duration: number;
|
||||
readonly id: number;
|
||||
readonly type: NoticeType;
|
||||
readonly duration: number;
|
||||
readonly message?: ReactNode;
|
||||
readonly i18n?: NoticeTranslationDescriptor;
|
||||
timerId?: ReturnType<typeof setTimeout>;
|
||||
}
|
||||
|
||||
type Listener = (notices: NoticeItem[]) => void;
|
||||
type NoticeContent = unknown;
|
||||
|
||||
type NoticeExtra = unknown;
|
||||
|
||||
type NoticeShortcut = (
|
||||
message: NoticeContent,
|
||||
...extras: NoticeExtra[]
|
||||
) => number;
|
||||
|
||||
type ShowNotice = ((
|
||||
type: NoticeType,
|
||||
message: NoticeContent,
|
||||
...extras: NoticeExtra[]
|
||||
) => number) & {
|
||||
success: NoticeShortcut;
|
||||
error: NoticeShortcut;
|
||||
info: NoticeShortcut;
|
||||
};
|
||||
|
||||
type NoticeSubscriber = () => void;
|
||||
|
||||
const DEFAULT_DURATIONS: Readonly<Record<NoticeType, number>> = {
|
||||
success: 3000,
|
||||
info: 5000,
|
||||
error: 8000,
|
||||
};
|
||||
|
||||
const TRANSLATION_KEY_PATTERN = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)+$/;
|
||||
|
||||
let nextId = 0;
|
||||
let notices: NoticeItem[] = [];
|
||||
const listeners: Set<Listener> = new Set();
|
||||
const subscribers: Set<NoticeSubscriber> = new Set();
|
||||
|
||||
function notifyListeners() {
|
||||
listeners.forEach((listener) => listener([...notices])); // Pass a copy
|
||||
function notifySubscribers() {
|
||||
subscribers.forEach((subscriber) => subscriber());
|
||||
}
|
||||
|
||||
// Shows a notification.
|
||||
interface ParsedNoticeExtras {
|
||||
params?: Record<string, unknown>;
|
||||
raw?: unknown;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export function showNotice(
|
||||
type: "success" | "error" | "info",
|
||||
message: ReactNode,
|
||||
duration?: number,
|
||||
): number {
|
||||
const id = nextId++;
|
||||
const effectiveDuration =
|
||||
duration ?? (type === "error" ? 8000 : type === "info" ? 5000 : 3000); // Longer defaults
|
||||
function parseNoticeExtras(extras: NoticeExtra[]): ParsedNoticeExtras {
|
||||
let params: Record<string, unknown> | undefined;
|
||||
let raw: unknown;
|
||||
let duration: number | undefined;
|
||||
|
||||
const newNotice: NoticeItem = {
|
||||
// Prioritize objects as translation params, then as raw payloads, while the first number wins as duration.
|
||||
for (const extra of extras) {
|
||||
if (extra === undefined) continue;
|
||||
|
||||
if (typeof extra === "number" && duration === undefined) {
|
||||
duration = extra;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPlainRecord(extra)) {
|
||||
if (!params) {
|
||||
params = extra;
|
||||
continue;
|
||||
}
|
||||
if (!raw) {
|
||||
raw = extra;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!raw) {
|
||||
raw = extra;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!params && isPlainRecord(extra)) {
|
||||
params = extra;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (duration === undefined && typeof extra === "number") {
|
||||
duration = extra;
|
||||
}
|
||||
}
|
||||
|
||||
return { params, raw, duration };
|
||||
}
|
||||
|
||||
function resolveDuration(type: NoticeType, override?: number) {
|
||||
return override ?? DEFAULT_DURATIONS[type];
|
||||
}
|
||||
|
||||
function buildNotice(
|
||||
id: number,
|
||||
type: NoticeType,
|
||||
duration: number,
|
||||
payload: { message?: ReactNode; i18n?: NoticeTranslationDescriptor },
|
||||
timerId?: ReturnType<typeof setTimeout>,
|
||||
): NoticeItem {
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
message,
|
||||
duration: effectiveDuration,
|
||||
duration,
|
||||
timerId,
|
||||
...payload,
|
||||
};
|
||||
|
||||
// Auto-hide timer (only if duration is not null/0)
|
||||
if (effectiveDuration > 0) {
|
||||
newNotice.timerId = setTimeout(() => {
|
||||
hideNotice(id);
|
||||
}, effectiveDuration);
|
||||
}
|
||||
|
||||
notices = [...notices, newNotice];
|
||||
notifyListeners();
|
||||
return id;
|
||||
}
|
||||
|
||||
// Hides a specific notification by its ID.
|
||||
function isMaybeTranslationDescriptor(
|
||||
message: unknown,
|
||||
): message is NoticeTranslationDescriptor {
|
||||
if (
|
||||
typeof message === "object" &&
|
||||
message !== null &&
|
||||
!Array.isArray(message) &&
|
||||
!isValidElement(message)
|
||||
) {
|
||||
return typeof (message as Record<string, unknown>).key === "string";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
value === null ||
|
||||
Array.isArray(value) ||
|
||||
value instanceof Error ||
|
||||
isValidElement(value)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const proto = Object.getPrototypeOf(value);
|
||||
return proto === Object.prototype || proto === null;
|
||||
}
|
||||
|
||||
function createRawDescriptor(message: string): NoticeTranslationDescriptor {
|
||||
return {
|
||||
key: "shared.feedback.notices.raw",
|
||||
params: { message },
|
||||
};
|
||||
}
|
||||
|
||||
function isLikelyTranslationKey(key: string) {
|
||||
return TRANSLATION_KEY_PATTERN.test(key);
|
||||
}
|
||||
|
||||
function shouldUseTranslationKey(
|
||||
key: string,
|
||||
params?: Record<string, unknown>,
|
||||
) {
|
||||
if (params && Object.keys(params).length > 0) return true;
|
||||
if (isLikelyTranslationKey(key)) return true;
|
||||
if (i18n.isInitialized) {
|
||||
return i18n.exists(key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function extractDisplayText(input: unknown): string | undefined {
|
||||
if (input === null || input === undefined) return undefined;
|
||||
if (typeof input === "string") return input;
|
||||
if (typeof input === "number" || typeof input === "boolean") {
|
||||
return String(input);
|
||||
}
|
||||
if (input instanceof Error) {
|
||||
return input.message || input.name;
|
||||
}
|
||||
if (typeof input === "object" && input !== null) {
|
||||
const maybeMessage = (input as { message?: unknown }).message;
|
||||
if (typeof maybeMessage === "string") return maybeMessage;
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(input);
|
||||
} catch {
|
||||
return String(input);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeNoticeMessage(
|
||||
message: NoticeContent,
|
||||
params?: Record<string, unknown>,
|
||||
raw?: unknown,
|
||||
): { message?: ReactNode; i18n?: NoticeTranslationDescriptor } {
|
||||
const rawText = raw !== undefined ? extractDisplayText(raw) : undefined;
|
||||
|
||||
if (isValidElement(message)) {
|
||||
return { message };
|
||||
}
|
||||
|
||||
if (isMaybeTranslationDescriptor(message)) {
|
||||
const originalParams = message.params ?? {};
|
||||
const mergedParams = Object.keys(params ?? {}).length
|
||||
? { ...originalParams, ...params }
|
||||
: { ...originalParams };
|
||||
|
||||
if (rawText !== undefined) {
|
||||
return {
|
||||
i18n: {
|
||||
key: "shared.feedback.notices.prefixedRaw",
|
||||
params: {
|
||||
...mergedParams,
|
||||
prefixKey: message.key,
|
||||
prefixParams: originalParams,
|
||||
message: rawText,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
i18n: {
|
||||
key: message.key,
|
||||
params: Object.keys(mergedParams).length ? mergedParams : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof message === "string") {
|
||||
if (rawText !== undefined) {
|
||||
if (shouldUseTranslationKey(message, params)) {
|
||||
return {
|
||||
i18n: {
|
||||
key: "shared.feedback.notices.prefixedRaw",
|
||||
params: {
|
||||
...(params ?? {}),
|
||||
prefixKey: message,
|
||||
message: rawText,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// Prefer showing the original string while still surfacing the raw details below.
|
||||
return {
|
||||
i18n: {
|
||||
key: "shared.feedback.notices.prefixedRaw",
|
||||
params: {
|
||||
...(params ?? {}),
|
||||
prefix: message,
|
||||
message: rawText,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (shouldUseTranslationKey(message, params)) {
|
||||
return {
|
||||
i18n: {
|
||||
key: message,
|
||||
params: params && Object.keys(params).length ? params : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
return { i18n: createRawDescriptor(message) };
|
||||
}
|
||||
|
||||
if (rawText !== undefined) {
|
||||
return { i18n: createRawDescriptor(rawText) };
|
||||
}
|
||||
|
||||
const extracted = extractDisplayText(message);
|
||||
if (extracted !== undefined) {
|
||||
return { i18n: createRawDescriptor(extracted) };
|
||||
}
|
||||
|
||||
return { i18n: createRawDescriptor("") };
|
||||
}
|
||||
|
||||
const baseShowNotice = (
|
||||
type: NoticeType,
|
||||
message: NoticeContent,
|
||||
...extras: NoticeExtra[]
|
||||
): number => {
|
||||
const id = nextId++;
|
||||
const { params, raw, duration } = parseNoticeExtras(extras);
|
||||
const effectiveDuration = resolveDuration(type, duration);
|
||||
const timerId =
|
||||
effectiveDuration > 0
|
||||
? setTimeout(() => hideNotice(id), effectiveDuration)
|
||||
: undefined;
|
||||
|
||||
const normalizedMessage = normalizeNoticeMessage(message, params, raw);
|
||||
const notice = buildNotice(
|
||||
id,
|
||||
type,
|
||||
effectiveDuration,
|
||||
normalizedMessage,
|
||||
timerId,
|
||||
);
|
||||
|
||||
notices = [...notices, notice];
|
||||
notifySubscribers();
|
||||
return id;
|
||||
};
|
||||
|
||||
export const showNotice: ShowNotice = Object.assign(baseShowNotice, {
|
||||
success: (message: NoticeContent, ...extras: NoticeExtra[]) =>
|
||||
baseShowNotice("success", message, ...extras),
|
||||
error: (message: NoticeContent, ...extras: NoticeExtra[]) =>
|
||||
baseShowNotice("error", message, ...extras),
|
||||
info: (message: NoticeContent, ...extras: NoticeExtra[]) =>
|
||||
baseShowNotice("info", message, ...extras),
|
||||
});
|
||||
|
||||
export function hideNotice(id: number) {
|
||||
const notice = notices.find((n) => n.id === id);
|
||||
const notice = notices.find((candidate) => candidate.id === id);
|
||||
if (notice?.timerId) {
|
||||
clearTimeout(notice.timerId); // Clear timeout if manually closed
|
||||
clearTimeout(notice.timerId);
|
||||
}
|
||||
notices = notices.filter((n) => n.id !== id);
|
||||
notifyListeners();
|
||||
notices = notices.filter((candidate) => candidate.id !== id);
|
||||
notifySubscribers();
|
||||
}
|
||||
|
||||
// Subscribes a listener function to notice state changes.
|
||||
|
||||
export function subscribeNotices(listener: () => void) {
|
||||
listeners.add(listener);
|
||||
export function subscribeNotices(subscriber: NoticeSubscriber) {
|
||||
subscribers.add(subscriber);
|
||||
return () => {
|
||||
listeners.delete(listener);
|
||||
subscribers.delete(subscriber);
|
||||
};
|
||||
}
|
||||
|
||||
export function getSnapshotNotices() {
|
||||
return notices;
|
||||
}
|
||||
|
||||
// Function to clear all notices at once
|
||||
export function clearAllNotices() {
|
||||
notices.forEach((n) => {
|
||||
if (n.timerId) clearTimeout(n.timerId);
|
||||
});
|
||||
notices = [];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Vendored
-888
@@ -1,888 +0,0 @@
|
||||
type Platform =
|
||||
| "aix"
|
||||
| "android"
|
||||
| "darwin"
|
||||
| "freebsd"
|
||||
| "haiku"
|
||||
| "linux"
|
||||
| "openbsd"
|
||||
| "sunos"
|
||||
| "win32"
|
||||
| "cygwin"
|
||||
| "netbsd";
|
||||
|
||||
/**
|
||||
* defines in `vite.config.ts`
|
||||
*/
|
||||
declare const OS_PLATFORM: Platform;
|
||||
|
||||
/**
|
||||
* Some interface for clash api
|
||||
*/
|
||||
interface IConfigData {
|
||||
port: number;
|
||||
mode: string;
|
||||
ipv6: boolean;
|
||||
"socket-port": number;
|
||||
"allow-lan": boolean;
|
||||
"log-level": string;
|
||||
"mixed-port": number;
|
||||
"redir-port": number;
|
||||
"socks-port": number;
|
||||
"tproxy-port": number;
|
||||
"external-controller": string;
|
||||
"external-controller-cors": {
|
||||
"allow-private-network": boolean;
|
||||
"allow-origins": string[];
|
||||
};
|
||||
secret: string;
|
||||
"unified-delay": boolean;
|
||||
tun: {
|
||||
stack: string;
|
||||
device: string;
|
||||
"auto-route": boolean;
|
||||
"auto-detect-interface": boolean;
|
||||
"dns-hijack": string[];
|
||||
"strict-route": boolean;
|
||||
mtu: number;
|
||||
};
|
||||
dns?: {
|
||||
enable?: boolean;
|
||||
listen?: string;
|
||||
"enhanced-mode"?: "fake-ip" | "redir-host";
|
||||
"fake-ip-range"?: string;
|
||||
"fake-ip-filter"?: string[];
|
||||
"fake-ip-filter-mode"?: "blacklist" | "whitelist";
|
||||
"prefer-h3"?: boolean;
|
||||
"respect-rules"?: boolean;
|
||||
nameserver?: string[];
|
||||
fallback?: string[];
|
||||
"default-nameserver"?: string[];
|
||||
"proxy-server-nameserver"?: string[];
|
||||
"direct-nameserver"?: string[];
|
||||
"direct-nameserver-follow-policy"?: boolean;
|
||||
"nameserver-policy"?: Record<string, any>;
|
||||
"use-hosts"?: boolean;
|
||||
"use-system-hosts"?: boolean;
|
||||
"fallback-filter"?: {
|
||||
geoip?: boolean;
|
||||
"geoip-code"?: string;
|
||||
ipcidr?: string[];
|
||||
domain?: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface IRuleItem {
|
||||
type: string;
|
||||
payload: string;
|
||||
proxy: string;
|
||||
}
|
||||
|
||||
interface IProxyItem {
|
||||
name: string;
|
||||
type: string;
|
||||
udp: boolean;
|
||||
xudp: boolean;
|
||||
tfo: boolean;
|
||||
mptcp: boolean;
|
||||
smux: boolean;
|
||||
history: {
|
||||
time: string;
|
||||
delay: number;
|
||||
}[];
|
||||
testUrl?: string;
|
||||
all?: string[];
|
||||
now?: string;
|
||||
hidden?: boolean;
|
||||
icon?: string;
|
||||
provider?: string; // 记录是否来自provider
|
||||
fixed?: string; // 记录固定(优先)的节点
|
||||
}
|
||||
|
||||
type IProxyGroupItem = Omit<IProxyItem, "all"> & {
|
||||
all: IProxyItem[];
|
||||
};
|
||||
|
||||
interface IProxyProviderItem {
|
||||
name: string;
|
||||
type: string;
|
||||
proxies: IProxyItem[];
|
||||
updatedAt: string;
|
||||
vehicleType: string;
|
||||
subscriptionInfo?: {
|
||||
Upload: number;
|
||||
Download: number;
|
||||
Total: number;
|
||||
Expire: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface IRuleProviderItem {
|
||||
name: string;
|
||||
behavior: string;
|
||||
format: string;
|
||||
ruleCount: number;
|
||||
type: string;
|
||||
updatedAt: string;
|
||||
vehicleType: string;
|
||||
}
|
||||
|
||||
interface ITrafficItem {
|
||||
up: number;
|
||||
down: number;
|
||||
up_rate?: number;
|
||||
down_rate?: number;
|
||||
last_updated?: number;
|
||||
}
|
||||
|
||||
interface IFormattedTrafficData {
|
||||
up_rate_formatted: string;
|
||||
down_rate_formatted: string;
|
||||
total_up_formatted: string;
|
||||
total_down_formatted: string;
|
||||
is_fresh: boolean;
|
||||
}
|
||||
|
||||
interface IFormattedMemoryData {
|
||||
inuse_formatted: string;
|
||||
oslimit_formatted: string;
|
||||
usage_percent: number;
|
||||
is_fresh: boolean;
|
||||
}
|
||||
|
||||
// 增强的类型安全接口定义,确保所有字段必需
|
||||
interface ISystemMonitorOverview {
|
||||
traffic: {
|
||||
raw: {
|
||||
up: number;
|
||||
down: number;
|
||||
up_rate: number;
|
||||
down_rate: number;
|
||||
};
|
||||
formatted: {
|
||||
up_rate: string;
|
||||
down_rate: string;
|
||||
total_up: string;
|
||||
total_down: string;
|
||||
};
|
||||
is_fresh: boolean;
|
||||
};
|
||||
memory: {
|
||||
raw: {
|
||||
inuse: number;
|
||||
oslimit: number;
|
||||
usage_percent: number;
|
||||
};
|
||||
formatted: {
|
||||
inuse: string;
|
||||
oslimit: string;
|
||||
usage_percent: number;
|
||||
};
|
||||
is_fresh: boolean;
|
||||
};
|
||||
overall_status: "active" | "inactive" | "error" | "unknown" | "healthy";
|
||||
}
|
||||
|
||||
// 类型安全的数据验证器
|
||||
interface ISystemMonitorOverviewValidator {
|
||||
validate(data: any): data is ISystemMonitorOverview;
|
||||
sanitize(data: any): ISystemMonitorOverview;
|
||||
}
|
||||
|
||||
interface ILogItem {
|
||||
type: string;
|
||||
time?: string;
|
||||
payload: string;
|
||||
}
|
||||
|
||||
interface IConnectionsItem {
|
||||
id: string;
|
||||
metadata: {
|
||||
network: string;
|
||||
type: string;
|
||||
host: string;
|
||||
sourceIP: string;
|
||||
sourcePort: string;
|
||||
destinationPort: string;
|
||||
destinationIP?: string;
|
||||
remoteDestination?: string;
|
||||
process?: string;
|
||||
processPath?: string;
|
||||
};
|
||||
upload: number;
|
||||
download: number;
|
||||
start: string;
|
||||
chains: string[];
|
||||
rule: string;
|
||||
rulePayload: string;
|
||||
curUpload?: number; // upload speed, calculate at runtime
|
||||
curDownload?: number; // download speed, calculate at runtime
|
||||
}
|
||||
|
||||
interface IConnections {
|
||||
downloadTotal: number;
|
||||
uploadTotal: number;
|
||||
connections: IConnectionsItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Some interface for command
|
||||
*/
|
||||
|
||||
interface IClashInfo {
|
||||
// status: string;
|
||||
mixed_port?: number; // clash mixed port
|
||||
socks_port?: number; // clash socks port
|
||||
redir_port?: number; // clash redir port
|
||||
tproxy_port?: number; // clash tproxy port
|
||||
port?: number; // clash http port
|
||||
server?: string; // external-controller
|
||||
secret?: string;
|
||||
}
|
||||
|
||||
interface IProfileItem {
|
||||
uid: string;
|
||||
type?: "local" | "remote" | "merge" | "script";
|
||||
name?: string;
|
||||
desc?: string;
|
||||
file?: string;
|
||||
url?: string;
|
||||
updated?: number;
|
||||
selected?: {
|
||||
name?: string;
|
||||
now?: string;
|
||||
}[];
|
||||
extra?: {
|
||||
upload: number;
|
||||
download: number;
|
||||
total: number;
|
||||
expire: number;
|
||||
};
|
||||
option?: IProfileOption;
|
||||
home?: string;
|
||||
}
|
||||
|
||||
interface IProfileOption {
|
||||
user_agent?: string;
|
||||
with_proxy?: boolean;
|
||||
self_proxy?: boolean;
|
||||
update_interval?: number;
|
||||
timeout_seconds?: number;
|
||||
danger_accept_invalid_certs?: boolean;
|
||||
allow_auto_update?: boolean;
|
||||
merge?: string;
|
||||
script?: string;
|
||||
rules?: string;
|
||||
proxies?: string;
|
||||
groups?: string;
|
||||
}
|
||||
|
||||
interface IProfilesConfig {
|
||||
current?: string;
|
||||
items?: IProfileItem[];
|
||||
}
|
||||
|
||||
interface IVergeTestItem {
|
||||
uid: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
url: string;
|
||||
}
|
||||
interface IAddress {
|
||||
V4?: {
|
||||
ip: string;
|
||||
broadcast?: string;
|
||||
netmask?: string;
|
||||
};
|
||||
V6?: {
|
||||
ip: string;
|
||||
broadcast?: string;
|
||||
netmask?: string;
|
||||
};
|
||||
}
|
||||
interface INetworkInterface {
|
||||
name: string;
|
||||
addr: IAddress[];
|
||||
mac_addr?: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface ISeqProfileConfig {
|
||||
prepend: [];
|
||||
append: [];
|
||||
delete: [];
|
||||
}
|
||||
|
||||
interface IProxyGroupConfig {
|
||||
name: string;
|
||||
type: "select" | "url-test" | "fallback" | "load-balance" | "relay";
|
||||
proxies?: string[];
|
||||
use?: string[];
|
||||
url?: string;
|
||||
interval?: number;
|
||||
lazy?: boolean;
|
||||
timeout?: number;
|
||||
"max-failed-times"?: number;
|
||||
"disable-udp"?: boolean;
|
||||
"interface-name": string;
|
||||
"routing-mark"?: number;
|
||||
"include-all"?: boolean;
|
||||
"include-all-proxies"?: boolean;
|
||||
"include-all-providers"?: boolean;
|
||||
filter?: string;
|
||||
"exclude-filter"?: string;
|
||||
"exclude-type"?: string;
|
||||
"expected-status"?: string;
|
||||
hidden?: boolean;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface WsOptions {
|
||||
path?: string;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
"max-early-data"?: number;
|
||||
"early-data-header-name"?: string;
|
||||
"v2ray-http-upgrade"?: boolean;
|
||||
"v2ray-http-upgrade-fast-open"?: boolean;
|
||||
}
|
||||
|
||||
interface HttpOptions {
|
||||
method?: string;
|
||||
path?: string[];
|
||||
headers?: {
|
||||
[key: string]: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface H2Options {
|
||||
path?: string;
|
||||
host?: string;
|
||||
}
|
||||
|
||||
interface GrpcOptions {
|
||||
"grpc-service-name"?: string;
|
||||
}
|
||||
|
||||
interface RealityOptions {
|
||||
"public-key"?: string;
|
||||
"short-id"?: string;
|
||||
}
|
||||
type ClientFingerprint =
|
||||
| "chrome"
|
||||
| "firefox"
|
||||
| "safari"
|
||||
| "iOS"
|
||||
| "android"
|
||||
| "edge"
|
||||
| "360"
|
||||
| "qq"
|
||||
| "random";
|
||||
type NetworkType = "ws" | "http" | "h2" | "grpc" | "tcp";
|
||||
type CipherType =
|
||||
| "none"
|
||||
| "auto"
|
||||
| "dummy"
|
||||
| "aes-128-gcm"
|
||||
| "aes-192-gcm"
|
||||
| "aes-256-gcm"
|
||||
| "lea-128-gcm"
|
||||
| "lea-192-gcm"
|
||||
| "lea-256-gcm"
|
||||
| "aes-128-gcm-siv"
|
||||
| "aes-256-gcm-siv"
|
||||
| "2022-blake3-aes-128-gcm"
|
||||
| "2022-blake3-aes-256-gcm"
|
||||
| "aes-128-cfb"
|
||||
| "aes-192-cfb"
|
||||
| "aes-256-cfb"
|
||||
| "aes-128-ctr"
|
||||
| "aes-192-ctr"
|
||||
| "aes-256-ctr"
|
||||
| "chacha20"
|
||||
| "chacha20-ietf"
|
||||
| "chacha20-ietf-poly1305"
|
||||
| "2022-blake3-chacha20-poly1305"
|
||||
| "rabbit128-poly1305"
|
||||
| "xchacha20-ietf-poly1305"
|
||||
| "xchacha20"
|
||||
| "aegis-128l"
|
||||
| "aegis-256"
|
||||
| "aez-384"
|
||||
| "deoxys-ii-256-128"
|
||||
| "rc4-md5";
|
||||
// base
|
||||
interface IProxyBaseConfig {
|
||||
tfo?: boolean;
|
||||
mptcp?: boolean;
|
||||
"interface-name"?: string;
|
||||
"routing-mark"?: number;
|
||||
"ip-version"?: "dual" | "ipv4" | "ipv6" | "ipv4-prefer" | "ipv6-prefer";
|
||||
"dialer-proxy"?: string;
|
||||
}
|
||||
// direct
|
||||
interface IProxyDirectConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "direct";
|
||||
}
|
||||
// dns
|
||||
interface IProxyDnsConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "dns";
|
||||
}
|
||||
// http
|
||||
interface IProxyHttpConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "http";
|
||||
server?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
tls?: boolean;
|
||||
sni?: string;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
// socks5
|
||||
interface IProxySocks5Config extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "socks5";
|
||||
server?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
tls?: boolean;
|
||||
udp?: boolean;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
}
|
||||
// ssh
|
||||
interface IProxySshConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "ssh";
|
||||
server?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
"private-key"?: string;
|
||||
"private-key-passphrase"?: string;
|
||||
"host-key"?: string;
|
||||
"host-key-algorithms"?: string;
|
||||
}
|
||||
// trojan
|
||||
interface IProxyTrojanConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "trojan";
|
||||
server?: string;
|
||||
port?: number;
|
||||
password?: string;
|
||||
alpn?: string[];
|
||||
sni?: string;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
udp?: boolean;
|
||||
network?: NetworkType;
|
||||
"reality-opts"?: RealityOptions;
|
||||
"grpc-opts"?: GrpcOptions;
|
||||
"ws-opts"?: WsOptions;
|
||||
"ss-opts"?: {
|
||||
enabled?: boolean;
|
||||
method?: string;
|
||||
password?: string;
|
||||
};
|
||||
"client-fingerprint"?: ClientFingerprint;
|
||||
}
|
||||
// tuic
|
||||
interface IProxyTuicConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "tuic";
|
||||
server?: string;
|
||||
port?: number;
|
||||
token?: string;
|
||||
uuid?: string;
|
||||
password?: string;
|
||||
ip?: string;
|
||||
"heartbeat-interval"?: number;
|
||||
alpn?: string[];
|
||||
"reduce-rtt"?: boolean;
|
||||
"request-timeout"?: number;
|
||||
"udp-relay-mode"?: string;
|
||||
"congestion-controller"?: string;
|
||||
"disable-sni"?: boolean;
|
||||
"max-udp-relay-packet-size"?: number;
|
||||
"fast-open"?: boolean;
|
||||
"max-open-streams"?: number;
|
||||
cwnd?: number;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
ca?: string;
|
||||
"ca-str"?: string;
|
||||
"recv-window-conn"?: number;
|
||||
"recv-window"?: number;
|
||||
"disable-mtu-discovery"?: boolean;
|
||||
"max-datagram-frame-size"?: number;
|
||||
sni?: string;
|
||||
"udp-over-stream"?: boolean;
|
||||
"udp-over-stream-version"?: number;
|
||||
}
|
||||
// vless
|
||||
interface IProxyVlessConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "vless";
|
||||
server?: string;
|
||||
port?: number;
|
||||
uuid?: string;
|
||||
flow?: string;
|
||||
tls?: boolean;
|
||||
alpn?: string[];
|
||||
udp?: boolean;
|
||||
"packet-addr"?: boolean;
|
||||
xudp?: boolean;
|
||||
"packet-encoding"?: string;
|
||||
network?: NetworkType;
|
||||
"reality-opts"?: RealityOptions;
|
||||
"http-opts"?: HttpOptions;
|
||||
"h2-opts"?: H2Options;
|
||||
"grpc-opts"?: GrpcOptions;
|
||||
"ws-opts"?: WsOptions;
|
||||
"ws-path"?: string;
|
||||
"ws-headers"?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
servername?: string;
|
||||
"client-fingerprint"?: ClientFingerprint;
|
||||
smux?: boolean;
|
||||
}
|
||||
// vmess
|
||||
interface IProxyVmessConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "vmess";
|
||||
server?: string;
|
||||
port?: number;
|
||||
uuid?: string;
|
||||
alterId?: number;
|
||||
cipher?: CipherType;
|
||||
udp?: boolean;
|
||||
network?: NetworkType;
|
||||
tls?: boolean;
|
||||
alpn?: string[];
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
servername?: string;
|
||||
"reality-opts"?: RealityOptions;
|
||||
"http-opts"?: HttpOptions;
|
||||
"h2-opts"?: H2Options;
|
||||
"grpc-opts"?: GrpcOptions;
|
||||
"ws-opts"?: WsOptions;
|
||||
"packet-addr"?: boolean;
|
||||
xudp?: boolean;
|
||||
"packet-encoding"?: string;
|
||||
"global-padding"?: boolean;
|
||||
"authenticated-length"?: boolean;
|
||||
"client-fingerprint"?: ClientFingerprint;
|
||||
smux?: boolean;
|
||||
}
|
||||
interface WireGuardPeerOptions {
|
||||
server?: string;
|
||||
port?: number;
|
||||
"public-key"?: string;
|
||||
"pre-shared-key"?: string;
|
||||
reserved?: number[];
|
||||
"allowed-ips"?: string[];
|
||||
}
|
||||
// wireguard
|
||||
interface IProxyWireguardConfig extends IProxyBaseConfig, WireGuardPeerOptions {
|
||||
name: string;
|
||||
type: "wireguard";
|
||||
ip?: string;
|
||||
ipv6?: string;
|
||||
"private-key"?: string;
|
||||
workers?: number;
|
||||
mtu?: number;
|
||||
udp?: boolean;
|
||||
"persistent-keepalive"?: number;
|
||||
peers?: WireGuardPeerOptions[];
|
||||
"remote-dns-resolve"?: boolean;
|
||||
dns?: string[];
|
||||
"refresh-server-ip-interval"?: number;
|
||||
}
|
||||
// hysteria
|
||||
interface IProxyHysteriaConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "hysteria";
|
||||
server?: string;
|
||||
port?: number;
|
||||
ports?: string;
|
||||
protocol?: string;
|
||||
"obfs-protocol"?: string;
|
||||
up?: string;
|
||||
"up-speed"?: number;
|
||||
down?: string;
|
||||
"down-speed"?: number;
|
||||
auth?: string;
|
||||
"auth-str"?: string;
|
||||
obfs?: string;
|
||||
sni?: string;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
alpn?: string[];
|
||||
ca?: string;
|
||||
"ca-str"?: string;
|
||||
"recv-window-conn"?: number;
|
||||
"recv-window"?: number;
|
||||
"disable-mtu-discovery"?: boolean;
|
||||
"fast-open"?: boolean;
|
||||
"hop-interval"?: number;
|
||||
}
|
||||
// hysteria2
|
||||
interface IProxyHysteria2Config extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "hysteria2";
|
||||
server?: string;
|
||||
port?: number;
|
||||
ports?: string;
|
||||
"hop-interval"?: number;
|
||||
protocol?: string;
|
||||
"obfs-protocol"?: string;
|
||||
up?: string;
|
||||
down?: string;
|
||||
password?: string;
|
||||
obfs?: string;
|
||||
"obfs-password"?: string;
|
||||
sni?: string;
|
||||
"skip-cert-verify"?: boolean;
|
||||
fingerprint?: string;
|
||||
alpn?: string[];
|
||||
ca?: string;
|
||||
"ca-str"?: string;
|
||||
cwnd?: number;
|
||||
"udp-mtu"?: number;
|
||||
}
|
||||
// shadowsocks
|
||||
interface IProxyShadowsocksConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "ss";
|
||||
server?: string;
|
||||
port?: number;
|
||||
password?: string;
|
||||
cipher?: CipherType;
|
||||
udp?: boolean;
|
||||
plugin?: "obfs" | "v2ray-plugin" | "shadow-tls" | "restls";
|
||||
"plugin-opts"?: {
|
||||
mode?: string;
|
||||
host?: string;
|
||||
password?: string;
|
||||
path?: string;
|
||||
tls?: string;
|
||||
fingerprint?: string;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
"skip-cert-verify"?: boolean;
|
||||
version?: number;
|
||||
mux?: boolean;
|
||||
"v2ray-http-upgrade"?: boolean;
|
||||
"v2ray-http-upgrade-fast-open"?: boolean;
|
||||
"version-hint"?: string;
|
||||
"restls-script"?: string;
|
||||
};
|
||||
"udp-over-tcp"?: boolean;
|
||||
"udp-over-tcp-version"?: number;
|
||||
"client-fingerprint"?: ClientFingerprint;
|
||||
smux?: boolean;
|
||||
}
|
||||
// shadowsocksR
|
||||
interface IProxyshadowsocksRConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "ssr";
|
||||
server?: string;
|
||||
port?: number;
|
||||
password?: string;
|
||||
cipher?: CipherType;
|
||||
obfs?: string;
|
||||
"obfs-param"?: string;
|
||||
protocol?: string;
|
||||
"protocol-param"?: string;
|
||||
udp?: boolean;
|
||||
}
|
||||
// sing-mux
|
||||
interface IProxySmuxConfig {
|
||||
smux?: {
|
||||
enabled?: boolean;
|
||||
protocol?: "smux" | "yamux" | "h2mux";
|
||||
"max-connections"?: number;
|
||||
"min-streams"?: number;
|
||||
"max-streams"?: number;
|
||||
padding?: boolean;
|
||||
statistic?: boolean;
|
||||
"only-tcp"?: boolean;
|
||||
"brutal-opts"?: {
|
||||
enabled?: boolean;
|
||||
up?: string;
|
||||
down?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
// snell
|
||||
interface IProxySnellConfig extends IProxyBaseConfig {
|
||||
name: string;
|
||||
type: "snell";
|
||||
server?: string;
|
||||
port?: number;
|
||||
psk?: string;
|
||||
udp?: boolean;
|
||||
version?: number;
|
||||
}
|
||||
interface IProxyConfig
|
||||
extends IProxyBaseConfig,
|
||||
IProxyDirectConfig,
|
||||
IProxyDnsConfig,
|
||||
IProxyHttpConfig,
|
||||
IProxySocks5Config,
|
||||
IProxySshConfig,
|
||||
IProxyTrojanConfig,
|
||||
IProxyTuicConfig,
|
||||
IProxyVlessConfig,
|
||||
IProxyVmessConfig,
|
||||
IProxyWireguardConfig,
|
||||
IProxyHysteriaConfig,
|
||||
IProxyHysteria2Config,
|
||||
IProxyShadowsocksConfig,
|
||||
IProxyshadowsocksRConfig,
|
||||
IProxySmuxConfig,
|
||||
IProxySnellConfig {
|
||||
type:
|
||||
| "ss"
|
||||
| "ssr"
|
||||
| "direct"
|
||||
| "dns"
|
||||
| "snell"
|
||||
| "http"
|
||||
| "trojan"
|
||||
| "hysteria"
|
||||
| "hysteria2"
|
||||
| "tuic"
|
||||
| "wireguard"
|
||||
| "ssh"
|
||||
| "socks5"
|
||||
| "vmess"
|
||||
| "vless";
|
||||
}
|
||||
|
||||
interface IVergeConfig {
|
||||
app_log_level?: "trace" | "debug" | "info" | "warn" | "error" | string;
|
||||
app_log_max_size?: number; // KB
|
||||
app_log_max_count?: number;
|
||||
language?: string;
|
||||
tray_event?:
|
||||
| "main_window"
|
||||
| "tray_menu"
|
||||
| "system_proxy"
|
||||
| "tun_mode"
|
||||
| string;
|
||||
env_type?: "bash" | "cmd" | "powershell" | "fish" | string;
|
||||
startup_script?: string;
|
||||
start_page?: string;
|
||||
clash_core?: string;
|
||||
theme_mode?: "light" | "dark" | "system";
|
||||
traffic_graph?: boolean;
|
||||
enable_memory_usage?: boolean;
|
||||
enable_group_icon?: boolean;
|
||||
menu_icon?: "monochrome" | "colorful" | "disable";
|
||||
menu_order?: string[];
|
||||
tray_icon?: "monochrome" | "colorful";
|
||||
common_tray_icon?: boolean;
|
||||
sysproxy_tray_icon?: boolean;
|
||||
tun_tray_icon?: boolean;
|
||||
// enable_tray_speed?: boolean;
|
||||
// enable_tray_icon?: boolean;
|
||||
tray_inline_proxy_groups?: boolean;
|
||||
enable_tun_mode?: boolean;
|
||||
enable_auto_light_weight_mode?: boolean;
|
||||
auto_light_weight_minutes?: number;
|
||||
enable_auto_launch?: boolean;
|
||||
enable_silent_start?: boolean;
|
||||
enable_system_proxy?: boolean;
|
||||
enable_global_hotkey?: boolean;
|
||||
enable_dns_settings?: boolean;
|
||||
proxy_auto_config?: boolean;
|
||||
pac_file_content?: string;
|
||||
proxy_host?: string;
|
||||
enable_random_port?: boolean;
|
||||
verge_mixed_port?: number;
|
||||
verge_socks_port?: number;
|
||||
verge_redir_port?: number;
|
||||
verge_tproxy_port?: number;
|
||||
verge_port?: number;
|
||||
verge_redir_enabled?: boolean;
|
||||
verge_tproxy_enabled?: boolean;
|
||||
verge_socks_enabled?: boolean;
|
||||
verge_http_enabled?: boolean;
|
||||
enable_proxy_guard?: boolean;
|
||||
enable_bypass_check?: boolean;
|
||||
use_default_bypass?: boolean;
|
||||
proxy_guard_duration?: number;
|
||||
system_proxy_bypass?: string;
|
||||
web_ui_list?: string[];
|
||||
hotkeys?: string[];
|
||||
theme_setting?: {
|
||||
primary_color?: string;
|
||||
secondary_color?: string;
|
||||
primary_text?: string;
|
||||
secondary_text?: string;
|
||||
info_color?: string;
|
||||
error_color?: string;
|
||||
warning_color?: string;
|
||||
success_color?: string;
|
||||
font_family?: string;
|
||||
css_injection?: string;
|
||||
background_image?: string;
|
||||
background_blend_mode?: string;
|
||||
background_opacity?: number;
|
||||
};
|
||||
auto_close_connection?: boolean;
|
||||
auto_check_update?: boolean;
|
||||
default_latency_test?: string;
|
||||
default_latency_timeout?: number;
|
||||
enable_auto_delay_detection?: boolean;
|
||||
enable_builtin_enhanced?: boolean;
|
||||
auto_log_clean?: 0 | 1 | 2 | 3 | 4;
|
||||
proxy_layout_column?: number;
|
||||
test_list?: IVergeTestItem[];
|
||||
webdav_url?: string;
|
||||
webdav_username?: string;
|
||||
webdav_password?: string;
|
||||
home_cards?: Record<string, boolean>;
|
||||
enable_hover_jump_navigator?: boolean;
|
||||
hover_jump_navigator_delay?: number;
|
||||
enable_external_controller?: boolean;
|
||||
}
|
||||
|
||||
interface IWebDavFile {
|
||||
filename: string;
|
||||
href: string;
|
||||
last_modified: string;
|
||||
content_length: number;
|
||||
content_type: string;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
interface ILocalBackupFile {
|
||||
filename: string;
|
||||
path: string;
|
||||
last_modified: string;
|
||||
content_length: number;
|
||||
}
|
||||
|
||||
interface IWebDavConfig {
|
||||
url: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
Reference in New Issue
Block a user