feat(NetworkConfigManager): add optional second interface name for dual server configurations; enhance UI to support interface naming for both servers
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s

This commit is contained in:
2026-01-22 13:18:46 +07:00
parent cf1dbfce37
commit fdc8b1f458
2 changed files with 87 additions and 25 deletions
+2
View File
@@ -54,6 +54,7 @@
{
"id": "if-gre-swe-de",
"name": "gre-swe-de",
"name2": "gre-de-swe",
"type": "GRE",
"localIp": "10.10.0.1",
"remoteIp": "10.10.0.2",
@@ -63,6 +64,7 @@
{
"id": "if-wg-swe-nl",
"name": "wg0",
"name2": "wg1",
"type": "WireGuard",
"localIp": "10.20.0.1",
"remoteIp": "10.20.0.2",
+85 -25
View File
@@ -73,6 +73,7 @@ const getEmptyGateway = () => ({
const getEmptyInterface = () => ({
id: `if-${Date.now()}-${Math.random().toString(16).slice(2, 6)}`,
name: '',
name2: '', // Имя интерфейса на втором сервере (опционально)
type: 'GRE',
localIp: '',
remoteIp: '',
@@ -511,8 +512,10 @@ function NetworkConfigManager() {
return '';
}
let code = '\n# === Настройка IP адресов интерфейсов на обоих серверах ===\n';
code += '# Эти команды нужно выполнить на соответствующих серверах\n\n';
let code = '\n# ====================================================================\n';
code += '# === НАСТРОЙКА IP АДРЕСОВ ИНТЕРФЕЙСОВ НА ОБОИХ СЕРВЕРАХ ===\n';
code += '# Эти команды нужно выполнить на соответствующих серверах\n';
code += '# ====================================================================\n\n';
// Группируем по серверам
const interfacesByServer = {};
@@ -524,43 +527,84 @@ function NetworkConfigManager() {
const server1Name = server1.dns || server1.ip || iface.serverId;
const server2Name = server2.dns || server2.ip || iface.serverId2;
const interfaceName = iface.name || `${iface.type}-tunnel`;
// Для первого сервера - используем localIp
// Имя интерфейса для первого сервера
const interfaceName1 = iface.name || `${iface.type}-tunnel`;
// Имя интерфейса для второго сервера (может отличаться)
const interfaceName2 = iface.name2 || iface.name || `${iface.type}-tunnel`;
// Для первого сервера - используем localIp как local, remoteIp как remote
if (!interfacesByServer[server1Name]) {
interfacesByServer[server1Name] = [];
interfacesByServer[server1Name] = {
serverInfo: server1,
interfaces: []
};
}
interfacesByServer[server1Name].push({
interfaceName,
ip: iface.localIp,
type: iface.type,
interfacesByServer[server1Name].interfaces.push({
interfaceName: interfaceName1,
localIp: iface.localIp,
remoteIp: iface.remoteIp,
server2Name
type: iface.type,
server2Name,
server2Info: server2
});
// Для второго сервера - используем remoteIp
// Для второго сервера - МЕНЯЕМ МЕСТАМИ: remoteIp становится localIp, localIp становится remoteIp
if (!interfacesByServer[server2Name]) {
interfacesByServer[server2Name] = [];
interfacesByServer[server2Name] = {
serverInfo: server2,
interfaces: []
};
}
interfacesByServer[server2Name].push({
interfaceName,
ip: iface.remoteIp,
interfacesByServer[server2Name].interfaces.push({
interfaceName: interfaceName2,
localIp: iface.remoteIp, // На втором сервере remoteIp первого становится localIp
remoteIp: iface.localIp, // На втором сервере localIp первого становится remoteIp
type: iface.type,
remoteIp: iface.localIp, // Для второго сервера remoteIp - это localIp первого
server2Name: server1Name
server2Name: server1Name,
server2Info: server1
});
});
Object.entries(interfacesByServer).forEach(([serverName, interfaces]) => {
code += `\n# --- Команды для сервера: ${serverName} ---\n`;
interfaces.forEach(iface => {
code += `# Интерфейс: ${iface.interfaceName} (${iface.type}) - связь с ${iface.server2Name}\n`;
code += `/ip address add address=${iface.ip}/32 interface="${iface.interfaceName}" comment="Interface: ${iface.interfaceName} (${iface.type}) to ${iface.server2Name}"\n`;
// Сортируем серверы по имени для консистентности
const sortedServers = Object.entries(interfacesByServer).sort(([a], [b]) => a.localeCompare(b));
sortedServers.forEach(([serverName, serverData], index) => {
const server = serverData.serverInfo;
// Отдельный блок для каждого сервера
code += '\n';
code += '# ' + '='.repeat(70) + '\n';
code += `# СЕРВЕР ${index + 1}: ${serverName}\n`;
if (server.provider) {
code += `# Провайдер: ${server.provider}\n`;
}
if (server.country) {
code += `# Страна: ${server.country}\n`;
}
if (server.ip && server.ip !== serverName) {
code += `# IP: ${server.ip}\n`;
}
code += '# ' + '='.repeat(70) + '\n';
code += '\n';
serverData.interfaces.forEach((iface, ifaceIndex) => {
code += `# Интерфейс ${ifaceIndex + 1}: ${iface.interfaceName} (${iface.type})\n`;
code += `# Связь с сервером: ${iface.server2Name}\n`;
code += `# Local IP: ${iface.localIp}\n`;
code += `# Remote IP: ${iface.remoteIp}\n`;
code += `/ip address add address=${iface.localIp}/32 interface="${iface.interfaceName}" comment="Interface: ${iface.interfaceName} (${iface.type}) to ${iface.server2Name}"\n`;
code += '\n';
});
code += '# Проверка настроенных адресов на этом сервере:\n';
code += '# /ip address print\n';
code += '\n';
});
code += '\n# Проверка настроенных адресов:\n';
code += '# /ip address print\n';
code += '# ' + '='.repeat(70) + '\n';
code += '# КОНЕЦ КОНФИГУРАЦИИ IP АДРЕСОВ ИНТЕРФЕЙСОВ\n';
code += '# ' + '='.repeat(70) + '\n';
return code;
};
@@ -967,6 +1011,7 @@ function NetworkConfigManager() {
<IconServer size={12} className="text-muted" />
<span className="text-muted">Сервер 1:</span>
<span>{server.dns || server.ip}</span>
{iface.name && <span className="text-muted ms-1">({iface.name})</span>}
</div>
)}
{server2 && (
@@ -974,6 +1019,9 @@ function NetworkConfigManager() {
<IconServer size={12} className="text-muted" />
<span className="text-muted">Сервер 2:</span>
<span>{server2.dns || server2.ip}</span>
{(iface.name2 || iface.name) && (
<span className="text-muted ms-1">({iface.name2 || iface.name})</span>
)}
</div>
)}
</div>
@@ -1714,7 +1762,7 @@ function NetworkConfigManager() {
</div>
<div className="col-md-6">
<FormField
label="Имя интерфейса"
label="Имя интерфейса (сервер 1)"
name="name"
value={editingInterface.name}
onChange={(val) => setEditingInterface({ ...editingInterface, name: val })}
@@ -1722,6 +1770,18 @@ function NetworkConfigManager() {
required
/>
</div>
{editingInterface.serverId2 && (
<div className="col-md-6">
<FormField
label="Имя интерфейса (сервер 2)"
name="name2"
value={editingInterface.name2 || ''}
onChange={(val) => setEditingInterface({ ...editingInterface, name2: val })}
placeholder="gre-tunnel2"
/>
<div className="form-text">Если не указано, будет использовано имя с сервера 1</div>
</div>
)}
<div className="col-md-6">
<FormField
label="Тип"