Files
adam-acs/app/controllers/tr069_trace_controller.ts
T
Kevin L'HereecandCopilot 6082a0e610 feat: TR-069 per-device SOAP trace logging + log viewer UI
- 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>
2026-06-01 14:33:44 +02:00

81 lines
2.7 KiB
TypeScript

import type { HttpContext } from '@adonisjs/core/http'
import logger from '@adonisjs/core/services/logger'
import DeviceModel from '#models/device'
import { Tr069TraceService } from '#services/tr069_trace_service'
import { readFileSync, existsSync } from 'node:fs'
import { join } from 'node:path'
const LOG_DIR = join(process.cwd(), 'logs', 'tr069')
const MAX_TAIL_LINES = 1000
export default class Tr069TraceController {
private get service(): Tr069TraceService {
return Tr069TraceService.getInstance()
}
async index({ response }: HttpContext) {
return response.status(200).json({ entries: this.service.list() })
}
async store({ request, response }: HttpContext) {
const deviceId = request.input('deviceId') as string | undefined
if (!deviceId) {
return response.status(422).json({ error: 'deviceId is required' })
}
if (this.service.isTraced(deviceId)) {
return response.status(409).json({ error: 'Device is already being traced' })
}
const record = await DeviceModel.findByIdentifier(deviceId)
if (!record) {
return response.status(404).json({ error: `Device ${deviceId} not found` })
}
await this.service.addDevice({
deviceId,
manufacturer: record.deviceId?.manufacturer ?? '',
oui: record.deviceId?.oui ?? '',
productClass: record.deviceId?.productClass ?? '',
serialNumber: record.deviceId?.serialNumber ?? '',
})
logger.info({ deviceId }, 'TR-069 trace enabled for device')
const entry = this.service.list().find((traceEntry) => traceEntry.deviceId === deviceId)
return response.status(201).json({ entry })
}
async destroy({ params, response }: HttpContext) {
const removed = await this.service.removeDevice(params.id)
if (!removed) {
return response.status(404).json({ error: `Device ${params.id} is not being traced` })
}
logger.info({ deviceId: params.id }, 'TR-069 trace disabled for device')
return response.status(204).send('')
}
async showLog({ params, request, response }: HttpContext) {
const deviceId = params.id
const lines = Math.min(Number(request.qs().lines ?? 200), MAX_TAIL_LINES)
if (!this.service.isTraced(deviceId)) {
return response.status(404).json({ error: `Device ${deviceId} is not being traced` })
}
const filePath = join(LOG_DIR, `${deviceId}.log`)
if (!existsSync(filePath)) {
return response.status(200).json({ deviceId, content: '', lines: 0 })
}
const raw = readFileSync(filePath, 'utf-8')
const allLines = raw.split('\n')
const tail = allLines.slice(-lines).join('\n')
return response.status(200).json({ deviceId, content: tail, lines: allLines.length })
}
}