feat: Add GraphView component for visualizing server connections and implement connection management in ServerManager
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 8m19s

This commit is contained in:
2025-07-15 19:24:45 +07:00
parent 1a31b37677
commit 2590b08262
4 changed files with 797 additions and 0 deletions
+70
View File
@@ -17,6 +17,7 @@ import {
IconChevronDown,
IconLink
} from '@tabler/icons-react';
import GraphView from './GraphView';
const API_URL = '/api';
@@ -74,6 +75,12 @@ function ServerManager() {
const [addServerModalOpen, setAddServerModalOpen] = useState(false);
const [customProvider, setCustomProvider] = useState('');
const [connections, setConnections] = useState([
// Пример связи для теста
// { from: '192.168.1.1', to: '192.168.1.2', tunnelType: 'GRE', ipA: '10.10.100.1', ipB: '10.10.100.2' }
]);
const [newConnection, setNewConnection] = useState({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });
useEffect(() => {
fetchServers();
}, []);
@@ -385,6 +392,17 @@ function ServerManager() {
);
}
// Добавление новой связи
const handleAddConnection = () => {
if (!newConnection.from || !newConnection.to || !newConnection.tunnelType || !newConnection.ipA || !newConnection.ipB) {
setError('Все поля для связи должны быть заполнены.');
return;
}
setConnections([...connections, newConnection]);
setNewConnection({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });
setError('');
};
return (
<>
{error && (
@@ -636,6 +654,58 @@ function ServerManager() {
onClose={handleCloseAddServerModal}
error={error}
/>
<div className="card w-100 mb-4">
<div className="card-header">
<h3 className="card-title mb-0">Граф связей между серверами</h3>
</div>
<div className="card-body">
<GraphView servers={servers} connections={connections} />
</div>
</div>
<div className="card w-100 mb-4">
<div className="card-header">
<h3 className="card-title mb-0">Добавить связь между серверами</h3>
</div>
<div className="card-body">
<div className="row g-2 align-items-end">
<div className="col-md-3">
<label className="form-label">Сервер A (IP)</label>
<select className="form-select" value={newConnection.from} onChange={e => setNewConnection({ ...newConnection, from: e.target.value })}>
<option value="">Выберите</option>
{servers.map(s => <option key={s.ip} value={s.ip}>{s.ip} ({s.dns})</option>)}
</select>
</div>
<div className="col-md-3">
<label className="form-label">Сервер B (IP)</label>
<select className="form-select" value={newConnection.to} onChange={e => setNewConnection({ ...newConnection, to: e.target.value })}>
<option value="">Выберите</option>
{servers.map(s => <option key={s.ip} value={s.ip}>{s.ip} ({s.dns})</option>)}
</select>
</div>
<div className="col-md-2">
<label className="form-label">Тип туннеля</label>
<select className="form-select" value={newConnection.tunnelType} onChange={e => setNewConnection({ ...newConnection, tunnelType: e.target.value })}>
<option value="GRE">GRE</option>
<option value="IPSec">IPSec</option>
<option value="WireGuard">WireGuard</option>
<option value="OpenVPN">OpenVPN</option>
</select>
</div>
<div className="col-md-2">
<label className="form-label">IP сервера A</label>
<input className="form-control" value={newConnection.ipA} onChange={e => setNewConnection({ ...newConnection, ipA: e.target.value })} placeholder="10.10.100.1" />
</div>
<div className="col-md-2">
<label className="form-label">IP сервера B</label>
<input className="form-control" value={newConnection.ipB} onChange={e => setNewConnection({ ...newConnection, ipB: e.target.value })} placeholder="10.10.100.2" />
</div>
<div className="col-md-12 mt-2">
<button className="btn btn-primary" type="button" onClick={handleAddConnection}>Добавить связь</button>
</div>
</div>
</div>
</div>
</>
);
}