import fs from "fs"; type TestSite = { id: string, name: string, url: string, svg: string } type ActionStatus = 'success' | 'error' | 'neutral'; const TEST_SITES: TestSite[] = [ { id: 'baidu', name: 'Baidu', url: 'https://www.baidu.com', svg: 'Baidu' }, { id: 'google', name: 'Google', url: 'https://www.google.com/ncr', svg: 'Google' }, { id: 'github', name: 'GitHub', url: 'https://github.com', svg: 'Github' }, { id: 'bilibili', name: 'Bilibili', url: 'https://www.bilibili.com', svg: 'bilibili' } ]; function restartService(statusElement: HTMLElement) { updateActionStatus(statusElement, _('Restarting Service'), 'neutral'); fs.exec('/etc/init.d/hijpass', ['restart']).then(function (result) { if (result.code === 0) { updateActionStatus(statusElement, _('Service Restarted'), 'success'); setTimeout(function () { location.reload(); }, 2000); } else { updateActionStatus(statusElement, _('Service Restart Failed') + (result.stderr || result.stdout), 'error'); } }).catch(function (error) { updateActionStatus(statusElement, _('Service Restart Failed') + error.message, 'error'); }); } function reloadFirewall(statusElement: HTMLElement) { updateActionStatus(statusElement, _('Refreshing Firewall'), 'neutral'); fs.exec('/usr/lib/hijpass/nft.sh', ['reset']).then(function (result) { if (result.code === 0) { updateActionStatus(statusElement, _('Firewall Refreshed'), 'success'); } else { updateActionStatus(statusElement, _('Firewall Refresh Failed') + (result.stderr || result.stdout), 'error'); } }).catch(function (error) { updateActionStatus(statusElement, _('Firewall Refresh Failed') + error.message, 'error'); }); } function updateActionStatus(statusElement: HTMLElement, message: string, status: ActionStatus) { statusElement.textContent = message; statusElement.style.color = status === 'success' ? '#4caf50' : status === 'error' ? '#f44336' : '#666'; } function createTestButton(info: TestSite) { const resultElement = E('div', { 'style': 'font-size: 13px; color: #f44336;' }, _('Click to Test')) as HTMLElement; let buttonElement: HTMLElement; buttonElement = E('div', { 'class': 'connectivity-btn cbi-section', 'style': 'width: 100%; height: 100px; display: flex; ' + 'flex-direction: column; align-items: center; justify-content: center; ' + 'font-size: 15px; border-radius: 8px; padding: 0; ' + 'box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15); border:1px solid rgba(0, 0, 0, .05); ' + 'cursor: pointer;', 'click': function () { testConnectivity(info.url, buttonElement, resultElement); } }, [ E('div', { 'style': 'display: flex; align-items: center; justify-content: center;' }, [ E('span', { 'style': 'width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;' }, [E('raw', {}, info.svg)]) ]), E('div', { 'style': 'font-weight: bold; font-size: 16px; margin-bottom: 2px;' }, _(info.name)), resultElement ]) as HTMLElement; return E('div', { 'class': 'connectivity-test-button', 'style': 'flex: 1; min-width: 120px;' }, [buttonElement]) } function testConnectivity(url: string, buttonElement: HTMLElement, resultElement: HTMLElement) { if (buttonElement.dataset.loading === '1') { return; } resultElement.textContent = _('Testing'); resultElement.style.color = '#666'; buttonElement.dataset.loading = '1'; buttonElement.style.opacity = '0.7'; fs.exec_direct("/usr/lib/hijpass/connect.sh", [url, '5'], 'json') .then(r => { updateTestResult(buttonElement, resultElement, r.success, r); }) .catch(e => { console.error(e) updateTestResult(buttonElement, resultElement, false, undefined) }) } function updateTestResult(buttonElement: HTMLElement, resultElement: HTMLElement, success: boolean, response: any) { delete buttonElement.dataset.loading; buttonElement.style.opacity = '1'; if (success) { if (/^([45])\d{2}$/.test(response.http_code)) { resultElement.textContent = _('Connection Abnormal') + response.http_code; resultElement.style.color = '#f44336'; } else { resultElement.textContent = response.tls_handshake_time_ms + ' ms' resultElement.style.color = '#4caf50'; } } else { resultElement.textContent = _('Test Failed'); resultElement.style.color = '#f44336'; } } const OverviewPanelUtils = { createActionButtons: function () { const statusElement = E('span', { 'style': 'margin-left: 10px; font-size: 12px; color: #666;' }) as HTMLElement; return E('div', {'class': 'cbi-value', 'style': 'margin-bottom: 2rem;'}, [ E('label', {'class': 'cbi-value-title', 'style': 'visibility: hidden'}, _('Service Control')), E('div', {'class': 'cbi-value-field'}, [ E('button', { 'class': 'cbi-button cbi-button-action', 'style': 'margin-right: 10px;', 'type': 'button', 'click': function (ev: Event) { ev.preventDefault(); restartService(statusElement); } }, _('Restart Service')), E('button', { 'class': 'cbi-button cbi-button-action', 'style': 'margin-right: 10px;', 'type': 'button', 'click': function (ev: Event) { ev.preventDefault(); reloadFirewall(statusElement); } }, _('Refresh Firewall')), statusElement ]) ]) }, createConnectivityTest: function () { return E('div', { 'id': 'custom-connectivity-test', 'class': 'cbi-map' }, [ E('h2', {}, _('Website Connectivity Test')), E('div', { 'class': 'cbi-section', 'style': 'display: flex; gap: 18px; padding: 18px; justify-content: center;' }, TEST_SITES.map(site => createTestButton(site))) ]); }, } export { OverviewPanelUtils }