From f6e347af78805a60162dcdaf958e9c6a9b073d9b Mon Sep 17 00:00:00 2001 From: divocat Date: Sat, 18 Oct 2025 22:15:43 +0300 Subject: [PATCH] feat: add logger --- fe-app-podkop/src/helpers/withTimeout.ts | 4 +- .../src/podkop/services/core.service.ts | 2 + fe-app-podkop/src/podkop/services/index.ts | 1 + .../src/podkop/services/logger.service.ts | 66 ++++++ .../src/podkop/services/socket.service.ts | 28 ++- .../podkop/tabs/dashboard/initController.ts | 44 ++-- .../tabs/diagnostic/checks/runDnsCheck.ts | 2 - .../tabs/diagnostic/checks/runFakeIPCheck.ts | 8 - .../tabs/diagnostic/checks/runNftCheck.ts | 2 - .../tabs/diagnostic/checks/runSingBoxCheck.ts | 2 - .../podkop/tabs/diagnostic/initController.ts | 54 +++-- .../luci-static/resources/view/podkop/main.js | 215 ++++++++++++------ 12 files changed, 298 insertions(+), 130 deletions(-) create mode 100644 fe-app-podkop/src/podkop/services/logger.service.ts diff --git a/fe-app-podkop/src/helpers/withTimeout.ts b/fe-app-podkop/src/helpers/withTimeout.ts index f06108a..e220740 100644 --- a/fe-app-podkop/src/helpers/withTimeout.ts +++ b/fe-app-podkop/src/helpers/withTimeout.ts @@ -1,3 +1,5 @@ +import { logger } from '../podkop'; + export async function withTimeout( promise: Promise, timeoutMs: number, @@ -16,6 +18,6 @@ export async function withTimeout( } finally { clearTimeout(timeoutId); const elapsed = performance.now() - start; - console.log(`[${operationName}] Execution time: ${elapsed.toFixed(2)} ms`); + logger.info('[SHELL]', `[${operationName}] took ${elapsed.toFixed(2)} ms`); } } diff --git a/fe-app-podkop/src/podkop/services/core.service.ts b/fe-app-podkop/src/podkop/services/core.service.ts index ac9df68..276475d 100644 --- a/fe-app-podkop/src/podkop/services/core.service.ts +++ b/fe-app-podkop/src/podkop/services/core.service.ts @@ -1,8 +1,10 @@ import { TabServiceInstance } from './tab.service'; import { store } from './store.service'; +import { logger } from './logger.service'; export function coreService() { TabServiceInstance.onChange((activeId, tabs) => { + logger.info('[TAB]', activeId); store.set({ tabService: { current: activeId || '', diff --git a/fe-app-podkop/src/podkop/services/index.ts b/fe-app-podkop/src/podkop/services/index.ts index d6477f7..e3160a8 100644 --- a/fe-app-podkop/src/podkop/services/index.ts +++ b/fe-app-podkop/src/podkop/services/index.ts @@ -2,3 +2,4 @@ export * from './tab.service'; export * from './core.service'; export * from './socket.service'; export * from './store.service'; +export * from './logger.service'; diff --git a/fe-app-podkop/src/podkop/services/logger.service.ts b/fe-app-podkop/src/podkop/services/logger.service.ts new file mode 100644 index 0000000..ecc0ff0 --- /dev/null +++ b/fe-app-podkop/src/podkop/services/logger.service.ts @@ -0,0 +1,66 @@ +import { downloadAsTxt } from '../../helpers/downloadAsTxt'; + +export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; + +export class Logger { + private logs: string[] = []; + private readonly levels: LogLevel[] = ['debug', 'info', 'warn', 'error']; + + private format(level: LogLevel, ...args: unknown[]): string { + return `[${level.toUpperCase()}] ${args.join(' ')}`; + } + + private push(level: LogLevel, ...args: unknown[]): void { + 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: unknown[]): void { + this.push('debug', ...args); + } + + info(...args: unknown[]): void { + this.push('info', ...args); + } + + warn(...args: unknown[]): void { + this.push('warn', ...args); + } + + error(...args: unknown[]): void { + this.push('error', ...args); + } + + clear(): void { + this.logs = []; + } + + getLogs(): string { + return this.logs.join('\n'); + } + + download(filename = 'logs.txt'): void { + if (typeof document === 'undefined') { + console.warn('Logger.download() доступен только в браузере'); + return; + } + downloadAsTxt(this.getLogs(), filename); + } +} + +export const logger = new Logger(); diff --git a/fe-app-podkop/src/podkop/services/socket.service.ts b/fe-app-podkop/src/podkop/services/socket.service.ts index adb6e13..2a21091 100644 --- a/fe-app-podkop/src/podkop/services/socket.service.ts +++ b/fe-app-podkop/src/podkop/services/socket.service.ts @@ -1,3 +1,5 @@ +import { logger } from './logger.service'; + // eslint-disable-next-line type Listener = (data: any) => void; type ErrorListener = (error: Event | string) => void; @@ -28,7 +30,11 @@ class SocketManager { ws.close(); } } catch (err) { - console.warn(`resetAll: failed to close socket ${url}`, err); + logger.error( + '[SOCKET]', + `resetAll: failed to close socket ${url}`, + err, + ); } } @@ -36,7 +42,7 @@ class SocketManager { this.listeners.clear(); this.errorListeners.clear(); this.connected.clear(); - console.info('[SocketManager] All connections and state have been reset.'); + logger.info('[SOCKET]', 'All connections and state have been reset.'); } connect(url: string): void { @@ -47,7 +53,11 @@ class SocketManager { try { ws = new WebSocket(url); } catch (err) { - console.error(`Failed to construct WebSocket for ${url}:`, err); + logger.error( + '[SOCKET]', + `failed to construct WebSocket for ${url}:`, + err, + ); this.triggerError(url, err instanceof Event ? err : String(err)); return; } @@ -59,7 +69,7 @@ class SocketManager { ws.addEventListener('open', () => { this.connected.set(url, true); - console.info(`Connected: ${url}`); + logger.info('[SOCKET]', 'Connected to', url); }); ws.addEventListener('message', (event) => { @@ -69,7 +79,7 @@ class SocketManager { try { handler(event.data); } catch (err) { - console.error(`Handler error for ${url}:`, err); + logger.error('[SOCKET]', `Handler error for ${url}:`, err); } } } @@ -77,12 +87,12 @@ class SocketManager { ws.addEventListener('close', () => { this.connected.set(url, false); - console.warn(`Disconnected: ${url}`); + logger.warn('[SOCKET]', `Disconnected: ${url}`); this.triggerError(url, 'Connection closed'); }); ws.addEventListener('error', (err) => { - console.error(`Socket error for ${url}:`, err); + logger.error('[SOCKET]', `Socket error for ${url}:`, err); this.triggerError(url, err); }); } @@ -118,7 +128,7 @@ class SocketManager { if (ws && this.connected.get(url)) { ws.send(typeof data === 'string' ? data : JSON.stringify(data)); } else { - console.warn(`Cannot send: not connected to ${url}`); + logger.warn('[SOCKET]', `Cannot send: not connected to ${url}`); this.triggerError(url, 'Not connected'); } } @@ -147,7 +157,7 @@ class SocketManager { try { cb(err); } catch (e) { - console.error(`Error handler threw for ${url}:`, e); + logger.error('[SOCKET]', `Error handler threw for ${url}:`, e); } } } diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts b/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts index 04b6af0..e1abeab 100644 --- a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts +++ b/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts @@ -5,7 +5,7 @@ import { } from '../../../helpers'; import { prettyBytes } from '../../../helpers/prettyBytes'; import { CustomPodkopMethods, PodkopShellMethods } from '../../methods'; -import { socket, store, StoreType } from '../../services'; +import { logger, socket, store, StoreType } from '../../services'; import { renderSections, renderWidget } from './partials'; import { fetchServicesInfo } from '../../fetchers'; @@ -24,7 +24,7 @@ async function fetchDashboardSections() { const { data, success } = await CustomPodkopMethods.getDashboardSections(); if (!success) { - console.log('[fetchDashboardSections]: failed to fetch'); + logger.error('[DASHBOARD]', 'fetchDashboardSections: failed to fetch'); } store.set({ @@ -38,7 +38,6 @@ async function fetchDashboardSections() { } async function connectToClashSockets() { - console.log('[SOCKET] connectToClashSockets'); socket.subscribe( `${getClashWsUrl()}/traffic?token=`, (msg) => { @@ -53,8 +52,9 @@ async function connectToClashSockets() { }); }, (_err) => { - console.log( - '[fetchDashboardSections]: failed to connect', + logger.error( + '[DASHBOARD]', + 'connectToClashSockets - traffic: failed to connect to', getClashWsUrl(), ); store.set({ @@ -92,8 +92,9 @@ async function connectToClashSockets() { }); }, (_err) => { - console.log( - '[fetchDashboardSections]: failed to connect', + logger.error( + '[DASHBOARD]', + 'connectToClashSockets - connections: failed to connect to', getClashWsUrl(), ); store.set({ @@ -163,7 +164,7 @@ async function handleTestProxyLatency(tag: string) { // Renderer async function renderSectionsWidget() { - console.log('renderSectionsWidget'); + logger.debug('[DASHBOARD]', 'renderSectionsWidget'); const sectionsWidget = store.get().sectionsWidget; const container = document.getElementById('dashboard-sections-grid'); @@ -212,7 +213,7 @@ async function renderSectionsWidget() { } async function renderBandwidthWidget() { - console.log('renderBandwidthWidget'); + logger.debug('[DASHBOARD]', 'renderBandwidthWidget'); const traffic = store.get().bandwidthWidget; const container = document.getElementById('dashboard-widget-traffic'); @@ -242,7 +243,7 @@ async function renderBandwidthWidget() { } async function renderTrafficTotalWidget() { - console.log('renderTrafficTotalWidget'); + logger.debug('[DASHBOARD]', 'renderTrafficTotalWidget'); const trafficTotalWidget = store.get().trafficTotalWidget; const container = document.getElementById('dashboard-widget-traffic-total'); @@ -278,7 +279,7 @@ async function renderTrafficTotalWidget() { } async function renderSystemInfoWidget() { - console.log('renderSystemInfoWidget'); + logger.debug('[DASHBOARD]', 'renderSystemInfoWidget'); const systemInfoWidget = store.get().systemInfoWidget; const container = document.getElementById('dashboard-widget-system-info'); @@ -314,7 +315,7 @@ async function renderSystemInfoWidget() { } async function renderServicesInfoWidget() { - console.log('renderServicesInfoWidget'); + logger.debug('[DASHBOARD]', 'renderServicesInfoWidget'); const servicesInfoWidget = store.get().servicesInfoWidget; const container = document.getElementById('dashboard-widget-service-info'); @@ -422,19 +423,29 @@ function registerLifecycleListeners() { diff.tabService && next.tabService.current !== prev.tabService.current ) { - console.log( - new Date().toISOString(), - '[Active Tab on dashboard]', + 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) { - onPageUnmount(); + logger.debug( + '[DASHBOARD]', + 'registerLifecycleListeners', + 'onPageUnmount', + ); + return onPageUnmount(); } } }); @@ -442,6 +453,7 @@ function registerLifecycleListeners() { export async function initController(): Promise { onMount('dashboard-status').then(() => { + logger.debug('[DASHBOARD]', 'initController', 'onMount'); onPageMount(); registerLifecycleListeners(); }); diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts index 9fe7454..c64cf7b 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts @@ -45,8 +45,6 @@ export async function runDnsCheck() { Boolean(data.bootstrap_dns_status) || Boolean(data.dns_status); - console.log('dnsChecks', dnsChecks); - function getStatus() { if (allGood) { return 'success'; diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts index dbb7169..780a547 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts @@ -20,12 +20,6 @@ export async function runFakeIPCheck() { const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck(); const checkIPResponse = await RemoteFakeIPMethods.getIpCheck(); - console.log('runFakeIPCheck', { - routerFakeIPResponse, - checkFakeIPResponse, - checkIPResponse, - }); - const checks = { router: routerFakeIPResponse.success && routerFakeIPResponse.data.fakeip, browserFakeIP: @@ -36,8 +30,6 @@ export async function runFakeIPCheck() { checkFakeIPResponse.data.IP !== checkIPResponse.data.IP, }; - console.log('checks', checks); - const allGood = checks.router || checks.browserFakeIP || checks.differentIP; const atLeastOneGood = checks.router && checks.browserFakeIP && checks.differentIP; diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts index 1d46031..46c46a1 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts @@ -54,8 +54,6 @@ export async function runNftCheck() { Boolean(data.rules_proxy_counters) || Boolean(data.rules_other_mark_exist); - console.log('nftablesChecks', nftablesChecks); - function getStatus() { if (allGood) { return 'success'; diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts index 4d73acc..15e0bc9 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts @@ -47,8 +47,6 @@ export async function runSingBoxCheck() { Boolean(data.sing_box_process_running) || Boolean(data.sing_box_ports_listening); - console.log('singBoxChecks', singBoxChecks); - function getStatus() { if (allGood) { return 'success'; diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts index 5daa4ea..400dff9 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts @@ -4,7 +4,7 @@ import { runSingBoxCheck } from './checks/runSingBoxCheck'; import { runNftCheck } from './checks/runNftCheck'; import { runFakeIPCheck } from './checks/runFakeIPCheck'; import { loadingDiagnosticsChecksStore } from './diagnostic.store'; -import { store, StoreType } from '../../services'; +import { logger, store, StoreType } from '../../services'; import { IRenderSystemInfoRow, renderAvailableActions, @@ -43,7 +43,7 @@ async function fetchSystemInfo() { } function renderDiagnosticsChecks() { - console.log('renderDiagnosticsChecks'); + logger.debug('[DIAGNOSTIC]', 'renderDiagnosticsChecks'); const diagnosticsChecks = store .get() .diagnosticsChecks.sort((a, b) => a.order - b.order); @@ -59,7 +59,7 @@ function renderDiagnosticsChecks() { } function renderDiagnosticRunActionWidget() { - console.log('renderDiagnosticRunActionWidget'); + logger.debug('[DIAGNOSTIC]', 'renderDiagnosticRunActionWidget'); const { loading } = store.get().diagnosticsRunAction; const container = document.getElementById('pdk_diagnostic-page-run-check'); @@ -86,7 +86,7 @@ async function handleRestart() { try { await PodkopShellMethods.restart(); } catch (e) { - console.log('handleRestart - e', e); + logger.error('[DIAGNOSTIC]', 'handleRestart - e', e); } finally { setTimeout(async () => { await fetchServicesInfo(); @@ -113,7 +113,7 @@ async function handleStop() { try { await PodkopShellMethods.stop(); } catch (e) { - console.log('handleStop - e', e); + logger.error('[DIAGNOSTIC]', 'handleStop - e', e); } finally { await fetchServicesInfo(); store.set({ @@ -138,7 +138,7 @@ async function handleStart() { try { await PodkopShellMethods.start(); } catch (e) { - console.log('handleStart - e', e); + logger.error('[DIAGNOSTIC]', 'handleStart - e', e); } finally { setTimeout(async () => { await fetchServicesInfo(); @@ -165,7 +165,7 @@ async function handleEnable() { try { await PodkopShellMethods.enable(); } catch (e) { - console.log('handleEnable - e', e); + logger.error('[DIAGNOSTIC]', 'handleEnable - e', e); } finally { await fetchServicesInfo(); store.set({ @@ -189,7 +189,7 @@ async function handleDisable() { try { await PodkopShellMethods.disable(); } catch (e) { - console.log('handleDisable - e', e); + logger.error('[DIAGNOSTIC]', 'handleDisable - e', e); } finally { await fetchServicesInfo(); store.set({ @@ -220,7 +220,7 @@ async function handleShowGlobalCheck() { ); } } catch (e) { - console.log('handleShowGlobalCheck - e', e); + logger.error('[DIAGNOSTIC]', 'handleShowGlobalCheck - e', e); } finally { store.set({ diagnosticsActions: { @@ -250,7 +250,7 @@ async function handleViewLogs() { ); } } catch (e) { - console.log('handleViewLogs - e', e); + logger.error('[DIAGNOSTIC]', 'handleViewLogs - e', e); } finally { store.set({ diagnosticsActions: { @@ -280,7 +280,7 @@ async function handleShowSingBoxConfig() { ); } } catch (e) { - console.log('handleViewLogs - e', e); + logger.error('[DIAGNOSTIC]', 'handleShowSingBoxConfig - e', e); } finally { store.set({ diagnosticsActions: { @@ -294,7 +294,7 @@ async function handleShowSingBoxConfig() { function renderDiagnosticAvailableActionsWidget() { const diagnosticsActions = store.get().diagnosticsActions; const servicesInfoWidget = store.get().servicesInfoWidget; - console.log('renderDiagnosticActionsWidget'); + logger.debug('[DIAGNOSTIC]', 'renderDiagnosticAvailableActionsWidget'); const podkopEnabled = Boolean(servicesInfoWidget.data.podkop); const singBoxRunning = Boolean(servicesInfoWidget.data.singbox); @@ -363,7 +363,7 @@ function renderDiagnosticAvailableActionsWidget() { } function renderDiagnosticSystemInfoWidget() { - console.log('renderDiagnosticSystemInfoWidget'); + logger.debug('[DIAGNOSTIC]', 'renderDiagnosticSystemInfoWidget'); const diagnosticsSystemInfo = store.get().diagnosticsSystemInfo; const container = document.getElementById('pdk_diagnostic-page-system-info'); @@ -471,14 +471,13 @@ async function runChecks() { await runFakeIPCheck(); } catch (e) { - console.log('runChecks - e', e); + logger.error('[DIAGNOSTIC]', 'runChecks - e', e); } finally { store.set({ diagnosticsRunAction: { loading: false } }); } } function onPageMount() { - console.log('diagnostic controller initialized.'); // Cleanup before mount onPageUnmount(); @@ -523,19 +522,29 @@ function registerLifecycleListeners() { diff.tabService && next.tabService.current !== prev.tabService.current ) { - console.log( - new Date().toISOString(), - '[Active Tab on diagnostics]', + logger.debug( + '[DIAGNOSTIC]', + 'active tab diff event, active tab:', diff.tabService.current, ); - const isDashboardVisible = next.tabService.current === 'diagnostic'; + const isDIAGNOSTICVisible = next.tabService.current === 'diagnostic'; - if (isDashboardVisible) { + if (isDIAGNOSTICVisible) { + logger.debug( + '[DIAGNOSTIC]', + 'registerLifecycleListeners', + 'onPageMount', + ); return onPageMount(); } - if (!isDashboardVisible) { - onPageUnmount(); + if (!isDIAGNOSTICVisible) { + logger.debug( + '[DIAGNOSTIC]', + 'registerLifecycleListeners', + 'onPageUnmount', + ); + return onPageUnmount(); } } }); @@ -543,6 +552,7 @@ function registerLifecycleListeners() { export async function initController(): Promise { onMount('diagnostic-status').then(() => { + logger.debug('[DIAGNOSTIC]', 'initController', 'onMount'); onPageMount(); registerLifecycleListeners(); }); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js index 4140a1f..3a9c4a1 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js @@ -1166,9 +1166,78 @@ var initialStore = { }; 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/core.service.ts function coreService() { TabServiceInstance.onChange((activeId, tabs) => { + logger.info("[TAB]", activeId); store.set({ tabService: { current: activeId || "", @@ -1199,14 +1268,18 @@ var SocketManager = class _SocketManager { ws.close(); } } catch (err) { - console.warn(`resetAll: failed to close socket ${url}`, err); + logger.error( + "[SOCKET]", + `resetAll: failed to close socket ${url}`, + err + ); } } this.sockets.clear(); this.listeners.clear(); this.errorListeners.clear(); this.connected.clear(); - console.info("[SocketManager] All connections and state have been reset."); + logger.info("[SOCKET]", "All connections and state have been reset."); } connect(url) { if (this.sockets.has(url)) return; @@ -1214,7 +1287,11 @@ var SocketManager = class _SocketManager { try { ws = new WebSocket(url); } catch (err) { - console.error(`Failed to construct WebSocket for ${url}:`, err); + logger.error( + "[SOCKET]", + `failed to construct WebSocket for ${url}:`, + err + ); this.triggerError(url, err instanceof Event ? err : String(err)); return; } @@ -1224,7 +1301,7 @@ var SocketManager = class _SocketManager { this.errorListeners.set(url, /* @__PURE__ */ new Set()); ws.addEventListener("open", () => { this.connected.set(url, true); - console.info(`Connected: ${url}`); + logger.info("[SOCKET]", "Connected to", url); }); ws.addEventListener("message", (event) => { const handlers = this.listeners.get(url); @@ -1233,18 +1310,18 @@ var SocketManager = class _SocketManager { try { handler(event.data); } catch (err) { - console.error(`Handler error for ${url}:`, err); + logger.error("[SOCKET]", `Handler error for ${url}:`, err); } } } }); ws.addEventListener("close", () => { this.connected.set(url, false); - console.warn(`Disconnected: ${url}`); + logger.warn("[SOCKET]", `Disconnected: ${url}`); this.triggerError(url, "Connection closed"); }); ws.addEventListener("error", (err) => { - console.error(`Socket error for ${url}:`, err); + logger.error("[SOCKET]", `Socket error for ${url}:`, err); this.triggerError(url, err); }); } @@ -1275,7 +1352,7 @@ var SocketManager = class _SocketManager { if (ws && this.connected.get(url)) { ws.send(typeof data === "string" ? data : JSON.stringify(data)); } else { - console.warn(`Cannot send: not connected to ${url}`); + logger.warn("[SOCKET]", `Cannot send: not connected to ${url}`); this.triggerError(url, "Not connected"); } } @@ -1301,7 +1378,7 @@ var SocketManager = class _SocketManager { try { cb(err); } catch (e) { - console.error(`Error handler threw for ${url}:`, e); + logger.error("[SOCKET]", `Error handler threw for ${url}:`, e); } } } @@ -1580,7 +1657,7 @@ async function fetchDashboardSections() { }); const { data, success } = await CustomPodkopMethods.getDashboardSections(); if (!success) { - console.log("[fetchDashboardSections]: failed to fetch"); + logger.error("[DASHBOARD]", "fetchDashboardSections: failed to fetch"); } store.set({ sectionsWidget: { @@ -1592,7 +1669,6 @@ async function fetchDashboardSections() { }); } async function connectToClashSockets() { - console.log("[SOCKET] connectToClashSockets"); socket.subscribe( `${getClashWsUrl()}/traffic?token=`, (msg) => { @@ -1606,8 +1682,9 @@ async function connectToClashSockets() { }); }, (_err) => { - console.log( - "[fetchDashboardSections]: failed to connect", + logger.error( + "[DASHBOARD]", + "connectToClashSockets - traffic: failed to connect to", getClashWsUrl() ); store.set({ @@ -1643,8 +1720,9 @@ async function connectToClashSockets() { }); }, (_err) => { - console.log( - "[fetchDashboardSections]: failed to connect", + logger.error( + "[DASHBOARD]", + "connectToClashSockets - connections: failed to connect to", getClashWsUrl() ); store.set({ @@ -1702,7 +1780,7 @@ async function handleTestProxyLatency(tag) { }); } async function renderSectionsWidget() { - console.log("renderSectionsWidget"); + logger.debug("[DASHBOARD]", "renderSectionsWidget"); const sectionsWidget = store.get().sectionsWidget; const container = document.getElementById("dashboard-sections-grid"); if (sectionsWidget.loading || sectionsWidget.failed) { @@ -1747,7 +1825,7 @@ async function renderSectionsWidget() { }); } async function renderBandwidthWidget() { - console.log("renderBandwidthWidget"); + logger.debug("[DASHBOARD]", "renderBandwidthWidget"); const traffic = store.get().bandwidthWidget; const container = document.getElementById("dashboard-widget-traffic"); if (traffic.loading || traffic.failed) { @@ -1771,7 +1849,7 @@ async function renderBandwidthWidget() { container.replaceChildren(renderedWidget); } async function renderTrafficTotalWidget() { - console.log("renderTrafficTotalWidget"); + logger.debug("[DASHBOARD]", "renderTrafficTotalWidget"); const trafficTotalWidget = store.get().trafficTotalWidget; const container = document.getElementById("dashboard-widget-traffic-total"); if (trafficTotalWidget.loading || trafficTotalWidget.failed) { @@ -1801,7 +1879,7 @@ async function renderTrafficTotalWidget() { container.replaceChildren(renderedWidget); } async function renderSystemInfoWidget() { - console.log("renderSystemInfoWidget"); + logger.debug("[DASHBOARD]", "renderSystemInfoWidget"); const systemInfoWidget = store.get().systemInfoWidget; const container = document.getElementById("dashboard-widget-system-info"); if (systemInfoWidget.loading || systemInfoWidget.failed) { @@ -1831,7 +1909,7 @@ async function renderSystemInfoWidget() { container.replaceChildren(renderedWidget); } async function renderServicesInfoWidget() { - console.log("renderServicesInfoWidget"); + logger.debug("[DASHBOARD]", "renderServicesInfoWidget"); const servicesInfoWidget = store.get().servicesInfoWidget; const container = document.getElementById("dashboard-widget-service-info"); if (servicesInfoWidget.loading || servicesInfoWidget.failed) { @@ -1904,23 +1982,34 @@ function onPageUnmount() { function registerLifecycleListeners() { store.subscribe((next, prev, diff) => { if (diff.tabService && next.tabService.current !== prev.tabService.current) { - console.log( - (/* @__PURE__ */ new Date()).toISOString(), - "[Active Tab on dashboard]", + 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) { - onPageUnmount(); + logger.debug( + "[DASHBOARD]", + "registerLifecycleListeners", + "onPageUnmount" + ); + return onPageUnmount(); } } }); } async function initController() { onMount("dashboard-status").then(() => { + logger.debug("[DASHBOARD]", "initController", "onMount"); onPageMount(); registerLifecycleListeners(); }); @@ -2111,7 +2200,6 @@ async function runDnsCheck() { 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); - console.log("dnsChecks", dnsChecks); function getStatus() { if (allGood) { return "success"; @@ -2186,7 +2274,6 @@ async function runSingBoxCheck() { 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); - console.log("singBoxChecks", singBoxChecks); function getStatus() { if (allGood) { return "success"; @@ -2268,7 +2355,6 @@ async function runNftCheck() { 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) && Boolean(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) || Boolean(data.rules_other_mark_exist); - console.log("nftablesChecks", nftablesChecks); function getStatus() { if (allGood) { return "success"; @@ -2346,17 +2432,11 @@ async function runFakeIPCheck() { const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP(); const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck(); const checkIPResponse = await RemoteFakeIPMethods.getIpCheck(); - console.log("runFakeIPCheck", { - routerFakeIPResponse, - checkFakeIPResponse, - checkIPResponse - }); 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 }; - console.log("checks", checks); const allGood = checks.router || checks.browserFakeIP || checks.differentIP; const atLeastOneGood = checks.router && checks.browserFakeIP && checks.differentIP; function getMeta() { @@ -3032,19 +3112,6 @@ function copyToClipboard(text) { document.body.removeChild(textarea); } -// 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/partials/modal/renderModal.ts function renderModal(text, name) { return E( @@ -3411,7 +3478,7 @@ async function fetchSystemInfo() { } } function renderDiagnosticsChecks() { - console.log("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( @@ -3422,7 +3489,7 @@ function renderDiagnosticsChecks() { }); } function renderDiagnosticRunActionWidget() { - console.log("renderDiagnosticRunActionWidget"); + logger.debug("[DIAGNOSTIC]", "renderDiagnosticRunActionWidget"); const { loading } = store.get().diagnosticsRunAction; const container = document.getElementById("pdk_diagnostic-page-run-check"); const renderedAction = renderRunAction({ @@ -3444,7 +3511,7 @@ async function handleRestart() { try { await PodkopShellMethods.restart(); } catch (e) { - console.log("handleRestart - e", e); + logger.error("[DIAGNOSTIC]", "handleRestart - e", e); } finally { setTimeout(async () => { await fetchServicesInfo(); @@ -3469,7 +3536,7 @@ async function handleStop() { try { await PodkopShellMethods.stop(); } catch (e) { - console.log("handleStop - e", e); + logger.error("[DIAGNOSTIC]", "handleStop - e", e); } finally { await fetchServicesInfo(); store.set({ @@ -3492,7 +3559,7 @@ async function handleStart() { try { await PodkopShellMethods.start(); } catch (e) { - console.log("handleStart - e", e); + logger.error("[DIAGNOSTIC]", "handleStart - e", e); } finally { setTimeout(async () => { await fetchServicesInfo(); @@ -3517,7 +3584,7 @@ async function handleEnable() { try { await PodkopShellMethods.enable(); } catch (e) { - console.log("handleEnable - e", e); + logger.error("[DIAGNOSTIC]", "handleEnable - e", e); } finally { await fetchServicesInfo(); store.set({ @@ -3539,7 +3606,7 @@ async function handleDisable() { try { await PodkopShellMethods.disable(); } catch (e) { - console.log("handleDisable - e", e); + logger.error("[DIAGNOSTIC]", "handleDisable - e", e); } finally { await fetchServicesInfo(); store.set({ @@ -3567,7 +3634,7 @@ async function handleShowGlobalCheck() { ); } } catch (e) { - console.log("handleShowGlobalCheck - e", e); + logger.error("[DIAGNOSTIC]", "handleShowGlobalCheck - e", e); } finally { store.set({ diagnosticsActions: { @@ -3594,7 +3661,7 @@ async function handleViewLogs() { ); } } catch (e) { - console.log("handleViewLogs - e", e); + logger.error("[DIAGNOSTIC]", "handleViewLogs - e", e); } finally { store.set({ diagnosticsActions: { @@ -3621,7 +3688,7 @@ async function handleShowSingBoxConfig() { ); } } catch (e) { - console.log("handleViewLogs - e", e); + logger.error("[DIAGNOSTIC]", "handleShowSingBoxConfig - e", e); } finally { store.set({ diagnosticsActions: { @@ -3634,7 +3701,7 @@ async function handleShowSingBoxConfig() { function renderDiagnosticAvailableActionsWidget() { const diagnosticsActions = store.get().diagnosticsActions; const servicesInfoWidget = store.get().servicesInfoWidget; - console.log("renderDiagnosticActionsWidget"); + 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; @@ -3694,7 +3761,7 @@ function renderDiagnosticAvailableActionsWidget() { }); } function renderDiagnosticSystemInfoWidget() { - console.log("renderDiagnosticSystemInfoWidget"); + logger.debug("[DIAGNOSTIC]", "renderDiagnosticSystemInfoWidget"); const diagnosticsSystemInfo = store.get().diagnosticsSystemInfo; const container = document.getElementById("pdk_diagnostic-page-system-info"); function getPodkopVersionRow() { @@ -3782,13 +3849,12 @@ async function runChecks() { await runNftCheck(); await runFakeIPCheck(); } catch (e) { - console.log("runChecks - e", e); + logger.error("[DIAGNOSTIC]", "runChecks - e", e); } finally { store.set({ diagnosticsRunAction: { loading: false } }); } } function onPageMount2() { - console.log("diagnostic controller initialized."); onPageUnmount2(); store.subscribe(onStoreUpdate2); renderDiagnosticsChecks(); @@ -3810,23 +3876,34 @@ function onPageUnmount2() { function registerLifecycleListeners2() { store.subscribe((next, prev, diff) => { if (diff.tabService && next.tabService.current !== prev.tabService.current) { - console.log( - (/* @__PURE__ */ new Date()).toISOString(), - "[Active Tab on diagnostics]", + logger.debug( + "[DIAGNOSTIC]", + "active tab diff event, active tab:", diff.tabService.current ); - const isDashboardVisible = next.tabService.current === "diagnostic"; - if (isDashboardVisible) { + const isDIAGNOSTICVisible = next.tabService.current === "diagnostic"; + if (isDIAGNOSTICVisible) { + logger.debug( + "[DIAGNOSTIC]", + "registerLifecycleListeners", + "onPageMount" + ); return onPageMount2(); } - if (!isDashboardVisible) { - onPageUnmount2(); + if (!isDIAGNOSTICVisible) { + logger.debug( + "[DIAGNOSTIC]", + "registerLifecycleListeners", + "onPageUnmount" + ); + return onPageUnmount2(); } } }); } async function initController2() { onMount("diagnostic-status").then(() => { + logger.debug("[DIAGNOSTIC]", "initController", "onMount"); onPageMount2(); registerLifecycleListeners2(); }); @@ -4139,7 +4216,7 @@ async function withTimeout(promise, timeoutMs, operationName, timeoutMessage = _ } finally { clearTimeout(timeoutId); const elapsed = performance.now() - start; - console.log(`[${operationName}] Execution time: ${elapsed.toFixed(2)} ms`); + logger.info("[SHELL]", `[${operationName}] took ${elapsed.toFixed(2)} ms`); } } @@ -4268,6 +4345,7 @@ return baseclass.extend({ FAKEIP_CHECK_DOMAIN, FETCH_TIMEOUT, IP_CHECK_DOMAIN, + Logger, PODKOP_LUCI_APP_VERSION, PodkopShellMethods, REGIONAL_OPTIONS, @@ -4285,6 +4363,7 @@ return baseclass.extend({ injectGlobalStyles, insertIf, insertIfObj, + logger, maskIP, onMount, parseQueryString,