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' import { Tr069TraceService } from '#services/tr069_trace_service' import logger from '@adonisjs/core/services/logger' /** * CWMP Logger Middleware * * Buffers SOAP request/response. After next(): * - Always logs a brief summary (INFO) to the app logger. * - For traced devices, writes full SOAP payloads to logs/tr069/{deviceId}.log. */ export default class CwmpLoggerMiddleware { private prettyXml(xml: string): string { try { return (formatXml as any)(xml, { indentation: ' ', collapseContent: true, lineSeparator: '\n', }) } catch { return xml } } async handle(ctx: HttpContext, next: NextFn) { const { request, response } = ctx 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}` let capturedResponseXml: string | null = null const originalSend = response.send.bind(response) response.send = (body: any) => { if (typeof body === 'string' && body.includes('soap-env:Envelope')) { capturedResponseXml = body } return originalSend(body) } await next() const deviceId = ctx.cwmpDeviceId const traceService = Tr069TraceService.getInstance() const isTraced = deviceId ? traceService.isTraced(deviceId) : false if (isTraced && deviceId) { if (rawBody && rawBody.trim() !== '') { traceService.writeExchange(deviceId, 'CPE→ACS', sessionId, this.prettyXml(rawBody)) } if (capturedResponseXml) { traceService.writeExchange(deviceId, 'ACS→CPE', sessionId, this.prettyXml(capturedResponseXml)) } } else if (rawBody && rawBody.trim() !== '') { // Brief app-level log only (no SOAP body) logger.debug({ sessionId, deviceId: deviceId ?? 'unknown' }, 'TR-069 exchange (not traced)') } } }