feat: добавлены новые компоненты для управления правилами и серверами
All checks were successful
Build and Deploy Gateway / build-and-deploy (push) Successful in 25s
All checks were successful
Build and Deploy Gateway / build-and-deploy (push) Successful in 25s
- Создан компонент RuleEditorDrawer для редактирования правил с поддержкой JSON. - Добавлен компонент ServersPage для отображения и управления серверами. - Реализован компонент SettingsPage для управления подписками и конфигурациями. - Создан компонент Sidebar для навигации по приложению. - Добавлен компонент StatusPane для отображения статуса сервера. - Реализован компонент Toasts для отображения уведомлений. - Создан компонент Topbar для отображения информации о текущем состоянии. - Добавлен модуль country.js для определения страны по тегу сервера. Refs: None
This commit is contained in:
91
src/web/components/StatusPane.jsx
Normal file
91
src/web/components/StatusPane.jsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React from 'react';
|
||||
import { formatBytes, formatRelative } from '../utils/format.js';
|
||||
|
||||
function StatusRow({ label, value, kind }) {
|
||||
return (
|
||||
<div className="row">
|
||||
<span className="key">{label}</span>
|
||||
<span className={`val ${kind ? 'text-' + kind : ''}`}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function StatusPane({ state, busy, onStop, onRestart, onShowConfig }) {
|
||||
const userInfo = state?.userInfo;
|
||||
const traffic = userInfo
|
||||
? `${formatBytes((userInfo.upload || 0) + (userInfo.download || 0))} / ${userInfo.total ? formatBytes(userInfo.total) : '∞'}`
|
||||
: '—';
|
||||
|
||||
let singboxStatus = 'Остановлен';
|
||||
let singboxKind = 'muted';
|
||||
if (state?.singboxRunning) {
|
||||
singboxStatus = `работает · ${formatRelative(state.singboxStartedAt)}`;
|
||||
singboxKind = 'success';
|
||||
} else if (state?.configExists) {
|
||||
singboxStatus = 'остановлен (конфиг есть)';
|
||||
singboxKind = 'warning';
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="status-pane">
|
||||
<div className="card compact flat">
|
||||
<div className="card-header no-margin">
|
||||
<h3>sing-box</h3>
|
||||
<span className={`badge ${state?.singboxRunning ? 'success' : 'neutral'}`}>
|
||||
{state?.singboxRunning ? '● online' : '○ offline'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="kv-list" style={{ marginTop: 12 }}>
|
||||
<StatusRow label="Статус" value={singboxStatus} kind={singboxKind} />
|
||||
<StatusRow label="UI порт" value={`:${state?.port || 3456}`} />
|
||||
<StatusRow label="Mixed proxy" value={`:${state?.proxyPort || 8080}`} />
|
||||
<StatusRow label="TProxy" value={`:${state?.tproxyPort || 7895}`} />
|
||||
<StatusRow label="RU direct" value={state?.routingRuDirect ? 'включено' : 'выключено'} />
|
||||
<StatusRow label="Трафик" value={traffic} />
|
||||
<StatusRow
|
||||
label="Применено"
|
||||
value={state?.appliedAt ? formatRelative(state.appliedAt) : 'не применено'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="btn-group" style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
<button
|
||||
className="btn btn-secondary sm block"
|
||||
disabled={busy || !state?.configExists}
|
||||
onClick={onRestart}
|
||||
>
|
||||
↻ Перезапустить
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost sm block"
|
||||
disabled={busy || !state?.singboxRunning}
|
||||
onClick={onStop}
|
||||
>
|
||||
■ Остановить
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost sm block"
|
||||
disabled={!state?.configExists}
|
||||
onClick={onShowConfig}
|
||||
>
|
||||
⌘ Показать config
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state?.appliedHistory?.length > 0 && (
|
||||
<div className="card compact flat">
|
||||
<h4 style={{ marginBottom: 8 }}>История применений</h4>
|
||||
<div className="events-list">
|
||||
{state.appliedHistory.slice(0, 5).map((h) => (
|
||||
<div key={h.at} className="event-row" style={{ gridTemplateColumns: '1fr auto' }}>
|
||||
<span className="text-truncate">{h.tag}</span>
|
||||
<span className="event-time">{formatRelative(h.at)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user