- 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
88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
import { Db } from 'mongodb'
|
|
import { getMongoDb } from '#providers/mongodb_provider'
|
|
|
|
/**
|
|
* Expected indexes on the `cwmp_exchanges` collection.
|
|
* Each entry describes the index name and key pattern for verification.
|
|
*/
|
|
const EXPECTED_INDEXES = [
|
|
{
|
|
name: 'idx_deviceId_timestamp',
|
|
key: { deviceId: 1, timestamp: 1 },
|
|
description: 'Compound index (deviceId, timestamp)',
|
|
},
|
|
{
|
|
name: 'idx_deviceId_sequenceNumber',
|
|
key: { deviceId: 1, sequenceNumber: 1 },
|
|
description: 'Unique index (deviceId, sequenceNumber)',
|
|
},
|
|
{
|
|
name: 'idx_createdAt_ttl',
|
|
key: { createdAt: 1 },
|
|
description: 'TTL index (createdAt, 86400s)',
|
|
},
|
|
]
|
|
|
|
export interface IndexCheckResult {
|
|
name: string
|
|
description: string
|
|
exists: boolean
|
|
}
|
|
|
|
export interface IndexVerificationResult {
|
|
collection: string
|
|
allPresent: boolean
|
|
indexes: IndexCheckResult[]
|
|
}
|
|
|
|
/**
|
|
* Verifies that all expected indexes exist on the cwmp_exchanges collection.
|
|
* Returns a structured result with per-index status.
|
|
*/
|
|
export async function verifyCwmpExchangesIndexes(db?: Db): Promise<IndexVerificationResult> {
|
|
const database = db ?? getMongoDb()
|
|
const collection = database.collection('cwmp_exchanges')
|
|
|
|
let existingIndexes: { name?: string }[]
|
|
try {
|
|
existingIndexes = await collection.indexes()
|
|
} catch {
|
|
// Collection may not exist yet
|
|
existingIndexes = []
|
|
}
|
|
|
|
const existingNames = new Set(existingIndexes.map((idx) => idx.name))
|
|
|
|
const indexes: IndexCheckResult[] = EXPECTED_INDEXES.map((expected) => ({
|
|
name: expected.name,
|
|
description: expected.description,
|
|
exists: existingNames.has(expected.name),
|
|
}))
|
|
|
|
const allPresent = indexes.every((idx) => idx.exists)
|
|
|
|
return {
|
|
collection: 'cwmp_exchanges',
|
|
allPresent,
|
|
indexes,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs index verification at startup and logs warnings for missing indexes.
|
|
* Intended to be called after MongoDB provider has booted.
|
|
*/
|
|
export async function verifyIndexesAtStartup(db: Db, log: { warn: (msg: string) => void; info: (msg: string) => void }): Promise<void> {
|
|
const result = await verifyCwmpExchangesIndexes(db)
|
|
|
|
if (result.allPresent) {
|
|
log.info('[ExchangeIndexCheck] All cwmp_exchanges indexes verified OK')
|
|
return
|
|
}
|
|
|
|
const missing = result.indexes.filter((idx) => !idx.exists)
|
|
for (const idx of missing) {
|
|
log.warn(`[ExchangeIndexCheck] Missing index on cwmp_exchanges: ${idx.name} (${idx.description})`)
|
|
}
|
|
}
|