diff --git a/services/targo-hub/lib/devices.js b/services/targo-hub/lib/devices.js index c27d5b4..0bbea05 100644 --- a/services/targo-hub/lib/devices.js +++ b/services/targo-hub/lib/devices.js @@ -609,4 +609,20 @@ async function handleACSConfig (req, res, method, path) { } } -module.exports = { handle, handleACSConfig, summarizeDevice, cacheDevice, getCached, getCacheStats, fetchDeviceDetails, startPoller, stopPoller, logDeviceEvent, getDeviceEvents } +// Reboot TR-069 (GenieACS) par n° de série — résout l'appareil (fetchDeviceDetails → _id ACS) puis pose une tâche +// `reboot` avec connection_request. Sert au reboot des ONU/modems tech-3 (TPLG) que olt-ops ne gère PAS (son reboot +// natif = SNMP Raisecom tech-2 seulement). Best-effort → { ok, status, device_id, error }. +async function rebootBySerial (serial) { + if (!serial) return { ok: false, error: 'serial requis' } + let sum = null + try { sum = await fetchDeviceDetails(String(serial)) } catch (e) { return { ok: false, error: 'résolution TR-069 : ' + e.message } } + const id = sum && sum._id + if (!id) return { ok: false, error: 'appareil TR-069 introuvable pour ' + serial } + try { + const r = await nbiRequest(`/devices/${encodeURIComponent(id)}/tasks?connection_request&timeout=15000`, 'POST', { name: 'reboot' }) + const ok = r.status >= 200 && r.status < 300 + return { ok, status: r.status, device_id: id, error: ok ? null : ('GenieACS a refusé la tâche reboot (HTTP ' + r.status + ')') } + } catch (e) { return { ok: false, device_id: id, error: e.message } } +} + +module.exports = { handle, handleACSConfig, summarizeDevice, cacheDevice, getCached, getCacheStats, fetchDeviceDetails, startPoller, stopPoller, logDeviceEvent, getDeviceEvents, rebootBySerial } diff --git a/services/targo-hub/lib/staff-agent.js b/services/targo-hub/lib/staff-agent.js index fb19859..357ec9d 100644 --- a/services/targo-hub/lib/staff-agent.js +++ b/services/targo-hub/lib/staff-agent.js @@ -297,15 +297,26 @@ const WRITES = { if (p.new_sn) opts.new_sn = String(p.new_sn) // APERÇU = plan olt-ops (LECTURE SEULE) : effet, cible réseau, avertissements, exécutable ? const pl = await oltOps().plan({ serial, action, ...opts }) - if (!pl.can_run) throw new Error(`« ${pl.label || action} » non exécutable pour ${serial}${(pl.warnings && pl.warnings.length) ? ' — ' + pl.warnings.join(' ') : ''}`) + const idempotencyKey = `sa-${action}-${serial}-${Date.now()}` // fixé au plan → un double-clic Confirmer rejoue la MÊME clé (dédup module) const who = pl.customer ? ` · client ${pl.customer}` : '' + if (!pl.can_run) { + // Repli REBOOT → TR-069 : les ONU tech-3 (TPLG, la plus grosse flotte) ne rebootent PAS via olt-ops (SNMP + // Raisecom = tech-2). On passe alors par GenieACS (devices.rebootBySerial). Autres actions : non exécutables. + if (action === 'reboot') { + return { title: META.device_action.title, preview: `Redémarrer le modem (TR-069) — ONU ${serial}${who} · le modem redémarre (~2 min hors ligne).`, severity: 'normal', params: { via: 'tr069', serial, action, idempotencyKey } } + } + throw new Error(`« ${pl.label || action} » non exécutable pour ${serial}${(pl.warnings && pl.warnings.length) ? ' — ' + pl.warnings.join(' ') : ''}`) + } const warn = (pl.warnings && pl.warnings.length) ? ' ⚠ ' + pl.warnings.join(' ') : '' const preview = `${pl.label} — ONU ${pl.target.serial}${pl.target.olt ? ' · OLT ' + pl.target.olt : ''}${who}${(pl.effects && pl.effects.length) ? ' · ' + pl.effects.join(' ') : ''}${warn}` const severity = /^(suspend|remove|replace|activate)$/.test(action) ? 'high' : 'normal' - const idempotencyKey = `sa-${action}-${serial}-${Date.now()}` // fixé au plan → un double-clic Confirmer rejoue la MÊME clé (dédup module) - return { title: META.device_action.title, preview, severity, params: { serial, action, profileid: opts.profileid || '', new_sn: opts.new_sn || '', idempotencyKey } } + return { title: META.device_action.title, preview, severity, params: { via: 'olt', serial, action, profileid: opts.profileid || '', new_sn: opts.new_sn || '', idempotencyKey } } }, async exec (p, email) { + if (p.via === 'tr069') { // reboot via GenieACS (ONU tech-3) + const r = await require('./devices').rebootBySerial(p.serial) + return { ok: !!(r && r.ok), message: (r && r.ok) ? `✅ Redémarrage (TR-069) envoyé — ${p.serial}` : `échec redémarrage TR-069 : ${(r && r.error) || 'erreur'}`, data: r } + } const opts = {} if (p.profileid) opts.profileid = p.profileid if (p.new_sn) opts.new_sn = p.new_sn