Files
podkop/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
2025-10-26 01:10:33 +03:00

4791 lines
125 KiB
JavaScript

// This file is autogenerated, please don't change manually
"use strict";
"require baseclass";
"require fs";
"require uci";
"require ui";
// src/validators/validateIp.ts
function validateIPV4(ip) {
const ipRegex = /^(?:(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/;
if (ipRegex.test(ip)) {
return { valid: true, message: _("Valid") };
}
return { valid: false, message: _("Invalid IP address") };
}
// src/validators/validateDomain.ts
function validateDomain(domain, allowDotTLD = false) {
const domainRegex = /^(?=.{1,253}(?:\/|$))(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+(?:[a-zA-Z]{2,}|xn--[a-zA-Z0-9-]{1,59}[a-zA-Z0-9])(?:\/[^\s]*)?$/;
if (allowDotTLD) {
const dotTLD = /^\.[a-zA-Z]{2,}$/;
if (dotTLD.test(domain)) {
return { valid: true, message: _("Valid") };
}
}
if (!domainRegex.test(domain)) {
return { valid: false, message: _("Invalid domain address") };
}
const hostname = domain.split("/")[0];
const parts = hostname.split(".");
const atLeastOneInvalidPart = parts.some((part) => part.length > 63);
if (atLeastOneInvalidPart) {
return { valid: false, message: _("Invalid domain address") };
}
return { valid: true, message: _("Valid") };
}
// src/validators/validateDns.ts
function validateDNS(value) {
if (!value) {
return { valid: false, message: _("DNS server address cannot be empty") };
}
const cleanedValueWithoutPort = value.replace(/:(\d+)(?=\/|$)/, "");
const cleanedIpWithoutPath = cleanedValueWithoutPort.split("/")[0];
if (validateIPV4(cleanedIpWithoutPath).valid) {
return { valid: true, message: _("Valid") };
}
if (validateDomain(cleanedValueWithoutPort).valid) {
return { valid: true, message: _("Valid") };
}
return {
valid: false,
message: _(
"Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
)
};
}
// src/validators/validateUrl.ts
function validateUrl(url, protocols = ["http:", "https:"]) {
try {
const parsedUrl = new URL(url);
if (!protocols.includes(parsedUrl.protocol)) {
return {
valid: false,
message: `${_("URL must use one of the following protocols:")} ${protocols.join(", ")}`
};
}
return { valid: true, message: _("Valid") };
} catch (_e) {
return { valid: false, message: _("Invalid URL format") };
}
}
// src/validators/validatePath.ts
function validatePath(value) {
if (!value) {
return {
valid: false,
message: _("Path cannot be empty")
};
}
const pathRegex = /^\/[a-zA-Z0-9_\-/.]+$/;
if (pathRegex.test(value)) {
return {
valid: true,
message: _("Valid")
};
}
return {
valid: false,
message: _(
'Invalid path format. Path must start with "/" and contain valid characters'
)
};
}
// src/validators/validateSubnet.ts
function validateSubnet(value) {
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?$/;
if (!subnetRegex.test(value)) {
return {
valid: false,
message: _("Invalid format. Use X.X.X.X or X.X.X.X/Y")
};
}
const [ip, cidr] = value.split("/");
if (ip === "0.0.0.0") {
return { valid: false, message: _("IP address 0.0.0.0 is not allowed") };
}
const ipCheck = validateIPV4(ip);
if (!ipCheck.valid) {
return ipCheck;
}
if (cidr) {
const cidrNum = parseInt(cidr, 10);
if (cidrNum < 0 || cidrNum > 32) {
return {
valid: false,
message: _("CIDR must be between 0 and 32")
};
}
}
return { valid: true, message: _("Valid") };
}
// src/validators/bulkValidate.ts
function bulkValidate(values, validate) {
const results = values.map((value) => ({ ...validate(value), value }));
return {
valid: results.every((r) => r.valid),
results
};
}
// src/validators/validateShadowsocksUrl.ts
function validateShadowsocksUrl(url) {
if (!url.startsWith("ss://")) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: must start with ss://")
};
}
try {
if (!url || /\s/.test(url)) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: must not contain spaces")
};
}
const mainPart = url.includes("?") ? url.split("?")[0] : url.split("#")[0];
const encryptedPart = mainPart.split("/")[2]?.split("@")[0];
if (!encryptedPart) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: missing credentials")
};
}
try {
const decoded = atob(encryptedPart);
if (!decoded.includes(":")) {
return {
valid: false,
message: _(
"Invalid Shadowsocks URL: decoded credentials must contain method:password"
)
};
}
} catch (_e) {
if (!encryptedPart.includes(":") && !encryptedPart.includes("-")) {
return {
valid: false,
message: _(
'Invalid Shadowsocks URL: missing method and password separator ":"'
)
};
}
}
const serverPart = url.split("@")[1];
if (!serverPart) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: missing server address")
};
}
const [server, portAndRest] = serverPart.split(":");
if (!server) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: missing server")
};
}
const port = portAndRest ? portAndRest.split(/[?#]/)[0] : null;
if (!port) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: missing port")
};
}
const portNum = parseInt(port, 10);
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
return {
valid: false,
message: _("Invalid port number. Must be between 1 and 65535")
};
}
} catch (_e) {
return {
valid: false,
message: _("Invalid Shadowsocks URL: parsing failed")
};
}
return { valid: true, message: _("Valid") };
}
// src/helpers/parseQueryString.ts
function parseQueryString(query) {
const clean = query.startsWith("?") ? query.slice(1) : query;
return clean.split("&").filter(Boolean).reduce(
(acc, pair) => {
const [rawKey, rawValue = ""] = pair.split("=");
if (!rawKey) {
return acc;
}
const key = decodeURIComponent(rawKey);
const value = decodeURIComponent(rawValue);
return { ...acc, [key]: value };
},
{}
);
}
// src/validators/validateVlessUrl.ts
function validateVlessUrl(url) {
try {
if (!url.startsWith("vless://"))
return {
valid: false,
message: "Invalid VLESS URL: must start with vless://"
};
if (/\s/.test(url))
return {
valid: false,
message: "Invalid VLESS URL: must not contain spaces"
};
const body = url.slice("vless://".length);
const [mainPart] = body.split("#");
const [userHostPort, queryString] = mainPart.split("?");
if (!userHostPort)
return {
valid: false,
message: "Invalid VLESS URL: missing host and UUID"
};
const [userPart, hostPortPart] = userHostPort.split("@");
if (!userPart)
return { valid: false, message: "Invalid VLESS URL: missing UUID" };
if (!hostPortPart)
return { valid: false, message: "Invalid VLESS URL: missing server" };
const [host, port] = hostPortPart.split(":");
if (!host)
return { valid: false, message: "Invalid VLESS URL: missing hostname" };
if (!port)
return { valid: false, message: "Invalid VLESS URL: missing port" };
const cleanedPort = port.replace("/", "");
const portNum = Number(cleanedPort);
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535)
return {
valid: false,
message: "Invalid VLESS URL: invalid port number"
};
if (!queryString)
return {
valid: false,
message: "Invalid VLESS URL: missing query parameters"
};
const params = parseQueryString(queryString);
const validTypes = [
"tcp",
"raw",
"udp",
"grpc",
"http",
"httpupgrade",
"xhttp",
"ws",
"kcp"
];
const validSecurities = ["tls", "reality", "none"];
if (!params.type || !validTypes.includes(params.type))
return {
valid: false,
message: "Invalid VLESS URL: unsupported or missing type"
};
if (!params.security || !validSecurities.includes(params.security))
return {
valid: false,
message: "Invalid VLESS URL: unsupported or missing security"
};
if (params.security === "reality") {
if (!params.pbk)
return {
valid: false,
message: "Invalid VLESS URL: missing pbk for reality"
};
if (!params.fp)
return {
valid: false,
message: "Invalid VLESS URL: missing fp for reality"
};
}
if (params.flow === "xtls-rprx-vision-udp443") {
return {
valid: false,
message: "Invalid VLESS URL: flow xtls-rprx-vision-udp443 is not supported"
};
}
return { valid: true, message: _("Valid") };
} catch (_e) {
return { valid: false, message: _("Invalid VLESS URL: parsing failed") };
}
}
// src/validators/validateOutboundJson.ts
function validateOutboundJson(value) {
try {
const parsed = JSON.parse(value);
if (!parsed.type || !parsed.server || !parsed.server_port) {
return {
valid: false,
message: _(
'Outbound JSON must contain at least "type", "server" and "server_port" fields'
)
};
}
return { valid: true, message: _("Valid") };
} catch {
return { valid: false, message: _("Invalid JSON format") };
}
}
// src/validators/validateTrojanUrl.ts
function validateTrojanUrl(url) {
try {
if (!url.startsWith("trojan://")) {
return {
valid: false,
message: _("Invalid Trojan URL: must start with trojan://")
};
}
if (!url || /\s/.test(url)) {
return {
valid: false,
message: _("Invalid Trojan URL: must not contain spaces")
};
}
const body = url.slice("trojan://".length);
const [mainPart] = body.split("#");
const [userHostPort] = mainPart.split("?");
const [userPart, hostPortPart] = userHostPort.split("@");
if (!userHostPort)
return {
valid: false,
message: "Invalid Trojan URL: missing credentials and host"
};
if (!userPart)
return { valid: false, message: "Invalid Trojan URL: missing password" };
if (!hostPortPart)
return {
valid: false,
message: "Invalid Trojan URL: missing hostname and port"
};
const [host, port] = hostPortPart.split(":");
if (!host)
return { valid: false, message: "Invalid Trojan URL: missing hostname" };
if (!port)
return { valid: false, message: "Invalid Trojan URL: missing port" };
const portNum = Number(port);
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535)
return {
valid: false,
message: "Invalid Trojan URL: invalid port number"
};
} catch (_e) {
return { valid: false, message: _("Invalid Trojan URL: parsing failed") };
}
return { valid: true, message: _("Valid") };
}
// src/validators/validateSocksUrl.ts
function validateSocksUrl(url) {
try {
if (!/^socks(4|4a|5):\/\//.test(url)) {
return {
valid: false,
message: _(
"Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
)
};
}
if (!url || /\s/.test(url)) {
return {
valid: false,
message: _("Invalid SOCKS URL: must not contain spaces")
};
}
const body = url.replace(/^socks(4|4a|5):\/\//, "");
const [authAndHost] = body.split("#");
const [credentials, hostPortPart] = authAndHost.includes("@") ? authAndHost.split("@") : [null, authAndHost];
if (credentials) {
const [username, _password] = credentials.split(":");
if (!username) {
return {
valid: false,
message: _("Invalid SOCKS URL: missing username")
};
}
}
if (!hostPortPart) {
return {
valid: false,
message: _("Invalid SOCKS URL: missing host and port")
};
}
const [host, port] = hostPortPart.split(":");
if (!host) {
return {
valid: false,
message: _("Invalid SOCKS URL: missing hostname or IP")
};
}
if (!port) {
return { valid: false, message: _("Invalid SOCKS URL: missing port") };
}
const portNum = Number(port);
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {
return {
valid: false,
message: _("Invalid SOCKS URL: invalid port number")
};
}
const ipv4Result = validateIPV4(host);
const domainResult = validateDomain(host);
if (!ipv4Result.valid && !domainResult.valid) {
return {
valid: false,
message: _("Invalid SOCKS URL: invalid host format")
};
}
} catch (_e) {
return { valid: false, message: _("Invalid SOCKS URL: parsing failed") };
}
return { valid: true, message: _("Valid") };
}
// src/validators/validateProxyUrl.ts
function validateProxyUrl(url) {
const trimmedUrl = url.trim();
if (trimmedUrl.startsWith("ss://")) {
return validateShadowsocksUrl(trimmedUrl);
}
if (trimmedUrl.startsWith("vless://")) {
return validateVlessUrl(trimmedUrl);
}
if (trimmedUrl.startsWith("trojan://")) {
return validateTrojanUrl(trimmedUrl);
}
if (/^socks(4|4a|5):\/\//.test(trimmedUrl)) {
return validateSocksUrl(trimmedUrl);
}
return {
valid: false,
message: _(
"URL must start with vless://, ss://, trojan://, or socks4/5://"
)
};
}
// src/helpers/parseValueList.ts
function parseValueList(value) {
return value.split(/\n/).map((line) => line.split("//")[0]).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
}
// src/podkop/methods/custom/getConfigSections.ts
async function getConfigSections() {
return uci.load("podkop").then(() => uci.sections("podkop"));
}
// src/podkop/methods/shell/callBaseMethod.ts
async function callBaseMethod(method, args = [], command = "/usr/bin/podkop") {
const response = await executeShellCommand({
command,
args: [method, ...args],
timeout: 15e3
});
if (response.stdout) {
try {
return {
success: true,
data: JSON.parse(response.stdout)
};
} catch (_e) {
return {
success: true,
data: response.stdout
};
}
}
return {
success: false,
error: ""
};
}
// src/podkop/types.ts
var Podkop;
((Podkop2) => {
let AvailableMethods;
((AvailableMethods2) => {
AvailableMethods2["CHECK_DNS_AVAILABLE"] = "check_dns_available";
AvailableMethods2["CHECK_FAKEIP"] = "check_fakeip";
AvailableMethods2["CHECK_NFT_RULES"] = "check_nft_rules";
AvailableMethods2["GET_STATUS"] = "get_status";
AvailableMethods2["CHECK_SING_BOX"] = "check_sing_box";
AvailableMethods2["GET_SING_BOX_STATUS"] = "get_sing_box_status";
AvailableMethods2["CLASH_API"] = "clash_api";
AvailableMethods2["RESTART"] = "restart";
AvailableMethods2["START"] = "start";
AvailableMethods2["STOP"] = "stop";
AvailableMethods2["ENABLE"] = "enable";
AvailableMethods2["DISABLE"] = "disable";
AvailableMethods2["GLOBAL_CHECK"] = "global_check";
AvailableMethods2["SHOW_SING_BOX_CONFIG"] = "show_sing_box_config";
AvailableMethods2["CHECK_LOGS"] = "check_logs";
AvailableMethods2["GET_SYSTEM_INFO"] = "get_system_info";
})(AvailableMethods = Podkop2.AvailableMethods || (Podkop2.AvailableMethods = {}));
let AvailableClashAPIMethods;
((AvailableClashAPIMethods2) => {
AvailableClashAPIMethods2["GET_PROXIES"] = "get_proxies";
AvailableClashAPIMethods2["GET_PROXY_LATENCY"] = "get_proxy_latency";
AvailableClashAPIMethods2["GET_GROUP_LATENCY"] = "get_group_latency";
AvailableClashAPIMethods2["SET_GROUP_PROXY"] = "set_group_proxy";
})(AvailableClashAPIMethods = Podkop2.AvailableClashAPIMethods || (Podkop2.AvailableClashAPIMethods = {}));
})(Podkop || (Podkop = {}));
// src/podkop/methods/shell/index.ts
var PodkopShellMethods = {
checkDNSAvailable: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_DNS_AVAILABLE
),
checkFakeIP: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_FAKEIP
),
checkNftRules: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_NFT_RULES
),
getStatus: async () => callBaseMethod(Podkop.AvailableMethods.GET_STATUS),
checkSingBox: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_SING_BOX
),
getSingBoxStatus: async () => callBaseMethod(
Podkop.AvailableMethods.GET_SING_BOX_STATUS
),
getClashApiProxies: async () => callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.GET_PROXIES
]),
getClashApiProxyLatency: async (tag) => callBaseMethod(
Podkop.AvailableMethods.CLASH_API,
[Podkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag]
),
getClashApiGroupLatency: async (tag) => callBaseMethod(
Podkop.AvailableMethods.CLASH_API,
[Podkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag]
),
setClashApiGroupProxy: async (group, proxy) => callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
group,
proxy
]),
restart: async () => callBaseMethod(
Podkop.AvailableMethods.RESTART,
[],
"/etc/init.d/podkop"
),
start: async () => callBaseMethod(
Podkop.AvailableMethods.START,
[],
"/etc/init.d/podkop"
),
stop: async () => callBaseMethod(
Podkop.AvailableMethods.STOP,
[],
"/etc/init.d/podkop"
),
enable: async () => callBaseMethod(
Podkop.AvailableMethods.ENABLE,
[],
"/etc/init.d/podkop"
),
disable: async () => callBaseMethod(
Podkop.AvailableMethods.DISABLE,
[],
"/etc/init.d/podkop"
),
globalCheck: async () => callBaseMethod(Podkop.AvailableMethods.GLOBAL_CHECK),
showSingBoxConfig: async () => callBaseMethod(Podkop.AvailableMethods.SHOW_SING_BOX_CONFIG),
checkLogs: async () => callBaseMethod(Podkop.AvailableMethods.CHECK_LOGS),
getSystemInfo: async () => callBaseMethod(
Podkop.AvailableMethods.GET_SYSTEM_INFO
)
};
// src/podkop/methods/custom/getDashboardSections.ts
async function getDashboardSections() {
const configSections = await getConfigSections();
const clashProxies = await PodkopShellMethods.getClashApiProxies();
if (!clashProxies.success) {
return {
success: false,
data: []
};
}
const proxies = Object.entries(clashProxies.data.proxies).map(
([key, value]) => ({
code: key,
value
})
);
const data = configSections.filter(
(section) => section.connection_type !== "block" && section[".type"] !== "settings"
).map((section) => {
if (section.connection_type === "proxy") {
if (section.proxy_config_type === "url") {
const outbound = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
const activeConfigs = splitProxyString(section.proxy_string);
const proxyDisplayName = getProxyUrlName(activeConfigs?.[0]) || outbound?.value?.name || "";
return {
withTagSelect: false,
code: outbound?.code || section[".name"],
displayName: section[".name"],
outbounds: [
{
code: outbound?.code || section[".name"],
displayName: proxyDisplayName,
latency: outbound?.value?.history?.[0]?.delay || 0,
type: outbound?.value?.type || "",
selected: true
}
]
};
}
if (section.proxy_config_type === "outbound") {
const outbound = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
const parsedOutbound = JSON.parse(section.outbound_json);
const parsedTag = parsedOutbound?.tag ? decodeURIComponent(parsedOutbound?.tag) : void 0;
const proxyDisplayName = parsedTag || outbound?.value?.name || "";
return {
withTagSelect: false,
code: outbound?.code || section[".name"],
displayName: section[".name"],
outbounds: [
{
code: outbound?.code || section[".name"],
displayName: proxyDisplayName,
latency: outbound?.value?.history?.[0]?.delay || 0,
type: outbound?.value?.type || "",
selected: true
}
]
};
}
if (section.proxy_config_type === "urltest") {
const selector = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
const outbound = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-urltest-out`
);
const outbounds = (outbound?.value?.all ?? []).map((code) => proxies.find((item) => item.code === code)).map((item, index) => ({
code: item?.code || "",
displayName: getProxyUrlName(section.urltest_proxy_links?.[index]) || item?.value?.name || "",
latency: item?.value?.history?.[0]?.delay || 0,
type: item?.value?.type || "",
selected: selector?.value?.now === item?.code
}));
return {
withTagSelect: true,
code: selector?.code || section[".name"],
displayName: section[".name"],
outbounds: [
{
code: outbound?.code || "",
displayName: _("Fastest"),
latency: outbound?.value?.history?.[0]?.delay || 0,
type: outbound?.value?.type || "",
selected: selector?.value?.now === outbound?.code
},
...outbounds
]
};
}
}
if (section.connection_type === "vpn") {
const outbound = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
return {
withTagSelect: false,
code: outbound?.code || section[".name"],
displayName: section[".name"],
outbounds: [
{
code: outbound?.code || section[".name"],
displayName: section.interface || outbound?.value?.name || "",
latency: outbound?.value?.history?.[0]?.delay || 0,
type: outbound?.value?.type || "",
selected: true
}
]
};
}
return {
withTagSelect: false,
code: section[".name"],
displayName: section[".name"],
outbounds: []
};
});
return {
success: true,
data
};
}
// src/podkop/methods/custom/index.ts
var CustomPodkopMethods = {
getConfigSections,
getDashboardSections
};
// src/constants.ts
var STATUS_COLORS = {
SUCCESS: "#4caf50",
ERROR: "#f44336",
WARNING: "#ff9800"
};
var PODKOP_LUCI_APP_VERSION = "__COMPILED_VERSION_VARIABLE__";
var FAKEIP_CHECK_DOMAIN = "fakeip.podkop.fyi";
var IP_CHECK_DOMAIN = "ip.podkop.fyi";
var REGIONAL_OPTIONS = [
"russia_inside",
"russia_outside",
"ukraine_inside"
];
var ALLOWED_WITH_RUSSIA_INSIDE = [
"russia_inside",
"meta",
"twitter",
"discord",
"telegram",
"cloudflare",
"google_ai",
"google_play",
"hetzner",
"ovh",
"hodca",
"digitalocean",
"cloudfront"
];
var DOMAIN_LIST_OPTIONS = {
russia_inside: "Russia inside",
russia_outside: "Russia outside",
ukraine_inside: "Ukraine",
geoblock: "Geo Block",
block: "Block",
porn: "Porn",
news: "News",
anime: "Anime",
youtube: "Youtube",
discord: "Discord",
meta: "Meta",
twitter: "Twitter (X)",
hdrezka: "HDRezka",
tiktok: "Tik-Tok",
telegram: "Telegram",
cloudflare: "Cloudflare",
google_ai: "Google AI",
google_play: "Google Play",
hodca: "H.O.D.C.A",
hetzner: "Hetzner ASN",
ovh: "OVH ASN",
digitalocean: "Digital Ocean ASN",
cloudfront: "CloudFront ASN"
};
var UPDATE_INTERVAL_OPTIONS = {
"1h": "Every hour",
"3h": "Every 3 hours",
"12h": "Every 12 hours",
"1d": "Every day",
"3d": "Every 3 days"
};
var DNS_SERVER_OPTIONS = {
"1.1.1.1": "1.1.1.1 (Cloudflare)",
"8.8.8.8": "8.8.8.8 (Google)",
"9.9.9.9": "9.9.9.9 (Quad9)",
"dns.adguard-dns.com": "dns.adguard-dns.com (AdGuard Default)",
"unfiltered.adguard-dns.com": "unfiltered.adguard-dns.com (AdGuard Unfiltered)",
"family.adguard-dns.com": "family.adguard-dns.com (AdGuard Family)"
};
var BOOTSTRAP_DNS_SERVER_OPTIONS = {
"77.88.8.8": "77.88.8.8 (Yandex DNS)",
"77.88.8.1": "77.88.8.1 (Yandex DNS)",
"1.1.1.1": "1.1.1.1 (Cloudflare DNS)",
"1.0.0.1": "1.0.0.1 (Cloudflare DNS)",
"8.8.8.8": "8.8.8.8 (Google DNS)",
"8.8.4.4": "8.8.4.4 (Google DNS)",
"9.9.9.9": "9.9.9.9 (Quad9 DNS)",
"9.9.9.11": "9.9.9.11 (Quad9 DNS)"
};
var DIAGNOSTICS_UPDATE_INTERVAL = 1e4;
var CACHE_TIMEOUT = DIAGNOSTICS_UPDATE_INTERVAL - 1e3;
var ERROR_POLL_INTERVAL = 1e4;
var COMMAND_TIMEOUT = 1e4;
var FETCH_TIMEOUT = 1e4;
var BUTTON_FEEDBACK_TIMEOUT = 1e3;
var DIAGNOSTICS_INITIAL_DELAY = 100;
var COMMAND_SCHEDULING = {
P0_PRIORITY: 0,
// Highest priority (no delay)
P1_PRIORITY: 100,
// Very high priority
P2_PRIORITY: 300,
// High priority
P3_PRIORITY: 500,
// Above average
P4_PRIORITY: 700,
// Standard priority
P5_PRIORITY: 900,
// Below average
P6_PRIORITY: 1100,
// Low priority
P7_PRIORITY: 1300,
// Very low priority
P8_PRIORITY: 1500,
// Background execution
P9_PRIORITY: 1700,
// Idle mode execution
P10_PRIORITY: 1900
// Lowest priority
};
// src/podkop/api.ts
async function createBaseApiRequest(fetchFn, options) {
const wrappedFn = () => options?.timeoutMs && options?.operationName ? withTimeout(
fetchFn(),
options.timeoutMs,
options.operationName,
options.timeoutMessage
) : fetchFn();
try {
const response = await wrappedFn();
if (!response.ok) {
return {
success: false,
message: `${_("HTTP error")} ${response.status}: ${response.statusText}`
};
}
const data = await response.json();
return {
success: true,
data
};
} catch (e) {
return {
success: false,
message: e instanceof Error ? e.message : _("Unknown error")
};
}
}
// src/podkop/methods/fakeip/getFakeIpCheck.ts
async function getFakeIpCheck() {
return createBaseApiRequest(
() => fetch(`https://${FAKEIP_CHECK_DOMAIN}/check`, {
method: "GET",
headers: { "Content-Type": "application/json" }
}),
{
operationName: "getFakeIpCheck",
timeoutMs: 5e3
}
);
}
// src/podkop/methods/fakeip/getIpCheck.ts
async function getIpCheck() {
return createBaseApiRequest(
() => fetch(`https://${IP_CHECK_DOMAIN}/check`, {
method: "GET",
headers: { "Content-Type": "application/json" }
}),
{
operationName: "getIpCheck",
timeoutMs: 5e3
}
);
}
// src/podkop/methods/fakeip/index.ts
var RemoteFakeIPMethods = {
getFakeIpCheck,
getIpCheck
};
// src/podkop/services/tab.service.ts
var TabService = class _TabService {
constructor() {
this.observer = null;
this.lastActiveId = null;
this.init();
}
static getInstance() {
if (!_TabService.instance) {
_TabService.instance = new _TabService();
}
return _TabService.instance;
}
init() {
this.observer = new MutationObserver(() => this.handleMutations());
this.observer.observe(document.body, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ["class"]
});
this.notify();
}
handleMutations() {
this.notify();
}
getTabsInfo() {
const tabs = Array.from(
document.querySelectorAll(".cbi-tab, .cbi-tab-disabled")
);
return tabs.map((el) => ({
el,
id: el.dataset.tab || "",
active: el.classList.contains("cbi-tab") && !el.classList.contains("cbi-tab-disabled")
}));
}
getActiveTabId() {
const active = document.querySelector(
".cbi-tab:not(.cbi-tab-disabled)"
);
return active?.dataset.tab || null;
}
notify() {
const tabs = this.getTabsInfo();
const activeId = this.getActiveTabId();
if (activeId !== this.lastActiveId) {
this.lastActiveId = activeId;
this.callback?.(activeId, tabs);
}
}
onChange(callback) {
this.callback = callback;
this.notify();
}
getAllTabs() {
return this.getTabsInfo();
}
getActiveTab() {
return this.getActiveTabId();
}
disconnect() {
this.observer?.disconnect();
this.observer = null;
}
};
var TabServiceInstance = TabService.getInstance();
// src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts
function getCheckTitle(name) {
return `${name} ${_("checks")}`;
}
// src/podkop/tabs/diagnostic/checks/contstants.ts
var DIAGNOSTICS_CHECKS_MAP = {
["DNS" /* DNS */]: {
order: 1,
title: getCheckTitle("DNS"),
code: "DNS" /* DNS */
},
["SINGBOX" /* SINGBOX */]: {
order: 2,
title: getCheckTitle("Sing-box"),
code: "SINGBOX" /* SINGBOX */
},
["NFT" /* NFT */]: {
order: 3,
title: getCheckTitle("Nftables"),
code: "NFT" /* NFT */
},
["OUTBOUNDS" /* OUTBOUNDS */]: {
order: 4,
title: getCheckTitle("Outbounds"),
code: "OUTBOUNDS" /* OUTBOUNDS */
},
["FAKEIP" /* FAKEIP */]: {
order: 5,
title: getCheckTitle("FakeIP"),
code: "FAKEIP" /* FAKEIP */
}
};
// src/podkop/tabs/diagnostic/diagnostic.store.ts
var initialDiagnosticStore = {
diagnosticsSystemInfo: {
loading: true,
podkop_version: "loading",
podkop_latest_version: "loading",
luci_app_version: "loading",
sing_box_version: "loading",
openwrt_version: "loading",
device_model: "loading"
},
diagnosticsActions: {
restart: {
loading: false
},
start: {
loading: false
},
stop: {
loading: false
},
enable: {
loading: false
},
disable: {
loading: false
},
globalCheck: {
loading: false
},
viewLogs: {
loading: false
},
showSingBoxConfig: {
loading: false
}
},
diagnosticsRunAction: { loading: false },
diagnosticsChecks: [
{
code: "DNS" /* DNS */,
title: DIAGNOSTICS_CHECKS_MAP.DNS.title,
order: DIAGNOSTICS_CHECKS_MAP.DNS.order,
description: _("Not running"),
items: [],
state: "skipped"
},
{
code: "SINGBOX" /* SINGBOX */,
title: DIAGNOSTICS_CHECKS_MAP.SINGBOX.title,
order: DIAGNOSTICS_CHECKS_MAP.SINGBOX.order,
description: _("Not running"),
items: [],
state: "skipped"
},
{
code: "NFT" /* NFT */,
title: DIAGNOSTICS_CHECKS_MAP.NFT.title,
order: DIAGNOSTICS_CHECKS_MAP.NFT.order,
description: _("Not running"),
items: [],
state: "skipped"
},
{
code: "OUTBOUNDS" /* OUTBOUNDS */,
title: DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS.title,
order: DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS.order,
description: _("Not running"),
items: [],
state: "skipped"
},
{
code: "FAKEIP" /* FAKEIP */,
title: DIAGNOSTICS_CHECKS_MAP.FAKEIP.title,
order: DIAGNOSTICS_CHECKS_MAP.FAKEIP.order,
description: _("Not running"),
items: [],
state: "skipped"
}
]
};
var loadingDiagnosticsChecksStore = {
diagnosticsChecks: [
{
code: "DNS" /* DNS */,
title: DIAGNOSTICS_CHECKS_MAP.DNS.title,
order: DIAGNOSTICS_CHECKS_MAP.DNS.order,
description: _("Pending"),
items: [],
state: "skipped"
},
{
code: "SINGBOX" /* SINGBOX */,
title: DIAGNOSTICS_CHECKS_MAP.SINGBOX.title,
order: DIAGNOSTICS_CHECKS_MAP.SINGBOX.order,
description: _("Pending"),
items: [],
state: "skipped"
},
{
code: "NFT" /* NFT */,
title: DIAGNOSTICS_CHECKS_MAP.NFT.title,
order: DIAGNOSTICS_CHECKS_MAP.NFT.order,
description: _("Pending"),
items: [],
state: "skipped"
},
{
code: "OUTBOUNDS" /* OUTBOUNDS */,
title: DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS.title,
order: DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS.order,
description: _("Pending"),
items: [],
state: "skipped"
},
{
code: "FAKEIP" /* FAKEIP */,
title: DIAGNOSTICS_CHECKS_MAP.FAKEIP.title,
order: DIAGNOSTICS_CHECKS_MAP.FAKEIP.order,
description: _("Pending"),
items: [],
state: "skipped"
}
]
};
// src/podkop/services/store.service.ts
function jsonStableStringify(obj) {
return JSON.stringify(obj, (_2, value) => {
if (value && typeof value === "object" && !Array.isArray(value)) {
return Object.keys(value).sort().reduce(
(acc, key) => {
acc[key] = value[key];
return acc;
},
{}
);
}
return value;
});
}
function jsonEqual(a, b) {
try {
return jsonStableStringify(a) === jsonStableStringify(b);
} catch {
return false;
}
}
var StoreService = class {
constructor(initial) {
this.listeners = /* @__PURE__ */ new Set();
this.lastHash = "";
this.value = initial;
this.initial = structuredClone(initial);
this.lastHash = jsonStableStringify(initial);
}
get() {
return this.value;
}
set(next) {
const prev = this.value;
const merged = { ...prev, ...next };
if (jsonEqual(prev, merged)) return;
this.value = merged;
this.lastHash = jsonStableStringify(merged);
const diff = {};
for (const key in merged) {
if (!jsonEqual(merged[key], prev[key])) diff[key] = merged[key];
}
this.listeners.forEach((cb) => cb(this.value, prev, diff));
}
reset(keys) {
const prev = this.value;
const next = structuredClone(this.value);
if (keys && keys.length > 0) {
keys.forEach((key) => {
next[key] = structuredClone(this.initial[key]);
});
} else {
Object.assign(next, structuredClone(this.initial));
}
if (jsonEqual(prev, next)) return;
this.value = next;
this.lastHash = jsonStableStringify(next);
const diff = {};
for (const key in next) {
if (!jsonEqual(next[key], prev[key])) diff[key] = next[key];
}
this.listeners.forEach((cb) => cb(this.value, prev, diff));
}
subscribe(cb) {
this.listeners.add(cb);
cb(this.value, this.value, {});
return () => this.listeners.delete(cb);
}
unsubscribe(cb) {
this.listeners.delete(cb);
}
patch(key, value) {
this.set({ [key]: value });
}
getKey(key) {
return this.value[key];
}
subscribeKey(key, cb) {
let prev = this.value[key];
const wrapper = (val) => {
if (!jsonEqual(val[key], prev)) {
prev = val[key];
cb(val[key]);
}
};
this.listeners.add(wrapper);
return () => this.listeners.delete(wrapper);
}
};
var initialStore = {
tabService: {
current: "",
all: []
},
bandwidthWidget: {
loading: true,
failed: false,
data: { up: 0, down: 0 }
},
trafficTotalWidget: {
loading: true,
failed: false,
data: { downloadTotal: 0, uploadTotal: 0 }
},
systemInfoWidget: {
loading: true,
failed: false,
data: { connections: 0, memory: 0 }
},
servicesInfoWidget: {
loading: true,
failed: false,
data: { singbox: 0, podkop: 0 }
},
sectionsWidget: {
loading: true,
failed: false,
latencyFetching: false,
data: []
},
...initialDiagnosticStore
};
var store = new StoreService(initialStore);
// src/helpers/downloadAsTxt.ts
function downloadAsTxt(text, filename) {
const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
const safeName = filename.endsWith(".txt") ? filename : `${filename}.txt`;
link.download = safeName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
}
// src/podkop/services/logger.service.ts
var Logger = class {
constructor() {
this.logs = [];
this.levels = ["debug", "info", "warn", "error"];
}
format(level, ...args) {
return `[${level.toUpperCase()}] ${args.join(" ")}`;
}
push(level, ...args) {
if (!this.levels.includes(level)) level = "info";
const message = this.format(level, ...args);
this.logs.push(message);
switch (level) {
case "error":
console.error(message);
break;
case "warn":
console.warn(message);
break;
case "info":
console.info(message);
break;
default:
console.log(message);
}
}
debug(...args) {
this.push("debug", ...args);
}
info(...args) {
this.push("info", ...args);
}
warn(...args) {
this.push("warn", ...args);
}
error(...args) {
this.push("error", ...args);
}
clear() {
this.logs = [];
}
getLogs() {
return this.logs.join("\n");
}
download(filename = "logs.txt") {
if (typeof document === "undefined") {
console.warn("Logger.download() \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435");
return;
}
downloadAsTxt(this.getLogs(), filename);
}
};
var logger = new Logger();
// src/podkop/services/podkopLogWatcher.service.ts
var PodkopLogWatcher = class _PodkopLogWatcher {
constructor() {
this.intervalMs = 5e3;
this.lastLines = /* @__PURE__ */ new Set();
this.running = false;
this.paused = false;
if (typeof document !== "undefined") {
document.addEventListener("visibilitychange", () => {
if (document.hidden) this.pause();
else this.resume();
});
}
}
static getInstance() {
if (!_PodkopLogWatcher.instance) {
_PodkopLogWatcher.instance = new _PodkopLogWatcher();
}
return _PodkopLogWatcher.instance;
}
init(fetcher, options) {
this.fetcher = fetcher;
this.onNewLog = options?.onNewLog;
this.intervalMs = options?.intervalMs ?? 5e3;
logger.info(
"[PodkopLogWatcher]",
`initialized (interval: ${this.intervalMs}ms)`
);
}
async checkOnce() {
if (!this.fetcher) {
logger.warn("[PodkopLogWatcher]", "fetcher not found");
return;
}
if (this.paused) {
logger.debug("[PodkopLogWatcher]", "skipped check \u2014 tab not visible");
return;
}
try {
const raw = await this.fetcher();
const lines = raw.split("\n").filter(Boolean);
for (const line of lines) {
if (!this.lastLines.has(line)) {
this.lastLines.add(line);
this.onNewLog?.(line);
}
}
if (this.lastLines.size > 500) {
const arr = Array.from(this.lastLines);
this.lastLines = new Set(arr.slice(-500));
}
} catch (err) {
logger.error("[PodkopLogWatcher]", "failed to read logs:", err);
}
}
start() {
if (this.running) return;
if (!this.fetcher) {
logger.warn("[PodkopLogWatcher]", "attempted to start without fetcher");
return;
}
this.running = true;
this.timer = setInterval(() => this.checkOnce(), this.intervalMs);
logger.info(
"[PodkopLogWatcher]",
`started (interval: ${this.intervalMs}ms)`
);
}
stop() {
if (!this.running) return;
this.running = false;
if (this.timer) clearInterval(this.timer);
logger.info("[PodkopLogWatcher]", "stopped");
}
pause() {
if (!this.running || this.paused) return;
this.paused = true;
logger.info("[PodkopLogWatcher]", "paused (tab not visible)");
}
resume() {
if (!this.running || !this.paused) return;
this.paused = false;
logger.info("[PodkopLogWatcher]", "resumed (tab active)");
this.checkOnce();
}
reset() {
this.lastLines.clear();
logger.info("[PodkopLogWatcher]", "log history reset");
}
};
// src/podkop/services/core.service.ts
function coreService() {
TabServiceInstance.onChange((activeId, tabs) => {
logger.info("[TAB]", activeId);
store.set({
tabService: {
current: activeId || "",
all: tabs.map((tab) => tab.id)
}
});
});
const watcher = PodkopLogWatcher.getInstance();
watcher.init(
async () => {
const logs = await PodkopShellMethods.checkLogs();
if (logs.success) {
return logs.data;
}
return "";
},
{
intervalMs: 3e3,
onNewLog: (line) => {
if (line.toLowerCase().includes("[error]") || line.toLowerCase().includes("[fatal]")) {
ui.addNotification("Podkop Error", E("div", {}, line), "error");
}
}
}
);
watcher.start();
}
// src/podkop/services/socket.service.ts
var SocketManager = class _SocketManager {
constructor() {
this.sockets = /* @__PURE__ */ new Map();
this.listeners = /* @__PURE__ */ new Map();
this.connected = /* @__PURE__ */ new Map();
this.errorListeners = /* @__PURE__ */ new Map();
}
static getInstance() {
if (!_SocketManager.instance) {
_SocketManager.instance = new _SocketManager();
}
return _SocketManager.instance;
}
resetAll() {
for (const [url, ws] of this.sockets.entries()) {
try {
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
ws.close();
}
} catch (err) {
logger.error(
"[SOCKET]",
`resetAll: failed to close socket ${url}`,
err
);
}
}
this.sockets.clear();
this.listeners.clear();
this.errorListeners.clear();
this.connected.clear();
logger.info("[SOCKET]", "All connections and state have been reset.");
}
connect(url) {
if (this.sockets.has(url)) return;
let ws;
try {
ws = new WebSocket(url);
} catch (err) {
logger.error(
"[SOCKET]",
`failed to construct WebSocket for ${url}:`,
err
);
this.triggerError(url, err instanceof Event ? err : String(err));
return;
}
this.sockets.set(url, ws);
this.connected.set(url, false);
this.listeners.set(url, /* @__PURE__ */ new Set());
this.errorListeners.set(url, /* @__PURE__ */ new Set());
ws.addEventListener("open", () => {
this.connected.set(url, true);
logger.info("[SOCKET]", "Connected to", url);
});
ws.addEventListener("message", (event) => {
const handlers = this.listeners.get(url);
if (handlers) {
for (const handler of handlers) {
try {
handler(event.data);
} catch (err) {
logger.error("[SOCKET]", `Handler error for ${url}:`, err);
}
}
}
});
ws.addEventListener("close", () => {
this.connected.set(url, false);
logger.warn("[SOCKET]", `Disconnected: ${url}`);
this.triggerError(url, "Connection closed");
});
ws.addEventListener("error", (err) => {
logger.error("[SOCKET]", `Socket error for ${url}:`, err);
this.triggerError(url, err);
});
}
subscribe(url, listener, onError) {
if (!this.errorListeners.has(url)) {
this.errorListeners.set(url, /* @__PURE__ */ new Set());
}
if (onError) {
this.errorListeners.get(url)?.add(onError);
}
if (!this.sockets.has(url)) {
this.connect(url);
}
if (!this.listeners.has(url)) {
this.listeners.set(url, /* @__PURE__ */ new Set());
}
this.listeners.get(url)?.add(listener);
}
unsubscribe(url, listener, onError) {
this.listeners.get(url)?.delete(listener);
if (onError) {
this.errorListeners.get(url)?.delete(onError);
}
}
// eslint-disable-next-line
send(url, data) {
const ws = this.sockets.get(url);
if (ws && this.connected.get(url)) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
} else {
logger.warn("[SOCKET]", `Cannot send: not connected to ${url}`);
this.triggerError(url, "Not connected");
}
}
disconnect(url) {
const ws = this.sockets.get(url);
if (ws) {
ws.close();
this.sockets.delete(url);
this.listeners.delete(url);
this.errorListeners.delete(url);
this.connected.delete(url);
}
}
disconnectAll() {
for (const url of this.sockets.keys()) {
this.disconnect(url);
}
}
triggerError(url, err) {
const handlers = this.errorListeners.get(url);
if (handlers) {
for (const cb of handlers) {
try {
cb(err);
} catch (e) {
logger.error("[SOCKET]", `Error handler threw for ${url}:`, e);
}
}
}
}
};
var socket = SocketManager.getInstance();
// src/podkop/tabs/dashboard/partials/renderSections.ts
function renderFailedState() {
return E(
"div",
{
class: "pdk_dashboard-page__outbound-section centered",
style: "height: 127px"
},
E("span", {}, [E("span", {}, _("Dashboard currently unavailable"))])
);
}
function renderLoadingState() {
return E("div", {
id: "dashboard-sections-grid-skeleton",
class: "pdk_dashboard-page__outbound-section skeleton",
style: "height: 127px"
});
}
function renderDefaultState({
section,
onChooseOutbound,
onTestLatency,
latencyFetching
}) {
function testLatency() {
if (section.withTagSelect) {
return onTestLatency(section.code);
}
if (section.outbounds.length) {
return onTestLatency(section.outbounds[0].code);
}
}
function renderOutbound(outbound) {
function getLatencyClass() {
if (!outbound.latency) {
return "pdk_dashboard-page__outbound-grid__item__latency--empty";
}
if (outbound.latency < 800) {
return "pdk_dashboard-page__outbound-grid__item__latency--green";
}
if (outbound.latency < 1500) {
return "pdk_dashboard-page__outbound-grid__item__latency--yellow";
}
return "pdk_dashboard-page__outbound-grid__item__latency--red";
}
return E(
"div",
{
class: `pdk_dashboard-page__outbound-grid__item ${outbound.selected ? "pdk_dashboard-page__outbound-grid__item--active" : ""} ${section.withTagSelect ? "pdk_dashboard-page__outbound-grid__item--selectable" : ""}`,
click: () => section.withTagSelect && onChooseOutbound(section.code, outbound.code)
},
[
E("b", {}, outbound.displayName),
E("div", { class: "pdk_dashboard-page__outbound-grid__item__footer" }, [
E(
"div",
{ class: "pdk_dashboard-page__outbound-grid__item__type" },
outbound.type
),
E(
"div",
{ class: getLatencyClass() },
outbound.latency ? `${outbound.latency}ms` : "N/A"
)
])
]
);
}
return E("div", { class: "pdk_dashboard-page__outbound-section" }, [
// Title with test latency
E("div", { class: "pdk_dashboard-page__outbound-section__title-section" }, [
E(
"div",
{
class: "pdk_dashboard-page__outbound-section__title-section__title"
},
section.displayName
),
latencyFetching ? E("div", { class: "skeleton", style: "width: 99px; height: 28px" }) : E(
"button",
{
class: "btn dashboard-sections-grid-item-test-latency",
click: () => testLatency()
},
_("Test latency")
)
]),
E(
"div",
{ class: "pdk_dashboard-page__outbound-grid" },
section.outbounds.map((outbound) => renderOutbound(outbound))
)
]);
}
function renderSections(props) {
if (props.failed) {
return renderFailedState();
}
if (props.loading) {
return renderLoadingState();
}
return renderDefaultState(props);
}
// src/podkop/tabs/dashboard/partials/renderWidget.ts
function renderFailedState2() {
return E(
"div",
{
id: "",
style: "height: 78px",
class: "pdk_dashboard-page__widgets-section__item centered"
},
_("Currently unavailable")
);
}
function renderLoadingState2() {
return E(
"div",
{
id: "",
style: "height: 78px",
class: "pdk_dashboard-page__widgets-section__item skeleton"
},
""
);
}
function renderDefaultState2({ title, items }) {
return E("div", { class: "pdk_dashboard-page__widgets-section__item" }, [
E(
"b",
{ class: "pdk_dashboard-page__widgets-section__item__title" },
title
),
...items.map(
(item) => E(
"div",
{
class: `pdk_dashboard-page__widgets-section__item__row ${item?.attributes?.class || ""}`
},
[
E(
"span",
{ class: "pdk_dashboard-page__widgets-section__item__row__key" },
`${item.key}: `
),
E(
"span",
{ class: "pdk_dashboard-page__widgets-section__item__row__value" },
item.value
)
]
)
)
]);
}
function renderWidget(props) {
if (props.loading) {
return renderLoadingState2();
}
if (props.failed) {
return renderFailedState2();
}
return renderDefaultState2(props);
}
// src/podkop/tabs/dashboard/render.ts
function render() {
return E(
"div",
{
id: "dashboard-status",
class: "pdk_dashboard-page"
},
[
// Widgets section
E("div", { class: "pdk_dashboard-page__widgets-section" }, [
E(
"div",
{ id: "dashboard-widget-traffic" },
renderWidget({ loading: true, failed: false, title: "", items: [] })
),
E(
"div",
{ id: "dashboard-widget-traffic-total" },
renderWidget({ loading: true, failed: false, title: "", items: [] })
),
E(
"div",
{ id: "dashboard-widget-system-info" },
renderWidget({ loading: true, failed: false, title: "", items: [] })
),
E(
"div",
{ id: "dashboard-widget-service-info" },
renderWidget({ loading: true, failed: false, title: "", items: [] })
)
]),
// All outbounds
E(
"div",
{ id: "dashboard-sections-grid" },
renderSections({
loading: true,
failed: false,
section: {
code: "",
displayName: "",
outbounds: [],
withTagSelect: false
},
onTestLatency: () => {
},
onChooseOutbound: () => {
},
latencyFetching: false
})
)
]
);
}
// src/helpers/prettyBytes.ts
function prettyBytes(n) {
const UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
if (n < 1e3) {
return n + " B";
}
const exponent = Math.min(Math.floor(Math.log10(n) / 3), UNITS.length - 1);
n = Number((n / Math.pow(1e3, exponent)).toPrecision(3));
const unit = UNITS[exponent];
return n + " " + unit;
}
// src/podkop/fetchers/fetchServicesInfo.ts
async function fetchServicesInfo() {
const [podkop, singbox] = await Promise.all([
PodkopShellMethods.getStatus(),
PodkopShellMethods.getSingBoxStatus()
]);
if (!podkop.success || !singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
failed: true,
data: { singbox: 0, podkop: 0 }
}
});
}
if (podkop.success && singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
failed: false,
data: { singbox: singbox.data.running, podkop: podkop.data.enabled }
}
});
}
}
// src/podkop/tabs/dashboard/initController.ts
async function fetchDashboardSections() {
const prev = store.get().sectionsWidget;
store.set({
sectionsWidget: {
...prev,
failed: false
}
});
const { data, success } = await CustomPodkopMethods.getDashboardSections();
if (!success) {
logger.error("[DASHBOARD]", "fetchDashboardSections: failed to fetch");
}
store.set({
sectionsWidget: {
latencyFetching: false,
loading: false,
failed: !success,
data
}
});
}
async function connectToClashSockets() {
socket.subscribe(
`${getClashWsUrl()}/traffic?token=`,
(msg) => {
const parsedMsg = JSON.parse(msg);
store.set({
bandwidthWidget: {
loading: false,
failed: false,
data: { up: parsedMsg.up, down: parsedMsg.down }
}
});
},
(_err) => {
logger.error(
"[DASHBOARD]",
"connectToClashSockets - traffic: failed to connect to",
getClashWsUrl()
);
store.set({
bandwidthWidget: {
loading: false,
failed: true,
data: { up: 0, down: 0 }
}
});
}
);
socket.subscribe(
`${getClashWsUrl()}/connections?token=`,
(msg) => {
const parsedMsg = JSON.parse(msg);
store.set({
trafficTotalWidget: {
loading: false,
failed: false,
data: {
downloadTotal: parsedMsg.downloadTotal,
uploadTotal: parsedMsg.uploadTotal
}
},
systemInfoWidget: {
loading: false,
failed: false,
data: {
connections: parsedMsg.connections?.length,
memory: parsedMsg.memory
}
}
});
},
(_err) => {
logger.error(
"[DASHBOARD]",
"connectToClashSockets - connections: failed to connect to",
getClashWsUrl()
);
store.set({
trafficTotalWidget: {
loading: false,
failed: true,
data: { downloadTotal: 0, uploadTotal: 0 }
},
systemInfoWidget: {
loading: false,
failed: true,
data: {
connections: 0,
memory: 0
}
}
});
}
);
}
async function handleChooseOutbound(selector, tag) {
await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
await fetchDashboardSections();
}
async function handleTestGroupLatency(tag) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true
}
});
await PodkopShellMethods.getClashApiGroupLatency(tag);
await fetchDashboardSections();
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: false
}
});
}
async function handleTestProxyLatency(tag) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true
}
});
await PodkopShellMethods.getClashApiProxyLatency(tag);
await fetchDashboardSections();
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: false
}
});
}
async function renderSectionsWidget() {
logger.debug("[DASHBOARD]", "renderSectionsWidget");
const sectionsWidget = store.get().sectionsWidget;
const container = document.getElementById("dashboard-sections-grid");
if (sectionsWidget.loading || sectionsWidget.failed) {
const renderedWidget = renderSections({
loading: sectionsWidget.loading,
failed: sectionsWidget.failed,
section: {
code: "",
displayName: "",
outbounds: [],
withTagSelect: false
},
onTestLatency: () => {
},
onChooseOutbound: () => {
},
latencyFetching: sectionsWidget.latencyFetching
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedWidget);
});
}
const renderedWidgets = sectionsWidget.data.map(
(section) => renderSections({
loading: sectionsWidget.loading,
failed: sectionsWidget.failed,
section,
latencyFetching: sectionsWidget.latencyFetching,
onTestLatency: (tag) => {
if (section.withTagSelect) {
return handleTestGroupLatency(tag);
}
return handleTestProxyLatency(tag);
},
onChooseOutbound: (selector, tag) => {
handleChooseOutbound(selector, tag);
}
})
);
return preserveScrollForPage(() => {
container.replaceChildren(...renderedWidgets);
});
}
async function renderBandwidthWidget() {
logger.debug("[DASHBOARD]", "renderBandwidthWidget");
const traffic = store.get().bandwidthWidget;
const container = document.getElementById("dashboard-widget-traffic");
if (traffic.loading || traffic.failed) {
const renderedWidget2 = renderWidget({
loading: traffic.loading,
failed: traffic.failed,
title: "",
items: []
});
return container.replaceChildren(renderedWidget2);
}
const renderedWidget = renderWidget({
loading: traffic.loading,
failed: traffic.failed,
title: _("Traffic"),
items: [
{ key: _("Uplink"), value: `${prettyBytes(traffic.data.up)}/s` },
{ key: _("Downlink"), value: `${prettyBytes(traffic.data.down)}/s` }
]
});
container.replaceChildren(renderedWidget);
}
async function renderTrafficTotalWidget() {
logger.debug("[DASHBOARD]", "renderTrafficTotalWidget");
const trafficTotalWidget = store.get().trafficTotalWidget;
const container = document.getElementById("dashboard-widget-traffic-total");
if (trafficTotalWidget.loading || trafficTotalWidget.failed) {
const renderedWidget2 = renderWidget({
loading: trafficTotalWidget.loading,
failed: trafficTotalWidget.failed,
title: "",
items: []
});
return container.replaceChildren(renderedWidget2);
}
const renderedWidget = renderWidget({
loading: trafficTotalWidget.loading,
failed: trafficTotalWidget.failed,
title: _("Traffic Total"),
items: [
{
key: _("Uplink"),
value: String(prettyBytes(trafficTotalWidget.data.uploadTotal))
},
{
key: _("Downlink"),
value: String(prettyBytes(trafficTotalWidget.data.downloadTotal))
}
]
});
container.replaceChildren(renderedWidget);
}
async function renderSystemInfoWidget() {
logger.debug("[DASHBOARD]", "renderSystemInfoWidget");
const systemInfoWidget = store.get().systemInfoWidget;
const container = document.getElementById("dashboard-widget-system-info");
if (systemInfoWidget.loading || systemInfoWidget.failed) {
const renderedWidget2 = renderWidget({
loading: systemInfoWidget.loading,
failed: systemInfoWidget.failed,
title: "",
items: []
});
return container.replaceChildren(renderedWidget2);
}
const renderedWidget = renderWidget({
loading: systemInfoWidget.loading,
failed: systemInfoWidget.failed,
title: _("System info"),
items: [
{
key: _("Active Connections"),
value: String(systemInfoWidget.data.connections)
},
{
key: _("Memory Usage"),
value: String(prettyBytes(systemInfoWidget.data.memory))
}
]
});
container.replaceChildren(renderedWidget);
}
async function renderServicesInfoWidget() {
logger.debug("[DASHBOARD]", "renderServicesInfoWidget");
const servicesInfoWidget = store.get().servicesInfoWidget;
const container = document.getElementById("dashboard-widget-service-info");
if (servicesInfoWidget.loading || servicesInfoWidget.failed) {
const renderedWidget2 = renderWidget({
loading: servicesInfoWidget.loading,
failed: servicesInfoWidget.failed,
title: "",
items: []
});
return container.replaceChildren(renderedWidget2);
}
const renderedWidget = renderWidget({
loading: servicesInfoWidget.loading,
failed: servicesInfoWidget.failed,
title: _("Services info"),
items: [
{
key: _("Podkop"),
value: servicesInfoWidget.data.podkop ? _("\u2714 Enabled") : _("\u2718 Disabled"),
attributes: {
class: servicesInfoWidget.data.podkop ? "pdk_dashboard-page__widgets-section__item__row--success" : "pdk_dashboard-page__widgets-section__item__row--error"
}
},
{
key: _("Sing-box"),
value: servicesInfoWidget.data.singbox ? _("\u2714 Running") : _("\u2718 Stopped"),
attributes: {
class: servicesInfoWidget.data.singbox ? "pdk_dashboard-page__widgets-section__item__row--success" : "pdk_dashboard-page__widgets-section__item__row--error"
}
}
]
});
container.replaceChildren(renderedWidget);
}
async function onStoreUpdate(next, prev, diff) {
if (diff.sectionsWidget) {
renderSectionsWidget();
}
if (diff.bandwidthWidget) {
renderBandwidthWidget();
}
if (diff.trafficTotalWidget) {
renderTrafficTotalWidget();
}
if (diff.systemInfoWidget) {
renderSystemInfoWidget();
}
if (diff.servicesInfoWidget) {
renderServicesInfoWidget();
}
}
async function onPageMount() {
onPageUnmount();
store.subscribe(onStoreUpdate);
await fetchDashboardSections();
await fetchServicesInfo();
await connectToClashSockets();
}
function onPageUnmount() {
store.unsubscribe(onStoreUpdate);
store.reset([
"bandwidthWidget",
"trafficTotalWidget",
"systemInfoWidget",
"servicesInfoWidget",
"sectionsWidget"
]);
socket.resetAll();
}
function registerLifecycleListeners() {
store.subscribe((next, prev, diff) => {
if (diff.tabService && next.tabService.current !== prev.tabService.current) {
logger.debug(
"[DASHBOARD]",
"active tab diff event, active tab:",
diff.tabService.current
);
const isDashboardVisible = next.tabService.current === "dashboard";
if (isDashboardVisible) {
logger.debug(
"[DASHBOARD]",
"registerLifecycleListeners",
"onPageMount"
);
return onPageMount();
}
if (!isDashboardVisible) {
logger.debug(
"[DASHBOARD]",
"registerLifecycleListeners",
"onPageUnmount"
);
return onPageUnmount();
}
}
});
}
async function initController() {
onMount("dashboard-status").then(() => {
logger.debug("[DASHBOARD]", "initController", "onMount");
onPageMount();
registerLifecycleListeners();
});
}
// src/podkop/tabs/dashboard/styles.ts
var styles = `
#cbi-podkop-dashboard-_mount_node > div {
width: 100%;
}
#cbi-podkop-dashboard > h3 {
display: none;
}
.pdk_dashboard-page {
width: 100%;
--dashboard-grid-columns: 4;
}
@media (max-width: 900px) {
.pdk_dashboard-page {
--dashboard-grid-columns: 2;
}
}
.pdk_dashboard-page__widgets-section {
margin-top: 10px;
display: grid;
grid-template-columns: repeat(var(--dashboard-grid-columns), 1fr);
grid-gap: 10px;
}
.pdk_dashboard-page__widgets-section__item {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
}
.pdk_dashboard-page__widgets-section__item__title {}
.pdk_dashboard-page__widgets-section__item__row {}
.pdk_dashboard-page__widgets-section__item__row--success .pdk_dashboard-page__widgets-section__item__row__value {
color: var(--success-color-medium, green);
}
.pdk_dashboard-page__widgets-section__item__row--error .pdk_dashboard-page__widgets-section__item__row__value {
color: var(--error-color-medium, red);
}
.pdk_dashboard-page__widgets-section__item__row__key {}
.pdk_dashboard-page__widgets-section__item__row__value {}
.pdk_dashboard-page__outbound-section {
margin-top: 10px;
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
}
.pdk_dashboard-page__outbound-section__title-section {
display: flex;
align-items: center;
justify-content: space-between;
}
.pdk_dashboard-page__outbound-section__title-section__title {
color: var(--text-color-high);
font-weight: 700;
}
.pdk_dashboard-page__outbound-grid {
margin-top: 5px;
display: grid;
grid-template-columns: repeat(var(--dashboard-grid-columns), 1fr);
grid-gap: 10px;
}
.pdk_dashboard-page__outbound-grid__item {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
transition: border 0.2s ease;
}
.pdk_dashboard-page__outbound-grid__item--selectable {
cursor: pointer;
}
.pdk_dashboard-page__outbound-grid__item--selectable:hover {
border-color: var(--primary-color-high, dodgerblue);
}
.pdk_dashboard-page__outbound-grid__item--active {
border-color: var(--success-color-medium, green);
}
.pdk_dashboard-page__outbound-grid__item__footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 10px;
}
.pdk_dashboard-page__outbound-grid__item__type {}
.pdk_dashboard-page__outbound-grid__item__latency--empty {
color: var(--primary-color-low, lightgray);
}
.pdk_dashboard-page__outbound-grid__item__latency--green {
color: var(--success-color-medium, green);
}
.pdk_dashboard-page__outbound-grid__item__latency--yellow {
color: var(--warn-color-medium, orange);
}
.pdk_dashboard-page__outbound-grid__item__latency--red {
color: var(--error-color-medium, red);
}
`;
// src/podkop/tabs/dashboard/index.ts
var DashboardTab = {
render,
initController,
styles
};
// src/podkop/tabs/diagnostic/renderDiagnostic.ts
function render2() {
return E("div", { id: "diagnostic-status", class: "pdk_diagnostic-page" }, [
E("div", { class: "pdk_diagnostic-page__left-bar" }, [
E("div", { id: "pdk_diagnostic-page-run-check" }),
E("div", {
class: "pdk_diagnostic-page__checks",
id: "pdk_diagnostic-page-checks"
})
]),
E("div", { class: "pdk_diagnostic-page__right-bar" }, [
E("div", { id: "pdk_diagnostic-page-wiki" }),
E("div", { id: "pdk_diagnostic-page-actions" }),
E("div", { id: "pdk_diagnostic-page-system-info" })
])
]);
}
// src/podkop/tabs/diagnostic/checks/updateCheckStore.ts
function updateCheckStore(check, minified) {
const diagnosticsChecks = store.get().diagnosticsChecks;
const other = diagnosticsChecks.filter((item) => item.code !== check.code);
const smallCheck = {
...check,
items: check.items.filter((item) => item.state !== "success")
};
const targetCheck = minified ? smallCheck : check;
store.set({
diagnosticsChecks: [...other, targetCheck]
});
}
// src/podkop/tabs/diagnostic/helpers/getMeta.ts
function getMeta({ allGood, atLeastOneGood }) {
if (allGood) {
return {
state: "success",
description: _("Checks passed")
};
}
if (atLeastOneGood) {
return {
state: "warning",
description: _("Issues detected")
};
}
return {
state: "error",
description: _("Checks failed")
};
}
// src/podkop/tabs/diagnostic/checks/runDnsCheck.ts
async function runDnsCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.DNS;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
const dnsChecks = await PodkopShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateCheckStore({
order,
code,
title,
description: _("Cannot receive checks result"),
state: "error",
items: []
});
throw new Error("DNS checks failed");
}
const data = dnsChecks.data;
const allGood = Boolean(data.dns_on_router) && Boolean(data.dhcp_config_status) && Boolean(data.bootstrap_dns_status) && Boolean(data.dns_status);
const atLeastOneGood = Boolean(data.dns_on_router) || Boolean(data.dhcp_config_status) || Boolean(data.bootstrap_dns_status) || Boolean(data.dns_status);
const { state, description } = getMeta({ atLeastOneGood, allGood });
updateCheckStore({
order,
code,
title,
description,
state,
items: [
...insertIf(
data.dns_type === "doh" || data.dns_type === "dot" || !data.bootstrap_dns_status,
[
{
state: data.bootstrap_dns_status ? "success" : "error",
key: _("Bootsrap DNS"),
value: data.bootstrap_dns_server
}
]
),
{
state: data.dns_status ? "success" : "error",
key: _("Main DNS"),
value: `${data.dns_server} [${data.dns_type}]`
},
{
state: data.dns_on_router ? "success" : "error",
key: _("DNS on router"),
value: ""
},
{
state: data.dhcp_config_status ? "success" : "error",
key: _("DHCP has DNS server"),
value: ""
}
]
});
if (!atLeastOneGood) {
throw new Error("DNS checks failed");
}
}
// src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts
async function runSingBoxCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.SINGBOX;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
const singBoxChecks = await PodkopShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateCheckStore({
order,
code,
title,
description: _("Cannot receive checks result"),
state: "error",
items: []
});
throw new Error("Sing-box checks failed");
}
const data = singBoxChecks.data;
const allGood = Boolean(data.sing_box_installed) && Boolean(data.sing_box_version_ok) && Boolean(data.sing_box_service_exist) && Boolean(data.sing_box_autostart_disabled) && Boolean(data.sing_box_process_running) && Boolean(data.sing_box_ports_listening);
const atLeastOneGood = Boolean(data.sing_box_installed) || Boolean(data.sing_box_version_ok) || Boolean(data.sing_box_service_exist) || Boolean(data.sing_box_autostart_disabled) || Boolean(data.sing_box_process_running) || Boolean(data.sing_box_ports_listening);
const { state, description } = getMeta({ atLeastOneGood, allGood });
updateCheckStore({
order,
code,
title,
description,
state,
items: [
{
state: data.sing_box_installed ? "success" : "error",
key: _("Sing-box installed"),
value: ""
},
{
state: data.sing_box_version_ok ? "success" : "error",
key: _("Sing-box version >= 1.12.4"),
value: ""
},
{
state: data.sing_box_service_exist ? "success" : "error",
key: _("Sing-box service exist"),
value: ""
},
{
state: data.sing_box_autostart_disabled ? "success" : "error",
key: _("Sing-box autostart disabled"),
value: ""
},
{
state: data.sing_box_process_running ? "success" : "error",
key: _("Sing-box process running"),
value: ""
},
{
state: data.sing_box_ports_listening ? "success" : "error",
key: _("Sing-box listening ports"),
value: ""
}
]
});
if (!atLeastOneGood || !data.sing_box_process_running) {
throw new Error("Sing-box checks failed");
}
}
// src/podkop/tabs/diagnostic/checks/runNftCheck.ts
async function runNftCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.NFT;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
await RemoteFakeIPMethods.getFakeIpCheck();
await RemoteFakeIPMethods.getIpCheck();
const nftablesChecks = await PodkopShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateCheckStore({
order,
code,
title,
description: _("Cannot receive checks result"),
state: "error",
items: []
});
throw new Error("Nftables checks failed");
}
const data = nftablesChecks.data;
const allGood = Boolean(data.table_exist) && Boolean(data.rules_mangle_exist) && Boolean(data.rules_mangle_counters) && Boolean(data.rules_mangle_output_exist) && Boolean(data.rules_mangle_output_counters) && Boolean(data.rules_proxy_exist) && Boolean(data.rules_proxy_counters) && !data.rules_other_mark_exist;
const atLeastOneGood = Boolean(data.table_exist) || Boolean(data.rules_mangle_exist) || Boolean(data.rules_mangle_counters) || Boolean(data.rules_mangle_output_exist) || Boolean(data.rules_mangle_output_counters) || Boolean(data.rules_proxy_exist) || Boolean(data.rules_proxy_counters) || !data.rules_other_mark_exist;
const { state, description } = getMeta({ atLeastOneGood, allGood });
updateCheckStore({
order,
code,
title,
description,
state,
items: [
{
state: data.table_exist ? "success" : "error",
key: _("Table exist"),
value: ""
},
{
state: data.rules_mangle_exist ? "success" : "error",
key: _("Rules mangle exist"),
value: ""
},
{
state: data.rules_mangle_counters ? "success" : "error",
key: _("Rules mangle counters"),
value: ""
},
{
state: data.rules_mangle_output_exist ? "success" : "error",
key: _("Rules mangle output exist"),
value: ""
},
{
state: data.rules_mangle_output_counters ? "success" : "error",
key: _("Rules mangle output counters"),
value: ""
},
{
state: data.rules_proxy_exist ? "success" : "error",
key: _("Rules proxy exist"),
value: ""
},
{
state: data.rules_proxy_counters ? "success" : "error",
key: _("Rules proxy counters"),
value: ""
},
{
state: !data.rules_other_mark_exist ? "success" : "warning",
key: !data.rules_other_mark_exist ? _("No other marking rules found") : _("Additional marking rules found"),
value: ""
}
]
});
if (!atLeastOneGood) {
throw new Error("Nftables checks failed");
}
}
// src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts
async function runFakeIPCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.FAKEIP;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP();
const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck();
const checkIPResponse = await RemoteFakeIPMethods.getIpCheck();
const checks = {
router: routerFakeIPResponse.success && routerFakeIPResponse.data.fakeip,
browserFakeIP: checkFakeIPResponse.success && checkFakeIPResponse.data.fakeip,
differentIP: checkFakeIPResponse.success && checkIPResponse.success && checkFakeIPResponse.data.IP !== checkIPResponse.data.IP
};
const allGood = checks.router || checks.browserFakeIP || checks.differentIP;
const atLeastOneGood = checks.router && checks.browserFakeIP && checks.differentIP;
const { state, description } = getMeta({ atLeastOneGood, allGood });
updateCheckStore({
order,
code,
title,
description,
state,
items: [
{
state: checks.router ? "success" : "warning",
key: checks.router ? _("Router DNS is routed through sing-box") : _("Router DNS is not routed through sing-box"),
value: ""
},
{
state: checks.browserFakeIP ? "success" : "error",
key: checks.browserFakeIP ? _("Browser is using FakeIP correctly") : _("Browser is not using FakeIP"),
value: ""
},
...insertIf(checks.browserFakeIP, [
{
state: checks.differentIP ? "success" : "error",
key: checks.differentIP ? _("Proxy traffic is routed via FakeIP") : _("Proxy traffic is not routed via FakeIP"),
value: ""
}
])
]
});
}
// src/partials/button/styles.ts
var styles2 = `
.pdk-partial-button {
text-align: center;
}
.pdk-partial-button--with-icon {
display: flex;
align-items: center;
justify-content: center;
}
.pdk-partial-button--loading {
}
.pdk-partial-button--disabled {
}
.pdk-partial-button__icon {
margin-right: 5px;
}
.pdk-partial-button__icon {
display: flex;
align-items: center;
justify-content: center;
}
.pdk-partial-button__icon svg {
width: 16px;
height: 16px;
}
`;
// src/partials/modal/styles.ts
var styles3 = `
.pdk-partial-modal__body {}
.pdk-partial-modal__content {
max-height: 70vh;
overflow: scroll;
border-radius: 4px;
}
.pdk-partial-modal__footer {
display: flex;
justify-content: flex-end;
}
.pdk-partial-modal__footer button {
margin-left: 10px;
}
`;
// src/icons/renderLoaderCircleIcon24.ts
function renderLoaderCircleIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-loader-circle rotate"
},
[
svgEl("path", {
d: "M21 12a9 9 0 1 1-6.219-8.56"
}),
svgEl("animateTransform", {
attributeName: "transform",
attributeType: "XML",
type: "rotate",
from: "0 12 12",
to: "360 12 12",
dur: "1s",
repeatCount: "indefinite"
})
]
);
}
// src/icons/renderCircleAlertIcon24.ts
function renderCircleAlertIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-alert-icon lucide-circle-alert"
},
[
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
}),
svgEl("line", {
x1: "12",
y1: "8",
x2: "12",
y2: "12"
}),
svgEl("line", {
x1: "12",
y1: "16",
x2: "12.01",
y2: "16"
})
]
);
}
// src/icons/renderCircleCheckIcon24.ts
function renderCircleCheckIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-check-icon lucide-circle-check"
},
[
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
}),
svgEl("path", {
d: "M9 12l2 2 4-4"
})
]
);
}
// src/icons/renderCircleSlashIcon24.ts
function renderCircleSlashIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-slash-icon lucide-circle-slash"
},
[
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
}),
svgEl("line", {
x1: "9",
y1: "15",
x2: "15",
y2: "9"
})
]
);
}
// src/icons/renderCircleXIcon24.ts
function renderCircleXIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-x-icon lucide-circle-x"
},
[
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
}),
svgEl("path", {
d: "M15 9L9 15"
}),
svgEl("path", {
d: "M9 9L15 15"
})
]
);
}
// src/icons/renderCheckIcon24.ts
function renderCheckIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-check-icon lucide-check"
},
[
svgEl("path", {
d: "M20 6 9 17l-5-5"
})
]
);
}
// src/icons/renderXIcon24.ts
function renderXIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-x-icon lucide-x"
},
[svgEl("path", { d: "M18 6 6 18" }), svgEl("path", { d: "m6 6 12 12" })]
);
}
// src/icons/renderTriangleAlertIcon24.ts
function renderTriangleAlertIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-triangle-alert-icon lucide-triangle-alert"
},
[
svgEl("path", {
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"
}),
svgEl("path", { d: "M12 9v4" }),
svgEl("path", { d: "M12 17h.01" })
]
);
}
// src/icons/renderPauseIcon24.ts
function renderPauseIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-pause-icon lucide-pause"
},
[
svgEl("rect", {
x: "14",
y: "3",
width: "5",
height: "18",
rx: "1"
}),
svgEl("rect", {
x: "5",
y: "3",
width: "5",
height: "18",
rx: "1"
})
]
);
}
// src/icons/renderPlayIcon24.ts
function renderPlayIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-play-icon lucide-play"
},
[
svgEl("path", {
d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"
})
]
);
}
// src/icons/renderRotateCcwIcon24.ts
function renderRotateCcwIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-rotate-ccw-icon lucide-rotate-ccw"
},
[
svgEl("path", {
d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"
}),
svgEl("path", {
d: "M3 3v5h5"
})
]
);
}
// src/icons/renderCircleStopIcon24.ts
function renderCircleStopIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-stop-icon lucide-circle-stop"
},
[
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
}),
svgEl("rect", {
x: "9",
y: "9",
width: "6",
height: "6",
rx: "1"
})
]
);
}
// src/icons/renderCirclePlayIcon24.ts
function renderCirclePlayIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-play-icon lucide-circle-play"
},
[
svgEl("path", {
d: "M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z"
}),
svgEl("circle", {
cx: "12",
cy: "12",
r: "10"
})
]
);
}
// src/icons/renderCircleCheckBigIcon24.ts
function renderCircleCheckBigIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-circle-check-big-icon lucide-circle-check-big"
},
[
svgEl("path", {
d: "M21.801 10A10 10 0 1 1 17 3.335"
}),
svgEl("path", {
d: "m9 11 3 3L22 4"
})
]
);
}
// src/icons/renderSquareChartGanttIcon24.ts
function renderSquareChartGanttIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-square-chart-gantt-icon lucide-square-chart-gantt"
},
[
svgEl("rect", {
width: "18",
height: "18",
x: "3",
y: "3",
rx: "2"
}),
svgEl("path", { d: "M9 8h7" }),
svgEl("path", { d: "M8 12h6" }),
svgEl("path", { d: "M11 16h5" })
]
);
}
// src/icons/renderCogIcon24.ts
function renderCogIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-cog-icon lucide-cog"
},
[
svgEl("path", { d: "M11 10.27 7 3.34" }),
svgEl("path", { d: "m11 13.73-4 6.93" }),
svgEl("path", { d: "M12 22v-2" }),
svgEl("path", { d: "M12 2v2" }),
svgEl("path", { d: "M14 12h8" }),
svgEl("path", { d: "m17 20.66-1-1.73" }),
svgEl("path", { d: "m17 3.34-1 1.73" }),
svgEl("path", { d: "M2 12h2" }),
svgEl("path", { d: "m20.66 17-1.73-1" }),
svgEl("path", { d: "m20.66 7-1.73 1" }),
svgEl("path", { d: "m3.34 17 1.73-1" }),
svgEl("path", { d: "m3.34 7 1.73 1" }),
svgEl("circle", { cx: "12", cy: "12", r: "2" }),
svgEl("circle", { cx: "12", cy: "12", r: "8" })
]
);
}
// src/icons/renderSearchIcon24.ts
function renderSearchIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-search-icon lucide-search"
},
[
svgEl("path", { d: "m21 21-4.34-4.34" }),
svgEl("circle", { cx: "11", cy: "11", r: "8" })
]
);
}
// src/icons/renderBookOpenTextIcon24.ts
function renderBookOpenTextIcon24() {
const NS = "http://www.w3.org/2000/svg";
return svgEl(
"svg",
{
xmlns: NS,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "lucide lucide-book-open-text-icon lucide-book-open-text"
},
[
svgEl("path", { d: "M12 7v14" }),
svgEl("path", { d: "M16 12h2" }),
svgEl("path", { d: "M16 8h2" }),
svgEl("path", {
d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"
}),
svgEl("path", { d: "M6 12h2" }),
svgEl("path", { d: "M6 8h2" })
]
);
}
// src/partials/button/renderButton.ts
function renderButton({
classNames = [],
disabled,
loading,
onClick,
text,
icon
}) {
const hasIcon = !!loading || !!icon;
function getWrappedIcon() {
const iconWrap = E("span", {
class: "pdk-partial-button__icon"
});
if (loading) {
iconWrap.appendChild(renderLoaderCircleIcon24());
return iconWrap;
}
if (icon) {
iconWrap.appendChild(icon());
return iconWrap;
}
return iconWrap;
}
function getClass() {
return [
"btn",
"pdk-partial-button",
...insertIf(Boolean(disabled), ["pdk-partial-button--disabled"]),
...insertIf(Boolean(loading), ["pdk-partial-button--loading"]),
...insertIf(Boolean(hasIcon), ["pdk-partial-button--with-icon"]),
...classNames
].filter(Boolean).join(" ");
}
function getDisabled() {
if (loading || disabled) {
return true;
}
return void 0;
}
return E(
"button",
{ class: getClass(), disabled: getDisabled(), click: onClick },
[...insertIf(hasIcon, [getWrappedIcon()]), E("span", {}, text)]
);
}
// src/helpers/showToast.ts
function showToast(message, type, duration = 3e3) {
let container = document.querySelector(".toast-container");
if (!container) {
container = document.createElement("div");
container.className = "toast-container";
document.body.appendChild(container);
}
const toast = document.createElement("div");
toast.className = `toast toast-${type}`;
toast.textContent = message;
container.appendChild(toast);
setTimeout(() => toast.classList.add("visible"), 100);
setTimeout(() => {
toast.classList.remove("visible");
setTimeout(() => toast.remove(), 300);
}, duration);
}
// src/helpers/copyToClipboard.ts
function copyToClipboard(text) {
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy");
showToast(_("Successfully copied!"), "success");
} catch (_err) {
showToast(_("Failed to copy!"), "error");
console.error("copyToClipboard - e", _err);
}
document.body.removeChild(textarea);
}
// src/partials/modal/renderModal.ts
function renderModal(text, name) {
return E(
"div",
{ class: "pdk-partial-modal__body" },
E("div", {}, [
E("pre", { class: "pdk-partial-modal__content" }, E("code", {}, text)),
E("div", { class: "pdk-partial-modal__footer" }, [
renderButton({
classNames: ["cbi-button-apply"],
text: _("Download"),
onClick: () => downloadAsTxt(text, name)
}),
renderButton({
classNames: ["cbi-button-apply"],
text: _("Copy"),
onClick: () => copyToClipboard(` \`\`\`${name}
${text}
\`\`\``)
}),
renderButton({
classNames: ["cbi-button-remove"],
text: _("Close"),
onClick: ui.hideModal
})
])
])
);
}
// src/partials/index.ts
var PartialStyles = `
${styles2}
${styles3}
`;
// src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
function renderAvailableActions({
restart,
start,
stop,
enable,
disable,
globalCheck,
viewLogs,
showSingBoxConfig
}) {
return E("div", { class: "pdk_diagnostic-page__right-bar__actions" }, [
E("b", {}, _("Available actions")),
...insertIf(restart.visible, [
renderButton({
classNames: ["cbi-button-apply"],
onClick: restart.onClick,
icon: renderRotateCcwIcon24,
text: _("Restart podkop"),
loading: restart.loading,
disabled: restart.disabled
})
]),
...insertIf(stop.visible, [
renderButton({
classNames: ["cbi-button-remove"],
onClick: stop.onClick,
icon: renderCircleStopIcon24,
text: _("Stop podkop"),
loading: stop.loading,
disabled: stop.disabled
})
]),
...insertIf(start.visible, [
renderButton({
classNames: ["cbi-button-save"],
onClick: start.onClick,
icon: renderCirclePlayIcon24,
text: _("Start podkop"),
loading: start.loading,
disabled: start.disabled
})
]),
...insertIf(disable.visible, [
renderButton({
classNames: ["cbi-button-remove"],
onClick: disable.onClick,
icon: renderPauseIcon24,
text: _("Disable autostart"),
loading: disable.loading,
disabled: disable.disabled
})
]),
...insertIf(enable.visible, [
renderButton({
classNames: ["cbi-button-save"],
onClick: enable.onClick,
icon: renderPlayIcon24,
text: _("Enable autostart"),
loading: enable.loading,
disabled: enable.disabled
})
]),
...insertIf(globalCheck.visible, [
renderButton({
onClick: globalCheck.onClick,
icon: renderCircleCheckBigIcon24,
text: _("Get global check"),
loading: globalCheck.loading,
disabled: globalCheck.disabled
})
]),
...insertIf(viewLogs.visible, [
renderButton({
onClick: viewLogs.onClick,
icon: renderSquareChartGanttIcon24,
text: _("View logs"),
loading: viewLogs.loading,
disabled: viewLogs.disabled
})
]),
...insertIf(showSingBoxConfig.visible, [
renderButton({
onClick: showSingBoxConfig.onClick,
icon: renderCogIcon24,
text: _("Show sing-box config"),
loading: showSingBoxConfig.loading,
disabled: showSingBoxConfig.disabled
})
])
]);
}
// src/podkop/tabs/diagnostic/partials/renderCheckSection.ts
function renderCheckSummary(items) {
if (!items.length) {
return E("div", {}, "");
}
const renderedItems = items.map((item) => {
function getIcon() {
const iconWrap = E("span", {
class: "pdk_diagnostic_alert__summary__item__icon"
});
if (item.state === "success") {
iconWrap.appendChild(renderCheckIcon24());
}
if (item.state === "warning") {
iconWrap.appendChild(renderTriangleAlertIcon24());
}
if (item.state === "error") {
iconWrap.appendChild(renderXIcon24());
}
return iconWrap;
}
return E(
"div",
{
class: `pdk_diagnostic_alert__summary__item pdk_diagnostic_alert__summary__item--${item.state}`
},
[getIcon(), E("b", {}, item.key), E("div", {}, item.value)]
);
});
return E("div", { class: "pdk_diagnostic_alert__summary" }, renderedItems);
}
function renderLoadingState3(props) {
const iconWrap = E("span", { class: "pdk_diagnostic_alert__icon" });
iconWrap.appendChild(renderLoaderCircleIcon24());
return E(
"div",
{ class: "pdk_diagnostic_alert pdk_diagnostic_alert--loading" },
[
iconWrap,
E("div", { class: "pdk_diagnostic_alert__content" }, [
E("b", { class: "pdk_diagnostic_alert__title" }, props.title),
E(
"div",
{ class: "pdk_diagnostic_alert__description" },
props.description
)
]),
E("div", {}, ""),
renderCheckSummary(props.items)
]
);
}
function renderWarningState(props) {
const iconWrap = E("span", { class: "pdk_diagnostic_alert__icon" });
iconWrap.appendChild(renderCircleAlertIcon24());
return E(
"div",
{ class: "pdk_diagnostic_alert pdk_diagnostic_alert--warning" },
[
iconWrap,
E("div", { class: "pdk_diagnostic_alert__content" }, [
E("b", { class: "pdk_diagnostic_alert__title" }, props.title),
E(
"div",
{ class: "pdk_diagnostic_alert__description" },
props.description
)
]),
E("div", {}, ""),
renderCheckSummary(props.items)
]
);
}
function renderErrorState(props) {
const iconWrap = E("span", { class: "pdk_diagnostic_alert__icon" });
iconWrap.appendChild(renderCircleXIcon24());
return E(
"div",
{ class: "pdk_diagnostic_alert pdk_diagnostic_alert--error" },
[
iconWrap,
E("div", { class: "pdk_diagnostic_alert__content" }, [
E("b", { class: "pdk_diagnostic_alert__title" }, props.title),
E(
"div",
{ class: "pdk_diagnostic_alert__description" },
props.description
)
]),
E("div", {}, ""),
renderCheckSummary(props.items)
]
);
}
function renderSuccessState(props) {
const iconWrap = E("span", { class: "pdk_diagnostic_alert__icon" });
iconWrap.appendChild(renderCircleCheckIcon24());
return E(
"div",
{ class: "pdk_diagnostic_alert pdk_diagnostic_alert--success" },
[
iconWrap,
E("div", { class: "pdk_diagnostic_alert__content" }, [
E("b", { class: "pdk_diagnostic_alert__title" }, props.title),
E(
"div",
{ class: "pdk_diagnostic_alert__description" },
props.description
)
]),
E("div", {}, ""),
renderCheckSummary(props.items)
]
);
}
function renderSkippedState(props) {
const iconWrap = E("span", { class: "pdk_diagnostic_alert__icon" });
iconWrap.appendChild(renderCircleSlashIcon24());
return E(
"div",
{ class: "pdk_diagnostic_alert pdk_diagnostic_alert--skipped" },
[
iconWrap,
E("div", { class: "pdk_diagnostic_alert__content" }, [
E("b", { class: "pdk_diagnostic_alert__title" }, props.title),
E(
"div",
{ class: "pdk_diagnostic_alert__description" },
props.description
)
]),
E("div", {}, ""),
renderCheckSummary(props.items)
]
);
}
function renderCheckSection(props) {
if (props.state === "loading") {
return renderLoadingState3(props);
}
if (props.state === "warning") {
return renderWarningState(props);
}
if (props.state === "error") {
return renderErrorState(props);
}
if (props.state === "success") {
return renderSuccessState(props);
}
if (props.state === "skipped") {
return renderSkippedState(props);
}
return E("div", {}, _("Not implement yet"));
}
// src/podkop/tabs/diagnostic/partials/renderRunAction.ts
function renderRunAction({
loading,
click
}) {
return E("div", { class: "pdk_diagnostic-page__run_check_wrapper" }, [
renderButton({
text: _("Run Diagnostic"),
onClick: click,
icon: renderSearchIcon24,
loading,
classNames: ["cbi-button-apply"]
})
]);
}
// src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts
function renderSystemInfo({ items }) {
return E("div", { class: "pdk_diagnostic-page__right-bar__system-info" }, [
E(
"b",
{ class: "pdk_diagnostic-page__right-bar__system-info__title" },
_("System information")
),
...items.map((item) => {
const tagClass = [
"pdk_diagnostic-page__right-bar__system-info__row__tag",
...insertIf(item.tag?.kind === "warning", [
"pdk_diagnostic-page__right-bar__system-info__row__tag--warning"
]),
...insertIf(item.tag?.kind === "success", [
"pdk_diagnostic-page__right-bar__system-info__row__tag--success"
])
].filter(Boolean).join(" ");
return E(
"div",
{ class: "pdk_diagnostic-page__right-bar__system-info__row" },
[
E("b", {}, item.key),
E("div", {}, [
E("span", {}, item.value),
E("span", { class: tagClass }, item?.tag?.label)
])
]
);
})
]);
}
// src/helpers/normalizeCompiledVersion.ts
function normalizeCompiledVersion(version) {
if (version.includes("COMPILED")) {
return "dev";
}
return version;
}
// src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts
function renderWikiDisclaimer(kind) {
const iconWrap = E("span", {
class: "pdk_diagnostic-page__right-bar__wiki__icon"
});
iconWrap.appendChild(renderBookOpenTextIcon24());
const className = [
"pdk_diagnostic-page__right-bar__wiki",
...insertIf(kind === "error", [
"pdk_diagnostic-page__right-bar__wiki--error"
]),
...insertIf(kind === "warning", [
"pdk_diagnostic-page__right-bar__wiki--warning"
])
].join(" ");
return E("div", { class: className }, [
E("div", { class: "pdk_diagnostic-page__right-bar__wiki__content" }, [
iconWrap,
E("div", { class: "pdk_diagnostic-page__right-bar__wiki__texts" }, [
E("b", {}, _("Troubleshooting")),
E("div", {}, _("Do not panic, everything can be fixed, just..."))
])
]),
renderButton({
classNames: ["cbi-button-save"],
text: _("Visit Wiki"),
onClick: () => window.open(
"https://podkop.net/docs/troubleshooting/?utm_source=podkop",
"_blank",
"noopener,noreferrer"
)
})
]);
}
// src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts
async function runSectionsCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
const sections = await getDashboardSections();
if (!sections.success) {
updateCheckStore({
order,
code,
title,
description: _("Cannot receive checks result"),
state: "error",
items: []
});
throw new Error("Sections checks failed");
}
const items = await Promise.all(
sections.data.map(async (section) => {
async function getLatency() {
if (section.withTagSelect) {
const latencyGroup = await PodkopShellMethods.getClashApiGroupLatency(
section.code
);
const success3 = latencyGroup.success && !latencyGroup.data.message;
if (success3) {
const latency2 = Object.values(latencyGroup.data).map((item) => item ? `${item}ms` : "n/a").join(" / ");
return {
success: true,
latency: latency2
};
}
return {
success: true,
latency: _("Not responding")
};
}
const latencyProxy = await PodkopShellMethods.getClashApiProxyLatency(
section.code
);
const success2 = latencyProxy.success && !latencyProxy.data.message;
if (success2) {
return {
success: true,
latency: `${latencyProxy.data.delay} ms`
};
}
return {
success: false,
latency: _("Not responding")
};
}
const { latency, success } = await getLatency();
return {
state: success ? "success" : "error",
key: section.displayName,
value: latency
};
})
);
const allGood = items.every((item) => item.state === "success");
const atLeastOneGood = items.some((item) => item.state === "success");
const { state, description } = getMeta({ atLeastOneGood, allGood });
updateCheckStore({
order,
code,
title,
description,
state,
items
});
if (!atLeastOneGood) {
throw new Error("Sections checks failed");
}
}
// src/podkop/tabs/diagnostic/initController.ts
async function fetchSystemInfo() {
const systemInfo = await PodkopShellMethods.getSystemInfo();
if (systemInfo.success) {
store.set({
diagnosticsSystemInfo: {
loading: false,
...systemInfo.data
}
});
} else {
store.set({
diagnosticsSystemInfo: {
loading: false,
podkop_version: _("unknown"),
podkop_latest_version: _("unknown"),
luci_app_version: _("unknown"),
sing_box_version: _("unknown"),
openwrt_version: _("unknown"),
device_model: _("unknown")
}
});
}
}
function renderDiagnosticsChecks() {
logger.debug("[DIAGNOSTIC]", "renderDiagnosticsChecks");
const diagnosticsChecks = store.get().diagnosticsChecks.sort((a, b) => a.order - b.order);
const container = document.getElementById("pdk_diagnostic-page-checks");
const renderedDiagnosticsChecks = diagnosticsChecks.map(
(check) => renderCheckSection(check)
);
return preserveScrollForPage(() => {
container.replaceChildren(...renderedDiagnosticsChecks);
});
}
function renderDiagnosticRunActionWidget() {
logger.debug("[DIAGNOSTIC]", "renderDiagnosticRunActionWidget");
const { loading } = store.get().diagnosticsRunAction;
const container = document.getElementById("pdk_diagnostic-page-run-check");
const renderedAction = renderRunAction({
loading,
click: () => runChecks()
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedAction);
});
}
async function handleRestart() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
restart: { loading: true }
}
});
try {
await PodkopShellMethods.restart();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleRestart - e", e);
} finally {
setTimeout(async () => {
await fetchServicesInfo();
store.set({
diagnosticsActions: {
...diagnosticsActions,
restart: { loading: false }
}
});
store.reset(["diagnosticsChecks"]);
}, 5e3);
}
}
async function handleStop() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
stop: { loading: true }
}
});
try {
await PodkopShellMethods.stop();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleStop - e", e);
} finally {
await fetchServicesInfo();
store.set({
diagnosticsActions: {
...diagnosticsActions,
stop: { loading: false }
}
});
store.reset(["diagnosticsChecks"]);
}
}
async function handleStart() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
start: { loading: true }
}
});
try {
await PodkopShellMethods.start();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleStart - e", e);
} finally {
setTimeout(async () => {
await fetchServicesInfo();
store.set({
diagnosticsActions: {
...diagnosticsActions,
start: { loading: false }
}
});
store.reset(["diagnosticsChecks"]);
}, 5e3);
}
}
async function handleEnable() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
enable: { loading: true }
}
});
try {
await PodkopShellMethods.enable();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleEnable - e", e);
} finally {
await fetchServicesInfo();
store.set({
diagnosticsActions: {
...diagnosticsActions,
enable: { loading: false }
}
});
}
}
async function handleDisable() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
disable: { loading: true }
}
});
try {
await PodkopShellMethods.disable();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleDisable - e", e);
} finally {
await fetchServicesInfo();
store.set({
diagnosticsActions: {
...diagnosticsActions,
disable: { loading: false }
}
});
}
}
async function handleShowGlobalCheck() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
globalCheck: { loading: true }
}
});
try {
const globalCheck = await PodkopShellMethods.globalCheck();
if (globalCheck.success) {
ui.showModal(
_("Global check"),
renderModal(globalCheck.data, "global_check")
);
} else {
logger.error("[DIAGNOSTIC]", "handleShowGlobalCheck - e", globalCheck);
showToast(_("Failed to execute!"), "error");
}
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleShowGlobalCheck - e", e);
showToast(_("Failed to execute!"), "error");
} finally {
store.set({
diagnosticsActions: {
...diagnosticsActions,
globalCheck: { loading: false }
}
});
}
}
async function handleViewLogs() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
viewLogs: { loading: true }
}
});
try {
const viewLogs = await PodkopShellMethods.checkLogs();
if (viewLogs.success) {
ui.showModal(
_("View logs"),
renderModal(viewLogs.data, "view_logs")
);
} else {
logger.error("[DIAGNOSTIC]", "handleViewLogs - e", viewLogs);
showToast(_("Failed to execute!"), "error");
}
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleViewLogs - e", e);
showToast(_("Failed to execute!"), "error");
} finally {
store.set({
diagnosticsActions: {
...diagnosticsActions,
viewLogs: { loading: false }
}
});
}
}
async function handleShowSingBoxConfig() {
const diagnosticsActions = store.get().diagnosticsActions;
store.set({
diagnosticsActions: {
...diagnosticsActions,
showSingBoxConfig: { loading: true }
}
});
try {
const showSingBoxConfig = await PodkopShellMethods.showSingBoxConfig();
if (showSingBoxConfig.success) {
ui.showModal(
_("Show sing-box config"),
renderModal(showSingBoxConfig.data, "show_sing_box_config")
);
} else {
logger.error(
"[DIAGNOSTIC]",
"handleShowSingBoxConfig - e",
showSingBoxConfig
);
showToast(_("Failed to execute!"), "error");
}
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleShowSingBoxConfig - e", e);
showToast(_("Failed to execute!"), "error");
} finally {
store.set({
diagnosticsActions: {
...diagnosticsActions,
showSingBoxConfig: { loading: false }
}
});
}
}
function renderWikiDisclaimerWidget() {
const diagnosticsChecks = store.get().diagnosticsChecks;
function getWikiKind() {
const allResults = diagnosticsChecks.map((check) => check.state);
if (allResults.includes("error")) {
return "error";
}
if (allResults.includes("warning")) {
return "warning";
}
return "default";
}
const container = document.getElementById("pdk_diagnostic-page-wiki");
return preserveScrollForPage(() => {
container.replaceChildren(renderWikiDisclaimer(getWikiKind()));
});
}
function renderDiagnosticAvailableActionsWidget() {
const diagnosticsActions = store.get().diagnosticsActions;
const servicesInfoWidget = store.get().servicesInfoWidget;
logger.debug("[DIAGNOSTIC]", "renderDiagnosticAvailableActionsWidget");
const podkopEnabled = Boolean(servicesInfoWidget.data.podkop);
const singBoxRunning = Boolean(servicesInfoWidget.data.singbox);
const atLeastOneServiceCommandLoading = servicesInfoWidget.loading || diagnosticsActions.restart.loading || diagnosticsActions.start.loading || diagnosticsActions.stop.loading;
const container = document.getElementById("pdk_diagnostic-page-actions");
const renderedActions = renderAvailableActions({
restart: {
loading: diagnosticsActions.restart.loading,
visible: true,
onClick: handleRestart,
disabled: atLeastOneServiceCommandLoading
},
start: {
loading: diagnosticsActions.start.loading,
visible: !singBoxRunning,
onClick: handleStart,
disabled: atLeastOneServiceCommandLoading
},
stop: {
loading: diagnosticsActions.stop.loading,
visible: singBoxRunning,
onClick: handleStop,
disabled: atLeastOneServiceCommandLoading
},
enable: {
loading: diagnosticsActions.enable.loading,
visible: !podkopEnabled,
onClick: handleEnable,
disabled: atLeastOneServiceCommandLoading
},
disable: {
loading: diagnosticsActions.disable.loading,
visible: podkopEnabled,
onClick: handleDisable,
disabled: atLeastOneServiceCommandLoading
},
globalCheck: {
loading: diagnosticsActions.globalCheck.loading,
visible: true,
onClick: handleShowGlobalCheck,
disabled: atLeastOneServiceCommandLoading
},
viewLogs: {
loading: diagnosticsActions.viewLogs.loading,
visible: true,
onClick: handleViewLogs,
disabled: atLeastOneServiceCommandLoading
},
showSingBoxConfig: {
loading: diagnosticsActions.showSingBoxConfig.loading,
visible: true,
onClick: handleShowSingBoxConfig,
disabled: atLeastOneServiceCommandLoading
}
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedActions);
});
}
function renderDiagnosticSystemInfoWidget() {
logger.debug("[DIAGNOSTIC]", "renderDiagnosticSystemInfoWidget");
const diagnosticsSystemInfo = store.get().diagnosticsSystemInfo;
const container = document.getElementById("pdk_diagnostic-page-system-info");
function getPodkopVersionRow() {
const loading = diagnosticsSystemInfo.loading;
const unknown = diagnosticsSystemInfo.podkop_version === _("unknown");
const hasActualVersion = Boolean(
diagnosticsSystemInfo.podkop_latest_version
);
const version = normalizeCompiledVersion(
diagnosticsSystemInfo.podkop_version
);
const isDevVersion = version === "dev";
if (loading || unknown || !hasActualVersion || isDevVersion) {
return {
key: "Podkop",
value: version
};
}
if (version !== `v${diagnosticsSystemInfo.podkop_latest_version}`) {
logger.debug(
"[DIAGNOSTIC]",
"diagnosticsSystemInfo",
diagnosticsSystemInfo
);
return {
key: "Podkop",
value: version,
tag: {
label: _("Outdated"),
kind: "warning"
}
};
}
return {
key: "Podkop",
value: version,
tag: {
label: _("Latest"),
kind: "success"
}
};
}
const renderedSystemInfo = renderSystemInfo({
items: [
getPodkopVersionRow(),
{
key: "Luci App",
value: normalizeCompiledVersion(PODKOP_LUCI_APP_VERSION)
},
{
key: "Sing-box",
value: diagnosticsSystemInfo.sing_box_version
},
{
key: "OS",
value: diagnosticsSystemInfo.openwrt_version
},
{
key: "Device",
value: diagnosticsSystemInfo.device_model
}
]
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedSystemInfo);
});
}
async function onStoreUpdate2(next, prev, diff) {
if (diff.diagnosticsChecks) {
renderDiagnosticsChecks();
renderWikiDisclaimerWidget();
}
if (diff.diagnosticsRunAction) {
renderDiagnosticRunActionWidget();
}
if (diff.diagnosticsActions || diff.servicesInfoWidget) {
renderDiagnosticAvailableActionsWidget();
}
if (diff.diagnosticsSystemInfo) {
renderDiagnosticSystemInfoWidget();
}
}
async function runChecks() {
try {
store.set({
diagnosticsRunAction: { loading: true },
diagnosticsChecks: loadingDiagnosticsChecksStore.diagnosticsChecks
});
await runDnsCheck();
await runSingBoxCheck();
await runNftCheck();
await runSectionsCheck();
await runFakeIPCheck();
} catch (e) {
logger.error("[DIAGNOSTIC]", "runChecks - e", e);
} finally {
store.set({ diagnosticsRunAction: { loading: false } });
}
}
function onPageMount2() {
onPageUnmount2();
store.subscribe(onStoreUpdate2);
renderDiagnosticsChecks();
renderDiagnosticRunActionWidget();
renderDiagnosticAvailableActionsWidget();
renderDiagnosticSystemInfoWidget();
renderWikiDisclaimerWidget();
fetchServicesInfo();
fetchSystemInfo();
}
function onPageUnmount2() {
store.unsubscribe(onStoreUpdate2);
store.reset([
"diagnosticsActions",
"diagnosticsSystemInfo",
"diagnosticsChecks",
"diagnosticsRunAction"
]);
}
function registerLifecycleListeners2() {
store.subscribe((next, prev, diff) => {
if (diff.tabService && next.tabService.current !== prev.tabService.current) {
logger.debug(
"[DIAGNOSTIC]",
"active tab diff event, active tab:",
diff.tabService.current
);
const isDIAGNOSTICVisible = next.tabService.current === "diagnostic";
if (isDIAGNOSTICVisible) {
logger.debug(
"[DIAGNOSTIC]",
"registerLifecycleListeners",
"onPageMount"
);
return onPageMount2();
}
if (!isDIAGNOSTICVisible) {
logger.debug(
"[DIAGNOSTIC]",
"registerLifecycleListeners",
"onPageUnmount"
);
return onPageUnmount2();
}
}
});
}
async function initController2() {
onMount("diagnostic-status").then(() => {
logger.debug("[DIAGNOSTIC]", "initController", "onMount");
onPageMount2();
registerLifecycleListeners2();
});
}
// src/podkop/tabs/diagnostic/styles.ts
var styles4 = `
#cbi-podkop-diagnostic-_mount_node > div {
width: 100%;
}
#cbi-podkop-diagnostic > h3 {
display: none;
}
.pdk_diagnostic-page {
display: grid;
grid-template-columns: 2fr 1fr;
grid-column-gap: 10px;
align-items: start;
}
@media (max-width: 800px) {
.pdk_diagnostic-page {
grid-template-columns: 1fr;
}
}
.pdk_diagnostic-page__right-bar {
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 10px;
}
.pdk_diagnostic-page__right-bar__wiki {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
display: grid;
grid-template-columns: auto;
grid-row-gap: 10px;
}
.pdk_diagnostic-page__right-bar__wiki--warning {
border: 2px var(--warn-color-medium, orange) solid;
}
.pdk_diagnostic-page__right-bar__wiki--error {
border: 2px var(--error-color-medium, red) solid;
}
.pdk_diagnostic-page__right-bar__wiki__content {
display: grid;
grid-template-columns: 1fr 5fr;
grid-column-gap: 10px;
}
.pdk_diagnostic-page__right-bar__wiki__texts {}
.pdk_diagnostic-page__right-bar__actions {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
display: grid;
grid-template-columns: auto;
grid-row-gap: 10px;
}
.pdk_diagnostic-page__right-bar__system-info {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
display: grid;
grid-template-columns: auto;
grid-row-gap: 10px;
}
.pdk_diagnostic-page__right-bar__system-info__title {
}
.pdk_diagnostic-page__right-bar__system-info__row {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 5px;
}
.pdk_diagnostic-page__right-bar__system-info__row__tag {
padding: 2px 4px;
border: 1px transparent solid;
border-radius: 4px;
margin-left: 5px;
}
.pdk_diagnostic-page__right-bar__system-info__row__tag--warning {
border: 1px var(--warn-color-medium, orange) solid;
color: var(--warn-color-medium, orange);
}
.pdk_diagnostic-page__right-bar__system-info__row__tag--success {
border: 1px var(--success-color-medium, green) solid;
color: var(--success-color-medium, green);
}
.pdk_diagnostic-page__left-bar {
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 10px;
}
.pdk_diagnostic-page__run_check_wrapper {}
.pdk_diagnostic-page__run_check_wrapper button {
width: 100%;
}
.pdk_diagnostic-page__checks {
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 10px;
}
.pdk_diagnostic_alert {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
display: grid;
grid-template-columns: 24px 1fr;
grid-column-gap: 10px;
align-items: center;
padding: 10px;
}
.pdk_diagnostic_alert--loading {
border: 2px var(--primary-color-high, dodgerblue) solid;
}
.pdk_diagnostic_alert--warning {
border: 2px var(--warn-color-medium, orange) solid;
color: var(--warn-color-medium, orange);
}
.pdk_diagnostic_alert--error {
border: 2px var(--error-color-medium, red) solid;
color: var(--error-color-medium, red);
}
.pdk_diagnostic_alert--success {
border: 2px var(--success-color-medium, green) solid;
color: var(--success-color-medium, green);
}
.pdk_diagnostic_alert--skipped {}
.pdk_diagnostic_alert__icon {}
.pdk_diagnostic_alert__content {}
.pdk_diagnostic_alert__title {
display: block;
}
.pdk_diagnostic_alert__description {}
.pdk_diagnostic_alert__summary {
margin-top: 10px;
}
.pdk_diagnostic_alert__summary__item {
display: grid;
grid-template-columns: 16px auto 1fr;
grid-column-gap: 10px;
}
.pdk_diagnostic_alert__summary__item--error {
color: var(--error-color-medium, red);
}
.pdk_diagnostic_alert__summary__item--warning {
color: var(--warn-color-medium, orange);
}
.pdk_diagnostic_alert__summary__item--success {
color: var(--success-color-medium, green);
}
.pdk_diagnostic_alert__summary__item__icon {
width: 16px;
height: 16px;
}
`;
// src/podkop/tabs/diagnostic/index.ts
var DiagnosticTab = {
render: render2,
initController: initController2,
styles: styles4
};
// src/styles.ts
var GlobalStyles = `
${DashboardTab.styles}
${DiagnosticTab.styles}
${PartialStyles}
/* Hide extra H3 for settings tab */
#cbi-podkop-settings > h3 {
display: none;
}
/* Hide extra H3 for sections tab */
#cbi-podkop-section > h3:nth-child(1) {
display: none;
}
/* Vertical align for remove section action button */
#cbi-podkop-section > .cbi-section-remove {
margin-bottom: -32px;
}
/* Centered class helper */
.centered {
display: flex;
align-items: center;
justify-content: center;
}
/* Rotate class helper */
.rotate {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Skeleton styles*/
.skeleton {
background-color: var(--background-color-low, #e0e0e0);
border-radius: 4px;
position: relative;
overflow: hidden;
}
.skeleton::after {
content: '';
position: absolute;
top: 0;
left: -150%;
width: 150%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.4),
transparent
);
animation: skeleton-shimmer 1.6s infinite;
}
@keyframes skeleton-shimmer {
100% {
left: 150%;
}
}
/* Toast */
.toast-container {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
z-index: 9999;
font-family: system-ui, sans-serif;
}
.toast {
opacity: 0;
transform: translateY(10px);
transition: opacity 0.3s ease, transform 0.3s ease;
padding: 10px 16px;
border-radius: 6px;
color: #fff;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
min-width: 220px;
max-width: 340px;
text-align: center;
}
.toast-success {
background-color: #28a745;
}
.toast-error {
background-color: #dc3545;
}
.toast.visible {
opacity: 1;
transform: translateY(0);
}
`;
// src/helpers/injectGlobalStyles.ts
function injectGlobalStyles() {
document.head.insertAdjacentHTML(
"beforeend",
`
<style>
${GlobalStyles}
</style>
`
);
}
// src/helpers/withTimeout.ts
async function withTimeout(promise, timeoutMs, operationName, timeoutMessage = _("Operation timed out")) {
let timeoutId;
const start = performance.now();
const timeoutPromise = new Promise((_2, reject) => {
timeoutId = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
clearTimeout(timeoutId);
const elapsed = performance.now() - start;
logger.info("[SHELL]", `[${operationName}] took ${elapsed.toFixed(2)} ms`);
}
}
// src/helpers/executeShellCommand.ts
async function executeShellCommand({
command,
args,
timeout = COMMAND_TIMEOUT
}) {
try {
return withTimeout(
fs.exec(command, args),
timeout,
[command, ...args].join(" ")
);
} catch (err) {
const error = err;
return { stdout: "", stderr: error?.message, code: 0 };
}
}
// src/helpers/maskIP.ts
function maskIP(ip = "") {
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
return ip.replace(ipv4Regex, (_match, _p1, _p2, _p3, p4) => `XX.XX.XX.${p4}`);
}
// src/helpers/getProxyUrlName.ts
function getProxyUrlName(url) {
try {
const [_link, hash] = url.split("#");
if (!hash) {
return "";
}
return decodeURIComponent(hash);
} catch {
return "";
}
}
// src/helpers/onMount.ts
async function onMount(id) {
return new Promise((resolve) => {
const el = document.getElementById(id);
if (el && el.offsetParent !== null) {
return resolve(el);
}
const observer = new MutationObserver(() => {
const target = document.getElementById(id);
if (target) {
const io = new IntersectionObserver((entries) => {
const visible = entries.some((e) => e.isIntersecting);
if (visible) {
observer.disconnect();
io.disconnect();
resolve(target);
}
});
io.observe(target);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
// src/helpers/getClashApiUrl.ts
function getClashWsUrl() {
const { hostname } = window.location;
return `ws://${hostname}:9090`;
}
function getClashUIUrl() {
const { hostname } = window.location;
return `http://${hostname}:9090/ui`;
}
// src/helpers/splitProxyString.ts
function splitProxyString(str) {
return str.split("\n").map((line) => line.trim()).filter((line) => !line.startsWith("//")).filter(Boolean);
}
// src/helpers/preserveScrollForPage.ts
function preserveScrollForPage(renderFn) {
const scrollY = window.scrollY;
renderFn();
requestAnimationFrame(() => {
window.scrollTo({ top: scrollY });
});
}
// src/helpers/svgEl.ts
function svgEl(tag, attrs = {}, children = []) {
const NS = "http://www.w3.org/2000/svg";
const el = document.createElementNS(NS, tag);
for (const [k, v] of Object.entries(attrs)) {
if (v != null) el.setAttribute(k, String(v));
}
(Array.isArray(children) ? children : [children]).filter(Boolean).forEach((ch) => el.appendChild(ch));
return el;
}
// src/helpers/insertIf.ts
function insertIf(condition, elements) {
return condition ? elements : [];
}
function insertIfObj(condition, object) {
return condition ? object : {};
}
return baseclass.extend({
ALLOWED_WITH_RUSSIA_INSIDE,
BOOTSTRAP_DNS_SERVER_OPTIONS,
BUTTON_FEEDBACK_TIMEOUT,
CACHE_TIMEOUT,
COMMAND_SCHEDULING,
COMMAND_TIMEOUT,
CustomPodkopMethods,
DIAGNOSTICS_INITIAL_DELAY,
DIAGNOSTICS_UPDATE_INTERVAL,
DNS_SERVER_OPTIONS,
DOMAIN_LIST_OPTIONS,
DashboardTab,
DiagnosticTab,
ERROR_POLL_INTERVAL,
FAKEIP_CHECK_DOMAIN,
FETCH_TIMEOUT,
IP_CHECK_DOMAIN,
Logger,
PODKOP_LUCI_APP_VERSION,
PodkopShellMethods,
REGIONAL_OPTIONS,
RemoteFakeIPMethods,
STATUS_COLORS,
TabService,
TabServiceInstance,
UPDATE_INTERVAL_OPTIONS,
bulkValidate,
coreService,
executeShellCommand,
getClashUIUrl,
getClashWsUrl,
getProxyUrlName,
injectGlobalStyles,
insertIf,
insertIfObj,
logger,
maskIP,
onMount,
parseQueryString,
parseValueList,
preserveScrollForPage,
socket,
splitProxyString,
store,
svgEl,
validateDNS,
validateDomain,
validateIPV4,
validateOutboundJson,
validatePath,
validateProxyUrl,
validateShadowsocksUrl,
validateSocksUrl,
validateSubnet,
validateTrojanUrl,
validateUrl,
validateVlessUrl,
withTimeout
});