fix: dashboard test button layout, outbounds latency fix, and WDTT/OlcRTC component update cards

This commit is contained in:
Dushnilin
2026-09-11 21:19:31 +03:00
parent 94708d8251
commit 77241861af
10 changed files with 413 additions and 48 deletions
@@ -7,6 +7,8 @@ export function renderLoaderCircleIcon24() {
{ {
xmlns: NS, xmlns: NS,
viewBox: '0 0 24 24', viewBox: '0 0 24 24',
width: '24',
height: '24',
fill: 'none', fill: 'none',
stroke: 'currentColor', stroke: 'currentColor',
'stroke-width': '2', 'stroke-width': '2',
@@ -1815,7 +1815,8 @@ async function renderSectionsWidget() {
type: 'button', type: 'button',
id: 'dashboard-test-all-sections-button', id: 'dashboard-test-all-sections-button',
class: 'btn', class: 'btn',
style: 'padding: 4px 14px; height: 32px; font-size: 13px;', style:
'display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap; padding: 4px 14px; height: 32px; font-size: 13px;',
disabled: isTestingAllSections ? true : undefined, disabled: isTestingAllSections ? true : undefined,
click: () => { click: () => {
void handleTestAllSections(); void handleTestAllSections();
@@ -1824,11 +1825,7 @@ async function renderSectionsWidget() {
isTestingAllSections isTestingAllSections
? [ ? [
renderLoaderCircleIcon24(), renderLoaderCircleIcon24(),
E( E('span', {}, _('Testing all sections...')),
'span',
{ style: 'margin-left: 6px;' },
_('Testing all sections...'),
),
] ]
: E('span', {}, _('Test all sections')), : E('span', {}, _('Test all sections')),
), ),
@@ -189,6 +189,27 @@ export const styles = `
opacity: 0.65; opacity: 0.65;
} }
#dashboard-test-all-sections-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
white-space: nowrap;
vertical-align: middle;
}
#dashboard-test-all-sections-button svg {
width: 15px;
height: 15px;
display: block;
flex: 0 0 15px;
}
#dashboard-test-all-sections-button[disabled] {
cursor: not-allowed;
opacity: 0.65;
}
.tachyon_dashboard-page__outbound-grid { .tachyon_dashboard-page__outbound-grid {
margin-top: 5px; margin-top: 5px;
display: grid; display: grid;
@@ -77,13 +77,27 @@ export async function runSectionsCheck() {
section.latencyTestTimeout, section.latencyTestTimeout,
); );
const proxySuccess = const proxySuccess =
latencyProxy.success && !latencyProxy.data.message; latencyProxy.success && !latencyProxy.data?.message;
if (proxySuccess) { if (proxySuccess) {
return { const delay = latencyProxy.data?.delay;
state: 'success', if (typeof delay === 'number') {
latency: `[${selectedOutbound.displayName ?? ''}] ${latencyProxy.data.delay}ms`, return {
}; state: 'success',
latency: `[${selectedOutbound.displayName ?? ''}] ${delay}ms`,
};
}
const groupDelays = Object.values(latencyProxy.data || {}).filter(
(v): v is number => typeof v === 'number' && v > 0,
);
if (groupDelays.length > 0) {
const minDelay = Math.min(...groupDelays);
return {
state: 'success',
latency: `[${selectedOutbound.displayName ?? ''}] ${minDelay}ms`,
};
}
} }
return { return {
@@ -95,7 +109,7 @@ export async function runSectionsCheck() {
const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency( const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency(
section.code, section.code,
); );
const success = latencyGroup.success && !latencyGroup.data.message; const success = latencyGroup.success && !latencyGroup.data?.message;
if (success) { if (success) {
const latencyValues = Object.values(latencyGroup.data); const latencyValues = Object.values(latencyGroup.data);
@@ -106,7 +120,7 @@ export async function runSectionsCheck() {
const selectedProxyDelay = const selectedProxyDelay =
latencyGroup.data?.[selectedOutbound?.code ?? '']; latencyGroup.data?.[selectedOutbound?.code ?? ''];
if (selectedProxyDelay) { if (typeof selectedProxyDelay === 'number') {
return { return {
state: sectionState, state: sectionState,
latency: `[${selectedOutbound?.displayName ?? ''}] ${selectedProxyDelay}ms`, latency: `[${selectedOutbound?.displayName ?? ''}] ${selectedProxyDelay}ms`,
@@ -169,13 +183,26 @@ export async function runSectionsCheck() {
section.latencyTestTimeout, section.latencyTestTimeout,
); );
const success = latencyProxy.success && !latencyProxy.data.message; const success = latencyProxy.success && !latencyProxy.data?.message;
if (success) { if (success) {
return { const delay = latencyProxy.data?.delay;
state: 'success', if (typeof delay === 'number') {
latency: `${latencyProxy.data.delay} ms`, return {
}; state: 'success',
latency: `${delay} ms`,
};
}
const groupDelays = Object.values(latencyProxy.data || {}).filter(
(v): v is number => typeof v === 'number' && v > 0,
);
if (groupDelays.length > 0) {
return {
state: 'success',
latency: `${Math.min(...groupDelays)} ms`,
};
}
} }
if (section.action === 'vpn' && selectedOutbound?.runtimeAvailable) { if (section.action === 'vpn' && selectedOutbound?.runtimeAvailable) {
@@ -198,4 +198,90 @@ describe('runSectionsCheck', () => {
}), }),
); );
}); });
it('extracts minimum delay from group latency map response without delay field', async () => {
mocks.getDashboardSections.mockResolvedValue({
success: true,
data: [
{
withTagSelect: true,
code: 'main-out',
sectionName: 'main',
displayName: 'Main',
outbounds: [
{
code: 'main-softportal-out',
displayName: 'SoftPortal',
latency: 0,
type: 'URLTest',
selected: true,
},
],
},
],
});
mocks.getClashApiProxyLatency.mockResolvedValue({
success: true,
data: { 'node-1': 140, 'node-2': 45 },
});
await expect(runSectionsCheck()).resolves.toBeUndefined();
expect(mocks.updateCheckStore).toHaveBeenLastCalledWith(
expect.objectContaining({
state: 'success',
items: [
{
state: 'success',
key: 'Main',
value: '[SoftPortal] 45ms',
},
],
}),
);
});
it('returns Not responding instead of undefinedms when latency response has no valid delay', async () => {
mocks.getDashboardSections.mockResolvedValue({
success: true,
data: [
{
withTagSelect: true,
code: 'main-out',
sectionName: 'main',
displayName: 'Main',
outbounds: [
{
code: 'main-softportal-out',
displayName: 'SoftPortal',
latency: 0,
type: 'URLTest',
selected: true,
},
],
},
],
});
mocks.getClashApiProxyLatency.mockResolvedValue({
success: true,
data: {},
});
await expect(runSectionsCheck()).rejects.toThrow(
'Rule outbounds checks failed',
);
expect(mocks.updateCheckStore).toHaveBeenLastCalledWith(
expect.objectContaining({
state: 'error',
items: [
{
state: 'error',
key: 'Main',
value: '[SoftPortal] Not responding',
},
],
}),
);
});
}); });
@@ -213,6 +213,8 @@ export const initialDiagnosticStore: Pick<
zapret: { status: null, latest_version: '', release_url: '' }, zapret: { status: null, latest_version: '', release_url: '' },
zapret2: { status: null, latest_version: '', release_url: '' }, zapret2: { status: null, latest_version: '', release_url: '' },
byedpi: { status: null, latest_version: '', release_url: '' }, byedpi: { status: null, latest_version: '', release_url: '' },
wdtt: { status: null, latest_version: '', release_url: '' },
olcrtc: { status: null, latest_version: '', release_url: '' },
tailscale: { status: null, latest_version: '', release_url: '' }, tailscale: { status: null, latest_version: '', release_url: '' },
direct_bypass: { status: null, latest_version: '', release_url: '' }, direct_bypass: { status: null, latest_version: '', release_url: '' },
torrserver_direct: { status: null, latest_version: '', release_url: '' }, torrserver_direct: { status: null, latest_version: '', release_url: '' },
@@ -70,6 +70,10 @@ function getComponentCardTitle(component: Tachyon.ComponentName): string {
return 'Zapret2'; return 'Zapret2';
case 'byedpi': case 'byedpi':
return 'ByeDPI'; return 'ByeDPI';
case 'wdtt':
return 'WDTT';
case 'olcrtc':
return 'OlcRTC';
case 'tailscale': case 'tailscale':
return 'Tailscale'; return 'Tailscale';
default: default:
@@ -92,6 +96,10 @@ function getComponentCurrentVersion(
return sys.zapret2_version; return sys.zapret2_version;
case 'byedpi': case 'byedpi':
return sys.byedpi_version; return sys.byedpi_version;
case 'wdtt':
return sys.wdtt_version;
case 'olcrtc':
return sys.olcrtc_version;
case 'tailscale': case 'tailscale':
return sys.tailscale_version; return sys.tailscale_version;
default: default:
@@ -396,6 +404,8 @@ function notifyActionProvidersAvailabilityChanged(
zapretInstalled: Boolean(systemInfo.zapret_installed), zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed), zapret2Installed: Boolean(systemInfo.zapret2_installed),
byedpiInstalled: Boolean(systemInfo.byedpi_installed), byedpiInstalled: Boolean(systemInfo.byedpi_installed),
wdttInstalled: Boolean(systemInfo.wdtt_installed),
olcrtcInstalled: Boolean(systemInfo.olcrtc_installed),
}, },
}), }),
); );
@@ -527,6 +537,30 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
} }
} }
if (result.component === 'wdtt') {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === 'remove') {
nextSystemInfo.wdtt_installed = 0;
nextSystemInfo.wdtt_version = 'not installed';
} else {
nextSystemInfo.wdtt_installed = 1;
nextSystemInfo.wdtt_version = version;
}
}
if (result.component === 'olcrtc') {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === 'remove') {
nextSystemInfo.olcrtc_installed = 0;
nextSystemInfo.olcrtc_version = 'not installed';
} else {
nextSystemInfo.olcrtc_installed = 1;
nextSystemInfo.olcrtc_version = version;
}
}
if (result.component === 'direct_bypass') { if (result.component === 'direct_bypass') {
nextSystemInfo.direct_bypass_enabled = result.action === 'enable' ? 1 : 0; nextSystemInfo.direct_bypass_enabled = result.action === 'enable' ? 1 : 0;
} }
@@ -546,7 +580,9 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
if ( if (
result.component === 'zapret' || result.component === 'zapret' ||
result.component === 'zapret2' || result.component === 'zapret2' ||
result.component === 'byedpi' result.component === 'byedpi' ||
result.component === 'wdtt' ||
result.component === 'olcrtc'
) { ) {
notifyActionProvidersAvailabilityChanged(normalizedSystemInfo); notifyActionProvidersAvailabilityChanged(normalizedSystemInfo);
} }
@@ -1043,6 +1079,10 @@ function getComponentInstallKey(
return 'zapret2Install'; return 'zapret2Install';
case 'byedpi': case 'byedpi':
return 'byedpiInstall'; return 'byedpiInstall';
case 'wdtt':
return 'wdttInstall';
case 'olcrtc':
return 'olcrtcInstall';
case 'tailscale': case 'tailscale':
return 'tailscaleInstall'; return 'tailscaleInstall';
default: default:
@@ -1086,6 +1126,10 @@ function getComponentBackupVersion(component: Tachyon.ComponentName): string {
return sys.zapret2_backup_version || ''; return sys.zapret2_backup_version || '';
case 'byedpi': case 'byedpi':
return sys.byedpi_backup_version || ''; return sys.byedpi_backup_version || '';
case 'wdtt':
return sys.wdtt_backup_version || '';
case 'olcrtc':
return sys.olcrtc_backup_version || '';
case 'tailscale': case 'tailscale':
return sys.tailscale_backup_version || ''; return sys.tailscale_backup_version || '';
default: default:
@@ -1115,7 +1159,7 @@ function getOptionalComponentActions({
removeKey, removeKey,
rollbackKey, rollbackKey,
}: { }: {
component: 'zapret' | 'zapret2' | 'byedpi' | 'tailscale'; component: 'zapret' | 'zapret2' | 'byedpi' | 'wdtt' | 'olcrtc' | 'tailscale';
installed: boolean; installed: boolean;
checkKey: UpdatesActionKey; checkKey: UpdatesActionKey;
installKey: UpdatesActionKey; installKey: UpdatesActionKey;
@@ -1151,6 +1195,8 @@ const COMPONENT_REPO_URLS: Record<Tachyon.ComponentName, string> = {
zapret: 'https://github.com/remittor/zapret-openwrt', zapret: 'https://github.com/remittor/zapret-openwrt',
zapret2: 'https://github.com/Dushnilin/zapret2-openwrt', zapret2: 'https://github.com/Dushnilin/zapret2-openwrt',
byedpi: 'https://github.com/DPITrickster/ByeDPI-OpenWrt', byedpi: 'https://github.com/DPITrickster/ByeDPI-OpenWrt',
wdtt: 'https://github.com/SpaceNeuroX/qwdtt-openwrt',
olcrtc: 'https://github.com/alekvol/openwrt-olcrtc',
tailscale: 'https://openwrt.org/packages/pkgdata/tailscale', tailscale: 'https://openwrt.org/packages/pkgdata/tailscale',
direct_bypass: '', direct_bypass: '',
torrserver_direct: '', torrserver_direct: '',
@@ -1164,6 +1210,8 @@ function getComponentCards(): ComponentCard[] {
const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapretInstalled = Boolean(systemInfo.zapret_installed);
const zapret2Installed = Boolean(systemInfo.zapret2_installed); const zapret2Installed = Boolean(systemInfo.zapret2_installed);
const byedpiInstalled = Boolean(systemInfo.byedpi_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
const wdttInstalled = Boolean(systemInfo.wdtt_installed);
const olcrtcInstalled = Boolean(systemInfo.olcrtc_installed);
const tailscaleInstalled = Boolean(systemInfo.tailscale_installed); const tailscaleInstalled = Boolean(systemInfo.tailscale_installed);
const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version); const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version);
const singBoxStable = const singBoxStable =
@@ -1275,6 +1323,22 @@ function getComponentCards(): ComponentCard[] {
removeKey: 'byedpiRemove', removeKey: 'byedpiRemove',
rollbackKey: 'byedpiRollback', rollbackKey: 'byedpiRollback',
}); });
const wdttActions = getOptionalComponentActions({
component: 'wdtt',
installed: wdttInstalled,
checkKey: 'wdttCheck',
installKey: 'wdttInstall',
removeKey: 'wdttRemove',
rollbackKey: 'wdttRollback',
});
const olcrtcActions = getOptionalComponentActions({
component: 'olcrtc',
installed: olcrtcInstalled,
checkKey: 'olcrtcCheck',
installKey: 'olcrtcInstall',
removeKey: 'olcrtcRemove',
rollbackKey: 'olcrtcRollback',
});
const tailscaleActions = getOptionalComponentActions({ const tailscaleActions = getOptionalComponentActions({
component: 'tailscale', component: 'tailscale',
installed: tailscaleInstalled, installed: tailscaleInstalled,
@@ -1443,6 +1507,36 @@ function getComponentCards(): ComponentCard[] {
repoUrl: COMPONENT_REPO_URLS.byedpi, repoUrl: COMPONENT_REPO_URLS.byedpi,
actions: byedpiActions, actions: byedpiActions,
}, },
{
component: 'wdtt',
column: 1,
title: 'WDTT',
version: systemInfoLoading
? _('Loading...')
: wdttInstalled
? systemInfo.wdtt_version
: _('Not installed'),
latestVersion: getLatestVersion('wdtt'),
releaseUrl: getGitHubReleaseUrl('wdtt'),
repoUrl: COMPONENT_REPO_URLS.wdtt,
actions: wdttActions,
supportsVersions: true,
},
{
component: 'olcrtc',
column: 1,
title: 'OlcRTC',
version: systemInfoLoading
? _('Loading...')
: olcrtcInstalled
? systemInfo.olcrtc_version
: _('Not installed'),
latestVersion: getLatestVersion('olcrtc'),
releaseUrl: getGitHubReleaseUrl('olcrtc'),
repoUrl: COMPONENT_REPO_URLS.olcrtc,
actions: olcrtcActions,
supportsVersions: true,
},
{ {
component: 'tailscale', component: 'tailscale',
column: 1, column: 1,
+2
View File
@@ -738,6 +738,8 @@ export namespace Tachyon {
| 'zapret' | 'zapret'
| 'zapret2' | 'zapret2'
| 'byedpi' | 'byedpi'
| 'wdtt'
| 'olcrtc'
| 'tailscale' | 'tailscale'
| 'direct_bypass' | 'direct_bypass'
| 'torrserver_direct'; | 'torrserver_direct';
@@ -1489,6 +1489,8 @@ function renderLoaderCircleIcon24() {
{ {
xmlns: NS, xmlns: NS,
viewBox: "0 0 24 24", viewBox: "0 0 24 24",
width: "24",
height: "24",
fill: "none", fill: "none",
stroke: "currentColor", stroke: "currentColor",
"stroke-width": "2", "stroke-width": "2",
@@ -6413,6 +6415,8 @@ var initialDiagnosticStore = {
zapret: { status: null, latest_version: "", release_url: "" }, zapret: { status: null, latest_version: "", release_url: "" },
zapret2: { status: null, latest_version: "", release_url: "" }, zapret2: { status: null, latest_version: "", release_url: "" },
byedpi: { status: null, latest_version: "", release_url: "" }, byedpi: { status: null, latest_version: "", release_url: "" },
wdtt: { status: null, latest_version: "", release_url: "" },
olcrtc: { status: null, latest_version: "", release_url: "" },
tailscale: { status: null, latest_version: "", release_url: "" }, tailscale: { status: null, latest_version: "", release_url: "" },
direct_bypass: { status: null, latest_version: "", release_url: "" }, direct_bypass: { status: null, latest_version: "", release_url: "" },
torrserver_direct: { status: null, latest_version: "", release_url: "" } torrserver_direct: { status: null, latest_version: "", release_url: "" }
@@ -9131,7 +9135,7 @@ async function renderSectionsWidget() {
type: "button", type: "button",
id: "dashboard-test-all-sections-button", id: "dashboard-test-all-sections-button",
class: "btn", class: "btn",
style: "padding: 4px 14px; height: 32px; font-size: 13px;", style: "display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap; padding: 4px 14px; height: 32px; font-size: 13px;",
disabled: isTestingAllSections ? true : void 0, disabled: isTestingAllSections ? true : void 0,
click: () => { click: () => {
void handleTestAllSections(); void handleTestAllSections();
@@ -9139,11 +9143,7 @@ async function renderSectionsWidget() {
}, },
isTestingAllSections ? [ isTestingAllSections ? [
renderLoaderCircleIcon24(), renderLoaderCircleIcon24(),
E( E("span", {}, _("Testing all sections..."))
"span",
{ style: "margin-left: 6px;" },
_("Testing all sections...")
)
] : E("span", {}, _("Test all sections")) ] : E("span", {}, _("Test all sections"))
) )
] ]
@@ -9777,6 +9777,27 @@ var styles = `
opacity: 0.65; opacity: 0.65;
} }
#dashboard-test-all-sections-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
white-space: nowrap;
vertical-align: middle;
}
#dashboard-test-all-sections-button svg {
width: 15px;
height: 15px;
display: block;
flex: 0 0 15px;
}
#dashboard-test-all-sections-button[disabled] {
cursor: not-allowed;
opacity: 0.65;
}
.tachyon_dashboard-page__outbound-grid { .tachyon_dashboard-page__outbound-grid {
margin-top: 5px; margin-top: 5px;
display: grid; display: grid;
@@ -15605,12 +15626,25 @@ async function runSectionsCheck() {
selectedOutbound2.code, selectedOutbound2.code,
section.latencyTestTimeout section.latencyTestTimeout
); );
const proxySuccess = latencyProxy2.success && !latencyProxy2.data.message; const proxySuccess = latencyProxy2.success && !latencyProxy2.data?.message;
if (proxySuccess) { if (proxySuccess) {
return { const delay = latencyProxy2.data?.delay;
state: "success", if (typeof delay === "number") {
latency: `[${selectedOutbound2.displayName ?? ""}] ${latencyProxy2.data.delay}ms` return {
}; state: "success",
latency: `[${selectedOutbound2.displayName ?? ""}] ${delay}ms`
};
}
const groupDelays = Object.values(latencyProxy2.data || {}).filter(
(v) => typeof v === "number" && v > 0
);
if (groupDelays.length > 0) {
const minDelay = Math.min(...groupDelays);
return {
state: "success",
latency: `[${selectedOutbound2.displayName ?? ""}] ${minDelay}ms`
};
}
} }
return { return {
state: "error", state: "error",
@@ -15620,12 +15654,12 @@ async function runSectionsCheck() {
const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency( const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency(
section.code section.code
); );
const success2 = latencyGroup.success && !latencyGroup.data.message; const success2 = latencyGroup.success && !latencyGroup.data?.message;
if (success2) { if (success2) {
const latencyValues = Object.values(latencyGroup.data); const latencyValues = Object.values(latencyGroup.data);
const sectionState = isSubscription ? getSubscriptionLatencyState(latencyValues) : "success"; const sectionState = isSubscription ? getSubscriptionLatencyState(latencyValues) : "success";
const selectedProxyDelay = latencyGroup.data?.[selectedOutbound2?.code ?? ""]; const selectedProxyDelay = latencyGroup.data?.[selectedOutbound2?.code ?? ""];
if (selectedProxyDelay) { if (typeof selectedProxyDelay === "number") {
return { return {
state: sectionState, state: sectionState,
latency: `[${selectedOutbound2?.displayName ?? ""}] ${selectedProxyDelay}ms` latency: `[${selectedOutbound2?.displayName ?? ""}] ${selectedProxyDelay}ms`
@@ -15678,12 +15712,24 @@ async function runSectionsCheck() {
section.code, section.code,
section.latencyTestTimeout section.latencyTestTimeout
); );
const success = latencyProxy.success && !latencyProxy.data.message; const success = latencyProxy.success && !latencyProxy.data?.message;
if (success) { if (success) {
return { const delay = latencyProxy.data?.delay;
state: "success", if (typeof delay === "number") {
latency: `${latencyProxy.data.delay} ms` return {
}; state: "success",
latency: `${delay} ms`
};
}
const groupDelays = Object.values(latencyProxy.data || {}).filter(
(v) => typeof v === "number" && v > 0
);
if (groupDelays.length > 0) {
return {
state: "success",
latency: `${Math.min(...groupDelays)} ms`
};
}
} }
if (section.action === "vpn" && selectedOutbound?.runtimeAvailable) { if (section.action === "vpn" && selectedOutbound?.runtimeAvailable) {
return { return {
@@ -20697,6 +20743,10 @@ function getComponentCardTitle(component) {
return "Zapret2"; return "Zapret2";
case "byedpi": case "byedpi":
return "ByeDPI"; return "ByeDPI";
case "wdtt":
return "WDTT";
case "olcrtc":
return "OlcRTC";
case "tailscale": case "tailscale":
return "Tailscale"; return "Tailscale";
default: default:
@@ -20716,6 +20766,10 @@ function getComponentCurrentVersion(component) {
return sys.zapret2_version; return sys.zapret2_version;
case "byedpi": case "byedpi":
return sys.byedpi_version; return sys.byedpi_version;
case "wdtt":
return sys.wdtt_version;
case "olcrtc":
return sys.olcrtc_version;
case "tailscale": case "tailscale":
return sys.tailscale_version; return sys.tailscale_version;
default: default:
@@ -20908,7 +20962,9 @@ function notifyActionProvidersAvailabilityChanged(systemInfo) {
detail: { detail: {
zapretInstalled: Boolean(systemInfo.zapret_installed), zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed), zapret2Installed: Boolean(systemInfo.zapret2_installed),
byedpiInstalled: Boolean(systemInfo.byedpi_installed) byedpiInstalled: Boolean(systemInfo.byedpi_installed),
wdttInstalled: Boolean(systemInfo.wdtt_installed),
olcrtcInstalled: Boolean(systemInfo.olcrtc_installed)
} }
}) })
); );
@@ -21014,6 +21070,26 @@ function patchSystemInfoAfterMutation(result) {
nextSystemInfo.byedpi_version = version; nextSystemInfo.byedpi_version = version;
} }
} }
if (result.component === "wdtt") {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === "remove") {
nextSystemInfo.wdtt_installed = 0;
nextSystemInfo.wdtt_version = "not installed";
} else {
nextSystemInfo.wdtt_installed = 1;
nextSystemInfo.wdtt_version = version;
}
}
if (result.component === "olcrtc") {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === "remove") {
nextSystemInfo.olcrtc_installed = 0;
nextSystemInfo.olcrtc_version = "not installed";
} else {
nextSystemInfo.olcrtc_installed = 1;
nextSystemInfo.olcrtc_version = version;
}
}
if (result.component === "direct_bypass") { if (result.component === "direct_bypass") {
nextSystemInfo.direct_bypass_enabled = result.action === "enable" ? 1 : 0; nextSystemInfo.direct_bypass_enabled = result.action === "enable" ? 1 : 0;
} }
@@ -21025,7 +21101,7 @@ function patchSystemInfoAfterMutation(result) {
store.set({ store.set({
diagnosticsSystemInfo: normalizedSystemInfo diagnosticsSystemInfo: normalizedSystemInfo
}); });
if (result.component === "zapret" || result.component === "zapret2" || result.component === "byedpi") { if (result.component === "zapret" || result.component === "zapret2" || result.component === "byedpi" || result.component === "wdtt" || result.component === "olcrtc") {
notifyActionProvidersAvailabilityChanged(normalizedSystemInfo); notifyActionProvidersAvailabilityChanged(normalizedSystemInfo);
} }
} }
@@ -21403,6 +21479,10 @@ function getComponentInstallKey(component) {
return "zapret2Install"; return "zapret2Install";
case "byedpi": case "byedpi":
return "byedpiInstall"; return "byedpiInstall";
case "wdtt":
return "wdttInstall";
case "olcrtc":
return "olcrtcInstall";
case "tailscale": case "tailscale":
return "tailscaleInstall"; return "tailscaleInstall";
default: default:
@@ -21435,6 +21515,10 @@ function getComponentBackupVersion(component) {
return sys.zapret2_backup_version || ""; return sys.zapret2_backup_version || "";
case "byedpi": case "byedpi":
return sys.byedpi_backup_version || ""; return sys.byedpi_backup_version || "";
case "wdtt":
return sys.wdtt_backup_version || "";
case "olcrtc":
return sys.olcrtc_backup_version || "";
case "tailscale": case "tailscale":
return sys.tailscale_backup_version || ""; return sys.tailscale_backup_version || "";
default: default:
@@ -21483,6 +21567,8 @@ var COMPONENT_REPO_URLS = {
zapret: "https://github.com/remittor/zapret-openwrt", zapret: "https://github.com/remittor/zapret-openwrt",
zapret2: "https://github.com/Dushnilin/zapret2-openwrt", zapret2: "https://github.com/Dushnilin/zapret2-openwrt",
byedpi: "https://github.com/DPITrickster/ByeDPI-OpenWrt", byedpi: "https://github.com/DPITrickster/ByeDPI-OpenWrt",
wdtt: "https://github.com/SpaceNeuroX/qwdtt-openwrt",
olcrtc: "https://github.com/alekvol/openwrt-olcrtc",
tailscale: "https://openwrt.org/packages/pkgdata/tailscale", tailscale: "https://openwrt.org/packages/pkgdata/tailscale",
direct_bypass: "", direct_bypass: "",
torrserver_direct: "" torrserver_direct: ""
@@ -21495,6 +21581,8 @@ function getComponentCards() {
const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapretInstalled = Boolean(systemInfo.zapret_installed);
const zapret2Installed = Boolean(systemInfo.zapret2_installed); const zapret2Installed = Boolean(systemInfo.zapret2_installed);
const byedpiInstalled = Boolean(systemInfo.byedpi_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
const wdttInstalled = Boolean(systemInfo.wdtt_installed);
const olcrtcInstalled = Boolean(systemInfo.olcrtc_installed);
const tailscaleInstalled = Boolean(systemInfo.tailscale_installed); const tailscaleInstalled = Boolean(systemInfo.tailscale_installed);
const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version); const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version);
const singBoxStable = singBoxInstalled && !systemInfo.sing_box_extended && !systemInfo.sing_box_tiny; const singBoxStable = singBoxInstalled && !systemInfo.sing_box_extended && !systemInfo.sing_box_tiny;
@@ -21593,6 +21681,22 @@ function getComponentCards() {
removeKey: "byedpiRemove", removeKey: "byedpiRemove",
rollbackKey: "byedpiRollback" rollbackKey: "byedpiRollback"
}); });
const wdttActions = getOptionalComponentActions({
component: "wdtt",
installed: wdttInstalled,
checkKey: "wdttCheck",
installKey: "wdttInstall",
removeKey: "wdttRemove",
rollbackKey: "wdttRollback"
});
const olcrtcActions = getOptionalComponentActions({
component: "olcrtc",
installed: olcrtcInstalled,
checkKey: "olcrtcCheck",
installKey: "olcrtcInstall",
removeKey: "olcrtcRemove",
rollbackKey: "olcrtcRollback"
});
const tailscaleActions = getOptionalComponentActions({ const tailscaleActions = getOptionalComponentActions({
component: "tailscale", component: "tailscale",
installed: tailscaleInstalled, installed: tailscaleInstalled,
@@ -21714,6 +21818,28 @@ function getComponentCards() {
repoUrl: COMPONENT_REPO_URLS.byedpi, repoUrl: COMPONENT_REPO_URLS.byedpi,
actions: byedpiActions actions: byedpiActions
}, },
{
component: "wdtt",
column: 1,
title: "WDTT",
version: systemInfoLoading ? _("Loading...") : wdttInstalled ? systemInfo.wdtt_version : _("Not installed"),
latestVersion: getLatestVersion("wdtt"),
releaseUrl: getGitHubReleaseUrl("wdtt"),
repoUrl: COMPONENT_REPO_URLS.wdtt,
actions: wdttActions,
supportsVersions: true
},
{
component: "olcrtc",
column: 1,
title: "OlcRTC",
version: systemInfoLoading ? _("Loading...") : olcrtcInstalled ? systemInfo.olcrtc_version : _("Not installed"),
latestVersion: getLatestVersion("olcrtc"),
releaseUrl: getGitHubReleaseUrl("olcrtc"),
repoUrl: COMPONENT_REPO_URLS.olcrtc,
actions: olcrtcActions,
supportsVersions: true
},
{ {
component: "tailscale", component: "tailscale",
column: 1, column: 1,
+16 -8
View File
@@ -1353,8 +1353,8 @@ function resolve_wdtt_release(arch, tag) {
let asset_ext = is_apk() ? "apk" : "ipk"; let asset_ext = is_apk() ? "apk" : "ipk";
let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ])); let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ]));
let releases_json = (tag != null && tag != "") ? let releases_json = (tag != null && tag != "") ?
fetch_github_release_by_tag_json("ret_dev", "wdtt", tag) : fetch_github_release_by_tag_json("SpaceNeuroX", "qwdtt-openwrt", tag) :
fetch_github_releases_json("ret_dev", "wdtt", "30"); fetch_github_releases_json("SpaceNeuroX", "qwdtt-openwrt", "30");
if (releases_json != "") { if (releases_json != "") {
let resolved = trim(helper_output_input(releases_json, "wdtt-select-asset", [ release_series, asset_ext, arch.candidates ])); let resolved = trim(helper_output_input(releases_json, "wdtt-select-asset", [ release_series, asset_ext, arch.candidates ]));
let fields = split(resolved, "\t"); let fields = split(resolved, "\t");
@@ -1375,8 +1375,8 @@ function resolve_wdtt_release(arch, tag) {
return { return {
arch: distrib_arch, arch: distrib_arch,
package_name: pkg_name, package_name: pkg_name,
package_url: "https://github.com/ret_dev/wdtt/releases/download/" + tag + "/" + pkg_name, package_url: "https://github.com/SpaceNeuroX/qwdtt-openwrt/releases/download/" + tag + "/" + pkg_name,
release_url: "https://github.com/ret_dev/wdtt/releases/tag/" + tag, release_url: "https://github.com/SpaceNeuroX/qwdtt-openwrt/releases/tag/" + tag,
version: tag version: tag
}; };
} }
@@ -1387,8 +1387,8 @@ function resolve_olcrtc_release(arch, tag) {
let asset_ext = is_apk() ? "apk" : "ipk"; let asset_ext = is_apk() ? "apk" : "ipk";
let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ])); let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ]));
let releases_json = (tag != null && tag != "") ? let releases_json = (tag != null && tag != "") ?
fetch_github_release_by_tag_json("ret_dev", "olcrtc", tag) : fetch_github_release_by_tag_json("alekvol", "openwrt-olcrtc", tag) :
fetch_github_releases_json("ret_dev", "olcrtc", "30"); fetch_github_releases_json("alekvol", "openwrt-olcrtc", "30");
if (releases_json != "") { if (releases_json != "") {
let resolved = trim(helper_output_input(releases_json, "olcrtc-select-asset", [ release_series, asset_ext, arch.candidates ])); let resolved = trim(helper_output_input(releases_json, "olcrtc-select-asset", [ release_series, asset_ext, arch.candidates ]));
let fields = split(resolved, "\t"); let fields = split(resolved, "\t");
@@ -1409,8 +1409,8 @@ function resolve_olcrtc_release(arch, tag) {
return { return {
arch: distrib_arch, arch: distrib_arch,
package_name: pkg_name, package_name: pkg_name,
package_url: "https://github.com/ret_dev/olcrtc/releases/download/" + tag + "/" + pkg_name, package_url: "https://github.com/alekvol/openwrt-olcrtc/releases/download/" + tag + "/" + pkg_name,
release_url: "https://github.com/ret_dev/olcrtc/releases/tag/" + tag, release_url: "https://github.com/alekvol/openwrt-olcrtc/releases/tag/" + tag,
version: tag version: tag
}; };
} }
@@ -3358,6 +3358,10 @@ function list_component_releases(component, count) {
owner = "Dushnilin"; repo = "zapret2-openwrt"; owner = "Dushnilin"; repo = "zapret2-openwrt";
} else if (component == "byedpi") { } else if (component == "byedpi") {
owner = "DPITrickster"; repo = "ByeDPI-OpenWrt"; owner = "DPITrickster"; repo = "ByeDPI-OpenWrt";
} else if (component == "wdtt") {
owner = "SpaceNeuroX"; repo = "qwdtt-openwrt";
} else if (component == "olcrtc") {
owner = "alekvol"; repo = "openwrt-olcrtc";
} else { } else {
print("[]\n"); return; print("[]\n"); return;
} }
@@ -3410,6 +3414,10 @@ function install_component_version(component, tag) {
install_zapret2("install", tag); install_zapret2("install", tag);
} else if (component == "byedpi") { } else if (component == "byedpi") {
install_byedpi("install", tag); install_byedpi("install", tag);
} else if (component == "wdtt") {
install_wdtt("install", tag);
} else if (component == "olcrtc") {
install_olcrtc("install", tag);
} else { } else {
action_fail(component, "install_version", "Component " + component + " does not support version installation"); action_fail(component, "install_version", "Component " + component + " does not support version installation");
} }