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: ''
},
{
id: 'google',
name: 'Google',
url: 'https://www.google.com/ncr',
svg: ''
},
{
id: 'github',
name: 'GitHub',
url: 'https://github.com',
svg: ''
},
{
id: 'bilibili',
name: 'Bilibili',
url: 'https://www.bilibili.com',
svg: ''
}
];
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 }