feat(ui): привести окно проверки доступности сервисов к нативному стилю LuCI и нормализовать наименования
This commit is contained in:
@@ -18,14 +18,107 @@ interface ServiceCheckResult extends ServiceCheckTarget {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export function renderServiceCheckModal() {
|
||||
let currentTargetMode: 'active' | 'all' = 'active';
|
||||
/**
|
||||
* Format raw backend route strings into clean, user-friendly labels.
|
||||
*/
|
||||
function formatRouteType(routeType: string): string {
|
||||
if (!routeType) return _('Direct');
|
||||
const clean = routeType.trim();
|
||||
|
||||
if (clean === 'connection' || clean === 'proxy' || clean === 'outbound') {
|
||||
return _('Proxy');
|
||||
}
|
||||
if (clean === 'direct') {
|
||||
return _('Direct');
|
||||
}
|
||||
if (clean === 'auto') {
|
||||
return _('Auto');
|
||||
}
|
||||
if (clean.startsWith('zapret2')) {
|
||||
return clean.replace(/^zapret2/, 'Zapret 2');
|
||||
}
|
||||
if (clean.startsWith('zapret')) {
|
||||
return clean.replace(/^zapret/, 'Zapret');
|
||||
}
|
||||
if (clean.startsWith('byedpi')) {
|
||||
return clean.replace(/^byedpi/, 'ByeDPI');
|
||||
}
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format raw section names for UI display.
|
||||
*/
|
||||
function formatSectionName(name: string): string {
|
||||
if (name === 'Свои домены') {
|
||||
return _('Custom Domains');
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a native LuCI status badge matching active theme colors.
|
||||
*/
|
||||
function renderStatusBadge(
|
||||
statusClass: string,
|
||||
isTesting = false,
|
||||
isPending = false,
|
||||
): HTMLElement {
|
||||
if (isPending) {
|
||||
return E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge cbi-value-title',
|
||||
style: 'opacity: 0.6; font-size: 11px; padding: 2px 8px;',
|
||||
},
|
||||
_('Pending'),
|
||||
);
|
||||
}
|
||||
if (isTesting) {
|
||||
return E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge cbi-button-action',
|
||||
style: 'font-size: 11px; padding: 2px 8px;',
|
||||
},
|
||||
_('Testing...'),
|
||||
);
|
||||
}
|
||||
|
||||
const cleanStatus = statusClass || '';
|
||||
const isOk =
|
||||
cleanStatus.toUpperCase().includes('OK') ||
|
||||
cleanStatus === '200' ||
|
||||
cleanStatus === 'OK';
|
||||
|
||||
if (isOk) {
|
||||
return E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge cbi-button-save',
|
||||
style: 'font-weight: bold; font-size: 11px; padding: 2px 8px;',
|
||||
},
|
||||
_('Available'),
|
||||
);
|
||||
}
|
||||
|
||||
return E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge cbi-button-reset',
|
||||
style: 'font-weight: bold; font-size: 11px; padding: 2px 8px;',
|
||||
},
|
||||
cleanStatus || _('Unavailable'),
|
||||
);
|
||||
}
|
||||
|
||||
export function renderServiceCheckModal() {
|
||||
const container = E(
|
||||
'div',
|
||||
{
|
||||
class: 'tachyon-service-check-modal-wrapper',
|
||||
style: 'width: 100%; max-width: 820px; box-sizing: border-box;',
|
||||
style: 'width: 100%; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
@@ -47,7 +140,7 @@ export function renderServiceCheckModal() {
|
||||
|
||||
const modalContent = E(
|
||||
'div',
|
||||
{ style: 'width: 100%; max-width: 820px; box-sizing: border-box;' },
|
||||
{ style: 'width: 100%; box-sizing: border-box;' },
|
||||
[
|
||||
container,
|
||||
E(
|
||||
@@ -55,7 +148,7 @@ export function renderServiceCheckModal() {
|
||||
{
|
||||
id: 'tachyon-service-check-footer',
|
||||
style:
|
||||
'margin-top: 15px; display: flex; justify-content: space-between; align-items: center; gap: 10px; border-top: 1px solid var(--border-color, rgba(255,255,255,0.15)); padding-top: 12px; flex-wrap: wrap;',
|
||||
'margin-top: 15px; display: flex; justify-content: space-between; align-items: center; gap: 10px; border-top: 1px solid var(--border-color, rgba(128,128,128,0.2)); padding-top: 12px; flex-wrap: wrap;',
|
||||
},
|
||||
[
|
||||
renderButton({
|
||||
@@ -67,10 +160,9 @@ export function renderServiceCheckModal() {
|
||||
],
|
||||
);
|
||||
|
||||
ui.showModal(_('Service Health Check'), modalContent);
|
||||
ui.showModal(_('Service Availability Check'), modalContent);
|
||||
|
||||
const loadTargets = (targetMode: 'active' | 'all') => {
|
||||
currentTargetMode = targetMode;
|
||||
container.innerHTML = '';
|
||||
container.appendChild(
|
||||
E(
|
||||
@@ -94,7 +186,7 @@ export function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
'p',
|
||||
{ style: 'color: #dc3545;' },
|
||||
{ style: 'color: var(--color-danger, #dc3545);' },
|
||||
_('Failed to run service check.'),
|
||||
),
|
||||
);
|
||||
@@ -112,7 +204,7 @@ export function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
'p',
|
||||
{ style: 'color: #dc3545;' },
|
||||
{ style: 'color: var(--color-danger, #dc3545);' },
|
||||
_('Failed to parse target list.'),
|
||||
),
|
||||
);
|
||||
@@ -128,7 +220,7 @@ export function renderServiceCheckModal() {
|
||||
|
||||
// Mode Switcher Toolbar
|
||||
const activeSectionsBtn = renderButton({
|
||||
text: _('Active Sections'),
|
||||
text: _('Active Routes'),
|
||||
classNames: [
|
||||
targetMode === 'active'
|
||||
? 'cbi-button-action'
|
||||
@@ -137,28 +229,28 @@ export function renderServiceCheckModal() {
|
||||
onClick: () => loadTargets('active'),
|
||||
});
|
||||
activeSectionsBtn.style.fontSize = '12px';
|
||||
activeSectionsBtn.style.padding = '3px 12px';
|
||||
activeSectionsBtn.style.padding = '4px 12px';
|
||||
|
||||
const allProfilesBtn = renderButton({
|
||||
text: _('All Profiles'),
|
||||
text: _('All Services'),
|
||||
classNames: [
|
||||
targetMode === 'all' ? 'cbi-button-action' : 'cbi-button-neutral',
|
||||
],
|
||||
onClick: () => loadTargets('all'),
|
||||
});
|
||||
allProfilesBtn.style.fontSize = '12px';
|
||||
allProfilesBtn.style.padding = '3px 12px';
|
||||
allProfilesBtn.style.padding = '4px 12px';
|
||||
|
||||
const modeSwitcherBar = E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'display: flex; gap: 8px; align-items: center; margin-bottom: 12px; border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.1)); padding-bottom: 8px;',
|
||||
'display: flex; gap: 10px; align-items: center; margin-bottom: 14px; border-bottom: 1px solid var(--border-color, rgba(128,128,128,0.2)); padding-bottom: 10px;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'span',
|
||||
{ style: 'font-weight: 600; font-size: 12px; opacity: 0.8;' },
|
||||
{ style: 'font-weight: 600; font-size: 13px; opacity: 0.9;' },
|
||||
_('Check Mode:'),
|
||||
),
|
||||
activeSectionsBtn,
|
||||
@@ -173,121 +265,80 @@ export function renderServiceCheckModal() {
|
||||
|
||||
// Summary Stats Elements
|
||||
const totalStatEl = E(
|
||||
'div',
|
||||
'b',
|
||||
{
|
||||
class: 'stat-val',
|
||||
style: 'font-size: 18px; font-weight: bold; margin-top: 2px;',
|
||||
style: 'font-size: 18px; display: block; margin-top: 2px;',
|
||||
},
|
||||
`${targets.length}`,
|
||||
);
|
||||
const passedStatEl = E(
|
||||
'div',
|
||||
'b',
|
||||
{
|
||||
class: 'stat-val',
|
||||
style:
|
||||
'font-size: 18px; font-weight: bold; color: #28a745; margin-top: 2px;',
|
||||
'font-size: 18px; color: var(--color-success, #28a745); display: block; margin-top: 2px;',
|
||||
},
|
||||
'0',
|
||||
);
|
||||
const failedStatEl = E(
|
||||
'div',
|
||||
'b',
|
||||
{
|
||||
class: 'stat-val',
|
||||
style:
|
||||
'font-size: 18px; font-weight: bold; color: #dc3545; margin-top: 2px;',
|
||||
'font-size: 18px; color: var(--color-danger, #dc3545); display: block; margin-top: 2px;',
|
||||
},
|
||||
'0',
|
||||
);
|
||||
const latencyStatEl = E(
|
||||
'div',
|
||||
'b',
|
||||
{
|
||||
class: 'stat-val',
|
||||
style:
|
||||
'font-size: 18px; font-weight: bold; color: #17a2b8; margin-top: 2px;',
|
||||
'font-size: 18px; color: var(--color-info, #17a2b8); display: block; margin-top: 2px;',
|
||||
},
|
||||
'-',
|
||||
);
|
||||
|
||||
const createStatCard = (title: string, valueEl: HTMLElement) => {
|
||||
return E(
|
||||
'div',
|
||||
{
|
||||
class: 'cbi-value',
|
||||
style:
|
||||
'flex: 1 1 110px; min-width: 100px; padding: 8px 10px; border: 1px solid var(--border-color, rgba(128,128,128,0.25)); border-radius: 6px; text-align: center; background: var(--background-color-secondary, rgba(128,128,128,0.05)); margin: 0; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'small',
|
||||
{ style: 'display: block; opacity: 0.75; font-size: 11px;' },
|
||||
title,
|
||||
),
|
||||
valueEl,
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
const statsBar = E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; width: 100%; box-sizing: border-box;',
|
||||
'display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; width: 100%; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'small',
|
||||
{ style: 'display: block; font-size: 11px; opacity: 0.75;' },
|
||||
_('Total Targets'),
|
||||
),
|
||||
totalStatEl,
|
||||
],
|
||||
),
|
||||
E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'small',
|
||||
{ style: 'display: block; font-size: 11px; opacity: 0.75;' },
|
||||
_('Passed'),
|
||||
),
|
||||
passedStatEl,
|
||||
],
|
||||
),
|
||||
E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'small',
|
||||
{ style: 'display: block; font-size: 11px; opacity: 0.75;' },
|
||||
_('Failed'),
|
||||
),
|
||||
failedStatEl,
|
||||
],
|
||||
),
|
||||
E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'small',
|
||||
{ style: 'display: block; font-size: 11px; opacity: 0.75;' },
|
||||
_('Avg Latency'),
|
||||
),
|
||||
latencyStatEl,
|
||||
],
|
||||
),
|
||||
createStatCard(_('Total Targets'), totalStatEl),
|
||||
createStatCard(_('Available'), passedStatEl),
|
||||
createStatCard(_('Unavailable'), failedStatEl),
|
||||
createStatCard(_('Avg Latency'), latencyStatEl),
|
||||
],
|
||||
);
|
||||
|
||||
// Progress Bar Container
|
||||
const progressBarInner = E('div', {
|
||||
style:
|
||||
'width: 0%; height: 100%; background: #28a745; transition: width 0.2s ease;',
|
||||
'width: 0%; height: 100%; background: var(--color-success, #28a745); transition: width 0.2s ease;',
|
||||
});
|
||||
const progressBarContainer = E(
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'width: 100%; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; margin-bottom: 12px; display: none;',
|
||||
'width: 100%; height: 6px; background: var(--border-color, rgba(128,128,128,0.2)); border-radius: 3px; overflow: hidden; margin-bottom: 14px; display: none;',
|
||||
},
|
||||
[progressBarInner],
|
||||
);
|
||||
@@ -303,8 +354,9 @@ export function renderServiceCheckModal() {
|
||||
const badge = E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge',
|
||||
style:
|
||||
'margin-left: 5px; padding: 1px 6px; border-radius: 10px; font-size: 10px; opacity: 0.85; background: rgba(255,255,255,0.18); font-weight: bold;',
|
||||
'margin-left: 6px; padding: 1px 6px; font-size: 10px; opacity: 0.85; border-radius: 10px; background: var(--border-color, rgba(128,128,128,0.3)); font-weight: bold;',
|
||||
},
|
||||
`${targetCount}`,
|
||||
);
|
||||
@@ -336,7 +388,9 @@ export function renderServiceCheckModal() {
|
||||
filterTabsContainer.innerHTML = '';
|
||||
filterTabsContainer.appendChild(createFilterTab(_('All'), 'ALL'));
|
||||
sectionNames.forEach((sec) => {
|
||||
filterTabsContainer.appendChild(createFilterTab(sec, sec));
|
||||
filterTabsContainer.appendChild(
|
||||
createFilterTab(formatSectionName(sec), sec),
|
||||
);
|
||||
});
|
||||
};
|
||||
updateFilterTabs();
|
||||
@@ -344,10 +398,10 @@ export function renderServiceCheckModal() {
|
||||
// Search Box
|
||||
const searchInput = E('input', {
|
||||
type: 'text',
|
||||
placeholder: _('Search domain...'),
|
||||
placeholder: _('Search domain or service...'),
|
||||
class: 'cbi-input-text',
|
||||
style:
|
||||
'width: 180px; padding: 4px 8px; font-size: 12px; border-radius: 4px;',
|
||||
'width: 200px; padding: 4px 10px; font-size: 12px; border-radius: 4px;',
|
||||
});
|
||||
searchInput.oninput = (e: Event) => {
|
||||
const targetInput = e.target as HTMLInputElement;
|
||||
@@ -368,24 +422,24 @@ export function renderServiceCheckModal() {
|
||||
const tableHead = E('tr', { class: 'tr cbi-section-table-titles' }, [
|
||||
E(
|
||||
'th',
|
||||
{ class: 'th', style: 'width: 28%; padding: 6px 8px;' },
|
||||
_('Section / Target'),
|
||||
{ class: 'th', style: 'width: 28%; padding: 8px;' },
|
||||
_('Service / Target'),
|
||||
),
|
||||
E(
|
||||
'th',
|
||||
{ class: 'th', style: 'width: 17%; padding: 6px 8px;' },
|
||||
_('Outbound Route'),
|
||||
{ class: 'th', style: 'width: 17%; padding: 8px;' },
|
||||
_('Route Type'),
|
||||
),
|
||||
E(
|
||||
'th',
|
||||
{ class: 'th', style: 'width: 20%; padding: 6px 8px;' },
|
||||
{ class: 'th', style: 'width: 20%; padding: 8px;' },
|
||||
_('Resolved IP / DNS'),
|
||||
),
|
||||
E(
|
||||
'th',
|
||||
{
|
||||
class: 'th',
|
||||
style: 'text-align: right; width: 7%; padding: 6px 4px;',
|
||||
style: 'text-align: right; width: 7%; padding: 8px 4px;',
|
||||
},
|
||||
_('TCP'),
|
||||
),
|
||||
@@ -393,7 +447,7 @@ export function renderServiceCheckModal() {
|
||||
'th',
|
||||
{
|
||||
class: 'th',
|
||||
style: 'text-align: right; width: 7%; padding: 6px 4px;',
|
||||
style: 'text-align: right; width: 7%; padding: 8px 4px;',
|
||||
},
|
||||
_('TLS'),
|
||||
),
|
||||
@@ -401,7 +455,7 @@ export function renderServiceCheckModal() {
|
||||
'th',
|
||||
{
|
||||
class: 'th',
|
||||
style: 'text-align: right; width: 7%; padding: 6px 4px;',
|
||||
style: 'text-align: right; width: 7%; padding: 8px 4px;',
|
||||
},
|
||||
_('HTTP'),
|
||||
),
|
||||
@@ -409,9 +463,9 @@ export function renderServiceCheckModal() {
|
||||
'th',
|
||||
{
|
||||
class: 'th',
|
||||
style: 'text-align: center; width: 14%; padding: 6px 6px;',
|
||||
style: 'text-align: center; width: 14%; padding: 8px 6px;',
|
||||
},
|
||||
_('Verdict'),
|
||||
_('Status'),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -428,7 +482,7 @@ export function renderServiceCheckModal() {
|
||||
'thead',
|
||||
{
|
||||
style:
|
||||
'position: sticky; top: 0; background: var(--background-color-primary, #1e1e1e); z-index: 2;',
|
||||
'position: sticky; top: 0; background: var(--background-color-primary, #ffffff); z-index: 2;',
|
||||
},
|
||||
[tableHead],
|
||||
),
|
||||
@@ -440,7 +494,7 @@ export function renderServiceCheckModal() {
|
||||
'div',
|
||||
{
|
||||
style:
|
||||
'max-height: 380px; overflow-y: auto; overflow-x: hidden; border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; width: 100%; box-sizing: border-box;',
|
||||
'max-height: 380px; overflow-y: auto; overflow-x: hidden; border: 1px solid var(--border-color, rgba(128,128,128,0.25)); border-radius: 6px; width: 100%; box-sizing: border-box;',
|
||||
},
|
||||
[table],
|
||||
);
|
||||
@@ -451,57 +505,69 @@ export function renderServiceCheckModal() {
|
||||
result?: ServiceCheckResult;
|
||||
}[] = [];
|
||||
|
||||
targets.forEach((item) => {
|
||||
const badge = E(
|
||||
'span',
|
||||
{
|
||||
style:
|
||||
'display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #6c757d; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;',
|
||||
},
|
||||
_('Pending'),
|
||||
);
|
||||
targets.forEach((item, index) => {
|
||||
const rowClass =
|
||||
index % 2 === 0 ? 'cbi-rowstyle-1' : 'cbi-rowstyle-2';
|
||||
const badge = renderStatusBadge('', false, true);
|
||||
|
||||
const tr = E('tr', { class: 'tr cbi-rowstyle-1' }, [
|
||||
E('td', { class: 'td', style: 'word-break: break-all;' }, [
|
||||
const tr = E('tr', { class: `tr ${rowClass}` }, [
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'word-break: break-all; padding: 8px;' },
|
||||
[
|
||||
E(
|
||||
'span',
|
||||
{ style: 'font-weight: 600; font-size: 12px;' },
|
||||
formatSectionName(item.section),
|
||||
),
|
||||
E('br'),
|
||||
E('small', { style: 'opacity: 0.8;' }, item.domain),
|
||||
],
|
||||
),
|
||||
E('td', { class: 'td', style: 'font-size: 12px; padding: 8px;' }, [
|
||||
E(
|
||||
'span',
|
||||
{ style: 'font-weight: 600; font-size: 12px;' },
|
||||
item.section,
|
||||
),
|
||||
E('br'),
|
||||
E('small', { style: 'opacity: 0.8;' }, item.domain),
|
||||
]),
|
||||
E('td', { class: 'td', style: 'font-size: 11px;' }, [
|
||||
E(
|
||||
'code',
|
||||
{
|
||||
style:
|
||||
'background: rgba(255,255,255,0.06); padding: 2px 4px; border-radius: 3px;',
|
||||
class: 'badge cbi-value-title',
|
||||
style: 'font-size: 11px; padding: 2px 6px;',
|
||||
},
|
||||
item.route_type,
|
||||
formatRouteType(item.route_type),
|
||||
),
|
||||
]),
|
||||
E('td', { class: 'td', style: 'font-size: 11px;' }, [
|
||||
E('td', { class: 'td', style: 'font-size: 12px; padding: 8px;' }, [
|
||||
E('span', {}, '?'),
|
||||
E('br'),
|
||||
E('small', { style: 'opacity: 0.6;' }, `-`),
|
||||
]),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right; font-size: 12px;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; font-size: 12px; padding: 8px 4px;',
|
||||
},
|
||||
'-',
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right; font-size: 12px;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; font-size: 12px; padding: 8px 4px;',
|
||||
},
|
||||
'-',
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right; font-size: 12px;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; font-size: 12px; padding: 8px 4px;',
|
||||
},
|
||||
'-',
|
||||
),
|
||||
E('td', { class: 'td', style: 'text-align: center;' }, badge),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: center; padding: 8px 6px;' },
|
||||
badge,
|
||||
),
|
||||
]);
|
||||
|
||||
rowMap.push({ target: item, tr });
|
||||
@@ -515,7 +581,10 @@ export function renderServiceCheckModal() {
|
||||
const matchesSearch =
|
||||
!searchQuery ||
|
||||
target.domain.toLowerCase().includes(searchQuery) ||
|
||||
target.section.toLowerCase().includes(searchQuery);
|
||||
target.section.toLowerCase().includes(searchQuery) ||
|
||||
formatSectionName(target.section)
|
||||
.toLowerCase()
|
||||
.includes(searchQuery);
|
||||
tr.style.display = matchesSection && matchesSearch ? '' : 'none';
|
||||
});
|
||||
};
|
||||
@@ -565,7 +634,7 @@ export function renderServiceCheckModal() {
|
||||
});
|
||||
|
||||
const customBtn = renderButton({
|
||||
text: _('Check Custom'),
|
||||
text: _('Check Domain'),
|
||||
classNames: ['cbi-button-neutral'],
|
||||
onClick: async () => {
|
||||
const domain = customDomainInput.value.trim();
|
||||
@@ -591,55 +660,84 @@ export function renderServiceCheckModal() {
|
||||
|
||||
if (cResults && cResults.length > 0) {
|
||||
const cItem: ServiceCheckResult = cResults[0];
|
||||
const cBadgeColor = cItem.success ? '#28a745' : '#dc3545';
|
||||
const cBadge = E(
|
||||
'span',
|
||||
{
|
||||
style: `display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: ${cBadgeColor}; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;`,
|
||||
},
|
||||
cItem.status_class,
|
||||
);
|
||||
const cBadge = renderStatusBadge(cItem.status_class);
|
||||
|
||||
const customTr = E('tr', { class: 'tr cbi-rowstyle-2' }, [
|
||||
const customTr = E('tr', { class: 'tr cbi-rowstyle-1' }, [
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'word-break: break-all;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'word-break: break-all; padding: 8px;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'span',
|
||||
{ style: 'font-weight:600;' },
|
||||
cItem.section,
|
||||
{ style: 'font-weight: 600;' },
|
||||
formatSectionName(cItem.section),
|
||||
),
|
||||
E('br'),
|
||||
E('small', {}, cItem.domain),
|
||||
],
|
||||
),
|
||||
E('td', { class: 'td', style: 'font-size: 11px;' }, [
|
||||
E('code', {}, cItem.route_type),
|
||||
]),
|
||||
E('td', { class: 'td', style: 'font-size: 11px;' }, [
|
||||
cItem.ip || '?',
|
||||
E('br'),
|
||||
E('small', {}, `${cItem.dns_ms}ms`),
|
||||
]),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'font-size: 12px; padding: 8px;',
|
||||
},
|
||||
[
|
||||
E(
|
||||
'span',
|
||||
{
|
||||
class: 'badge cbi-value-title',
|
||||
style: 'font-size: 11px; padding: 2px 6px;',
|
||||
},
|
||||
formatRouteType(cItem.route_type),
|
||||
),
|
||||
],
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{
|
||||
class: 'td',
|
||||
style: 'font-size: 12px; padding: 8px;',
|
||||
},
|
||||
[
|
||||
cItem.ip || '?',
|
||||
E('br'),
|
||||
E('small', {}, `${cItem.dns_ms}ms`),
|
||||
],
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; padding: 8px 4px;',
|
||||
},
|
||||
cItem.tcp_ms > 0 ? `${cItem.tcp_ms}` : '-',
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; padding: 8px 4px;',
|
||||
},
|
||||
cItem.tls_ms > 0 ? `${cItem.tls_ms}` : '-',
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: right;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: right; padding: 8px 4px;',
|
||||
},
|
||||
cItem.http_ms > 0 ? `${cItem.http_ms}` : '-',
|
||||
),
|
||||
E(
|
||||
'td',
|
||||
{ class: 'td', style: 'text-align: center;' },
|
||||
{
|
||||
class: 'td',
|
||||
style: 'text-align: center; padding: 8px 6px;',
|
||||
},
|
||||
cBadge,
|
||||
),
|
||||
]);
|
||||
@@ -662,7 +760,7 @@ export function renderServiceCheckModal() {
|
||||
// Ignore custom check errors
|
||||
} finally {
|
||||
customBtn.disabled = false;
|
||||
customBtn.textContent = _('Check Custom');
|
||||
customBtn.textContent = _('Check Domain');
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -694,16 +792,7 @@ export function renderServiceCheckModal() {
|
||||
|
||||
const badgeCell = tr.childNodes[6] as HTMLElement;
|
||||
badgeCell.innerHTML = '';
|
||||
badgeCell.appendChild(
|
||||
E(
|
||||
'span',
|
||||
{
|
||||
style:
|
||||
'display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #007bff; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;',
|
||||
},
|
||||
_('Testing...'),
|
||||
),
|
||||
);
|
||||
badgeCell.appendChild(renderStatusBadge('', true));
|
||||
|
||||
try {
|
||||
const cRes = (await callBaseMethod(
|
||||
@@ -724,14 +813,7 @@ export function renderServiceCheckModal() {
|
||||
const cItem: ServiceCheckResult = cResults[0];
|
||||
itemObj.result = cItem;
|
||||
|
||||
const cBadgeColor = cItem.success ? '#28a745' : '#dc3545';
|
||||
const cBadge = E(
|
||||
'span',
|
||||
{
|
||||
style: `display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: ${cBadgeColor}; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;`,
|
||||
},
|
||||
cItem.status_class,
|
||||
);
|
||||
const cBadge = renderStatusBadge(cItem.status_class);
|
||||
|
||||
(tr.childNodes[2] as HTMLElement).innerHTML = '';
|
||||
(tr.childNodes[2] as HTMLElement).appendChild(
|
||||
@@ -759,16 +841,7 @@ export function renderServiceCheckModal() {
|
||||
}
|
||||
} catch (_e) {
|
||||
badgeCell.innerHTML = '';
|
||||
badgeCell.appendChild(
|
||||
E(
|
||||
'span',
|
||||
{
|
||||
style:
|
||||
'display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #dc3545; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;',
|
||||
},
|
||||
_('Failed'),
|
||||
),
|
||||
);
|
||||
badgeCell.appendChild(renderStatusBadge('Failed'));
|
||||
}
|
||||
|
||||
updateSummaryStats();
|
||||
@@ -798,7 +871,7 @@ export function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
'p',
|
||||
{ style: 'color: #dc3545;' },
|
||||
{ style: 'color: var(--color-danger, #dc3545);' },
|
||||
_('Failed to run service check.'),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -10036,13 +10036,83 @@ function renderRunAction({
|
||||
}
|
||||
|
||||
// src/tachyon/tabs/diagnostic/partials/renderServiceCheckModal.ts
|
||||
function formatRouteType(routeType) {
|
||||
if (!routeType) return _("Direct");
|
||||
const clean = routeType.trim();
|
||||
if (clean === "connection" || clean === "proxy" || clean === "outbound") {
|
||||
return _("Proxy");
|
||||
}
|
||||
if (clean === "direct") {
|
||||
return _("Direct");
|
||||
}
|
||||
if (clean === "auto") {
|
||||
return _("Auto");
|
||||
}
|
||||
if (clean.startsWith("zapret2")) {
|
||||
return clean.replace(/^zapret2/, "Zapret 2");
|
||||
}
|
||||
if (clean.startsWith("zapret")) {
|
||||
return clean.replace(/^zapret/, "Zapret");
|
||||
}
|
||||
if (clean.startsWith("byedpi")) {
|
||||
return clean.replace(/^byedpi/, "ByeDPI");
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
function formatSectionName(name) {
|
||||
if (name === "\u0421\u0432\u043E\u0438 \u0434\u043E\u043C\u0435\u043D\u044B") {
|
||||
return _("Custom Domains");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
function renderStatusBadge(statusClass, isTesting = false, isPending = false) {
|
||||
if (isPending) {
|
||||
return E(
|
||||
"span",
|
||||
{
|
||||
class: "badge cbi-value-title",
|
||||
style: "opacity: 0.6; font-size: 11px; padding: 2px 8px;"
|
||||
},
|
||||
_("Pending")
|
||||
);
|
||||
}
|
||||
if (isTesting) {
|
||||
return E(
|
||||
"span",
|
||||
{
|
||||
class: "badge cbi-button-action",
|
||||
style: "font-size: 11px; padding: 2px 8px;"
|
||||
},
|
||||
_("Testing...")
|
||||
);
|
||||
}
|
||||
const cleanStatus = statusClass || "";
|
||||
const isOk = cleanStatus.toUpperCase().includes("OK") || cleanStatus === "200" || cleanStatus === "OK";
|
||||
if (isOk) {
|
||||
return E(
|
||||
"span",
|
||||
{
|
||||
class: "badge cbi-button-save",
|
||||
style: "font-weight: bold; font-size: 11px; padding: 2px 8px;"
|
||||
},
|
||||
_("Available")
|
||||
);
|
||||
}
|
||||
return E(
|
||||
"span",
|
||||
{
|
||||
class: "badge cbi-button-reset",
|
||||
style: "font-weight: bold; font-size: 11px; padding: 2px 8px;"
|
||||
},
|
||||
cleanStatus || _("Unavailable")
|
||||
);
|
||||
}
|
||||
function renderServiceCheckModal() {
|
||||
let currentTargetMode = "active";
|
||||
const container = E(
|
||||
"div",
|
||||
{
|
||||
class: "tachyon-service-check-modal-wrapper",
|
||||
style: "width: 100%; max-width: 820px; box-sizing: border-box;"
|
||||
style: "width: 100%; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
@@ -10062,14 +10132,14 @@ function renderServiceCheckModal() {
|
||||
);
|
||||
const modalContent = E(
|
||||
"div",
|
||||
{ style: "width: 100%; max-width: 820px; box-sizing: border-box;" },
|
||||
{ style: "width: 100%; box-sizing: border-box;" },
|
||||
[
|
||||
container,
|
||||
E(
|
||||
"div",
|
||||
{
|
||||
id: "tachyon-service-check-footer",
|
||||
style: "margin-top: 15px; display: flex; justify-content: space-between; align-items: center; gap: 10px; border-top: 1px solid var(--border-color, rgba(255,255,255,0.15)); padding-top: 12px; flex-wrap: wrap;"
|
||||
style: "margin-top: 15px; display: flex; justify-content: space-between; align-items: center; gap: 10px; border-top: 1px solid var(--border-color, rgba(128,128,128,0.2)); padding-top: 12px; flex-wrap: wrap;"
|
||||
},
|
||||
[
|
||||
renderButton({
|
||||
@@ -10080,9 +10150,8 @@ function renderServiceCheckModal() {
|
||||
)
|
||||
]
|
||||
);
|
||||
ui.showModal(_("Service Health Check"), modalContent);
|
||||
ui.showModal(_("Service Availability Check"), modalContent);
|
||||
const loadTargets = (targetMode) => {
|
||||
currentTargetMode = targetMode;
|
||||
container.innerHTML = "";
|
||||
container.appendChild(
|
||||
E(
|
||||
@@ -10099,7 +10168,7 @@ function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
"p",
|
||||
{ style: "color: #dc3545;" },
|
||||
{ style: "color: var(--color-danger, #dc3545);" },
|
||||
_("Failed to run service check.")
|
||||
)
|
||||
);
|
||||
@@ -10116,7 +10185,7 @@ function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
"p",
|
||||
{ style: "color: #dc3545;" },
|
||||
{ style: "color: var(--color-danger, #dc3545);" },
|
||||
_("Failed to parse target list.")
|
||||
)
|
||||
);
|
||||
@@ -10129,32 +10198,32 @@ function renderServiceCheckModal() {
|
||||
return;
|
||||
}
|
||||
const activeSectionsBtn = renderButton({
|
||||
text: _("Active Sections"),
|
||||
text: _("Active Routes"),
|
||||
classNames: [
|
||||
targetMode === "active" ? "cbi-button-action" : "cbi-button-neutral"
|
||||
],
|
||||
onClick: () => loadTargets("active")
|
||||
});
|
||||
activeSectionsBtn.style.fontSize = "12px";
|
||||
activeSectionsBtn.style.padding = "3px 12px";
|
||||
activeSectionsBtn.style.padding = "4px 12px";
|
||||
const allProfilesBtn = renderButton({
|
||||
text: _("All Profiles"),
|
||||
text: _("All Services"),
|
||||
classNames: [
|
||||
targetMode === "all" ? "cbi-button-action" : "cbi-button-neutral"
|
||||
],
|
||||
onClick: () => loadTargets("all")
|
||||
});
|
||||
allProfilesBtn.style.fontSize = "12px";
|
||||
allProfilesBtn.style.padding = "3px 12px";
|
||||
allProfilesBtn.style.padding = "4px 12px";
|
||||
const modeSwitcherBar = E(
|
||||
"div",
|
||||
{
|
||||
style: "display: flex; gap: 8px; align-items: center; margin-bottom: 12px; border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.1)); padding-bottom: 8px;"
|
||||
style: "display: flex; gap: 10px; align-items: center; margin-bottom: 14px; border-bottom: 1px solid var(--border-color, rgba(128,128,128,0.2)); padding-bottom: 10px;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"span",
|
||||
{ style: "font-weight: 600; font-size: 12px; opacity: 0.8;" },
|
||||
{ style: "font-weight: 600; font-size: 13px; opacity: 0.9;" },
|
||||
_("Check Mode:")
|
||||
),
|
||||
activeSectionsBtn,
|
||||
@@ -10165,108 +10234,69 @@ function renderServiceCheckModal() {
|
||||
let activeFilter = "ALL";
|
||||
let searchQuery2 = "";
|
||||
const totalStatEl = E(
|
||||
"div",
|
||||
"b",
|
||||
{
|
||||
class: "stat-val",
|
||||
style: "font-size: 18px; font-weight: bold; margin-top: 2px;"
|
||||
style: "font-size: 18px; display: block; margin-top: 2px;"
|
||||
},
|
||||
`${targets.length}`
|
||||
);
|
||||
const passedStatEl = E(
|
||||
"div",
|
||||
"b",
|
||||
{
|
||||
class: "stat-val",
|
||||
style: "font-size: 18px; font-weight: bold; color: #28a745; margin-top: 2px;"
|
||||
style: "font-size: 18px; color: var(--color-success, #28a745); display: block; margin-top: 2px;"
|
||||
},
|
||||
"0"
|
||||
);
|
||||
const failedStatEl = E(
|
||||
"div",
|
||||
"b",
|
||||
{
|
||||
class: "stat-val",
|
||||
style: "font-size: 18px; font-weight: bold; color: #dc3545; margin-top: 2px;"
|
||||
style: "font-size: 18px; color: var(--color-danger, #dc3545); display: block; margin-top: 2px;"
|
||||
},
|
||||
"0"
|
||||
);
|
||||
const latencyStatEl = E(
|
||||
"div",
|
||||
"b",
|
||||
{
|
||||
class: "stat-val",
|
||||
style: "font-size: 18px; font-weight: bold; color: #17a2b8; margin-top: 2px;"
|
||||
style: "font-size: 18px; color: var(--color-info, #17a2b8); display: block; margin-top: 2px;"
|
||||
},
|
||||
"-"
|
||||
);
|
||||
const createStatCard = (title, valueEl) => {
|
||||
return E(
|
||||
"div",
|
||||
{
|
||||
class: "cbi-value",
|
||||
style: "flex: 1 1 110px; min-width: 100px; padding: 8px 10px; border: 1px solid var(--border-color, rgba(128,128,128,0.25)); border-radius: 6px; text-align: center; background: var(--background-color-secondary, rgba(128,128,128,0.05)); margin: 0; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"small",
|
||||
{ style: "display: block; opacity: 0.75; font-size: 11px;" },
|
||||
title
|
||||
),
|
||||
valueEl
|
||||
]
|
||||
);
|
||||
};
|
||||
const statsBar = E(
|
||||
"div",
|
||||
{
|
||||
style: "display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; width: 100%; box-sizing: border-box;"
|
||||
style: "display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; width: 100%; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"div",
|
||||
{
|
||||
style: "flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"small",
|
||||
{ style: "display: block; font-size: 11px; opacity: 0.75;" },
|
||||
_("Total Targets")
|
||||
),
|
||||
totalStatEl
|
||||
]
|
||||
),
|
||||
E(
|
||||
"div",
|
||||
{
|
||||
style: "flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"small",
|
||||
{ style: "display: block; font-size: 11px; opacity: 0.75;" },
|
||||
_("Passed")
|
||||
),
|
||||
passedStatEl
|
||||
]
|
||||
),
|
||||
E(
|
||||
"div",
|
||||
{
|
||||
style: "flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"small",
|
||||
{ style: "display: block; font-size: 11px; opacity: 0.75;" },
|
||||
_("Failed")
|
||||
),
|
||||
failedStatEl
|
||||
]
|
||||
),
|
||||
E(
|
||||
"div",
|
||||
{
|
||||
style: "flex: 1 1 120px; min-width: 110px; background: var(--background-color-secondary, rgba(255,255,255,0.05)); border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; padding: 6px 10px; text-align: center; box-sizing: border-box;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"small",
|
||||
{ style: "display: block; font-size: 11px; opacity: 0.75;" },
|
||||
_("Avg Latency")
|
||||
),
|
||||
latencyStatEl
|
||||
]
|
||||
)
|
||||
createStatCard(_("Total Targets"), totalStatEl),
|
||||
createStatCard(_("Available"), passedStatEl),
|
||||
createStatCard(_("Unavailable"), failedStatEl),
|
||||
createStatCard(_("Avg Latency"), latencyStatEl)
|
||||
]
|
||||
);
|
||||
const progressBarInner = E("div", {
|
||||
style: "width: 0%; height: 100%; background: #28a745; transition: width 0.2s ease;"
|
||||
style: "width: 0%; height: 100%; background: var(--color-success, #28a745); transition: width 0.2s ease;"
|
||||
});
|
||||
const progressBarContainer = E(
|
||||
"div",
|
||||
{
|
||||
style: "width: 100%; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; margin-bottom: 12px; display: none;"
|
||||
style: "width: 100%; height: 6px; background: var(--border-color, rgba(128,128,128,0.2)); border-radius: 3px; overflow: hidden; margin-bottom: 14px; display: none;"
|
||||
},
|
||||
[progressBarInner]
|
||||
);
|
||||
@@ -10276,7 +10306,8 @@ function renderServiceCheckModal() {
|
||||
const badge = E(
|
||||
"span",
|
||||
{
|
||||
style: "margin-left: 5px; padding: 1px 6px; border-radius: 10px; font-size: 10px; opacity: 0.85; background: rgba(255,255,255,0.18); font-weight: bold;"
|
||||
class: "badge",
|
||||
style: "margin-left: 6px; padding: 1px 6px; font-size: 10px; opacity: 0.85; border-radius: 10px; background: var(--border-color, rgba(128,128,128,0.3)); font-weight: bold;"
|
||||
},
|
||||
`${targetCount}`
|
||||
);
|
||||
@@ -10303,15 +10334,17 @@ function renderServiceCheckModal() {
|
||||
filterTabsContainer.innerHTML = "";
|
||||
filterTabsContainer.appendChild(createFilterTab(_("All"), "ALL"));
|
||||
sectionNames.forEach((sec) => {
|
||||
filterTabsContainer.appendChild(createFilterTab(sec, sec));
|
||||
filterTabsContainer.appendChild(
|
||||
createFilterTab(formatSectionName(sec), sec)
|
||||
);
|
||||
});
|
||||
};
|
||||
updateFilterTabs();
|
||||
const searchInput = E("input", {
|
||||
type: "text",
|
||||
placeholder: _("Search domain..."),
|
||||
placeholder: _("Search domain or service..."),
|
||||
class: "cbi-input-text",
|
||||
style: "width: 180px; padding: 4px 8px; font-size: 12px; border-radius: 4px;"
|
||||
style: "width: 200px; padding: 4px 10px; font-size: 12px; border-radius: 4px;"
|
||||
});
|
||||
searchInput.oninput = (e) => {
|
||||
const targetInput = e.target;
|
||||
@@ -10328,24 +10361,24 @@ function renderServiceCheckModal() {
|
||||
const tableHead = E("tr", { class: "tr cbi-section-table-titles" }, [
|
||||
E(
|
||||
"th",
|
||||
{ class: "th", style: "width: 28%; padding: 6px 8px;" },
|
||||
_("Section / Target")
|
||||
{ class: "th", style: "width: 28%; padding: 8px;" },
|
||||
_("Service / Target")
|
||||
),
|
||||
E(
|
||||
"th",
|
||||
{ class: "th", style: "width: 17%; padding: 6px 8px;" },
|
||||
_("Outbound Route")
|
||||
{ class: "th", style: "width: 17%; padding: 8px;" },
|
||||
_("Route Type")
|
||||
),
|
||||
E(
|
||||
"th",
|
||||
{ class: "th", style: "width: 20%; padding: 6px 8px;" },
|
||||
{ class: "th", style: "width: 20%; padding: 8px;" },
|
||||
_("Resolved IP / DNS")
|
||||
),
|
||||
E(
|
||||
"th",
|
||||
{
|
||||
class: "th",
|
||||
style: "text-align: right; width: 7%; padding: 6px 4px;"
|
||||
style: "text-align: right; width: 7%; padding: 8px 4px;"
|
||||
},
|
||||
_("TCP")
|
||||
),
|
||||
@@ -10353,7 +10386,7 @@ function renderServiceCheckModal() {
|
||||
"th",
|
||||
{
|
||||
class: "th",
|
||||
style: "text-align: right; width: 7%; padding: 6px 4px;"
|
||||
style: "text-align: right; width: 7%; padding: 8px 4px;"
|
||||
},
|
||||
_("TLS")
|
||||
),
|
||||
@@ -10361,7 +10394,7 @@ function renderServiceCheckModal() {
|
||||
"th",
|
||||
{
|
||||
class: "th",
|
||||
style: "text-align: right; width: 7%; padding: 6px 4px;"
|
||||
style: "text-align: right; width: 7%; padding: 8px 4px;"
|
||||
},
|
||||
_("HTTP")
|
||||
),
|
||||
@@ -10369,9 +10402,9 @@ function renderServiceCheckModal() {
|
||||
"th",
|
||||
{
|
||||
class: "th",
|
||||
style: "text-align: center; width: 14%; padding: 6px 6px;"
|
||||
style: "text-align: center; width: 14%; padding: 8px 6px;"
|
||||
},
|
||||
_("Verdict")
|
||||
_("Status")
|
||||
)
|
||||
]);
|
||||
const tableBody = E("tbody", {});
|
||||
@@ -10385,7 +10418,7 @@ function renderServiceCheckModal() {
|
||||
E(
|
||||
"thead",
|
||||
{
|
||||
style: "position: sticky; top: 0; background: var(--background-color-primary, #1e1e1e); z-index: 2;"
|
||||
style: "position: sticky; top: 0; background: var(--background-color-primary, #ffffff); z-index: 2;"
|
||||
},
|
||||
[tableHead]
|
||||
),
|
||||
@@ -10395,59 +10428,72 @@ function renderServiceCheckModal() {
|
||||
const tableScrollWrapper = E(
|
||||
"div",
|
||||
{
|
||||
style: "max-height: 380px; overflow-y: auto; overflow-x: hidden; border: 1px solid var(--border-color, rgba(255,255,255,0.15)); border-radius: 6px; width: 100%; box-sizing: border-box;"
|
||||
style: "max-height: 380px; overflow-y: auto; overflow-x: hidden; border: 1px solid var(--border-color, rgba(128,128,128,0.25)); border-radius: 6px; width: 100%; box-sizing: border-box;"
|
||||
},
|
||||
[table]
|
||||
);
|
||||
const rowMap = [];
|
||||
targets.forEach((item) => {
|
||||
const badge = E(
|
||||
"span",
|
||||
{
|
||||
style: "display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #6c757d; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;"
|
||||
},
|
||||
_("Pending")
|
||||
);
|
||||
const tr = E("tr", { class: "tr cbi-rowstyle-1" }, [
|
||||
E("td", { class: "td", style: "word-break: break-all;" }, [
|
||||
targets.forEach((item, index) => {
|
||||
const rowClass = index % 2 === 0 ? "cbi-rowstyle-1" : "cbi-rowstyle-2";
|
||||
const badge = renderStatusBadge("", false, true);
|
||||
const tr = E("tr", { class: `tr ${rowClass}` }, [
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "word-break: break-all; padding: 8px;" },
|
||||
[
|
||||
E(
|
||||
"span",
|
||||
{ style: "font-weight: 600; font-size: 12px;" },
|
||||
formatSectionName(item.section)
|
||||
),
|
||||
E("br"),
|
||||
E("small", { style: "opacity: 0.8;" }, item.domain)
|
||||
]
|
||||
),
|
||||
E("td", { class: "td", style: "font-size: 12px; padding: 8px;" }, [
|
||||
E(
|
||||
"span",
|
||||
{ style: "font-weight: 600; font-size: 12px;" },
|
||||
item.section
|
||||
),
|
||||
E("br"),
|
||||
E("small", { style: "opacity: 0.8;" }, item.domain)
|
||||
]),
|
||||
E("td", { class: "td", style: "font-size: 11px;" }, [
|
||||
E(
|
||||
"code",
|
||||
{
|
||||
style: "background: rgba(255,255,255,0.06); padding: 2px 4px; border-radius: 3px;"
|
||||
class: "badge cbi-value-title",
|
||||
style: "font-size: 11px; padding: 2px 6px;"
|
||||
},
|
||||
item.route_type
|
||||
formatRouteType(item.route_type)
|
||||
)
|
||||
]),
|
||||
E("td", { class: "td", style: "font-size: 11px;" }, [
|
||||
E("td", { class: "td", style: "font-size: 12px; padding: 8px;" }, [
|
||||
E("span", {}, "?"),
|
||||
E("br"),
|
||||
E("small", { style: "opacity: 0.6;" }, `-`)
|
||||
]),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right; font-size: 12px;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; font-size: 12px; padding: 8px 4px;"
|
||||
},
|
||||
"-"
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right; font-size: 12px;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; font-size: 12px; padding: 8px 4px;"
|
||||
},
|
||||
"-"
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right; font-size: 12px;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; font-size: 12px; padding: 8px 4px;"
|
||||
},
|
||||
"-"
|
||||
),
|
||||
E("td", { class: "td", style: "text-align: center;" }, badge)
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: center; padding: 8px 6px;" },
|
||||
badge
|
||||
)
|
||||
]);
|
||||
rowMap.push({ target: item, tr });
|
||||
tableBody.appendChild(tr);
|
||||
@@ -10455,7 +10501,7 @@ function renderServiceCheckModal() {
|
||||
const applyTableFilter = () => {
|
||||
rowMap.forEach(({ target, tr }) => {
|
||||
const matchesSection = activeFilter === "ALL" || target.section === activeFilter;
|
||||
const matchesSearch = !searchQuery2 || target.domain.toLowerCase().includes(searchQuery2) || target.section.toLowerCase().includes(searchQuery2);
|
||||
const matchesSearch = !searchQuery2 || target.domain.toLowerCase().includes(searchQuery2) || target.section.toLowerCase().includes(searchQuery2) || formatSectionName(target.section).toLowerCase().includes(searchQuery2);
|
||||
tr.style.display = matchesSection && matchesSearch ? "" : "none";
|
||||
});
|
||||
};
|
||||
@@ -10495,7 +10541,7 @@ function renderServiceCheckModal() {
|
||||
style: "width: 260px; font-size: 12px;"
|
||||
});
|
||||
const customBtn = renderButton({
|
||||
text: _("Check Custom"),
|
||||
text: _("Check Domain"),
|
||||
classNames: ["cbi-button-neutral"],
|
||||
onClick: async () => {
|
||||
const domain = customDomainInput.value.trim();
|
||||
@@ -10511,54 +10557,83 @@ function renderServiceCheckModal() {
|
||||
const cResults = typeof cRes.data === "string" ? JSON.parse(cRes.data) : cRes.data || [];
|
||||
if (cResults && cResults.length > 0) {
|
||||
const cItem = cResults[0];
|
||||
const cBadgeColor = cItem.success ? "#28a745" : "#dc3545";
|
||||
const cBadge = E(
|
||||
"span",
|
||||
{
|
||||
style: `display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: ${cBadgeColor}; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;`
|
||||
},
|
||||
cItem.status_class
|
||||
);
|
||||
const customTr = E("tr", { class: "tr cbi-rowstyle-2" }, [
|
||||
const cBadge = renderStatusBadge(cItem.status_class);
|
||||
const customTr = E("tr", { class: "tr cbi-rowstyle-1" }, [
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "word-break: break-all;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "word-break: break-all; padding: 8px;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"span",
|
||||
{ style: "font-weight:600;" },
|
||||
cItem.section
|
||||
{ style: "font-weight: 600;" },
|
||||
formatSectionName(cItem.section)
|
||||
),
|
||||
E("br"),
|
||||
E("small", {}, cItem.domain)
|
||||
]
|
||||
),
|
||||
E("td", { class: "td", style: "font-size: 11px;" }, [
|
||||
E("code", {}, cItem.route_type)
|
||||
]),
|
||||
E("td", { class: "td", style: "font-size: 11px;" }, [
|
||||
cItem.ip || "?",
|
||||
E("br"),
|
||||
E("small", {}, `${cItem.dns_ms}ms`)
|
||||
]),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "font-size: 12px; padding: 8px;"
|
||||
},
|
||||
[
|
||||
E(
|
||||
"span",
|
||||
{
|
||||
class: "badge cbi-value-title",
|
||||
style: "font-size: 11px; padding: 2px 6px;"
|
||||
},
|
||||
formatRouteType(cItem.route_type)
|
||||
)
|
||||
]
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{
|
||||
class: "td",
|
||||
style: "font-size: 12px; padding: 8px;"
|
||||
},
|
||||
[
|
||||
cItem.ip || "?",
|
||||
E("br"),
|
||||
E("small", {}, `${cItem.dns_ms}ms`)
|
||||
]
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; padding: 8px 4px;"
|
||||
},
|
||||
cItem.tcp_ms > 0 ? `${cItem.tcp_ms}` : "-"
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; padding: 8px 4px;"
|
||||
},
|
||||
cItem.tls_ms > 0 ? `${cItem.tls_ms}` : "-"
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: right;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: right; padding: 8px 4px;"
|
||||
},
|
||||
cItem.http_ms > 0 ? `${cItem.http_ms}` : "-"
|
||||
),
|
||||
E(
|
||||
"td",
|
||||
{ class: "td", style: "text-align: center;" },
|
||||
{
|
||||
class: "td",
|
||||
style: "text-align: center; padding: 8px 6px;"
|
||||
},
|
||||
cBadge
|
||||
)
|
||||
]);
|
||||
@@ -10579,7 +10654,7 @@ function renderServiceCheckModal() {
|
||||
} catch (_e) {
|
||||
} finally {
|
||||
customBtn.disabled = false;
|
||||
customBtn.textContent = _("Check Custom");
|
||||
customBtn.textContent = _("Check Domain");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -10604,15 +10679,7 @@ function renderServiceCheckModal() {
|
||||
progressBarInner.style.width = `${Math.round((i + 1) / rowMap.length * 100)}%`;
|
||||
const badgeCell = tr.childNodes[6];
|
||||
badgeCell.innerHTML = "";
|
||||
badgeCell.appendChild(
|
||||
E(
|
||||
"span",
|
||||
{
|
||||
style: "display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #007bff; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;"
|
||||
},
|
||||
_("Testing...")
|
||||
)
|
||||
);
|
||||
badgeCell.appendChild(renderStatusBadge("", true));
|
||||
try {
|
||||
const cRes = await callBaseMethod(
|
||||
Tachyon.AvailableMethods.SERVICE_HEALTH_CHECK,
|
||||
@@ -10623,14 +10690,7 @@ function renderServiceCheckModal() {
|
||||
if (cResults && cResults.length > 0) {
|
||||
const cItem = cResults[0];
|
||||
itemObj.result = cItem;
|
||||
const cBadgeColor = cItem.success ? "#28a745" : "#dc3545";
|
||||
const cBadge = E(
|
||||
"span",
|
||||
{
|
||||
style: `display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: ${cBadgeColor}; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;`
|
||||
},
|
||||
cItem.status_class
|
||||
);
|
||||
const cBadge = renderStatusBadge(cItem.status_class);
|
||||
tr.childNodes[2].innerHTML = "";
|
||||
tr.childNodes[2].appendChild(
|
||||
document.createTextNode(cItem.ip || "?")
|
||||
@@ -10652,15 +10712,7 @@ function renderServiceCheckModal() {
|
||||
}
|
||||
} catch (_e) {
|
||||
badgeCell.innerHTML = "";
|
||||
badgeCell.appendChild(
|
||||
E(
|
||||
"span",
|
||||
{
|
||||
style: "display: inline-block; padding: 2px 6px; border-radius: 8px; color: #fff; font-size: 10px; font-weight: 500; background: #dc3545; text-align: center; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;"
|
||||
},
|
||||
_("Failed")
|
||||
)
|
||||
);
|
||||
badgeCell.appendChild(renderStatusBadge("Failed"));
|
||||
}
|
||||
updateSummaryStats();
|
||||
}
|
||||
@@ -10684,7 +10736,7 @@ function renderServiceCheckModal() {
|
||||
container.appendChild(
|
||||
E(
|
||||
"p",
|
||||
{ style: "color: #dc3545;" },
|
||||
{ style: "color: var(--color-danger, #dc3545);" },
|
||||
_("Failed to run service check.")
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user