import type { HttpContext } from '@adonisjs/core/http' import type { NextFn } from '@adonisjs/core/types/http' // @ts-ignore - xml-formatter types conflict with ESM interop import formatXml from 'xml-formatter' /** * CWMP Logger Middleware * * Middleware dédié à l'observation des messages TR-069 XML. * Trace les échanges CPE↔ACS (requêtes entrantes et réponses sortantes) * avec le XML pretty-printed directement sur stdout pour lisibilité humaine. * * Doit être placé avant le cwmp_middleware dans la chaîne. */ export default class CwmpLoggerMiddleware { private prettyXml(xml: string): string { try { return (formatXml as any)(xml, { indentation: ' ', collapseContent: true, lineSeparator: '\n' }) } catch { return xml } } private log(direction: string, sessionId: string, xml: string) { const timestamp = new Date().toISOString() const separator = '─'.repeat(80) process.stdout.write( `\n${separator}\n` + `[TR-069] ${direction} | ${timestamp} | ${sessionId}\n` + `${separator}\n` + `${xml}\n` + `${separator}\n\n` ) } async handle({ request, response }: HttpContext, next: NextFn) { const rawBody = request.raw() || '' const socket = request.request?.socket const remoteIp = request.ip() const remotePort = socket?.remotePort || 0 const localAddress = socket?.localAddress || 'unknown' const localPort = socket?.localPort || 0 const sessionId = `${remoteIp}:${remotePort}->${localAddress}:${localPort}` // Log incoming TR-069 message if (rawBody && rawBody.trim() !== '') { this.log('CPE→ACS', sessionId, this.prettyXml(rawBody)) } // Intercept response to log outgoing XML const originalSend = response.send.bind(response) response.send = (body: any) => { if (typeof body === 'string' && body.includes('soap-env:Envelope')) { this.log('ACS→CPE', sessionId, this.prettyXml(body)) } return originalSend(body) } return next() } }