'use strict' // ── Diagnostic CLIENT en libre-service via lien tokenisé ────────────────────────────────────────── // On envoie au client un lien signé /diag/ (scope 'customer'). Il ouvre une page mobile qui montre, // EN DIRECT, ce qui fonctionne (Internet, fibre, Wi-Fi…) — sans aucune donnée interne (série, IP, OLT, dBm). // Réutilise verifyJwt (magic-link) + serviceStatus (ticket-collab). Route PUBLIQUE : le token EST le secret. const cfg = require('./config') const { log } = require('./helpers') const { verifyJwt } = require('./magic-link') function sendHtml (res, code, html) { res.writeHead(code, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' }); res.end(html) } function sendJson (res, code, obj) { res.writeHead(code, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); res.end(JSON.stringify(obj)) } // Transforme le statut interne en vue CLIENT (aucune fuite : pas de série, MAC, IP, OLT, dBm). function clientView (s, payload) { const sum = (s && s.summary) || {} // Vue CLIENT : pilotée par « en ligne » (sur FTTH, si on est en ligne, la fibre est up). Le détail fibre/OLT // reste côté agent — un poller OLT en panne chez NOUS ne doit jamais afficher « problème » à un client servi. const online = sum.online === true const tel = (s && s.subscriptions ? s.subscriptions : []).some(x => /t[ée]l[ée]phon|voip|sip/i.test(String(x.plan || ''))) const checks = [ { key: 'internet', label: 'Internet', ok: online }, { key: 'wifi', label: 'Wi-Fi', ok: online }, ] if (tel) checks.push({ key: 'tel', label: 'Téléphonie', ok: online }) return { name: (payload && payload.name) || (s && s.customer && s.customer.customer_name) || '', allOk: checks.every(c => c.ok), online, checks, plans: (s && s.subscriptions ? s.subscriptions : []).map(x => x.plan).filter(Boolean).slice(0, 4), address: s && s.location ? (s.location.address || '') : '', ts: new Date().toISOString(), } } const ESC = (t) => String(t == null ? '' : t).replace(/[<&>"]/g, c => ({ '<': '<', '&': '&', '>': '>', '"': '"' }[c])) function expiredPage () { return `Lien expiré

Gigafibre

Ce lien de diagnostic a expiré ou n'est pas valide.

Répondez à notre message et nous vous en enverrons un nouveau.

` } function page (token) { return `Diagnostic Gigafibre
Gigafibre
Diagnostic de votre service
Vérification en cours…
Toujours un souci ? Discutez avec l'assistant ci-dessus — un agent prend le relais au besoin.
© Gigafibre · TARGO
` } async function route (req, res, method, path) { const m = path.match(/^\/diag\/([A-Za-z0-9._-]+?)(\/data|\/chat)?$/) if (!m) return sendHtml(res, 404, '

404

') const token = m[1]; const sub = m[2] || '' const payload = verifyJwt(token) if (!payload || payload.scope !== 'customer' || !payload.sub) { return sub ? sendJson(res, 401, { error: 'expired' }) : sendHtml(res, 200, expiredPage()) } // CHAT : 1er message du client → crée une conversation (liée au compte) + déclenche l'IA (même moteur que le SMS/web-chat). // L'IA a l'outil check_device_status → réponses diagnostiques ; la conversation remonte dans l'Inbox agent (escalade). if (sub === '/chat' && method === 'POST') { let body = ''; await new Promise(r => { req.on('data', c => { body += c }); req.on('end', r); req.on('error', r) }) let text = ''; try { text = String((JSON.parse(body || '{}')).text || '').trim() } catch (e) { /* */ } if (!text) return sendJson(res, 400, { error: 'text requis' }) const conv = require('./conversation') const c = conv.createConversation({ phone: null, customer: payload.sub, customerName: payload.name || payload.sub, subject: 'Diagnostic en ligne — ' + (payload.name || payload.sub) }) c.channel = 'webchat'; c.channelKey = 'diag:' + payload.sub; c.queue = 'Supports'; if (payload.email) c.email = payload.email // escalade fiable vers l'Inbox agent conv.addMessage(c, { from: 'customer', text, via: 'webchat' }) try { conv.triggerAgent(c) } catch (e) { /* */ } log('client-diag chat: conv ' + c.token + ' (' + payload.sub + ')') return sendJson(res, 200, { ok: true, convToken: c.token }) } if (method !== 'GET') return sendHtml(res, 404, '

404

') if (sub === '/data') { try { const s = await require('./ticket-collab').serviceStatus({ customer: payload.sub }); return sendJson(res, 200, clientView(s, payload)) } catch (e) { log('client-diag data ' + payload.sub + ': ' + e.message); return sendJson(res, 500, { error: 'indisponible' }) } } return sendHtml(res, 200, page(token)) } module.exports = { route, clientView }