- MongoDB storage: cwmp_exchanges collection with indexes (deviceId+timestamp, deviceId+sequenceNumber unique, TTL 24h) - ExchangeStoreService: CWMP method extraction, per-device queue for ordering, retry logic, message cap (5000/device) - Exchange API: GET/DELETE/SSE stream endpoints with Last-Event-ID reconnection - CwmpLoggerMiddleware: dual-write (MongoDB + legacy files) with fire-and-forget - Frontend: SequenceViewer (CPE↔ACS lifelines, arrows, auto-scroll, virtual scroll) + InspectorPanel (formatted XML, copy, syntax highlighting) - Tr069Trace page: integrated new viewer with col-6/col-6 layout - DeviceDetail page: collapsible exchange section when device is traced - 14 property-based tests, 40+ unit tests, 5 integration tests - Health check includes index verification - Config: feature flags, SSE timeouts, retry policy
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import type { HttpContext } from '@adonisjs/core/http'
|
|
import { getMongoDb } from '#providers/mongodb_provider'
|
|
import { verifyCwmpExchangesIndexes } from '#services/exchange_index_checker'
|
|
import { ExchangeStoreService } from '#services/exchange_store_service'
|
|
|
|
export default class HealthController {
|
|
async checklife({ response }: HttpContext) {
|
|
return response.json({ status: 'OK' })
|
|
}
|
|
|
|
async checkservice({ response }: HttpContext) {
|
|
try {
|
|
const db = getMongoDb()
|
|
await db.command({ ping: 1 })
|
|
|
|
const indexCheck = await verifyCwmpExchangesIndexes(db).catch(() => null)
|
|
|
|
return response.json({
|
|
status: 'OK',
|
|
version: '1.0.0',
|
|
indexes: indexCheck
|
|
? {
|
|
cwmp_exchanges: {
|
|
allPresent: indexCheck.allPresent,
|
|
indexes: indexCheck.indexes,
|
|
},
|
|
}
|
|
: undefined,
|
|
})
|
|
} catch {
|
|
return response.status(503).json({ status: 'KO', version: '1.0.0' })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/health/tr069-exchanges
|
|
*
|
|
* Health check endpoint for the TR-069 exchange viewer subsystem.
|
|
* Reports:
|
|
* - MongoDB connectivity and latency
|
|
* - Active SSE connection count
|
|
* - Collection stats (document count, size)
|
|
* - Overall status: healthy / degraded / unhealthy
|
|
*
|
|
* Status logic:
|
|
* - healthy: MongoDB connected, latency < 100ms, indexes present
|
|
* - degraded: MongoDB connected but latency >= 100ms, or missing indexes
|
|
* - unhealthy: MongoDB unreachable or ping fails
|
|
*/
|
|
async checkTr069Exchanges({ response }: HttpContext) {
|
|
const timestamp = new Date().toISOString()
|
|
let mongoConnected = false
|
|
let latencyMs = -1
|
|
let collectionStats: { count: number; sizeMb: number } | null = null
|
|
let indexCheck: Awaited<ReturnType<typeof verifyCwmpExchangesIndexes>> | null = null
|
|
|
|
// Check MongoDB connectivity and measure latency
|
|
try {
|
|
const db = getMongoDb()
|
|
const start = Date.now()
|
|
await db.command({ ping: 1 })
|
|
latencyMs = Date.now() - start
|
|
mongoConnected = true
|
|
|
|
// Get collection stats
|
|
const collection = db.collection('cwmp_exchanges')
|
|
const count = await collection.estimatedDocumentCount()
|
|
|
|
let sizeMb = 0
|
|
try {
|
|
const stats = await db.command({ collStats: 'cwmp_exchanges' })
|
|
sizeMb = Math.round((stats.size / (1024 * 1024)) * 100) / 100
|
|
} catch {
|
|
// Collection may not exist yet or collStats may not be available
|
|
sizeMb = 0
|
|
}
|
|
|
|
collectionStats = { count, sizeMb }
|
|
|
|
// Check indexes
|
|
indexCheck = await verifyCwmpExchangesIndexes(db)
|
|
} catch {
|
|
mongoConnected = false
|
|
}
|
|
|
|
// Get active SSE connection count
|
|
const exchangeStore = ExchangeStoreService.getInstance()
|
|
const activeConnections = exchangeStore.activeConnectionCount
|
|
|
|
// Determine overall status
|
|
let status: 'healthy' | 'degraded' | 'unhealthy'
|
|
if (!mongoConnected) {
|
|
status = 'unhealthy'
|
|
} else if (latencyMs >= 100 || (indexCheck && !indexCheck.allPresent)) {
|
|
status = 'degraded'
|
|
} else {
|
|
status = 'healthy'
|
|
}
|
|
|
|
const statusCode = status === 'unhealthy' ? 503 : 200
|
|
|
|
return response.status(statusCode).json({
|
|
status,
|
|
mongodb: {
|
|
connected: mongoConnected,
|
|
latency_ms: latencyMs >= 0 ? latencyMs : null,
|
|
collections: collectionStats
|
|
? {
|
|
cwmp_exchanges: {
|
|
count: collectionStats.count,
|
|
size_mb: collectionStats.sizeMb,
|
|
},
|
|
}
|
|
: null,
|
|
indexes: indexCheck
|
|
? {
|
|
allPresent: indexCheck.allPresent,
|
|
details: indexCheck.indexes,
|
|
}
|
|
: null,
|
|
},
|
|
sse: {
|
|
active_connections: activeConnections,
|
|
},
|
|
timestamp,
|
|
})
|
|
}
|
|
}
|