feat(NetworkConfigManager): add server-wide MikroTik code generation for interfaces, improving bulk configuration capabilities and user feedback
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m40s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m40s
This commit is contained in:
@@ -1836,6 +1836,62 @@ function NetworkConfigManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// === Генерация кода MikroTik для всех интерфейсов сервера ===
|
||||||
|
const handleGenerateMikrotikCodeForServer = async (serverId, type) => {
|
||||||
|
if (!serverId) {
|
||||||
|
notify.error('Сервер не указан');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
if (type === 'interface') {
|
||||||
|
// Получаем все интерфейсы для этого сервера (где сервер участвует как первый или второй)
|
||||||
|
// Фильтруем только те, где оба сервера указаны и есть IP адреса
|
||||||
|
const serverInterfaces = (config.tunnelInterfaces || []).filter(iface =>
|
||||||
|
(iface.serverId === serverId || iface.serverId2 === serverId) &&
|
||||||
|
iface.serverId && iface.serverId2 && iface.localIp && iface.remoteIp
|
||||||
|
);
|
||||||
|
|
||||||
|
if (serverInterfaces.length === 0) {
|
||||||
|
notify.error('Нет интерфейсов с полной конфигурацией для этого сервера');
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blocks = await generateMikrotikInterfaceAddressesForSelection(serverInterfaces);
|
||||||
|
if (!blocks || blocks.length === 0) {
|
||||||
|
notify.error('Нет данных для генерации кода MikroTik для этого сервера');
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фильтруем блоки, чтобы показать только те, что относятся к данному серверу
|
||||||
|
const server = getServerInfo(serverId);
|
||||||
|
const serverName = server?.dns || server?.ip || serverId;
|
||||||
|
const filteredBlocks = blocks.filter(block =>
|
||||||
|
block.serverName === serverName || block.server?.id === serverId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredBlocks.length === 0) {
|
||||||
|
notify.error('Не удалось сгенерировать код для этого сервера');
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setGeneratedMikrotikCode(filteredBlocks);
|
||||||
|
setMikrotikCodeModalOpen(true);
|
||||||
|
} else {
|
||||||
|
notify.error('Генерация кода для gateways по серверу пока не реализована');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating MikroTik code for server:', error);
|
||||||
|
notify.error('Ошибка при генерации кода MikroTik для этого сервера');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// === Генерация кода MikroTik только для одного интерфейса ===
|
// === Генерация кода MikroTik только для одного интерфейса ===
|
||||||
const handleGenerateMikrotikCodeForInterface = async (iface) => {
|
const handleGenerateMikrotikCodeForInterface = async (iface) => {
|
||||||
if (!iface) {
|
if (!iface) {
|
||||||
@@ -2186,55 +2242,93 @@ function NetworkConfigManager() {
|
|||||||
const isUnassigned = serverId === '__unassigned__';
|
const isUnassigned = serverId === '__unassigned__';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={serverId} className="mb-4">
|
<div key={serverId} className="card mb-3">
|
||||||
{/* Server header */}
|
{/* Server header - более заметный и читаемый */}
|
||||||
<div className="d-flex align-items-center mb-2 px-1">
|
<div
|
||||||
<div className="d-flex align-items-center">
|
className="card-header py-3"
|
||||||
{isUnassigned ? (
|
style={{
|
||||||
<>
|
backgroundColor: 'rgba(32, 107, 196, 0.08)',
|
||||||
<span className="avatar avatar-sm bg-secondary-lt me-2">
|
borderBottom: '2px solid rgba(32, 107, 196, 0.2)'
|
||||||
<IconServer size={16} className="text-muted" />
|
}}
|
||||||
</span>
|
>
|
||||||
<span className="text-muted">Без привязки к серверу</span>
|
<div className="d-flex align-items-center justify-content-between">
|
||||||
</>
|
<div className="d-flex align-items-center flex-grow-1">
|
||||||
) : (
|
{isUnassigned ? (
|
||||||
<>
|
<>
|
||||||
<span className="avatar avatar-sm bg-primary-lt me-2">
|
<span className="avatar avatar-md bg-secondary-lt me-3">
|
||||||
<IconServer size={16} className="text-primary" />
|
<IconServer size={20} className="text-muted" />
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<span className="fw-medium">
|
|
||||||
{server?.dns ? server.dns.split('.')[0] : (server?.name || serverId)}
|
|
||||||
</span>
|
</span>
|
||||||
{server?.dns && (
|
<div>
|
||||||
<span className="text-muted ms-2 small">
|
<h4 className="mb-0 fw-semibold text-muted">Без привязки к серверу</h4>
|
||||||
{server.dns}
|
</div>
|
||||||
</span>
|
</>
|
||||||
)}
|
) : (
|
||||||
{server && (
|
<>
|
||||||
<span className="text-muted ms-2 small">
|
<span className="avatar avatar-md bg-primary-lt me-3">
|
||||||
{server.ip}
|
<IconServer size={20} className="text-primary" />
|
||||||
{server.country && (
|
</span>
|
||||||
<span className="ms-1">{countryToFlag(server.country)} {server.country}</span>
|
<div className="flex-grow-1">
|
||||||
|
<div className="d-flex align-items-center flex-wrap gap-2">
|
||||||
|
<h4 className="mb-0 fw-semibold">
|
||||||
|
{server?.dns ? server.dns.split('.')[0] : (server?.name || serverId)}
|
||||||
|
</h4>
|
||||||
|
{server?.dns && (
|
||||||
|
<code className="text-muted small" style={{ fontSize: '0.875rem' }}>
|
||||||
|
{server.dns}
|
||||||
|
</code>
|
||||||
)}
|
)}
|
||||||
</span>
|
{server?.ip && (
|
||||||
)}
|
<code className="text-muted small" style={{ fontSize: '0.875rem' }}>
|
||||||
</div>
|
{server.ip}
|
||||||
</>
|
</code>
|
||||||
)}
|
)}
|
||||||
|
{server?.country && (
|
||||||
|
<span className="badge bg-blue-lt text-blue" style={{ fontSize: '1rem' }}>
|
||||||
|
{countryToFlag(server.country)} {server.country}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{server?.provider && (
|
||||||
|
<span className={`badge bg-${getProviderColor(server.provider)}-lt text-${getProviderColor(server.provider)}`}>
|
||||||
|
{server.provider}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="d-flex align-items-center gap-2">
|
||||||
|
<span className="badge bg-primary-lt text-primary" style={{ fontSize: '0.875rem', padding: '6px 12px' }}>
|
||||||
|
{items.length} {type === 'gateway' ? (items.length === 1 ? 'gateway' : 'gateways') : (items.length === 1 ? 'интерфейс' : 'интерфейсов')}
|
||||||
|
</span>
|
||||||
|
{type === 'interface' && !isUnassigned && items.length > 0 && (
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-primary btn-sm"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleGenerateMikrotikCodeForServer(serverId, type);
|
||||||
|
}}
|
||||||
|
title="Получить код MikroTik для всех интерфейсов этого сервера"
|
||||||
|
>
|
||||||
|
<IconCode size={16} className="me-1" />
|
||||||
|
Код для MikroTik
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="badge bg-secondary-lt ms-auto">{items.length}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Items */}
|
{/* Items */}
|
||||||
<div className="row row-cards">
|
<div className="card-body p-3">
|
||||||
{items.map(item => (
|
<div className="row row-cards">
|
||||||
<div key={item.id} className="col-12 col-md-6 col-lg-6">
|
{items.map(item => (
|
||||||
{type === 'gateway' ? renderGatewayCard(item) :
|
<div key={item.id} className="col-12 col-md-6 col-lg-6">
|
||||||
type === 'interface' ? renderInterfaceCard(item, serverId) :
|
{type === 'gateway' ? renderGatewayCard(item) :
|
||||||
type === 'pool' ? renderPoolCard(item) : null}
|
type === 'interface' ? renderInterfaceCard(item, serverId) :
|
||||||
</div>
|
type === 'pool' ? renderPoolCard(item) : null}
|
||||||
))}
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user