refactor(olt-ops): dédup plomberie n8n + effets pilotés par le spec ACTIONS
- extrait n8nRequest(verb,url,body,timeout) partagé par dostuff() et wifiClients() (fin de la double implémentation https-GET-with-body) - effets déplacés dans ACTIONS[action].effect → supprime l'échelle de if dans plan() Comportement préservé (vérifié live : plan tech-3 suspend identique, gardes run inchangées). Déployé prod. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
23dde4ecf0
commit
218bef8c5a
|
|
@ -39,15 +39,16 @@ const RC = {
|
||||||
const rcOltId = (s, p, o) => Number(s) * 10000000 + Number(p) * 100000 + Number(o)
|
const rcOltId = (s, p, o) => Number(s) * 10000000 + Number(p) * 100000 + Number(o)
|
||||||
const rcOnuId = (s, p, o) => (Number(s) + 32) * 8388608 + Number(p) * 65536 + Number(o)
|
const rcOnuId = (s, p, o) => (Number(s) + 32) * 8388608 + Number(p) * 65536 + Number(o)
|
||||||
|
|
||||||
// action → verbe n8n (tech-3), champs requis, techs supportées. tech-3 = n8n ; tech-2 = SNMP natif (G3).
|
// action → verbe n8n (tech-3), champs requis, techs supportées, effet (affiché dans le plan).
|
||||||
|
// tech-3 = n8n ; tech-2 = SNMP natif (G3).
|
||||||
const ACTIONS = {
|
const ACTIONS = {
|
||||||
activate: { verb: 'POST', label: 'Activer / provisionner l’ONU', needs: ['sn', 'olt', 'profileid'], techs: [3] },
|
activate: { verb: 'POST', label: 'Activer / provisionner l’ONU', needs: ['sn', 'olt', 'profileid'], techs: [3], effect: 'Provisionne l’ONU sur l’OLT + met Service Equipment « Actif ».' },
|
||||||
replace: { verb: 'PUT', label: 'Remplacer l’ONU (échange de série)', needs: ['old_sn', 'new_sn', 'olt'], techs: [3] },
|
replace: { verb: 'PUT', label: 'Remplacer l’ONU (échange de série)', needs: ['old_sn', 'new_sn', 'olt'], techs: [3], effect: 'Ré-authentifie la nouvelle série sur l’OLT + met à jour le numéro de série de Service Equipment.' },
|
||||||
speed: { verb: 'PATCH', label: 'Changer le forfait / la vitesse', needs: ['sn', 'olt', 'profileid'], extra: { trigger: 'speedchange' }, techs: [3, 2] },
|
speed: { verb: 'PATCH', label: 'Changer le forfait / la vitesse', needs: ['sn', 'olt', 'profileid'], extra: { trigger: 'speedchange' }, techs: [3, 2], effect: 'Change le profil de ligne (l’ONU redémarre).' },
|
||||||
suspend: { verb: 'PATCH', label: 'Suspendre l’internet (VLAN)', needs: ['sn', 'olt'], extra: { profileid: '', trigger: 'suspend' }, techs: [3, 2] },
|
suspend: { verb: 'PATCH', label: 'Suspendre l’internet (VLAN)', needs: ['sn', 'olt'], extra: { profileid: '', trigger: 'suspend' }, techs: [3, 2], effect: 'Bascule le VLAN internet → coupe l’accès (facturation inchangée).' },
|
||||||
unsuspend: { verb: 'PATCH', label: 'Rétablir l’internet (VLAN)', needs: ['sn', 'olt'], extra: { profileid: '', trigger: 'unsuspend' }, techs: [3, 2] },
|
unsuspend: { verb: 'PATCH', label: 'Rétablir l’internet (VLAN)', needs: ['sn', 'olt'], extra: { profileid: '', trigger: 'unsuspend' }, techs: [3, 2], effect: 'Rétablit le VLAN internet.' },
|
||||||
remove: { verb: 'DELETE', label: 'Retirer / désactiver l’ONU', needs: ['sn', 'olt'], techs: [3] },
|
remove: { verb: 'DELETE', label: 'Retirer / désactiver l’ONU', needs: ['sn', 'olt'], techs: [3], effect: 'Retire l’ONU de l’OLT + délie Service Equipment.' },
|
||||||
reboot: { verb: null, label: 'Redémarrer l’ONU', needs: ['sn', 'olt'], techs: [2] }, // tech-3 : utiliser le reboot TR-069
|
reboot: { verb: null, label: 'Redémarrer l’ONU', needs: ['sn', 'olt'], techs: [2], effect: 'Redémarre l’ONU.' }, // tech-3 : utiliser le reboot TR-069
|
||||||
}
|
}
|
||||||
|
|
||||||
// SNMP SET Raisecom (tech-2) : applique l'action + SAVE, via net-snmp. Renvoie {ok,error}. LECTURE : voir olt-snmp.
|
// SNMP SET Raisecom (tech-2) : applique l'action + SAVE, via net-snmp. Renvoie {ok,error}. LECTURE : voir olt-snmp.
|
||||||
|
|
@ -119,25 +120,33 @@ async function resolveEquip (serial) {
|
||||||
return equip
|
return equip
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appel n8n dostuff avec un VERBE arbitraire + body JSON (le module https, comme dostuffStatus).
|
// Requête n8n : VERBE HTTP arbitraire + body JSON via le module https (fetch refuse un body en GET). Renvoie le
|
||||||
function dostuff (verb, payload, timeoutMs = 60000) {
|
// JSON brut. Base commune de dostuff() et wifiClients() (F fait pareil).
|
||||||
|
function n8nRequest (verb, targetUrl, body, timeoutMs) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let https; try { https = require('https') } catch (e) { return resolve({ ok: false, error: 'https indisponible' }) }
|
let https; try { https = require('https') } catch (e) { return resolve({ ok: false, error: 'https indisponible' }) }
|
||||||
const body = JSON.stringify(payload)
|
const payload = JSON.stringify(body)
|
||||||
const req = https.request(N8N_DOSTUFF, { method: verb, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: timeoutMs }, (res) => {
|
const req = https.request(targetUrl, { method: verb, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: timeoutMs }, (res) => {
|
||||||
let d = ''; res.on('data', c => { d += c }); res.on('end', () => {
|
let d = ''; res.on('data', c => { d += c }); res.on('end', () => {
|
||||||
let parsed; try { parsed = JSON.parse(d) } catch (e) { parsed = d }
|
let parsed; try { parsed = JSON.parse(d) } catch (e) { parsed = d }
|
||||||
const r = Array.isArray(parsed) ? parsed[0] : parsed
|
resolve({ ok: res.statusCode < 400, status: res.statusCode, data: parsed })
|
||||||
const recognized = !(r && r.ErrorCode)
|
|
||||||
resolve({ ok: recognized && res.statusCode < 400, status: res.statusCode, recognized, error: (r && r.ErrorCode) || null, data: r })
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
req.on('error', e => resolve({ ok: false, error: e.message }))
|
req.on('error', e => resolve({ ok: false, error: e.message }))
|
||||||
req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'délai n8n dépassé (~' + Math.round(timeoutMs / 1000) + ' s)' }) })
|
req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'délai n8n dépassé (~' + Math.round(timeoutMs / 1000) + ' s)' }) })
|
||||||
req.write(body); req.end()
|
req.write(payload); req.end()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dostuff : verbe = action ; « OLT non reconnue » (ErrorCode) = tech ≠ 3 → non reconnu.
|
||||||
|
async function dostuff (verb, payload, timeoutMs = 60000) {
|
||||||
|
const res = await n8nRequest(verb, N8N_DOSTUFF, payload, timeoutMs)
|
||||||
|
if (res.error) return { ok: false, error: res.error }
|
||||||
|
const r = Array.isArray(res.data) ? res.data[0] : res.data
|
||||||
|
const recognized = !(r && r.ErrorCode)
|
||||||
|
return { ok: recognized && res.ok, status: res.status, recognized, error: (r && r.ErrorCode) || null, data: r }
|
||||||
|
}
|
||||||
|
|
||||||
// Construit le payload n8n EXACT pour une action, à partir du contexte résolu + overrides.
|
// Construit le payload n8n EXACT pour une action, à partir du contexte résolu + overrides.
|
||||||
function buildPayload (action, equip, opts = {}) {
|
function buildPayload (action, equip, opts = {}) {
|
||||||
const spec = ACTIONS[action]
|
const spec = ACTIONS[action]
|
||||||
|
|
@ -179,13 +188,7 @@ async function plan ({ serial, action, ...opts } = {}) {
|
||||||
const missing = method === 'n8n' ? spec.needs.filter(k => !payload[k] && payload[k] !== 0) : []
|
const missing = method === 'n8n' ? spec.needs.filter(k => !payload[k] && payload[k] !== 0) : []
|
||||||
if (missing.length) warnings.push('Champs manquants : ' + missing.join(', '))
|
if (missing.length) warnings.push('Champs manquants : ' + missing.join(', '))
|
||||||
const canRun = supportsTech && !!payload.olt && (method === 'n8n' ? (tech === 3 && !missing.length) : snmpReady)
|
const canRun = supportsTech && !!payload.olt && (method === 'n8n' ? (tech === 3 && !missing.length) : snmpReady)
|
||||||
const effects = []
|
const effects = spec.effect ? [spec.effect] : []
|
||||||
if (action === 'activate') effects.push('Provisionne l’ONU sur l’OLT + met Service Equipment « Actif ».')
|
|
||||||
if (action === 'replace') effects.push('Ré-authentifie la nouvelle série sur l’OLT + met à jour le numéro de série de Service Equipment.')
|
|
||||||
if (action === 'speed') effects.push('Change le profil de ligne (l’ONU redémarre).')
|
|
||||||
if (action === 'suspend') effects.push('Bascule le VLAN internet → coupe l’accès (facturation inchangée).')
|
|
||||||
if (action === 'unsuspend') effects.push('Rétablit le VLAN internet.')
|
|
||||||
if (action === 'remove') effects.push('Retire l’ONU de l’OLT + délie Service Equipment.')
|
|
||||||
return {
|
return {
|
||||||
ok: canRun,
|
ok: canRun,
|
||||||
action, label: spec.label, verb: spec.verb, tech, method,
|
action, label: spec.label, verb: spec.verb, tech, method,
|
||||||
|
|
@ -233,18 +236,12 @@ async function run ({ serial, action, confirm, idempotencyKey, actor, ...opts }
|
||||||
}
|
}
|
||||||
|
|
||||||
// G1 reste : clients WiFi connectés d'un ONU (n8n get_wifi_info) — LECTURE.
|
// G1 reste : clients WiFi connectés d'un ONU (n8n get_wifi_info) — LECTURE.
|
||||||
function wifiClients ({ sn } = {}) {
|
async function wifiClients ({ sn } = {}) {
|
||||||
return new Promise((resolve) => {
|
if (!sn) return { ok: false, error: 'sn requis' }
|
||||||
if (!sn) return resolve({ ok: false, error: 'sn requis' })
|
const res = await n8nRequest('GET', 'https://n8napi.targo.ca/webhook/get_wifi_info', { sn: String(sn) }, 20000)
|
||||||
let https; try { https = require('https') } catch (e) { return resolve({ ok: false, error: 'https indisponible' }) }
|
if (res.error) return { ok: false, error: res.error }
|
||||||
const body = JSON.stringify({ sn: String(sn) })
|
const j = res.data
|
||||||
const req = https.request('https://n8napi.targo.ca/webhook/get_wifi_info', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 20000 }, (res) => {
|
return { ok: !!j, clients: Array.isArray(j) ? j : (j && j.clients) || [], raw: j }
|
||||||
let d = ''; res.on('data', c => { d += c }); res.on('end', () => { let j; try { j = JSON.parse(d) } catch (e) { j = null } resolve({ ok: !!j, clients: Array.isArray(j) ? j : (j && j.clients) || [], raw: j }) })
|
|
||||||
})
|
|
||||||
req.on('error', e => resolve({ ok: false, error: e.message }))
|
|
||||||
req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'délai dépassé' }) })
|
|
||||||
req.write(body); req.end()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { plan, run, wifiClients, ACTIONS }
|
module.exports = { plan, run, wifiClients, ACTIONS }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user