const GenieACSApi = require('./api'); const GenieACSConfig = require('./config'); const logger = require('../utils/logger'); class GenieACSManager { constructor() { this.api = new GenieACSApi(); this.config = new GenieACSConfig(); logger.info('GenieACSManager inicializado'); console.log('📡 GenieACSManager inicializado'); } /** * Verifica se ao menos uma instância está configurada. */ isReady() { return this.config.isReady(); } /** * Busca informações de um roteador/CPE pelo serial number ou pela TAG (username TopSapp). * Tenta por serial primeiro; se não encontrar, tenta por TAG. * Se o termo contém "@", também tenta a parte antes do @ como TAG. * * @param {string} termo - Serial ou TAG do dispositivo * @param {string} location - Texto de localização (opcional) * @returns {Promise} - { success, device, instance, searchedBy, error } */ async buscarRoteador(termo, location = '') { if (!this.isReady()) { return { success: false, error: 'GenieACS não está configurado. Verifique as variáveis GENIEACS_* no arquivo .env' }; } const instance = this.config.getInstanceByLocation(location); const termoLimpo = termo.trim(); // 1ª tentativa: busca por serial (exato e uppercase) logger.info(`[GenieACS] Tentando como serial: "${termoLimpo}"`); const porSerial = await this.api.buscarDispositivo(termoLimpo, instance); if (porSerial.success) { return { ...porSerial, instance: instance.name }; } // 2ª tentativa: busca por TAG (termo completo) logger.info(`[GenieACS] Não encontrado como serial. Tentando como TAG: "${termoLimpo}"`); const porTag = await this.api.buscarPorTag(termoLimpo, instance); if (porTag.success) { return { ...porTag, instance: instance.name }; } // 3ª tentativa: se o termo tem "@", tentar só a parte antes do @ como TAG // (ex.: "clickcolmeia2025@isp" -> tenta "clickcolmeia2025") if (termoLimpo.includes('@')) { const tagBase = termoLimpo.split('@')[0].toLowerCase(); logger.info(`[GenieACS] Tentando TAG sem domínio: "${tagBase}"`); const porTagBase = await this.api.buscarPorTag(tagBase, instance); if (porTagBase.success) { return { ...porTagBase, instance: instance.name }; } } // Nenhum resultado const errConexao = porSerial.error || porTag.error; return { success: false, error: errConexao || `Nenhum dispositivo encontrado com serial ou TAG "${termoLimpo}"` }; } /** * Sincroniza o login PPPoE do TopSapp como TAG no GenieACS. * * Fluxo: * 1. Busca o dispositivo pelo serial (vindo do TopSapp) * 2. Deriva a TAG do login PPPoE (parte antes do "@", em minúsculo) * 3. Se a TAG ainda não existir no dispositivo, adiciona via NBI * * @param {string} serial - Serial do dispositivo (do cadastro TopSapp) * @param {string} pppoeLogin - Login PPPoE do cliente (ex.: "clickcolmeia2025" ou "user@isp") * @param {string} location - Texto de localização (opcional) * @returns {Promise} - { success, tag, deviceId, already, error } */ async sincronizarTagPPPoE(serial, pppoeLogin, location = '') { if (!this.isReady() || !serial || !pppoeLogin) { return { success: false, skipped: true }; } const instance = this.config.getInstanceByLocation(location); // A TAG usa apenas a parte antes do "@", em minúsculo, // igualando ao padrão que o GenieACS já exibe na UI (ex.: "clickcolmeia2025") const tag = pppoeLogin.split('@')[0].toLowerCase(); // Localizar dispositivo pelo serial const result = await this.api.buscarDispositivo(serial.trim(), instance); if (!result.success || !result.device?.deviceId) { logger.warn(`[GenieACS] Sync TAG: dispositivo serial "${serial}" não encontrado`); return { success: false, error: 'Dispositivo não encontrado pelo serial' }; } const deviceId = result.device.deviceId; const tagsAtuais = result.device.tags || []; // Verificar se a tag já está presente (case-insensitive) if (tagsAtuais.some(t => t.toLowerCase() === tag)) { logger.info(`[GenieACS] Sync TAG: "${tag}" já existe em ${deviceId}`); return { success: true, already: true, tag, deviceId }; } // Adicionar a TAG logger.info(`[GenieACS] Sync TAG: adicionando "${tag}" em ${deviceId}`); const addResult = await this.api.adicionarTag(deviceId, tag, instance); return { ...addResult, tag, deviceId }; } /** * Aplica um conjunto de setParameterValues a um dispositivo já conhecido pelo deviceId. * * @param {string} deviceId - _id do dispositivo no GenieACS * @param {Array} paramValues - Array de [paramName, value, xsdType] * @param {string} location - Texto de localização (opcional) * @returns {Promise} - { success, error } */ async configurarRoteador(deviceId, paramValues, location = '') { if (!this.isReady() || !deviceId) { return { success: false, error: 'GenieACS não configurado ou deviceId ausente' }; } const instance = this.config.getInstanceByLocation(location); return this.api.setParameterValues(deviceId, paramValues, instance); } /** * Relê um dispositivo pelo seu _id para obter dados atualizados. * * @param {string} deviceId - _id do dispositivo * @param {string} location - Texto de localização (opcional) * @returns {Promise} - { success, device, instance, searchedBy } */ async buscarRoteadorPorId(deviceId, location = '') { if (!this.isReady() || !deviceId) { return { success: false, error: 'GenieACS não configurado ou deviceId ausente' }; } const instance = this.config.getInstanceByLocation(location); const result = await this.api._buscar({ '_id': deviceId }, instance, 'id'); if (result.success) return { ...result, instance: instance.name }; return result; } } module.exports = GenieACSManager;