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:
74
src/web/components/RouteChecker.jsx
Normal file
74
src/web/components/RouteChecker.jsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import { api } from '../api.js';
|
||||
|
||||
export function RouteChecker() {
|
||||
const [host, setHost] = useState('');
|
||||
const [port, setPort] = useState('443');
|
||||
const [network, setNetwork] = useState('tcp');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
async function check() {
|
||||
setBusy(true);
|
||||
setError('');
|
||||
setResult(null);
|
||||
try {
|
||||
const data = await api.route.check({ host, port: port || undefined, network });
|
||||
setResult(data);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
const r = result?.result;
|
||||
const kind = r?.outbound?.startsWith('direct') ? 'success'
|
||||
: r?.outbound === 'block' ? 'danger'
|
||||
: r?.outbound?.includes('VPN') || r?.outbound?.includes('vpn') ? 'info'
|
||||
: 'warning';
|
||||
|
||||
return (
|
||||
<div className="card flat compact">
|
||||
<div className="card-header no-margin"><h3>Проверить маршрут</h3></div>
|
||||
<div className="filter-bar" style={{ marginTop: 12 }}>
|
||||
<input
|
||||
className="input"
|
||||
placeholder="домен или IP (riotgames.com)"
|
||||
value={host}
|
||||
onChange={(e) => setHost(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && check()}
|
||||
style={{ minWidth: 220, flex: 1 }}
|
||||
/>
|
||||
<input
|
||||
className="input"
|
||||
placeholder="port"
|
||||
value={port}
|
||||
onChange={(e) => setPort(e.target.value)}
|
||||
style={{ width: 90 }}
|
||||
/>
|
||||
<select className="select" value={network} onChange={(e) => setNetwork(e.target.value)} style={{ width: 90 }}>
|
||||
<option value="tcp">tcp</option>
|
||||
<option value="udp">udp</option>
|
||||
</select>
|
||||
<button className="btn btn-primary" onClick={check} disabled={busy || !host}>Проверить</button>
|
||||
</div>
|
||||
|
||||
{error && <div className="field-error" style={{ marginTop: 10 }}>{error}</div>}
|
||||
|
||||
{r && (
|
||||
<div className="route-result" style={{ marginTop: 12 }}>
|
||||
<div className="flex-between">
|
||||
<strong>{r.ruleIndex >= 0 ? `Правило #${r.ruleIndex + 1}: ${r.ruleName}` : r.ruleName}</strong>
|
||||
<span className={`badge ${kind}`}>→ {r.outbound}</span>
|
||||
</div>
|
||||
{result.resolvedIp && result.resolvedFrom && (
|
||||
<small className="muted text-mono">DNS: {result.resolvedFrom} → {result.resolvedIp}</small>
|
||||
)}
|
||||
<small className="muted">{r.reason}</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user