- Remove SOAP from Docker stdout; route to per-device trace files only - Fix trace service paths using process.cwd() (/app/data/, /app/logs/tr069/) - Fix Docker volume permissions: dedicated /app/data & /app/logs/tr069 dirs - Add GET /api/v1/tr069-trace/:id/log endpoint (tail N lines) - Rewrite Tr069Trace.vue: clickable rows, dark log viewer, 3s auto-refresh - Add simulator-ui-env.js to public/ (remove from config/) - TR-181/InternetGatewayDevice dataModel auto-detection - Parameter writability config with W/R fallback default - Fix device_action_executor for GetParameterNames detection flow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
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)')
|
|
}
|
|
}
|
|
}
|