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

This commit is contained in:
2025-07-11 07:56:43 +07:00
parent 4efe958c2e
commit 546d9aabac
2 changed files with 2537 additions and 59 deletions
+2309
View File
File diff suppressed because it is too large Load Diff
+228 -59
View File
@@ -65,6 +65,14 @@ function ServerManager() {
version: 'v4.rsc'
});
// Состояние для мониторинга сети
const [networkStatus, setNetworkStatus] = useState({});
const [monitoringEnabled, setMonitoringEnabled] = useState(false);
// Состояние для модального окна добавления сервера
const [addServerModalOpen, setAddServerModalOpen] = useState(false);
const [customProvider, setCustomProvider] = useState('');
useEffect(() => {
fetchServers();
}, []);
@@ -106,6 +114,44 @@ function ServerManager() {
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) => {
setEditModalServer({ ...server });
setEditModalOpen(true);
@@ -354,68 +400,20 @@ function ServerManager() {
<div className="card-header">
<h3 className="card-title">
<IconPlus className="icon me-2" />
Добавить новый сервер
Управление серверами
</h3>
</div>
<div className="card-body">
<form onSubmit={(e) => { e.preventDefault(); handleAddServer(); }}>
<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) => setNewServer({ ...newServer, ip: e.target.value })}
/>
</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 className="form-footer">
<button
type="button"
className="btn btn-primary w-100"
onClick={handleOpenAddServerModal}
>
<IconPlus className="icon me-2" />
Добавить новый сервер
</button>
</div>
</div>
</div>
@@ -547,6 +545,8 @@ function ServerManager() {
</span>
)}
</th>
<th className="text-center">Основной шлюз</th>
<th className="text-center">Статус</th>
<th className="text-end">Ссылка</th>
<th className="text-end">Действия</th>
</tr>
@@ -559,6 +559,14 @@ function ServerManager() {
<td><span className="badge bg-blue-lt text-blue">{countryToFlag(server.country)} {server.country}</span></td>
<td>{server.provider}</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">
<button
className="btn btn-outline-info btn-icon"
@@ -634,6 +642,18 @@ function ServerManager() {
onCopyToClipboard={copyToClipboard}
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;