diff --git a/fe-app-tachyon/package-lock.json b/fe-app-tachyon/package-lock.json index 398ddd73..8892d3c4 100644 --- a/fe-app-tachyon/package-lock.json +++ b/fe-app-tachyon/package-lock.json @@ -1,12 +1,12 @@ { "name": "fe-app-tachyon", - "version": "1.3.17", + "version": "1.3.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fe-app-tachyon", - "version": "1.3.17", + "version": "1.3.18", "license": "MIT", "dependencies": { "@esbuild/win32-x64": "^0.28.2", diff --git a/fe-app-tachyon/package.json b/fe-app-tachyon/package.json index 6b697415..08967ab8 100644 --- a/fe-app-tachyon/package.json +++ b/fe-app-tachyon/package.json @@ -1,6 +1,6 @@ { "name": "fe-app-tachyon", - "version": "1.3.17", + "version": "1.3.18", "license": "MIT", "type": "module", "scripts": { diff --git a/fe-app-tachyon/src/tachyon/fetchers/fetchServicesInfo.ts b/fe-app-tachyon/src/tachyon/fetchers/fetchServicesInfo.ts index dcd93cee..7518c87f 100644 --- a/fe-app-tachyon/src/tachyon/fetchers/fetchServicesInfo.ts +++ b/fe-app-tachyon/src/tachyon/fetchers/fetchServicesInfo.ts @@ -59,6 +59,9 @@ export async function fetchServicesInfo() { failed: !tachyon.success || !singbox.success, data: { singbox: singbox.success ? singbox.data.running : previousData.singbox, + singboxMemoryMb: singbox.success + ? singbox.data.memory_rss_mb + : previousData.singboxMemoryMb, tachyonRunning: tachyon.success ? tachyon.data.running : previousData.tachyonRunning, @@ -71,6 +74,8 @@ export async function fetchServicesInfo() { watchdogRunning: watchdog.success ? Number((watchdog.data as { running: boolean }).running) : previousData.watchdogRunning, + zapret2Running: previousData.zapret2Running, + zapret2MemoryMb: previousData.zapret2MemoryMb, }, }, }); diff --git a/fe-app-tachyon/src/tachyon/helpers/singBoxVariant.ts b/fe-app-tachyon/src/tachyon/helpers/singBoxVariant.ts index 5a454f22..7bd2a68c 100644 --- a/fe-app-tachyon/src/tachyon/helpers/singBoxVariant.ts +++ b/fe-app-tachyon/src/tachyon/helpers/singBoxVariant.ts @@ -56,6 +56,8 @@ export function formatSingBoxVersion(value: SingBoxVariantFields) { variant = _('compressed'); } else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) { variant = _('lx'); + } else if (normalizedValue.sing_box_extended) { + variant = _('extended'); } else if (normalizedValue.sing_box_tiny) { variant = _('tiny'); } @@ -66,15 +68,69 @@ export function formatSingBoxVersion(value: SingBoxVariantFields) { export function normalizeSingBoxVariantFields( value: T, ): T { - const versionExtended = isExtendedSingBoxVersion(value.sing_box_version); + const version = String(value.sing_box_version || ''); + const versionExtended = isExtendedSingBoxVersion(version); + const versionLx = version.includes('-lx'); const singBoxExtended = Boolean(value.sing_box_extended) || versionExtended; + const singBoxLx = + singBoxExtended && (Boolean(value.sing_box_lx) || versionLx); return { ...value, sing_box_extended: singBoxExtended ? 1 : 0, sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 1 : 0, sing_box_compressed: singBoxExtended && value.sing_box_compressed ? 1 : 0, - sing_box_lx: singBoxExtended && value.sing_box_lx ? 1 : 0, + sing_box_lx: singBoxLx ? 1 : 0, sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0, } as T; } + +export function renderSingBoxVariantBadge( + value: SingBoxVariantFields, +): HTMLSpanElement | null { + const version = String(value.sing_box_version || ''); + if (!version || isVersionPlaceholder(version)) { + return null; + } + + const normalized = normalizeSingBoxVariantFields(value); + const badge = document.createElement('span'); + badge.className = 'tachyon-badge'; + badge.style.display = 'inline-block'; + badge.style.marginLeft = '8px'; + badge.style.padding = '2px 8px'; + badge.style.borderRadius = '9999px'; + badge.style.fontSize = '11px'; + badge.style.fontWeight = '600'; + badge.style.lineHeight = '16px'; + badge.style.verticalAlign = 'middle'; + + if (normalized.sing_box_lx) { + badge.textContent = 'Leadaxe (lx)'; + badge.style.backgroundColor = 'rgba(16, 185, 129, 0.15)'; + badge.style.color = '#10b981'; + badge.style.border = '1px solid rgba(16, 185, 129, 0.35)'; + } else if (normalized.sing_box_compressed) { + badge.textContent = 'Extended (compressed)'; + badge.style.backgroundColor = 'rgba(59, 130, 246, 0.15)'; + badge.style.color = '#3b82f6'; + badge.style.border = '1px solid rgba(59, 130, 246, 0.35)'; + } else if (normalized.sing_box_extended) { + badge.textContent = 'Extended (shtorm-7)'; + badge.style.backgroundColor = 'rgba(59, 130, 246, 0.15)'; + badge.style.color = '#3b82f6'; + badge.style.border = '1px solid rgba(59, 130, 246, 0.35)'; + } else if (normalized.sing_box_tiny) { + badge.textContent = 'Tiny'; + badge.style.backgroundColor = 'rgba(245, 158, 11, 0.15)'; + badge.style.color = '#f59e0b'; + badge.style.border = '1px solid rgba(245, 158, 11, 0.35)'; + } else { + badge.textContent = 'Official'; + badge.style.backgroundColor = 'rgba(107, 114, 128, 0.15)'; + badge.style.color = '#6b7280'; + badge.style.border = '1px solid rgba(107, 114, 128, 0.35)'; + } + + return badge; +} diff --git a/fe-app-tachyon/src/tachyon/services/store.service.ts b/fe-app-tachyon/src/tachyon/services/store.service.ts index 0b360bb2..125be6c3 100644 --- a/fe-app-tachyon/src/tachyon/services/store.service.ts +++ b/fe-app-tachyon/src/tachyon/services/store.service.ts @@ -167,6 +167,9 @@ export interface StoreType { tachyonEnabled: number; tachyonStatus: string; watchdogRunning: number; + singboxMemoryMb?: number; + zapret2Running?: number; + zapret2MemoryMb?: number; }; }; tailscaleWidget: { diff --git a/fe-app-tachyon/src/tachyon/services/uiState.service.ts b/fe-app-tachyon/src/tachyon/services/uiState.service.ts index 180253be..42d3d5d1 100644 --- a/fe-app-tachyon/src/tachyon/services/uiState.service.ts +++ b/fe-app-tachyon/src/tachyon/services/uiState.service.ts @@ -100,10 +100,17 @@ function applyServiceState(uiState: Tachyon.UiState) { failed: false, data: { singbox: uiState.service.sing_box.running, + singboxMemoryMb: uiState.service.sing_box.memory_rss_mb, tachyonRunning: uiState.service.tachyon.running, tachyonEnabled: uiState.service.tachyon.enabled, tachyonStatus: uiState.service.tachyon.status, watchdogRunning: store.get().servicesInfoWidget.data.watchdogRunning, + zapret2Running: uiState.service.zapret2 + ? uiState.service.zapret2.running + : undefined, + zapret2MemoryMb: uiState.service.zapret2 + ? uiState.service.zapret2.memory_rss_mb + : undefined, }, }, diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo), diff --git a/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts b/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts index 8178221b..1478ddab 100644 --- a/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts +++ b/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts @@ -1885,26 +1885,47 @@ async function renderServicesInfoWidget() { 'dashboard-widget-service-info', 'servicesInfoWidget', _('Services info'), - (data) => [ - { - key: 'Tachyon', - value: data.tachyonRunning ? _('✔ Running') : _('✘ Stopped'), - attributes: { - class: data.tachyonRunning - ? 'tachyon_dashboard-page__widgets-section__item__row--success' - : 'tachyon_dashboard-page__widgets-section__item__row--error', + (data) => { + const items = [ + { + key: 'Tachyon', + value: data.tachyonRunning ? _('✔ Running') : _('✘ Stopped'), + attributes: { + class: data.tachyonRunning + ? 'tachyon_dashboard-page__widgets-section__item__row--success' + : 'tachyon_dashboard-page__widgets-section__item__row--error', + }, }, - }, - { - key: 'Sing-box', - value: data.singbox ? _('✔ Running') : _('✘ Stopped'), - attributes: { - class: data.singbox - ? 'tachyon_dashboard-page__widgets-section__item__row--success' - : 'tachyon_dashboard-page__widgets-section__item__row--error', + { + key: 'Sing-box', + value: data.singbox + ? data.singboxMemoryMb + ? `${_('✔ Running')} (${data.singboxMemoryMb} MB)` + : _('✔ Running') + : _('✘ Stopped'), + attributes: { + class: data.singbox + ? 'tachyon_dashboard-page__widgets-section__item__row--success' + : 'tachyon_dashboard-page__widgets-section__item__row--error', + }, }, - }, - ], + ]; + + if (data.zapret2Running) { + items.push({ + key: 'Zapret2', + value: data.zapret2MemoryMb + ? `${_('✔ Running')} (${data.zapret2MemoryMb} MB)` + : _('✔ Running'), + attributes: { + class: + 'tachyon_dashboard-page__widgets-section__item__row--success', + }, + }); + } + + return items; + }, 'renderServicesInfoWidget', ); } diff --git a/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts b/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts index e6118e0b..c8ac7ed5 100644 --- a/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts +++ b/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts @@ -19,6 +19,7 @@ import { shouldShowLoadingForRestoredAction } from '../../helpers/restoredAction import { formatSingBoxVersion, normalizeSingBoxVariantFields, + renderSingBoxVariantBadge, } from '../../helpers/singBoxVariant'; import { shouldApplyCompletedComponentActionResult } from './componentActionCompletion'; import { @@ -115,6 +116,7 @@ interface ComponentCard { releaseUrl?: string; repoUrl?: string; actions: ComponentActionButton[]; + badgeNode?: Node | null; } let updatesLifecycleRegistered = false; @@ -1271,6 +1273,9 @@ function getComponentCards(): ComponentCard[] { version: systemInfoLoading ? _('Loading...') : formatSingBoxVersion(systemInfo), + badgeNode: systemInfoLoading + ? null + : renderSingBoxVariantBadge(systemInfo), latestVersion: getLatestVersion('sing_box'), releaseUrl: getGitHubReleaseUrl('sing_box'), repoUrl: @@ -1350,12 +1355,17 @@ function renderComponentCard(card: ComponentCard) { const headerChildren: Node[] = [ E('b', { class: 'tachyon_updates-page__component__title' }, card.title), + ]; + if (card.badgeNode) { + headerChildren.push(card.badgeNode); + } + headerChildren.push( E( 'span', { class: 'tachyon_updates-page__component__header-version' }, card.version, ), - ]; + ); if (card.repoUrl) { headerChildren.push( E( diff --git a/fe-app-tachyon/src/tachyon/types.ts b/fe-app-tachyon/src/tachyon/types.ts index e0c172eb..6ffeb69f 100644 --- a/fe-app-tachyon/src/tachyon/types.ts +++ b/fe-app-tachyon/src/tachyon/types.ts @@ -571,6 +571,7 @@ export namespace Tachyon { running: number; enabled: number; status: string; + memory_rss_mb?: number; } export interface GetSystemInfo { @@ -654,6 +655,10 @@ export namespace Tachyon { service: { tachyon: GetStatus; sing_box: GetSingBoxStatus; + zapret2?: { + running: number; + memory_rss_mb?: number; + }; }; capabilities: GetUiCapabilities; actions: { diff --git a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js index 2b0a9912..0dc5f27e 100644 --- a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js +++ b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js @@ -6261,23 +6261,72 @@ function formatSingBoxVersion(value) { variant = _("compressed"); } else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) { variant = _("lx"); + } else if (normalizedValue.sing_box_extended) { + variant = _("extended"); } else if (normalizedValue.sing_box_tiny) { variant = _("tiny"); } return variant ? `${version} (${variant})` : version; } function normalizeSingBoxVariantFields(value) { - const versionExtended = isExtendedSingBoxVersion(value.sing_box_version); + const version = String(value.sing_box_version || ""); + const versionExtended = isExtendedSingBoxVersion(version); + const versionLx = version.includes("-lx"); const singBoxExtended = Boolean(value.sing_box_extended) || versionExtended; + const singBoxLx = singBoxExtended && (Boolean(value.sing_box_lx) || versionLx); return { ...value, sing_box_extended: singBoxExtended ? 1 : 0, sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 1 : 0, sing_box_compressed: singBoxExtended && value.sing_box_compressed ? 1 : 0, - sing_box_lx: singBoxExtended && value.sing_box_lx ? 1 : 0, + sing_box_lx: singBoxLx ? 1 : 0, sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0 }; } +function renderSingBoxVariantBadge(value) { + const version = String(value.sing_box_version || ""); + if (!version || isVersionPlaceholder(version)) { + return null; + } + const normalized = normalizeSingBoxVariantFields(value); + const badge = document.createElement("span"); + badge.className = "tachyon-badge"; + badge.style.display = "inline-block"; + badge.style.marginLeft = "8px"; + badge.style.padding = "2px 8px"; + badge.style.borderRadius = "9999px"; + badge.style.fontSize = "11px"; + badge.style.fontWeight = "600"; + badge.style.lineHeight = "16px"; + badge.style.verticalAlign = "middle"; + if (normalized.sing_box_lx) { + badge.textContent = "Leadaxe (lx)"; + badge.style.backgroundColor = "rgba(16, 185, 129, 0.15)"; + badge.style.color = "#10b981"; + badge.style.border = "1px solid rgba(16, 185, 129, 0.35)"; + } else if (normalized.sing_box_compressed) { + badge.textContent = "Extended (compressed)"; + badge.style.backgroundColor = "rgba(59, 130, 246, 0.15)"; + badge.style.color = "#3b82f6"; + badge.style.border = "1px solid rgba(59, 130, 246, 0.35)"; + } else if (normalized.sing_box_extended) { + badge.textContent = "Extended (shtorm-7)"; + badge.style.backgroundColor = "rgba(59, 130, 246, 0.15)"; + badge.style.color = "#3b82f6"; + badge.style.border = "1px solid rgba(59, 130, 246, 0.35)"; + } else if (normalized.sing_box_tiny) { + badge.textContent = "Tiny"; + badge.style.backgroundColor = "rgba(245, 158, 11, 0.15)"; + badge.style.color = "#f59e0b"; + badge.style.border = "1px solid rgba(245, 158, 11, 0.35)"; + } else { + badge.textContent = "Official"; + badge.style.backgroundColor = "rgba(107, 114, 128, 0.15)"; + badge.style.color = "#6b7280"; + badge.style.border = "1px solid rgba(107, 114, 128, 0.35)"; + } + return badge; +} // src/tachyon/services/localActionOverlay.service.ts var componentActions = /* @__PURE__ */ new Set(); @@ -6398,10 +6447,13 @@ function applyServiceState(uiState) { failed: false, data: { singbox: uiState.service.sing_box.running, + singboxMemoryMb: uiState.service.sing_box.memory_rss_mb, tachyonRunning: uiState.service.tachyon.running, tachyonEnabled: uiState.service.tachyon.enabled, tachyonStatus: uiState.service.tachyon.status, - watchdogRunning: store.get().servicesInfoWidget.data.watchdogRunning + watchdogRunning: store.get().servicesInfoWidget.data.watchdogRunning, + zapret2Running: uiState.service.zapret2 ? uiState.service.zapret2.running : void 0, + zapret2MemoryMb: uiState.service.zapret2 ? uiState.service.zapret2.memory_rss_mb : void 0 } }, diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo) @@ -7024,10 +7076,13 @@ async function fetchServicesInfo() { failed: !tachyon.success || !singbox.success, data: { singbox: singbox.success ? singbox.data.running : previousData.singbox, + singboxMemoryMb: singbox.success ? singbox.data.memory_rss_mb : previousData.singboxMemoryMb, tachyonRunning: tachyon.success ? tachyon.data.running : previousData.tachyonRunning, tachyonEnabled: tachyon.success ? tachyon.data.enabled : previousData.tachyonEnabled, tachyonStatus: tachyon.success ? tachyon.data.status : previousData.tachyonStatus, - watchdogRunning: watchdog.success ? Number(watchdog.data.running) : previousData.watchdogRunning + watchdogRunning: watchdog.success ? Number(watchdog.data.running) : previousData.watchdogRunning, + zapret2Running: previousData.zapret2Running, + zapret2MemoryMb: previousData.zapret2MemoryMb } } }); @@ -8537,22 +8592,34 @@ async function renderServicesInfoWidget() { "dashboard-widget-service-info", "servicesInfoWidget", _("Services info"), - (data) => [ - { - key: "Tachyon", - value: data.tachyonRunning ? _("\u2714 Running") : _("\u2718 Stopped"), - attributes: { - class: data.tachyonRunning ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" - } - }, - { - key: "Sing-box", - value: data.singbox ? _("\u2714 Running") : _("\u2718 Stopped"), - attributes: { - class: data.singbox ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" + (data) => { + const items = [ + { + key: "Tachyon", + value: data.tachyonRunning ? _("\u2714 Running") : _("\u2718 Stopped"), + attributes: { + class: data.tachyonRunning ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" + } + }, + { + key: "Sing-box", + value: data.singbox ? data.singboxMemoryMb ? `${_("\u2714 Running")} (${data.singboxMemoryMb} MB)` : _("\u2714 Running") : _("\u2718 Stopped"), + attributes: { + class: data.singbox ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" + } } + ]; + if (data.zapret2Running) { + items.push({ + key: "Zapret2", + value: data.zapret2MemoryMb ? `${_("\u2714 Running")} (${data.zapret2MemoryMb} MB)` : _("\u2714 Running"), + attributes: { + class: "tachyon_dashboard-page__widgets-section__item__row--success" + } + }); } - ], + return items; + }, "renderServicesInfoWidget" ); } @@ -20142,6 +20209,7 @@ function getComponentCards() { column: 0, title: "Sing-box", version: systemInfoLoading ? _("Loading...") : formatSingBoxVersion(systemInfo), + badgeNode: systemInfoLoading ? null : renderSingBoxVariantBadge(systemInfo), latestVersion: getLatestVersion("sing_box"), releaseUrl: getGitHubReleaseUrl("sing_box"), repoUrl: systemInfo.sing_box_repo_url || (singBoxLx ? "https://github.com/Leadaxe/sing-box-lx" : singBoxExtended || singBoxExtendedCompressed ? "https://github.com/shtorm-7/sing-box-extended" : COMPONENT_REPO_URLS.sing_box), @@ -20195,13 +20263,18 @@ function renderComponentCard(card) { const systemInfoLoading = isSystemInfoLoading(); const checkResult = getVisibleCheckResult(card.component); const headerChildren = [ - E("b", { class: "tachyon_updates-page__component__title" }, card.title), + E("b", { class: "tachyon_updates-page__component__title" }, card.title) + ]; + if (card.badgeNode) { + headerChildren.push(card.badgeNode); + } + headerChildren.push( E( "span", { class: "tachyon_updates-page__component__header-version" }, card.version ) - ]; + ); if (card.repoUrl) { headerChildren.push( E( diff --git a/tachyon/files/usr/lib/components/action.uc b/tachyon/files/usr/lib/components/action.uc index ae81edda..75fdfec5 100644 --- a/tachyon/files/usr/lib/components/action.uc +++ b/tachyon/files/usr/lib/components/action.uc @@ -839,7 +839,8 @@ function clear_version_caches() { } function managed_sing_box_service_installed() { - return file_exists("/etc/init.d/sing-box") && index(read_file("/etc/init.d/sing-box"), SB_MANAGED_SERVICE_MARKER) >= 0; + let data = fs.readfile("/etc/init.d/sing-box"); + return data != null && index(as_string(data), SB_MANAGED_SERVICE_MARKER) >= 0 && index(as_string(data), "GOGC") >= 0; } function managed_sing_box_service_text() { @@ -851,7 +852,7 @@ function managed_sing_box_service_text() { "start_service() {\n" + " config_load \"sing-box\"\n" + " local enabled config_file working_directory\n" + - " local log_stderr\n\n" + + " local log_stderr mem_total_kb mem_limit_mb gogc scale scale_pct\n\n" + " config_get_bool enabled \"main\" \"enabled\" \"0\"\n" + " [ \"$enabled\" -eq \"1\" ] || return 0\n\n" + " config_get config_file \"main\" \"conffile\" \"/etc/sing-box/config.json\"\n" + @@ -863,6 +864,34 @@ function managed_sing_box_service_text() { " procd_set_param stderr \"$log_stderr\"\n" + " procd_set_param limits core=\"unlimited\"\n" + " procd_set_param limits nofile=\"1000000 1000000\"\n" + + " mem_total_kb=\"$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)\"\n" + + " if [ \"$mem_total_kb\" -gt 0 ]; then\n" + + " if [ \"$mem_total_kb\" -lt 131072 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 16 ] && mem_limit_mb=16\n" + + " gogc=20\n" + + " elif [ \"$mem_total_kb\" -lt 262144 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 35 / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 24 ] && mem_limit_mb=24\n" + + " gogc=25\n" + + " elif [ \"$mem_total_kb\" -lt 524288 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 40 / 100 ))\n" + + " gogc=30\n" + + " else\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" + + " [ \"$mem_limit_mb\" -gt 256 ] && mem_limit_mb=256\n" + + " gogc=40\n" + + " fi\n" + + " if [ -r \"/etc/tachyon/mem_scale\" ]; then\n" + + " scale=\"$(cat /etc/tachyon/mem_scale 2>/dev/null)\"\n" + + " scale_pct=\"$(awk -v s=\"$scale\" 'BEGIN { pct=int(s*100); if (pct < 20) pct=20; if (pct > 100) pct=100; print pct }')\"\n" + + " if [ -n \"$scale_pct\" ] && [ \"$scale_pct\" -lt 100 ]; then\n" + + " mem_limit_mb=$(( mem_limit_mb * scale_pct / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 12 ] && mem_limit_mb=12\n" + + " fi\n" + + " fi\n" + + " procd_set_param env GOMEMLIMIT=\"${mem_limit_mb}MiB\" GOGC=\"$gogc\"\n" + + " fi\n" + " procd_set_param term_timeout 15\n" + " procd_set_param respawn\n" + " procd_close_instance\n" + diff --git a/tachyon/files/usr/lib/config/validator.uc b/tachyon/files/usr/lib/config/validator.uc index 06793bee..c9e32ef5 100644 --- a/tachyon/files/usr/lib/config/validator.uc +++ b/tachyon/files/usr/lib/config/validator.uc @@ -2082,7 +2082,7 @@ function managed_sing_box_service_script(marker) { "start_service() {\n" + " config_load \"sing-box\"\n" + " local enabled config_file working_directory\n" + - " local log_stderr\n" + + " local log_stderr mem_total_kb mem_limit_mb gogc scale scale_pct\n" + "\n" + " config_get_bool enabled \"main\" \"enabled\" \"0\"\n" + " [ \"$enabled\" -eq \"1\" ] || return 0\n" + @@ -2097,6 +2097,34 @@ function managed_sing_box_service_script(marker) { " procd_set_param stderr \"$log_stderr\"\n" + " procd_set_param limits core=\"unlimited\"\n" + " procd_set_param limits nofile=\"1000000 1000000\"\n" + + " mem_total_kb=\"$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)\"\n" + + " if [ \"$mem_total_kb\" -gt 0 ]; then\n" + + " if [ \"$mem_total_kb\" -lt 131072 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 16 ] && mem_limit_mb=16\n" + + " gogc=20\n" + + " elif [ \"$mem_total_kb\" -lt 262144 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 35 / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 24 ] && mem_limit_mb=24\n" + + " gogc=25\n" + + " elif [ \"$mem_total_kb\" -lt 524288 ]; then\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 40 / 100 ))\n" + + " gogc=30\n" + + " else\n" + + " mem_limit_mb=$(( mem_total_kb / 1024 * 30 / 100 ))\n" + + " [ \"$mem_limit_mb\" -gt 256 ] && mem_limit_mb=256\n" + + " gogc=40\n" + + " fi\n" + + " if [ -r \"/etc/tachyon/mem_scale\" ]; then\n" + + " scale=\"$(cat /etc/tachyon/mem_scale 2>/dev/null)\"\n" + + " scale_pct=\"$(awk -v s=\"$scale\" 'BEGIN { pct=int(s*100); if (pct < 20) pct=20; if (pct > 100) pct=100; print pct }')\"\n" + + " if [ -n \"$scale_pct\" ] && [ \"$scale_pct\" -lt 100 ]; then\n" + + " mem_limit_mb=$(( mem_limit_mb * scale_pct / 100 ))\n" + + " [ \"$mem_limit_mb\" -lt 12 ] && mem_limit_mb=12\n" + + " fi\n" + + " fi\n" + + " procd_set_param env GOMEMLIMIT=\"${mem_limit_mb}MiB\" GOGC=\"$gogc\"\n" + + " fi\n" + " procd_set_param term_timeout 15\n" + " procd_set_param respawn\n" + " procd_close_instance\n" + diff --git a/tachyon/files/usr/lib/nft/apply.uc b/tachyon/files/usr/lib/nft/apply.uc index 279a7e1b..5abd8858 100644 --- a/tachyon/files/usr/lib/nft/apply.uc +++ b/tachyon/files/usr/lib/nft/apply.uc @@ -1959,11 +1959,13 @@ function ensure_bridge_netfilter_disabled() { // TCP keepalive: detect dead connections in ~90s instead of kernel default (hours). // Conntrack: expire stale TCP entries in 10 min instead of 5 days. +// Buffers & TCP fastopen: accelerate QUIC/Hysteria2 and reduce TLS handshake RTT. function apply_connection_tuning() { let sysctls = [ [ "net.ipv4.tcp_keepalive_time", "60" ], [ "net.ipv4.tcp_keepalive_intvl", "10" ], [ "net.ipv4.tcp_keepalive_probes", "3" ], + [ "net.ipv4.tcp_fastopen", "3" ], [ "net.netfilter.nf_conntrack_tcp_timeout_established", "600" ], [ "net.netfilter.nf_conntrack_tcp_timeout_time_wait", "30" ] ]; @@ -1974,8 +1976,21 @@ function apply_connection_tuning() { if (!run_args_quiet([ "sysctl", "-w", pair[0] + "=" + pair[1] ])) ok = false; } + + let ct_max = int(trim(command_output_from_args([ "sysctl", "-n", "net.netfilter.nf_conntrack_max" ]) || "0")); + if (ct_max > 0 && ct_max < 65536) + run_args_quiet([ "sysctl", "-w", "net.netfilter.nf_conntrack_max=65536" ]); + + let rmem_max = int(trim(command_output_from_args([ "sysctl", "-n", "net.core.rmem_max" ]) || "0")); + if (rmem_max > 0 && rmem_max < 2621440) + run_args_quiet([ "sysctl", "-w", "net.core.rmem_max=2621440" ]); + + let wmem_max = int(trim(command_output_from_args([ "sysctl", "-n", "net.core.wmem_max" ]) || "0")); + if (wmem_max > 0 && wmem_max < 2621440) + run_args_quiet([ "sysctl", "-w", "net.core.wmem_max=2621440" ]); + if (ok) - log_debug("Connection tuning applied: tcp_keepalive=60/10/3, conntrack_established=600"); + log_debug("Connection tuning applied: tcp_keepalive=60/10/3, tcp_fastopen=3, conntrack_established=600"); return ok; } diff --git a/tachyon/files/usr/lib/service/api.uc b/tachyon/files/usr/lib/service/api.uc index bf3ea2a3..521a8774 100644 --- a/tachyon/files/usr/lib/service/api.uc +++ b/tachyon/files/usr/lib/service/api.uc @@ -103,6 +103,19 @@ function get_system_status() { let sb_running = command_success_from_args(["pidof", "sing-box"]); status_obj.singbox_running = sb_running; status_obj.singbox = sb_running ? "🟢 running" : "🔴 stopped"; + let sb_variant = trim(as_string(fs.readfile("/etc/tachyon/sing-box-variant") || "")); + if (sb_variant != "") + status_obj.singbox_variant = sb_variant; + + // zapret2 status + let z2_installed = fs.stat("/opt/zapret2/nfq2/nfqws2") != null; + status_obj.zapret2_installed = z2_installed; + if (z2_installed) { + let z2_running = command_success_from_args(["pidof", "nfqws2"]); + status_obj.zapret2_running = z2_running; + status_obj.zapret2 = z2_running ? "🟢 running" : "🔴 stopped"; + } + status_obj.tachyon_running = command_success_from_args(["/etc/init.d/tachyon", "status"]); // Active servers for all Selector groups diff --git a/tachyon/files/usr/lib/service/telegram.uc b/tachyon/files/usr/lib/service/telegram.uc index e7a9d654..2b95eef1 100644 --- a/tachyon/files/usr/lib/service/telegram.uc +++ b/tachyon/files/usr/lib/service/telegram.uc @@ -864,7 +864,8 @@ function view_status(token, chat_id, msg_id) { t("status_uptime") + ": " + sys.uptime + "\n" + t("status_cpu") + ": " + sys.cpu + "\n" + "RAM: " + sys.ram_avail + "MB / " + sys.ram_total + "MB\n\n" + - "sing-box: " + sys.singbox + "\n" + + "sing-box: " + sys.singbox + (sys.singbox_variant ? " [" + sys.singbox_variant + "]" : "") + "\n" + + (sys.zapret2_installed ? "zapret2: " + sys.zapret2 + "\n" : "") + "Watchdog: " + (sys.watchdog_running ? "🟢 running" : "🔴 stopped") + "\n\n" + "WAN: " + (sys.wan_ip || "?") + " " + (conn && conn.direct ? "✅" : "❌") + "\n" + @@ -1754,9 +1755,12 @@ function view_system_info(token, chat_id, msg_id) { text += "🔧 Tachyon: " + as_string(info.tachyon_version || "N/A") + "\n"; text += "📱 LuCI App: " + as_string(info.luci_app_version || "N/A") + "\n\n"; text += "📦 sing-box: " + as_string(info.sing_box_version || "N/A") + "\n"; - if (info.sing_box_extended) text += " Extended: ✅\n"; - if (info.sing_box_tiny) text += " Tiny: ✅\n"; - if (info.sing_box_tailscale) text += " Tailscale: ✅\n"; + if (info.sing_box_lx == 1) text += " Fork: Leadaxe (lx) 🟢\n"; + else if (info.sing_box_compressed == 1) text += " Fork: Extended (compressed) 🔵\n"; + else if (info.sing_box_extended == 1) text += " Fork: Extended (shtorm-7) 🔵\n"; + else if (info.sing_box_tiny == 1) text += " Variant: Tiny 🟡\n"; + else text += " Variant: Official / Stock ⚪\n"; + if (info.sing_box_tailscale == 1) text += " Tailscale: ✅\n"; text += "\n"; if (info.zapret_installed == "1") text += "🛡 Zapret: " + as_string(info.zapret_version || "?") + "\n"; if (info.zapret2_installed == "1") text += "🛡 Zapret2: " + as_string(info.zapret2_version || "?") + "\n"; diff --git a/tachyon/files/usr/lib/service/ui.uc b/tachyon/files/usr/lib/service/ui.uc index eb2c772a..893e4a68 100644 --- a/tachyon/files/usr/lib/service/ui.uc +++ b/tachyon/files/usr/lib/service/ui.uc @@ -1044,6 +1044,24 @@ function ui_capabilities_json() { write_json(capability_flags()); } +function process_memory_rss_mb(process_name) { + let output = common.command_output_from_args([ "pidof", process_name ]); + let pids = split(trim(as_string(output)), /[ \t\r\n]+/); + let total_kb = 0; + for (let p in pids) { + let pid = trim(as_string(p)); + if (!pid || match(pid, /^[0-9]+$/) == null) + continue; + let status = fs.readfile("/proc/" + pid + "/status"); + if (!status) + continue; + let m = match(status, /VmRSS:[ \t]+([0-9]+)/); + if (m) + total_kb += int(m[1]); + } + return total_kb > 0 ? int(total_kb / 1024) : 0; +} + function current_ui_state_json() { refresh_action_dirs(); @@ -1065,6 +1083,9 @@ function current_ui_state_json() { else if (active_action == "reload") tachyon_status = "reloading"; + let sing_box_rss = sing_box_is_running ? process_memory_rss_mb("sing-box") : 0; + let zapret2_rss = file_executable(ZAPRET2_PROVIDER_NFQWS2_BIN) ? process_memory_rss_mb("nfqws2") : 0; + write_json({ service: { tachyon: { @@ -1076,7 +1097,12 @@ function current_ui_state_json() { sing_box: { running: sing_box_is_running, enabled: sing_box_is_enabled, - status: sing_box_status + status: sing_box_status, + memory_rss_mb: sing_box_rss + }, + zapret2: { + running: zapret2_rss > 0 ? 1 : 0, + memory_rss_mb: zapret2_rss } }, capabilities,