feat: variant badges, live RSS RAM tracking, sysctl network tuning, telegram updates (1.3.18)

This commit is contained in:
Dushnilin
2026-09-03 23:23:39 +03:00
parent 875c483b37
commit 196782b18d
16 changed files with 348 additions and 53 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "fe-app-tachyon", "name": "fe-app-tachyon",
"version": "1.3.17", "version": "1.3.18",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "fe-app-tachyon", "name": "fe-app-tachyon",
"version": "1.3.17", "version": "1.3.18",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@esbuild/win32-x64": "^0.28.2", "@esbuild/win32-x64": "^0.28.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "fe-app-tachyon", "name": "fe-app-tachyon",
"version": "1.3.17", "version": "1.3.18",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -59,6 +59,9 @@ export async function fetchServicesInfo() {
failed: !tachyon.success || !singbox.success, failed: !tachyon.success || !singbox.success,
data: { data: {
singbox: singbox.success ? singbox.data.running : previousData.singbox, singbox: singbox.success ? singbox.data.running : previousData.singbox,
singboxMemoryMb: singbox.success
? singbox.data.memory_rss_mb
: previousData.singboxMemoryMb,
tachyonRunning: tachyon.success tachyonRunning: tachyon.success
? tachyon.data.running ? tachyon.data.running
: previousData.tachyonRunning, : previousData.tachyonRunning,
@@ -71,6 +74,8 @@ export async function fetchServicesInfo() {
watchdogRunning: watchdog.success watchdogRunning: watchdog.success
? Number((watchdog.data as { running: boolean }).running) ? Number((watchdog.data as { running: boolean }).running)
: previousData.watchdogRunning, : previousData.watchdogRunning,
zapret2Running: previousData.zapret2Running,
zapret2MemoryMb: previousData.zapret2MemoryMb,
}, },
}, },
}); });
@@ -56,6 +56,8 @@ export function formatSingBoxVersion(value: SingBoxVariantFields) {
variant = _('compressed'); variant = _('compressed');
} else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) { } else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) {
variant = _('lx'); variant = _('lx');
} else if (normalizedValue.sing_box_extended) {
variant = _('extended');
} else if (normalizedValue.sing_box_tiny) { } else if (normalizedValue.sing_box_tiny) {
variant = _('tiny'); variant = _('tiny');
} }
@@ -66,15 +68,69 @@ export function formatSingBoxVersion(value: SingBoxVariantFields) {
export function normalizeSingBoxVariantFields<T extends SingBoxVariantFields>( export function normalizeSingBoxVariantFields<T extends SingBoxVariantFields>(
value: T, value: T,
): 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 singBoxExtended = Boolean(value.sing_box_extended) || versionExtended;
const singBoxLx =
singBoxExtended && (Boolean(value.sing_box_lx) || versionLx);
return { return {
...value, ...value,
sing_box_extended: singBoxExtended ? 1 : 0, sing_box_extended: singBoxExtended ? 1 : 0,
sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 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_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, sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0,
} as T; } 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;
}
@@ -167,6 +167,9 @@ export interface StoreType {
tachyonEnabled: number; tachyonEnabled: number;
tachyonStatus: string; tachyonStatus: string;
watchdogRunning: number; watchdogRunning: number;
singboxMemoryMb?: number;
zapret2Running?: number;
zapret2MemoryMb?: number;
}; };
}; };
tailscaleWidget: { tailscaleWidget: {
@@ -100,10 +100,17 @@ function applyServiceState(uiState: Tachyon.UiState) {
failed: false, failed: false,
data: { data: {
singbox: uiState.service.sing_box.running, singbox: uiState.service.sing_box.running,
singboxMemoryMb: uiState.service.sing_box.memory_rss_mb,
tachyonRunning: uiState.service.tachyon.running, tachyonRunning: uiState.service.tachyon.running,
tachyonEnabled: uiState.service.tachyon.enabled, tachyonEnabled: uiState.service.tachyon.enabled,
tachyonStatus: uiState.service.tachyon.status, 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
: undefined,
zapret2MemoryMb: uiState.service.zapret2
? uiState.service.zapret2.memory_rss_mb
: undefined,
}, },
}, },
diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo), diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo),
@@ -1885,26 +1885,47 @@ async function renderServicesInfoWidget() {
'dashboard-widget-service-info', 'dashboard-widget-service-info',
'servicesInfoWidget', 'servicesInfoWidget',
_('Services info'), _('Services info'),
(data) => [ (data) => {
{ const items = [
key: 'Tachyon', {
value: data.tachyonRunning ? _('✔ Running') : _('✘ Stopped'), key: 'Tachyon',
attributes: { value: data.tachyonRunning ? _('✔ Running') : _('✘ Stopped'),
class: data.tachyonRunning attributes: {
? 'tachyon_dashboard-page__widgets-section__item__row--success' class: data.tachyonRunning
: 'tachyon_dashboard-page__widgets-section__item__row--error', ? 'tachyon_dashboard-page__widgets-section__item__row--success'
: 'tachyon_dashboard-page__widgets-section__item__row--error',
},
}, },
}, {
{ key: 'Sing-box',
key: 'Sing-box', value: data.singbox
value: data.singbox ? _('✔ Running') : _('✘ Stopped'), ? data.singboxMemoryMb
attributes: { ? `${_('✔ Running')} (${data.singboxMemoryMb} MB)`
class: data.singbox : _('✔ Running')
? 'tachyon_dashboard-page__widgets-section__item__row--success' : _('✘ Stopped'),
: 'tachyon_dashboard-page__widgets-section__item__row--error', 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', 'renderServicesInfoWidget',
); );
} }
@@ -19,6 +19,7 @@ import { shouldShowLoadingForRestoredAction } from '../../helpers/restoredAction
import { import {
formatSingBoxVersion, formatSingBoxVersion,
normalizeSingBoxVariantFields, normalizeSingBoxVariantFields,
renderSingBoxVariantBadge,
} from '../../helpers/singBoxVariant'; } from '../../helpers/singBoxVariant';
import { shouldApplyCompletedComponentActionResult } from './componentActionCompletion'; import { shouldApplyCompletedComponentActionResult } from './componentActionCompletion';
import { import {
@@ -115,6 +116,7 @@ interface ComponentCard {
releaseUrl?: string; releaseUrl?: string;
repoUrl?: string; repoUrl?: string;
actions: ComponentActionButton[]; actions: ComponentActionButton[];
badgeNode?: Node | null;
} }
let updatesLifecycleRegistered = false; let updatesLifecycleRegistered = false;
@@ -1271,6 +1273,9 @@ function getComponentCards(): ComponentCard[] {
version: systemInfoLoading version: systemInfoLoading
? _('Loading...') ? _('Loading...')
: formatSingBoxVersion(systemInfo), : formatSingBoxVersion(systemInfo),
badgeNode: systemInfoLoading
? null
: renderSingBoxVariantBadge(systemInfo),
latestVersion: getLatestVersion('sing_box'), latestVersion: getLatestVersion('sing_box'),
releaseUrl: getGitHubReleaseUrl('sing_box'), releaseUrl: getGitHubReleaseUrl('sing_box'),
repoUrl: repoUrl:
@@ -1350,12 +1355,17 @@ function renderComponentCard(card: ComponentCard) {
const headerChildren: Node[] = [ const headerChildren: Node[] = [
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( E(
'span', 'span',
{ class: 'tachyon_updates-page__component__header-version' }, { class: 'tachyon_updates-page__component__header-version' },
card.version, card.version,
), ),
]; );
if (card.repoUrl) { if (card.repoUrl) {
headerChildren.push( headerChildren.push(
E( E(
+5
View File
@@ -571,6 +571,7 @@ export namespace Tachyon {
running: number; running: number;
enabled: number; enabled: number;
status: string; status: string;
memory_rss_mb?: number;
} }
export interface GetSystemInfo { export interface GetSystemInfo {
@@ -654,6 +655,10 @@ export namespace Tachyon {
service: { service: {
tachyon: GetStatus; tachyon: GetStatus;
sing_box: GetSingBoxStatus; sing_box: GetSingBoxStatus;
zapret2?: {
running: number;
memory_rss_mb?: number;
};
}; };
capabilities: GetUiCapabilities; capabilities: GetUiCapabilities;
actions: { actions: {
@@ -6261,23 +6261,72 @@ function formatSingBoxVersion(value) {
variant = _("compressed"); variant = _("compressed");
} else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) { } else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) {
variant = _("lx"); variant = _("lx");
} else if (normalizedValue.sing_box_extended) {
variant = _("extended");
} else if (normalizedValue.sing_box_tiny) { } else if (normalizedValue.sing_box_tiny) {
variant = _("tiny"); variant = _("tiny");
} }
return variant ? `${version} (${variant})` : version; return variant ? `${version} (${variant})` : version;
} }
function normalizeSingBoxVariantFields(value) { 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 singBoxExtended = Boolean(value.sing_box_extended) || versionExtended;
const singBoxLx = singBoxExtended && (Boolean(value.sing_box_lx) || versionLx);
return { return {
...value, ...value,
sing_box_extended: singBoxExtended ? 1 : 0, sing_box_extended: singBoxExtended ? 1 : 0,
sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 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_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 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 // src/tachyon/services/localActionOverlay.service.ts
var componentActions = /* @__PURE__ */ new Set(); var componentActions = /* @__PURE__ */ new Set();
@@ -6398,10 +6447,13 @@ function applyServiceState(uiState) {
failed: false, failed: false,
data: { data: {
singbox: uiState.service.sing_box.running, singbox: uiState.service.sing_box.running,
singboxMemoryMb: uiState.service.sing_box.memory_rss_mb,
tachyonRunning: uiState.service.tachyon.running, tachyonRunning: uiState.service.tachyon.running,
tachyonEnabled: uiState.service.tachyon.enabled, tachyonEnabled: uiState.service.tachyon.enabled,
tachyonStatus: uiState.service.tachyon.status, 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) diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo)
@@ -7024,10 +7076,13 @@ async function fetchServicesInfo() {
failed: !tachyon.success || !singbox.success, failed: !tachyon.success || !singbox.success,
data: { data: {
singbox: singbox.success ? singbox.data.running : previousData.singbox, 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, tachyonRunning: tachyon.success ? tachyon.data.running : previousData.tachyonRunning,
tachyonEnabled: tachyon.success ? tachyon.data.enabled : previousData.tachyonEnabled, tachyonEnabled: tachyon.success ? tachyon.data.enabled : previousData.tachyonEnabled,
tachyonStatus: tachyon.success ? tachyon.data.status : previousData.tachyonStatus, 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", "dashboard-widget-service-info",
"servicesInfoWidget", "servicesInfoWidget",
_("Services info"), _("Services info"),
(data) => [ (data) => {
{ const items = [
key: "Tachyon", {
value: data.tachyonRunning ? _("\u2714 Running") : _("\u2718 Stopped"), key: "Tachyon",
attributes: { value: data.tachyonRunning ? _("\u2714 Running") : _("\u2718 Stopped"),
class: data.tachyonRunning ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" 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"), key: "Sing-box",
attributes: { value: data.singbox ? data.singboxMemoryMb ? `${_("\u2714 Running")} (${data.singboxMemoryMb} MB)` : _("\u2714 Running") : _("\u2718 Stopped"),
class: data.singbox ? "tachyon_dashboard-page__widgets-section__item__row--success" : "tachyon_dashboard-page__widgets-section__item__row--error" 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" "renderServicesInfoWidget"
); );
} }
@@ -20142,6 +20209,7 @@ function getComponentCards() {
column: 0, column: 0,
title: "Sing-box", title: "Sing-box",
version: systemInfoLoading ? _("Loading...") : formatSingBoxVersion(systemInfo), version: systemInfoLoading ? _("Loading...") : formatSingBoxVersion(systemInfo),
badgeNode: systemInfoLoading ? null : renderSingBoxVariantBadge(systemInfo),
latestVersion: getLatestVersion("sing_box"), latestVersion: getLatestVersion("sing_box"),
releaseUrl: getGitHubReleaseUrl("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), 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 systemInfoLoading = isSystemInfoLoading();
const checkResult = getVisibleCheckResult(card.component); const checkResult = getVisibleCheckResult(card.component);
const headerChildren = [ 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( E(
"span", "span",
{ class: "tachyon_updates-page__component__header-version" }, { class: "tachyon_updates-page__component__header-version" },
card.version card.version
) )
]; );
if (card.repoUrl) { if (card.repoUrl) {
headerChildren.push( headerChildren.push(
E( E(
+31 -2
View File
@@ -839,7 +839,8 @@ function clear_version_caches() {
} }
function managed_sing_box_service_installed() { 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() { function managed_sing_box_service_text() {
@@ -851,7 +852,7 @@ function managed_sing_box_service_text() {
"start_service() {\n" + "start_service() {\n" +
" config_load \"sing-box\"\n" + " config_load \"sing-box\"\n" +
" local enabled config_file working_directory\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" + " config_get_bool enabled \"main\" \"enabled\" \"0\"\n" +
" [ \"$enabled\" -eq \"1\" ] || return 0\n\n" + " [ \"$enabled\" -eq \"1\" ] || return 0\n\n" +
" config_get config_file \"main\" \"conffile\" \"/etc/sing-box/config.json\"\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 stderr \"$log_stderr\"\n" +
" procd_set_param limits core=\"unlimited\"\n" + " procd_set_param limits core=\"unlimited\"\n" +
" procd_set_param limits nofile=\"1000000 1000000\"\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 term_timeout 15\n" +
" procd_set_param respawn\n" + " procd_set_param respawn\n" +
" procd_close_instance\n" + " procd_close_instance\n" +
+29 -1
View File
@@ -2082,7 +2082,7 @@ function managed_sing_box_service_script(marker) {
"start_service() {\n" + "start_service() {\n" +
" config_load \"sing-box\"\n" + " config_load \"sing-box\"\n" +
" local enabled config_file working_directory\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" + "\n" +
" config_get_bool enabled \"main\" \"enabled\" \"0\"\n" + " config_get_bool enabled \"main\" \"enabled\" \"0\"\n" +
" [ \"$enabled\" -eq \"1\" ] || return 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 stderr \"$log_stderr\"\n" +
" procd_set_param limits core=\"unlimited\"\n" + " procd_set_param limits core=\"unlimited\"\n" +
" procd_set_param limits nofile=\"1000000 1000000\"\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 term_timeout 15\n" +
" procd_set_param respawn\n" + " procd_set_param respawn\n" +
" procd_close_instance\n" + " procd_close_instance\n" +
+16 -1
View File
@@ -1959,11 +1959,13 @@ function ensure_bridge_netfilter_disabled() {
// TCP keepalive: detect dead connections in ~90s instead of kernel default (hours). // TCP keepalive: detect dead connections in ~90s instead of kernel default (hours).
// Conntrack: expire stale TCP entries in 10 min instead of 5 days. // 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() { function apply_connection_tuning() {
let sysctls = [ let sysctls = [
[ "net.ipv4.tcp_keepalive_time", "60" ], [ "net.ipv4.tcp_keepalive_time", "60" ],
[ "net.ipv4.tcp_keepalive_intvl", "10" ], [ "net.ipv4.tcp_keepalive_intvl", "10" ],
[ "net.ipv4.tcp_keepalive_probes", "3" ], [ "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_established", "600" ],
[ "net.netfilter.nf_conntrack_tcp_timeout_time_wait", "30" ] [ "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] ])) if (!run_args_quiet([ "sysctl", "-w", pair[0] + "=" + pair[1] ]))
ok = false; 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) 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; return ok;
} }
+13
View File
@@ -103,6 +103,19 @@ function get_system_status() {
let sb_running = command_success_from_args(["pidof", "sing-box"]); let sb_running = command_success_from_args(["pidof", "sing-box"]);
status_obj.singbox_running = sb_running; status_obj.singbox_running = sb_running;
status_obj.singbox = sb_running ? "🟢 running" : "🔴 stopped"; 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"]); status_obj.tachyon_running = command_success_from_args(["/etc/init.d/tachyon", "status"]);
// Active servers for all Selector groups // Active servers for all Selector groups
+8 -4
View File
@@ -864,7 +864,8 @@ function view_status(token, chat_id, msg_id) {
t("status_uptime") + ": <code>" + sys.uptime + "</code>\n" + t("status_uptime") + ": <code>" + sys.uptime + "</code>\n" +
t("status_cpu") + ": <code>" + sys.cpu + "</code>\n" + t("status_cpu") + ": <code>" + sys.cpu + "</code>\n" +
"RAM: <code>" + sys.ram_avail + "MB / " + sys.ram_total + "MB</code>\n\n" + "RAM: <code>" + sys.ram_avail + "MB / " + sys.ram_total + "MB</code>\n\n" +
"sing-box: <code>" + sys.singbox + "</code>\n" + "sing-box: <code>" + sys.singbox + (sys.singbox_variant ? " [" + sys.singbox_variant + "]" : "") + "</code>\n" +
(sys.zapret2_installed ? "zapret2: <code>" + sys.zapret2 + "</code>\n" : "") +
"Watchdog: <code>" + (sys.watchdog_running ? "🟢 running" : "🔴 stopped") + "</code>\n\n" + "Watchdog: <code>" + (sys.watchdog_running ? "🟢 running" : "🔴 stopped") + "</code>\n\n" +
"WAN: <code>" + (sys.wan_ip || "?") + "</code> " + "WAN: <code>" + (sys.wan_ip || "?") + "</code> " +
(conn && conn.direct ? "✅" : "❌") + "\n" + (conn && conn.direct ? "✅" : "❌") + "\n" +
@@ -1754,9 +1755,12 @@ function view_system_info(token, chat_id, msg_id) {
text += "🔧 Tachyon: <code>" + as_string(info.tachyon_version || "N/A") + "</code>\n"; text += "🔧 Tachyon: <code>" + as_string(info.tachyon_version || "N/A") + "</code>\n";
text += "📱 LuCI App: <code>" + as_string(info.luci_app_version || "N/A") + "</code>\n\n"; text += "📱 LuCI App: <code>" + as_string(info.luci_app_version || "N/A") + "</code>\n\n";
text += "📦 sing-box: <code>" + as_string(info.sing_box_version || "N/A") + "</code>\n"; text += "📦 sing-box: <code>" + as_string(info.sing_box_version || "N/A") + "</code>\n";
if (info.sing_box_extended) text += " Extended: ✅\n"; if (info.sing_box_lx == 1) text += " Fork: Leadaxe (lx) 🟢\n";
if (info.sing_box_tiny) text += " Tiny: ✅\n"; else if (info.sing_box_compressed == 1) text += " Fork: Extended (compressed) 🔵\n";
if (info.sing_box_tailscale) text += " Tailscale: ✅\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"; text += "\n";
if (info.zapret_installed == "1") text += "🛡 Zapret: <code>" + as_string(info.zapret_version || "?") + "</code>\n"; if (info.zapret_installed == "1") text += "🛡 Zapret: <code>" + as_string(info.zapret_version || "?") + "</code>\n";
if (info.zapret2_installed == "1") text += "🛡 Zapret2: <code>" + as_string(info.zapret2_version || "?") + "</code>\n"; if (info.zapret2_installed == "1") text += "🛡 Zapret2: <code>" + as_string(info.zapret2_version || "?") + "</code>\n";
+27 -1
View File
@@ -1044,6 +1044,24 @@ function ui_capabilities_json() {
write_json(capability_flags()); 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() { function current_ui_state_json() {
refresh_action_dirs(); refresh_action_dirs();
@@ -1065,6 +1083,9 @@ function current_ui_state_json() {
else if (active_action == "reload") else if (active_action == "reload")
tachyon_status = "reloading"; 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({ write_json({
service: { service: {
tachyon: { tachyon: {
@@ -1076,7 +1097,12 @@ function current_ui_state_json() {
sing_box: { sing_box: {
running: sing_box_is_running, running: sing_box_is_running,
enabled: sing_box_is_enabled, 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, capabilities,