125 lines
4.2 KiB
TypeScript
125 lines
4.2 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, mkdirSync } from 'node:fs'
|
|
import { writeFile } from 'node:fs/promises'
|
|
import { join } from 'node:path'
|
|
|
|
const APP_ROOT = process.cwd()
|
|
const CONFIG_PATH = join(APP_ROOT, 'data', 'tr069-trace.json')
|
|
const LOG_DIR = join(APP_ROOT, '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 })
|
|
}
|
|
|
|
async rawConfig({ response }: HttpContext) {
|
|
try {
|
|
if (!existsSync(CONFIG_PATH)) {
|
|
return response.status(200).json({ raw: JSON.stringify({ entries: [] }, null, 2) })
|
|
}
|
|
const raw = readFileSync(CONFIG_PATH, 'utf-8')
|
|
return response.status(200).json({ raw })
|
|
} catch (error) {
|
|
return response.status(500).json({ error: 'Failed to read config file' })
|
|
}
|
|
}
|
|
|
|
async saveRawConfig({ request, response }: HttpContext) {
|
|
const raw = request.input('raw') as string | undefined
|
|
|
|
if (!raw) {
|
|
return response.status(422).json({ error: 'raw JSON content is required' })
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(raw)
|
|
if (!parsed.entries || !Array.isArray(parsed.entries)) {
|
|
return response.status(422).json({ error: 'JSON must contain an "entries" array' })
|
|
}
|
|
|
|
mkdirSync(join(APP_ROOT, 'data'), { recursive: true })
|
|
await writeFile(CONFIG_PATH, JSON.stringify(parsed, null, 2), 'utf-8')
|
|
|
|
// Reload the service in-memory state
|
|
this.service.reload()
|
|
|
|
logger.info('TR-069 trace config updated via raw editor')
|
|
return response.status(200).json({ entries: this.service.list() })
|
|
} catch (error) {
|
|
if (error instanceof SyntaxError) {
|
|
return response.status(422).json({ error: `Invalid JSON: ${error.message}` })
|
|
}
|
|
return response.status(500).json({ error: 'Failed to save config file' })
|
|
}
|
|
}
|
|
}
|