feat(NetworkConfigManager): add optional interface names for servers; enhance interface name generation logic with DNS parsing and user-defined names
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m41s

This commit is contained in:
2026-01-22 18:56:50 +07:00
parent 011740df1f
commit 39f3510512
+102 -32
View File
@@ -138,6 +138,8 @@ function NetworkConfigManager() {
const [templateServer2, setTemplateServer2] = useState('');
const [templateType, setTemplateType] = useState('GRE');
const [templateNamePrefix, setTemplateNamePrefix] = useState('');
const [templateName1, setTemplateName1] = useState(''); // Опциональное имя для сервера 1
const [templateName2, setTemplateName2] = useState(''); // Опциональное имя для сервера 2
// === MikroTik Code Generation ===
const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false);
@@ -441,12 +443,44 @@ function NetworkConfigManager() {
};
// === Генерация имени интерфейса на основе серверов ===
const generateInterfaceName = (server1, server2, type, prefix = '') => {
const server1Name = server1?.dns?.split('.')[0] || server1?.ip?.split('.').join('-') || 'server1';
const server2Name = server2?.dns?.split('.')[0] || server2?.ip?.split('.').join('-') || 'server2';
const typeLower = type.toLowerCase();
// Для сервера 1: берем часть из DNS сервера 2 (например, msk) и имя хоста сервера 2 (ihor)
// Для сервера 2: берем часть из DNS сервера 1 (например, swe) и имя хоста сервера 1 (hiphost)
const generateInterfaceName = (currentServer, otherServer, type, prefix = '') => {
// Парсим DNS имена
const parseDnsParts = (dns) => {
if (!dns) return null;
const parts = dns.split('.');
if (parts.length >= 2) {
return {
hostname: parts[0], // первая часть (имя хоста)
location: parts[1] // вторая часть (локация, например msk, swe)
};
}
return null;
};
const currentParts = parseDnsParts(currentServer?.dns);
const otherParts = parseDnsParts(otherServer?.dns);
let name;
if (currentParts && otherParts) {
// Используем локацию из другого сервера и имя хоста из другого сервера
// Например: для сервера 1 (hiphost.swe.shx.su) с сервером 2 (ihor.msk.rt.shx.su)
// Имя на сервере 1: msk-ihor
name = `${otherParts.location}-${otherParts.hostname}`;
} else if (currentServer?.dns) {
// Fallback: используем первую часть DNS
name = currentServer.dns.split('.')[0];
} else if (currentServer?.ip) {
// Fallback: используем IP
name = currentServer.ip.split('.').join('-');
} else {
name = 'server';
}
const prefixPart = prefix ? `${prefix}-` : '';
return `${prefixPart}${typeLower}-${server1Name}-${server2Name}`;
return `${prefixPart}${name}`;
};
// === Создание интерфейса из шаблона ===
@@ -469,9 +503,9 @@ function NetworkConfigManager() {
return;
}
// Генерируем имена интерфейсов
const name1 = generateInterfaceName(server1, server2, templateType, templateNamePrefix);
const name2 = generateInterfaceName(server2, server1, templateType, templateNamePrefix);
// Генерируем имена интерфейсов (используем опциональные имена если указаны)
const name1 = templateName1.trim() || generateInterfaceName(server1, server2, templateType, templateNamePrefix);
const name2 = templateName2.trim() || generateInterfaceName(server2, server1, templateType, templateNamePrefix);
// Генерируем IP адреса
const localIp = generateFreePrivateIp(null, false, null);
@@ -518,6 +552,8 @@ function NetworkConfigManager() {
setTemplateServer2('');
setTemplateType('GRE');
setTemplateNamePrefix('');
setTemplateName1('');
setTemplateName2('');
};
// === CRUD для Interfaces ===
@@ -2749,6 +2785,8 @@ function NetworkConfigManager() {
setTemplateServer2('');
setTemplateType('GRE');
setTemplateNamePrefix('');
setTemplateName1('');
setTemplateName2('');
}}
onSubmit={handleCreateInterfaceFromTemplate}
title="Создать интерфейс из шаблона"
@@ -2819,38 +2857,70 @@ function NetworkConfigManager() {
const server2 = getServerInfo(templateServer2);
if (!server1 || !server2) return null;
const previewName1 = generateInterfaceName(server1, server2, templateType, templateNamePrefix);
const previewName2 = generateInterfaceName(server2, server1, templateType, templateNamePrefix);
const autoName1 = generateInterfaceName(server1, server2, templateType, templateNamePrefix);
const autoName2 = generateInterfaceName(server2, server1, templateType, templateNamePrefix);
const previewName1 = templateName1.trim() || autoName1;
const previewName2 = templateName2.trim() || autoName2;
const previewLocalIp = generateFreePrivateIp(null, false, null);
const previewRemoteIp = previewLocalIp ? generateFreePrivateIp(null, true, previewLocalIp) : null;
return (
<div className="col-12">
<div className="card card-sm border">
<div className="card-body">
<h6 className="card-title mb-3">Предпросмотр интерфейса:</h6>
<div className="row g-2">
<div className="col-md-6">
<div className="small text-muted mb-1">Сервер 1 ({server1.dns || server1.ip}):</div>
<div><strong>Имя:</strong> <code>{previewName1}</code></div>
<div><strong>Local IP:</strong> <code>{previewLocalIp || '—'}</code></div>
</div>
<div className="col-md-6">
<div className="small text-muted mb-1">Сервер 2 ({server2.dns || server2.ip}):</div>
<div><strong>Имя:</strong> <code>{previewName2}</code></div>
<div><strong>Local IP:</strong> <code>{previewRemoteIp || '—'}</code></div>
</div>
{previewLocalIp && previewRemoteIp && (
<div className="col-12 mt-2">
<div className="badge bg-success-lt text-success">
{previewLocalIp} {previewRemoteIp}
</div>
<>
<div className="col-md-6">
<label className="form-label">
Имя интерфейса (сервер 1)
{!templateName1 && <span className="text-muted small ms-1">(авто: {autoName1})</span>}
</label>
<input
type="text"
className="form-control"
value={templateName1}
onChange={(e) => setTemplateName1(e.target.value)}
placeholder={autoName1}
/>
<div className="form-text">Оставьте пустым для автогенерации</div>
</div>
<div className="col-md-6">
<label className="form-label">
Имя интерфейса (сервер 2)
{!templateName2 && <span className="text-muted small ms-1">(авто: {autoName2})</span>}
</label>
<input
type="text"
className="form-control"
value={templateName2}
onChange={(e) => setTemplateName2(e.target.value)}
placeholder={autoName2}
/>
<div className="form-text">Оставьте пустым для автогенерации</div>
</div>
<div className="col-12">
<div className="card card-sm border">
<div className="card-body">
<h6 className="card-title mb-3">Предпросмотр интерфейса:</h6>
<div className="row g-2">
<div className="col-md-6">
<div className="small text-muted mb-1">Сервер 1 ({server1.dns || server1.ip}):</div>
<div><strong>Имя:</strong> <code>{previewName1}</code></div>
<div><strong>Local IP:</strong> <code>{previewLocalIp || '—'}</code></div>
</div>
)}
<div className="col-md-6">
<div className="small text-muted mb-1">Сервер 2 ({server2.dns || server2.ip}):</div>
<div><strong>Имя:</strong> <code>{previewName2}</code></div>
<div><strong>Local IP:</strong> <code>{previewRemoteIp || '—'}</code></div>
</div>
{previewLocalIp && previewRemoteIp && (
<div className="col-12 mt-2">
<div className="badge bg-success-lt text-success">
{previewLocalIp} {previewRemoteIp}
</div>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</>
);
})()}
</div>