/** * Общие утилиты для работы с серверами и шлюзами * Используется в ServerManager, FilterManager и других компонентах */ /** * Создаёт объект шлюза с дефолтными значениями * @param {Object} overrides - Переопределяемые поля * @returns {Object} Объект шлюза */ export const makeGateway = (overrides = {}) => ({ id: `${Date.now()}-${Math.random().toString(16).slice(2, 8)}`, name: '', ip: '', comment: '', primary: false, ...overrides, }); /** * Нормализует массив шлюзов: очищает, удаляет пустые, гарантирует один primary * @param {Array} gateways - Массив шлюзов * @param {string} fallbackName - Имя по умолчанию если массив пуст * @returns {Array} Нормализованный массив шлюзов */ export const normalizeGateways = (gateways = [], fallbackName = '') => { if (!Array.isArray(gateways)) return []; const cleaned = gateways .map((g, idx) => ({ id: g?.id || `${Date.now()}-${idx}`, name: String(g?.name || '').trim(), ip: String(g?.ip || '').trim(), comment: String(g?.comment || '').trim(), primary: Boolean(g?.primary), })) .filter(g => g.name || g.ip || g.comment); if (cleaned.length === 0) { return [{ id: `${Date.now()}-primary`, name: fallbackName || 'gateway', ip: '', comment: '', primary: true }]; } // Гарантируем только один primary let hasPrimary = false; cleaned.forEach((g) => { if (g.primary && !hasPrimary) { hasPrimary = true; } else { g.primary = false; } }); if (!hasPrimary) { cleaned[0].primary = true; } return cleaned; }; /** * Конвертирует код страны в эмодзи флага * @param {string} code - Двухбуквенный код страны (ISO 3166-1 alpha-2) * @returns {string} Эмодзи флага или пустая строка */ export const countryToFlag = (code) => { if (!code || typeof code !== 'string') return ''; const cc = code.trim().toUpperCase(); if (cc.length !== 2) return ''; const codePoints = [...cc].map((c) => 0x1f1e6 - 65 + c.charCodeAt(0)); return String.fromCodePoint(...codePoints); }; /** * Проверяет, требует ли тип сервера наличие шлюзов * @param {string} type - Тип сервера * @returns {boolean} */ export const needsGateways = (type) => { return ['jumphost', 'exit'].includes(String(type || '').toLowerCase()); }; /** * Константы типов серверов */ export const SERVER_TYPE_OPTIONS = [ { value: 'jumphost', label: 'Jumphost' }, { value: 'exit', label: 'Выходная нода' }, ]; /** * Типы туннелей */ export const TUNNEL_TYPES = ['GRE', 'IPSec', 'WireGuard', 'VXLAN', 'OpenVPN']; /** * Генерирует уникальный ID * @returns {string} */ export const generateId = () => { return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; }; /** * Нормализует community value (добавляет ASN если отсутствует) * @param {string} value - Значение community * @param {string} defaultASN - ASN по умолчанию * @returns {string} */ export const normalizeCommunityValue = (value, defaultASN = '65001') => { const trimmed = String(value || '').trim(); if (!trimmed) return ''; return trimmed.includes(':') ? trimmed : `${defaultASN}:${trimmed}`; }; /** * Извлекает короткую часть community (после двоеточия) * @param {string} value - Полное значение community (например "65000:120") * @returns {string} Короткая часть (например "120") */ export const getShortCommunityValue = (value) => { const str = String(value || '').trim(); if (!str.includes(':')) return str; return str.split(':')[1] || str; };