feat: Implement add server modal in ServerManager for enhanced server management, including validation and custom provider support
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 17m24s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 17m24s
This commit is contained in:
+2309
File diff suppressed because it is too large
Load Diff
+228
-59
@@ -65,6 +65,14 @@ function ServerManager() {
|
|||||||
version: 'v4.rsc'
|
version: 'v4.rsc'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Состояние для мониторинга сети
|
||||||
|
const [networkStatus, setNetworkStatus] = useState({});
|
||||||
|
const [monitoringEnabled, setMonitoringEnabled] = useState(false);
|
||||||
|
|
||||||
|
// Состояние для модального окна добавления сервера
|
||||||
|
const [addServerModalOpen, setAddServerModalOpen] = useState(false);
|
||||||
|
const [customProvider, setCustomProvider] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchServers();
|
fetchServers();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -106,6 +114,44 @@ function ServerManager() {
|
|||||||
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
|
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Функции для модального окна добавления сервера
|
||||||
|
const handleOpenAddServerModal = () => {
|
||||||
|
setAddServerModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseAddServerModal = () => {
|
||||||
|
setAddServerModalOpen(false);
|
||||||
|
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
|
||||||
|
setCustomProvider('');
|
||||||
|
setError('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddServerFromModal = () => {
|
||||||
|
if (!newServer.ip.trim() || !newServer.dns.trim() || !newServer.country.trim() || !newServer.provider.trim() || !newServer.tunnel.trim()) {
|
||||||
|
setError('Все поля должны быть заполнены.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, если выбран "Другой" провайдер, то customProvider должен быть заполнен
|
||||||
|
if (newServer.provider === 'Другой' && !customProvider.trim()) {
|
||||||
|
setError('Пожалуйста, введите название провайдера.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
// Используем customProvider если выбран "Другой"
|
||||||
|
const finalProvider = newServer.provider === 'Другой' ? customProvider : newServer.provider;
|
||||||
|
const serverToAdd = { ...newServer, provider: finalProvider };
|
||||||
|
|
||||||
|
setServers([...servers, serverToAdd]);
|
||||||
|
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
|
||||||
|
setCustomProvider('');
|
||||||
|
setAddServerModalOpen(false);
|
||||||
|
setSuccess('Сервер успешно добавлен!');
|
||||||
|
setTimeout(() => setSuccess(''), 3000);
|
||||||
|
};
|
||||||
|
|
||||||
const handleEdit = (server) => {
|
const handleEdit = (server) => {
|
||||||
setEditModalServer({ ...server });
|
setEditModalServer({ ...server });
|
||||||
setEditModalOpen(true);
|
setEditModalOpen(true);
|
||||||
@@ -354,68 +400,20 @@ function ServerManager() {
|
|||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
<h3 className="card-title">
|
<h3 className="card-title">
|
||||||
<IconPlus className="icon me-2" />
|
<IconPlus className="icon me-2" />
|
||||||
Добавить новый сервер
|
Управление серверами
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<form onSubmit={(e) => { e.preventDefault(); handleAddServer(); }}>
|
<div className="form-footer">
|
||||||
<div className="mb-3">
|
<button
|
||||||
<label className="form-label">IP адрес</label>
|
type="button"
|
||||||
<input
|
className="btn btn-primary w-100"
|
||||||
type="text"
|
onClick={handleOpenAddServerModal}
|
||||||
className="form-control"
|
>
|
||||||
placeholder="192.168.1.1"
|
<IconPlus className="icon me-2" />
|
||||||
value={newServer.ip}
|
Добавить новый сервер
|
||||||
onChange={(e) => setNewServer({ ...newServer, ip: e.target.value })}
|
</button>
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label className="form-label">DNS имя</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
placeholder="srv1.shx.su"
|
|
||||||
value={newServer.dns}
|
|
||||||
onChange={(e) => setNewServer({ ...newServer, dns: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label className="form-label">Страна</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
placeholder="RU"
|
|
||||||
value={newServer.country}
|
|
||||||
onChange={(e) => setNewServer({ ...newServer, country: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label className="form-label">Провайдер</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Yandex"
|
|
||||||
value={newServer.provider}
|
|
||||||
onChange={(e) => setNewServer({ ...newServer, provider: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label className="form-label">Тип туннеля</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
placeholder="GRE"
|
|
||||||
value={newServer.tunnel}
|
|
||||||
onChange={(e) => setNewServer({ ...newServer, tunnel: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-footer">
|
|
||||||
<button type="submit" className="btn btn-primary w-100">
|
|
||||||
<IconPlus className="icon me-2" />
|
|
||||||
Добавить сервер
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -547,6 +545,8 @@ function ServerManager() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</th>
|
</th>
|
||||||
|
<th className="text-center">Основной шлюз</th>
|
||||||
|
<th className="text-center">Статус</th>
|
||||||
<th className="text-end">Ссылка</th>
|
<th className="text-end">Ссылка</th>
|
||||||
<th className="text-end">Действия</th>
|
<th className="text-end">Действия</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -559,6 +559,14 @@ function ServerManager() {
|
|||||||
<td><span className="badge bg-blue-lt text-blue">{countryToFlag(server.country)} {server.country}</span></td>
|
<td><span className="badge bg-blue-lt text-blue">{countryToFlag(server.country)} {server.country}</span></td>
|
||||||
<td>{server.provider}</td>
|
<td>{server.provider}</td>
|
||||||
<td><span className="badge bg-green-lt text-green">{server.tunnel}</span></td>
|
<td><span className="badge bg-green-lt text-green">{server.tunnel}</span></td>
|
||||||
|
<td className="text-center">
|
||||||
|
<span className="badge bg-blue-lt text-blue">
|
||||||
|
{server.provider}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="text-center">
|
||||||
|
<span className="badge bg-success-lt text-success">Онлайн</span>
|
||||||
|
</td>
|
||||||
<td className="text-end">
|
<td className="text-end">
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline-info btn-icon"
|
className="btn btn-outline-info btn-icon"
|
||||||
@@ -634,6 +642,18 @@ function ServerManager() {
|
|||||||
onCopyToClipboard={copyToClipboard}
|
onCopyToClipboard={copyToClipboard}
|
||||||
onClose={handleLinkGeneratorClose}
|
onClose={handleLinkGeneratorClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Add Server Modal */}
|
||||||
|
<AddServerModal
|
||||||
|
show={addServerModalOpen}
|
||||||
|
newServer={newServer}
|
||||||
|
customProvider={customProvider}
|
||||||
|
onNewServerChange={setNewServer}
|
||||||
|
onCustomProviderChange={setCustomProvider}
|
||||||
|
onAddServer={handleAddServerFromModal}
|
||||||
|
onClose={handleCloseAddServerModal}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -934,4 +954,153 @@ function LinkGeneratorModal({ show, server, urlSettings, onUrlSettingsChange, on
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Модальное окно для добавления нового сервера
|
||||||
|
function AddServerModal({ show, newServer, customProvider, onNewServerChange, onCustomProviderChange, onAddServer, onClose, error }) {
|
||||||
|
const modalRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.Tabler && window.Tabler.Modal && modalRef.current) {
|
||||||
|
const modalInstance = window.Tabler.Modal.getOrCreateInstance(modalRef.current);
|
||||||
|
if (show) {
|
||||||
|
modalInstance.show();
|
||||||
|
} else {
|
||||||
|
modalInstance.hide();
|
||||||
|
}
|
||||||
|
const handler = () => onClose && onClose();
|
||||||
|
modalRef.current.addEventListener('hide.bs.modal', handler);
|
||||||
|
return () => {
|
||||||
|
if (modalRef.current) {
|
||||||
|
modalRef.current.removeEventListener('hide.bs.modal', handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [show, onClose]);
|
||||||
|
|
||||||
|
const handleChange = (field, value) => {
|
||||||
|
onNewServerChange({ ...newServer, [field]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onAddServer();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal" tabIndex="-1" ref={modalRef}>
|
||||||
|
<div className="modal-dialog">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-header">
|
||||||
|
<h5 className="modal-title">
|
||||||
|
<IconPlus className="icon me-2" />
|
||||||
|
Добавить новый сервер
|
||||||
|
</h5>
|
||||||
|
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
{error && (
|
||||||
|
<div className="alert alert-danger" role="alert">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">IP адрес *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="192.168.1.1"
|
||||||
|
value={newServer.ip}
|
||||||
|
onChange={(e) => handleChange('ip', e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">DNS имя *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="srv1.shx.su"
|
||||||
|
value={newServer.dns}
|
||||||
|
onChange={(e) => handleChange('dns', e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Страна *</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={newServer.country}
|
||||||
|
onChange={(e) => handleChange('country', e.target.value)}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">Выберите страну</option>
|
||||||
|
<option value="RU">Россия (RU)</option>
|
||||||
|
<option value="SWE">Швеция (SWE)</option>
|
||||||
|
<option value="US">США (US)</option>
|
||||||
|
<option value="DE">Германия (DE)</option>
|
||||||
|
<option value="NL">Нидерланды (NL)</option>
|
||||||
|
<option value="SG">Сингапур (SG)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Провайдер *</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={newServer.provider}
|
||||||
|
onChange={(e) => handleChange('provider', e.target.value)}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">Выберите провайдера</option>
|
||||||
|
<option value="Yandex">Yandex Cloud</option>
|
||||||
|
<option value="AWS">Amazon Web Services</option>
|
||||||
|
<option value="Google">Google Cloud</option>
|
||||||
|
<option value="Azure">Microsoft Azure</option>
|
||||||
|
<option value="Hetzner">Hetzner</option>
|
||||||
|
<option value="OVH">OVH</option>
|
||||||
|
<option value="DigitalOcean">DigitalOcean</option>
|
||||||
|
<option value="Vultr">Vultr</option>
|
||||||
|
<option value="Linode">Linode</option>
|
||||||
|
<option value="VPSVILLE">VPSVILLE</option>
|
||||||
|
<option value="HIPHOST">HIPHOST</option>
|
||||||
|
<option value="Другой">Другой</option>
|
||||||
|
</select>
|
||||||
|
{newServer.provider === 'Другой' && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control mt-2"
|
||||||
|
placeholder="Введите название провайдера"
|
||||||
|
value={customProvider}
|
||||||
|
onChange={(e) => onCustomProviderChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Тип туннеля *</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={newServer.tunnel}
|
||||||
|
onChange={(e) => handleChange('tunnel', e.target.value)}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="GRE">GRE (Generic Routing Encapsulation)</option>
|
||||||
|
<option value="IPSec">IPSec</option>
|
||||||
|
<option value="WireGuard">WireGuard</option>
|
||||||
|
<option value="OpenVPN">OpenVPN</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div className="modal-footer">
|
||||||
|
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||||
|
<button type="button" className="btn btn-primary" onClick={onAddServer}>
|
||||||
|
<IconPlus className="icon me-2" />
|
||||||
|
Добавить сервер
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default ServerManager;
|
export default ServerManager;
|
||||||
Reference in New Issue
Block a user