feat: Enhance ServerManager component by adding optional external and internal IP fields, improving server addition and editing functionality, and updating validation messages for better user experience.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m45s

This commit is contained in:
2025-12-01 15:57:53 +07:00
parent c3e0f99ff1
commit bf4b1b1554
+98 -10
View File
@@ -31,7 +31,10 @@ const API_URL = '/api';
function ServerManager() {
const [servers, setServers] = useState([]);
const [newServer, setNewServer] = useState({
id: '',
ip: '',
extIp: '',
internalIp: '',
dns: '',
country: '',
provider: '',
@@ -132,12 +135,27 @@ function ServerManager() {
const handleAddServer = () => {
if (!newServer.ip.trim() || !newServer.dns.trim() || !newServer.country.trim() || !newServer.provider.trim() || !newServer.tunnel.trim()) {
setError('Все поля должны быть заполнены.');
setError('Все обязательные поля должны быть заполнены.');
return;
}
setError('');
setServers([...servers, { ...newServer }]);
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
const nextId = `srv-${servers.length + 1}`;
const serverToAdd = {
...newServer,
id: newServer.id || nextId,
};
setServers([...servers, serverToAdd]);
setNewServer({
id: '',
ip: '',
extIp: '',
internalIp: '',
dns: '',
country: '',
provider: '',
tunnel: 'GRE',
gateway: ''
});
};
// Функции для модального окна добавления сервера
@@ -147,14 +165,24 @@ function ServerManager() {
const handleCloseAddServerModal = () => {
setAddServerModalOpen(false);
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
setNewServer({
id: '',
ip: '',
extIp: '',
internalIp: '',
dns: '',
country: '',
provider: '',
tunnel: 'GRE',
gateway: ''
});
setCustomProvider('');
setError('');
};
const handleAddServerFromModal = () => {
if (!newServer.ip.trim() || !newServer.dns.trim() || !newServer.country.trim() || !newServer.provider.trim() || !newServer.tunnel.trim() || !newServer.gateway.trim()) {
setError('Все поля должны быть заполнены.');
setError('Все обязательные поля должны быть заполнены.');
return;
}
@@ -168,10 +196,25 @@ function ServerManager() {
// Используем customProvider если выбран "Другой"
const finalProvider = newServer.provider === 'Другой' ? customProvider : newServer.provider;
const serverToAdd = { ...newServer, provider: finalProvider };
const nextId = `srv-${servers.length + 1}`;
const serverToAdd = {
...newServer,
provider: finalProvider,
id: newServer.id || nextId,
};
setServers([...servers, serverToAdd]);
setNewServer({ ip: '', dns: '', country: '', provider: '', tunnel: 'GRE' });
setNewServer({
id: '',
ip: '',
extIp: '',
internalIp: '',
dns: '',
country: '',
provider: '',
tunnel: 'GRE',
gateway: ''
});
setCustomProvider('');
setAddServerModalOpen(false);
setSuccess('Сервер успешно добавлен!');
@@ -188,7 +231,11 @@ function ServerManager() {
};
const handleEditModalSave = (updatedServer) => {
setServers(servers.map(s => s.ip === updatedServer.ip ? { ...updatedServer } : s));
setServers(servers.map(s => {
const sameId = s.id && updatedServer.id ? s.id === updatedServer.id : false;
const sameIp = s.ip === updatedServer.ip;
return (sameId || sameIp) ? { ...s, ...updatedServer } : s;
}));
setEditModalOpen(false);
setEditModalServer(null);
};
@@ -276,9 +323,12 @@ function ServerManager() {
.filter(s => (!filterCountry || s.country === filterCountry))
.filter(s => (!filterProvider || s.provider === filterProvider));
const csvContent = [
['IP', 'DNS', 'Country', 'Provider', 'Tunnel', 'Generated URL'],
['ID', 'IP', 'Ext IP', 'Internal IP', 'DNS', 'Country', 'Provider', 'Tunnel', 'Generated URL'],
...filtered.map(server => [
server.id || '',
server.ip,
server.extIp || '',
server.internalIp || '',
server.dns,
server.country,
server.provider,
@@ -570,6 +620,12 @@ function ServerManager() {
</span>
)}
</th>
<th>
Внешний IP
</th>
<th>
Внутренний IP
</th>
<th className="cursor-pointer" onClick={() => handleSort('dns')}>
DNS имя
{sortField === 'dns' && (
@@ -620,8 +676,10 @@ function ServerManager() {
<td colSpan={8} className="text-center text-muted py-4">Ничего не найдено. Измените фильтры или поиск.</td>
</tr>
) : paginatedServers.map((server) => (
<tr key={server.ip} className={editingServer === server.ip ? 'table-info' : ''}>
<tr key={server.id || server.ip} className={editingServer === server.ip ? 'table-info' : ''}>
<td>{server.ip}</td>
<td>{server.extIp || <span className="text-muted"></span>}</td>
<td>{server.internalIp || <span className="text-muted"></span>}</td>
<td>{server.dns}</td>
<td><span className="badge bg-blue-lt text-blue">{countryToFlag(server.country)} {server.country}</span></td>
<td>{server.provider}</td>
@@ -761,6 +819,24 @@ function ServerManager() {
helpText="Введите IPv4 адрес сервера"
/>
<FormField
label="Внешний IP (опционально)"
name="extIp"
value={newServer.extIp}
onChange={(val) => setNewServer({ ...newServer, extIp: val })}
placeholder="Публичный IP, если отличается"
icon={IconServer}
/>
<FormField
label="Внутренний IP (опционально)"
name="internalIp"
value={newServer.internalIp}
onChange={(val) => setNewServer({ ...newServer, internalIp: val })}
placeholder="10.x.x.x / 192.168.x.x"
icon={IconServer}
/>
<FormField
label="DNS имя"
name="dns"
@@ -1066,10 +1142,22 @@ function EditServerModal({ show, server, onChange, onSave, onClose }) {
</div>
<div className="modal-body">
<form onSubmit={e => { e.preventDefault(); onSave && onSave(localServer); }}>
<div className="mb-3">
<label className="form-label">ID (опционально)</label>
<input type="text" className="form-control" value={localServer.id || ''} onChange={e => handleChange('id', e.target.value)} placeholder="srv-1" />
</div>
<div className="mb-3">
<label className="form-label">IP адрес</label>
<input type="text" className="form-control" value={localServer.ip || ''} onChange={e => handleChange('ip', e.target.value)} />
</div>
<div className="mb-3">
<label className="form-label">Внешний IP (опционально)</label>
<input type="text" className="form-control" value={localServer.extIp || ''} onChange={e => handleChange('extIp', e.target.value)} placeholder="публичный IP, если отличается" />
</div>
<div className="mb-3">
<label className="form-label">Внутренний IP (опционально)</label>
<input type="text" className="form-control" value={localServer.internalIp || ''} onChange={e => handleChange('internalIp', e.target.value)} placeholder="10.x.x.x / 192.168.x.x" />
</div>
<div className="mb-3">
<label className="form-label">DNS имя</label>
<input type="text" className="form-control" value={localServer.dns || ''} onChange={e => handleChange('dns', e.target.value)} />