From c0b35c865d26fe74a3aae14bb06914e1ead1a5c6 Mon Sep 17 00:00:00 2001 From: divocat Date: Wed, 15 Oct 2025 21:13:52 +0300 Subject: [PATCH] feat: actualize system actions behavior --- .../src/podkop/fetchers/fetchServicesInfo.ts | 29 ++++ fe-app-podkop/src/podkop/fetchers/index.ts | 1 + .../podkop/tabs/dashboard/initController.ts | 28 +--- .../tabs/diagnostic/checks/runDnsCheck.ts | 10 +- .../podkop/tabs/diagnostic/initController.ts | 69 +++++--- .../partials/renderAvailableActions.ts | 13 +- .../luci-static/resources/view/podkop/main.js | 150 +++++++++++------- 7 files changed, 184 insertions(+), 116 deletions(-) create mode 100644 fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts create mode 100644 fe-app-podkop/src/podkop/fetchers/index.ts diff --git a/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts b/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts new file mode 100644 index 0000000..777ab94 --- /dev/null +++ b/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts @@ -0,0 +1,29 @@ +import { PodkopShellMethods } from '../methods'; +import { store } from '../services'; + +export 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 }, + }, + }); + } +} diff --git a/fe-app-podkop/src/podkop/fetchers/index.ts b/fe-app-podkop/src/podkop/fetchers/index.ts new file mode 100644 index 0000000..9b58655 --- /dev/null +++ b/fe-app-podkop/src/podkop/fetchers/index.ts @@ -0,0 +1 @@ +export * from './fetchServicesInfo'; diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts b/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts index a5e1ae2..4734098 100644 --- a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts +++ b/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts @@ -7,6 +7,7 @@ import { prettyBytes } from '../../../helpers/prettyBytes'; import { CustomPodkopMethods, PodkopShellMethods } from '../../methods'; import { socket, store, StoreType } from '../../services'; import { renderSections, renderWidget } from './partials'; +import { fetchServicesInfo } from '../../fetchers'; // Fetchers @@ -36,33 +37,6 @@ async function fetchDashboardSections() { }); } -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 }, - }, - }); - } -} - async function connectToClashSockets() { socket.subscribe( `${getClashWsUrl()}/traffic?token=`, 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 58d9f56..c8ee05a 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts @@ -40,10 +40,10 @@ export async function runDnsCheck() { Boolean(data.dns_status); const atLeastOneGood = - Boolean(data.dns_on_router) || - Boolean(data.dhcp_has_dns_server) || - Boolean(data.bootstrap_dns_status) || - Boolean(data.dns_status); + Boolean(data.dns_on_router) || + Boolean(data.dhcp_has_dns_server) || + Boolean(data.bootstrap_dns_status) || + Boolean(data.dns_status); console.log('dnsChecks', dnsChecks); @@ -88,7 +88,7 @@ export async function runDnsCheck() { }, { state: data.dhcp_has_dns_server ? 'success' : 'error', - key: _('Dhcp has dns server'), + key: _('DHCP has DNS server'), value: '', }, ], diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts index d69c636..469ff7a 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts @@ -12,6 +12,7 @@ import { renderSystemInfo, } from './partials'; import { PodkopShellMethods } from '../../methods'; +import { fetchServicesInfo } from '../../fetchers'; function renderDiagnosticsChecks() { console.log('renderDiagnosticsChecks'); @@ -59,13 +60,15 @@ async function handleRestart() { } catch (e) { console.log('handleRestart - e', e); } finally { - store.set({ - diagnosticsActions: { - ...diagnosticsActions, - restart: { loading: false }, - }, - }); - location.reload(); + setTimeout(async () => { + await fetchServicesInfo(); + store.set({ + diagnosticsActions: { + ...diagnosticsActions, + restart: { loading: false }, + }, + }); + }, 5000); } } @@ -83,13 +86,13 @@ async function handleStop() { } catch (e) { console.log('handleStop - e', e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, stop: { loading: false }, }, }); - // TODO actualize dashboard } } @@ -107,13 +110,15 @@ async function handleStart() { } catch (e) { console.log('handleStart - e', e); } finally { - store.set({ - diagnosticsActions: { - ...diagnosticsActions, - start: { loading: false }, - }, - }); - location.reload(); + setTimeout(async () => { + await fetchServicesInfo(); + store.set({ + diagnosticsActions: { + ...diagnosticsActions, + start: { loading: false }, + }, + }); + }, 5000); } } @@ -131,13 +136,13 @@ async function handleEnable() { } catch (e) { console.log('handleEnable - e', e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, enable: { loading: false }, }, }); - //TODO actualize dashboard } } @@ -155,20 +160,29 @@ async function handleDisable() { } catch (e) { console.log('handleDisable - e', e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, disable: { loading: false }, }, }); - //TODO actualize dashboard } } function renderDiagnosticAvailableActionsWidget() { const diagnosticsActions = store.get().diagnosticsActions; + const servicesInfoWidget = store.get().servicesInfoWidget; console.log('renderDiagnosticActionsWidget'); + 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({ @@ -176,41 +190,49 @@ function renderDiagnosticAvailableActionsWidget() { loading: diagnosticsActions.restart.loading, visible: true, onClick: handleRestart, + disabled: atLeastOneServiceCommandLoading, }, start: { loading: diagnosticsActions.start.loading, - visible: true, + visible: !singBoxRunning, onClick: handleStart, + disabled: atLeastOneServiceCommandLoading, }, stop: { loading: diagnosticsActions.stop.loading, - visible: true, + visible: singBoxRunning, onClick: handleStop, + disabled: atLeastOneServiceCommandLoading, }, enable: { loading: diagnosticsActions.enable.loading, - visible: true, + visible: !podkopEnabled, onClick: handleEnable, + disabled: atLeastOneServiceCommandLoading, }, disable: { loading: diagnosticsActions.disable.loading, - visible: true, + visible: podkopEnabled, onClick: handleDisable, + disabled: atLeastOneServiceCommandLoading, }, globalCheck: { loading: diagnosticsActions.globalCheck.loading, visible: true, onClick: () => {}, + disabled: atLeastOneServiceCommandLoading, }, viewLogs: { loading: diagnosticsActions.viewLogs.loading, visible: true, onClick: () => {}, + disabled: atLeastOneServiceCommandLoading, }, showSingBoxConfig: { loading: diagnosticsActions.showSingBoxConfig.loading, visible: true, onClick: () => {}, + disabled: atLeastOneServiceCommandLoading, }, }); @@ -267,7 +289,7 @@ async function onStoreUpdate( renderDiagnosticRunActionWidget(); } - if (diff.diagnosticsActions) { + if (diff.diagnosticsActions || diff.servicesInfoWidget) { renderDiagnosticAvailableActionsWidget(); } } @@ -313,5 +335,8 @@ export async function initController(): Promise { // Initial system info render renderDiagnosticSystemInfoWidget(); + + // Initial services info fetch + fetchServicesInfo(); }); } diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts index e24f436..a943ba2 100644 --- a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts +++ b/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts @@ -14,6 +14,7 @@ import { insertIf } from '../../../../helpers'; interface ActionProps { loading: boolean; visible: boolean; + disabled: boolean; onClick: () => void; } @@ -47,6 +48,7 @@ export function renderAvailableActions({ icon: renderRotateCcwIcon24, text: 'Restart podkop', loading: restart.loading, + disabled: restart.disabled, }), ]), ...insertIf(stop.visible, [ @@ -56,6 +58,7 @@ export function renderAvailableActions({ icon: renderCircleStopIcon24, text: 'Stop podkop', loading: stop.loading, + disabled: stop.disabled, }), ]), ...insertIf(start.visible, [ @@ -65,6 +68,7 @@ export function renderAvailableActions({ icon: renderCirclePlayIcon24, text: 'Start podkop', loading: start.loading, + disabled: start.disabled, }), ]), ...insertIf(disable.visible, [ @@ -72,8 +76,9 @@ export function renderAvailableActions({ classNames: ['cbi-button-remove'], onClick: disable.onClick, icon: renderPauseIcon24, - text: 'Disable podkop', + text: 'Disable autostart', loading: disable.loading, + disabled: disable.disabled, }), ]), ...insertIf(enable.visible, [ @@ -81,8 +86,9 @@ export function renderAvailableActions({ classNames: ['cbi-button-save'], onClick: enable.onClick, icon: renderPlayIcon24, - text: 'Enable podkop', + text: 'Enable autostart', loading: enable.loading, + disabled: enable.disabled, }), ]), ...insertIf(globalCheck.visible, [ @@ -91,6 +97,7 @@ export function renderAvailableActions({ icon: renderCircleCheckBigIcon24, text: 'Get global check', loading: globalCheck.loading, + disabled: globalCheck.disabled, }), ]), ...insertIf(viewLogs.visible, [ @@ -99,6 +106,7 @@ export function renderAvailableActions({ icon: renderSquareChartGanttIcon24, text: 'View logs', loading: viewLogs.loading, + disabled: viewLogs.disabled, }), ]), ...insertIf(showSingBoxConfig.visible, [ @@ -107,6 +115,7 @@ export function renderAvailableActions({ icon: renderCogIcon24, text: 'Show sing-box config', loading: showSingBoxConfig.loading, + disabled: showSingBoxConfig.disabled, }), ]), ]); 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 395ff9f..ab81562 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 @@ -1507,28 +1507,7 @@ function prettyBytes(n) { return n + " " + unit; } -// 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) { - console.log("[fetchDashboardSections]: failed to fetch"); - } - store.set({ - sectionsWidget: { - latencyFetching: false, - loading: false, - failed: !success, - data - } - }); -} +// src/podkop/fetchers/fetchServicesInfo.ts async function fetchServicesInfo() { const [podkop, singbox] = await Promise.all([ PodkopShellMethods.getStatus(), @@ -1553,6 +1532,29 @@ async function fetchServicesInfo() { }); } } + +// 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) { + console.log("[fetchDashboardSections]: failed to fetch"); + } + store.set({ + sectionsWidget: { + latencyFetching: false, + loading: false, + failed: !success, + data + } + }); +} async function connectToClashSockets() { socket.subscribe( `${getClashWsUrl()}/traffic?token=`, @@ -2085,7 +2087,7 @@ async function runDnsCheck() { }, { state: data.dhcp_has_dns_server ? "success" : "error", - key: _("Dhcp has dns server"), + key: _("DHCP has DNS server"), value: "" } ] @@ -2935,7 +2937,8 @@ function renderAvailableActions({ onClick: restart.onClick, icon: renderRotateCcwIcon24, text: "Restart podkop", - loading: restart.loading + loading: restart.loading, + disabled: restart.disabled }) ]), ...insertIf(stop.visible, [ @@ -2944,7 +2947,8 @@ function renderAvailableActions({ onClick: stop.onClick, icon: renderCircleStopIcon24, text: "Stop podkop", - loading: stop.loading + loading: stop.loading, + disabled: stop.disabled }) ]), ...insertIf(start.visible, [ @@ -2953,7 +2957,8 @@ function renderAvailableActions({ onClick: start.onClick, icon: renderCirclePlayIcon24, text: "Start podkop", - loading: start.loading + loading: start.loading, + disabled: start.disabled }) ]), ...insertIf(disable.visible, [ @@ -2961,8 +2966,9 @@ function renderAvailableActions({ classNames: ["cbi-button-remove"], onClick: disable.onClick, icon: renderPauseIcon24, - text: "Disable podkop", - loading: disable.loading + text: "Disable autostart", + loading: disable.loading, + disabled: disable.disabled }) ]), ...insertIf(enable.visible, [ @@ -2970,8 +2976,9 @@ function renderAvailableActions({ classNames: ["cbi-button-save"], onClick: enable.onClick, icon: renderPlayIcon24, - text: "Enable podkop", - loading: enable.loading + text: "Enable autostart", + loading: enable.loading, + disabled: enable.disabled }) ]), ...insertIf(globalCheck.visible, [ @@ -2979,7 +2986,8 @@ function renderAvailableActions({ onClick: globalCheck.onClick, icon: renderCircleCheckBigIcon24, text: "Get global check", - loading: globalCheck.loading + loading: globalCheck.loading, + disabled: globalCheck.disabled }) ]), ...insertIf(viewLogs.visible, [ @@ -2987,7 +2995,8 @@ function renderAvailableActions({ onClick: viewLogs.onClick, icon: renderSquareChartGanttIcon24, text: "View logs", - loading: viewLogs.loading + loading: viewLogs.loading, + disabled: viewLogs.disabled }) ]), ...insertIf(showSingBoxConfig.visible, [ @@ -2995,7 +3004,8 @@ function renderAvailableActions({ onClick: showSingBoxConfig.onClick, icon: renderCogIcon24, text: "Show sing-box config", - loading: showSingBoxConfig.loading + loading: showSingBoxConfig.loading, + disabled: showSingBoxConfig.disabled }) ]) ]); @@ -3226,13 +3236,15 @@ async function handleRestart() { } catch (e) { console.log("handleRestart - e", e); } finally { - store.set({ - diagnosticsActions: { - ...diagnosticsActions, - restart: { loading: false } - } - }); - location.reload(); + setTimeout(async () => { + await fetchServicesInfo(); + store.set({ + diagnosticsActions: { + ...diagnosticsActions, + restart: { loading: false } + } + }); + }, 5e3); } } async function handleStop() { @@ -3248,6 +3260,7 @@ async function handleStop() { } catch (e) { console.log("handleStop - e", e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, @@ -3269,13 +3282,15 @@ async function handleStart() { } catch (e) { console.log("handleStart - e", e); } finally { - store.set({ - diagnosticsActions: { - ...diagnosticsActions, - start: { loading: false } - } - }); - location.reload(); + setTimeout(async () => { + await fetchServicesInfo(); + store.set({ + diagnosticsActions: { + ...diagnosticsActions, + start: { loading: false } + } + }); + }, 5e3); } } async function handleEnable() { @@ -3291,6 +3306,7 @@ async function handleEnable() { } catch (e) { console.log("handleEnable - e", e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, @@ -3312,6 +3328,7 @@ async function handleDisable() { } catch (e) { console.log("handleDisable - e", e); } finally { + await fetchServicesInfo(); store.set({ diagnosticsActions: { ...diagnosticsActions, @@ -3322,51 +3339,63 @@ async function handleDisable() { } function renderDiagnosticAvailableActionsWidget() { const diagnosticsActions = store.get().diagnosticsActions; + const servicesInfoWidget = store.get().servicesInfoWidget; console.log("renderDiagnosticActionsWidget"); + 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 + onClick: handleRestart, + disabled: atLeastOneServiceCommandLoading }, start: { loading: diagnosticsActions.start.loading, - visible: true, - onClick: handleStart + visible: !singBoxRunning, + onClick: handleStart, + disabled: atLeastOneServiceCommandLoading }, stop: { loading: diagnosticsActions.stop.loading, - visible: true, - onClick: handleStop + visible: singBoxRunning, + onClick: handleStop, + disabled: atLeastOneServiceCommandLoading }, enable: { loading: diagnosticsActions.enable.loading, - visible: true, - onClick: handleEnable + visible: !podkopEnabled, + onClick: handleEnable, + disabled: atLeastOneServiceCommandLoading }, disable: { loading: diagnosticsActions.disable.loading, - visible: true, - onClick: handleDisable + visible: podkopEnabled, + onClick: handleDisable, + disabled: atLeastOneServiceCommandLoading }, globalCheck: { loading: diagnosticsActions.globalCheck.loading, visible: true, onClick: () => { - } + }, + disabled: atLeastOneServiceCommandLoading }, viewLogs: { loading: diagnosticsActions.viewLogs.loading, visible: true, onClick: () => { - } + }, + disabled: atLeastOneServiceCommandLoading }, showSingBoxConfig: { loading: diagnosticsActions.showSingBoxConfig.loading, visible: true, onClick: () => { - } + }, + disabled: atLeastOneServiceCommandLoading } }); return preserveScrollForPage(() => { @@ -3411,7 +3440,7 @@ async function onStoreUpdate2(next, prev, diff) { if (diff.diagnosticsRunAction) { renderDiagnosticRunActionWidget(); } - if (diff.diagnosticsActions) { + if (diff.diagnosticsActions || diff.servicesInfoWidget) { renderDiagnosticAvailableActionsWidget(); } } @@ -3440,6 +3469,7 @@ async function initController2() { renderDiagnosticRunActionWidget(); renderDiagnosticAvailableActionsWidget(); renderDiagnosticSystemInfoWidget(); + fetchServicesInfo(); }); }