feat: estimated next connection + fix build errors
- Store lastPeriodicInformAt on device when PERIODIC Inform received - Display 'Prochaine connexion' in DeviceDetail identity card - Fix tsconfig exclude (tests, inertia, coverage) for Docker build - Fix TS errors in tests (CommandResult type, unused imports) - Fix distinctCriteriaValues typing in controller
This commit is contained in:
1 parent
e1e8ae5097
commit
31578e368f
7 files changed
+55
-10
No files matched your search
@@ -172,6 +172,12 @@ export default class CwmpController {
|
||||
|
||||
logger.info({ events }, 'CWMP Inform events extracted')
|
||||
|
||||
// Track last periodic inform timestamp
|
||||
const isPeriodic = events.some((e) => String(e).toUpperCase().includes('PERIODIC') || String(e) === '2')
|
||||
if (isPeriodic) {
|
||||
await DeviceModel.updateOne({ identifier: hashId }, { $set: { lastPeriodicInformAt: new Date() } }).catch(() => {})
|
||||
}
|
||||
|
||||
// Start state machine
|
||||
await manager.startStateMachine(device, session, { events })
|
||||
manager.bindRemoteToDevice(remoteKey, device.deviceId)
|
||||
|
||||
@@ -8,7 +8,7 @@ export default class DevicesController {
|
||||
private deviceService = new DeviceService()
|
||||
|
||||
async criteriaValues({ response }: HttpContext) {
|
||||
const values = await DeviceModel.distinctCriteriaValues()
|
||||
const values = await (DeviceModel as any).distinctCriteriaValues()
|
||||
return response.json(values)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface DeviceRecord {
|
||||
compliance?: DeviceComplianceInfo
|
||||
provisioningCode?: string
|
||||
references?: DeviceReference[]
|
||||
lastPeriodicInformAt?: Date
|
||||
updatedAt?: Date
|
||||
createdAt?: Date
|
||||
}
|
||||
@@ -126,6 +126,18 @@ function submitGetModel() {
|
||||
})
|
||||
}
|
||||
|
||||
// Estimated next connection = lastPeriodicInformAt + PeriodicInformInterval
|
||||
const estimatedNextConnection = (() => {
|
||||
const lastPeriodic = device.lastPeriodicInformAt
|
||||
if (!lastPeriodic) return null
|
||||
const allParams = [...(device.properties || []), ...(device.parameters || [])]
|
||||
const intervalParam = allParams.find((p: any) => p.name?.includes('PeriodicInformInterval'))
|
||||
const intervalSeconds = intervalParam?.value ? parseInt(intervalParam.value, 10) : 0
|
||||
if (!intervalSeconds || intervalSeconds <= 0) return null
|
||||
const next = new Date(new Date(lastPeriodic).getTime() + intervalSeconds * 1000)
|
||||
return next.toLocaleString()
|
||||
})()
|
||||
|
||||
// Check if the device has a ConnectionRequestURL (needed for immediate mode / Refresh)
|
||||
const hasConnectionRequestUrl = (() => {
|
||||
const allParams = [...(device.properties || []), ...(device.parameters || [])]
|
||||
@@ -333,6 +345,23 @@ async function resolveAllProviders() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zone 5b : Prochaine connexion estimée -->
|
||||
<div v-if="estimatedNextConnection" class="col-sm-6 col-lg-4">
|
||||
<div class="rounded-3 px-3 py-3 d-flex align-items-center gap-3" style="background:#f6f7f9">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center flex-shrink-0"
|
||||
style="width:34px;height:34px;background:#e4e6ea">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" class="text-muted">
|
||||
<path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71z"/>
|
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted fw-bold text-uppercase mb-1" style="font-size:0.62rem;letter-spacing:.06em">Prochaine connexion</div>
|
||||
<div class="font-monospace small fw-medium">{{ estimatedNextConnection }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Zone 6 : Références client -->
|
||||
|
||||
@@ -41,15 +41,15 @@ describe('Command', () => {
|
||||
it('complete transitions SENT → COMPLETED', () => {
|
||||
const cmd = makeCommand()
|
||||
cmd.markAsSent()
|
||||
cmd.complete({ success: true })
|
||||
cmd.complete({ success: true, timestamp: new Date() })
|
||||
expect(cmd.status).toBe(CommandStatus.COMPLETED)
|
||||
expect(cmd.result).toEqual({ success: true })
|
||||
expect(cmd.result?.success).toBe(true)
|
||||
expect(cmd.isCompleted()).toBe(true)
|
||||
})
|
||||
|
||||
it('complete throws if PENDING', () => {
|
||||
const cmd = makeCommand()
|
||||
expect(() => cmd.complete({})).toThrow('Cannot complete command')
|
||||
expect(() => cmd.complete({ success: true, timestamp: new Date() } as any)).toThrow('Cannot complete command')
|
||||
})
|
||||
|
||||
it('fail transitions to FAILED', () => {
|
||||
@@ -64,7 +64,7 @@ describe('Command', () => {
|
||||
it('fail throws if already COMPLETED', () => {
|
||||
const cmd = makeCommand()
|
||||
cmd.markAsSent()
|
||||
cmd.complete({})
|
||||
cmd.complete({ success: true, timestamp: new Date() } as any)
|
||||
expect(() => cmd.fail('oops')).toThrow('Cannot fail a successful command')
|
||||
})
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('Command', () => {
|
||||
it('cancel throws if COMPLETED', () => {
|
||||
const cmd = makeCommand()
|
||||
cmd.markAsSent()
|
||||
cmd.complete({})
|
||||
cmd.complete({ success: true, timestamp: new Date() } as any)
|
||||
expect(() => cmd.cancel()).toThrow('Cannot cancel command')
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
|
||||
// Mock logger + DeviceModel
|
||||
vi.mock('@adonisjs/core/services/logger', () => ({
|
||||
|
||||
+12
-3
@@ -2,7 +2,16 @@
|
||||
"extends": "@adonisjs/tsconfig/tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"jsx": "react",
|
||||
"outDir": "./build"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vueCompilerOptions": {
|
||||
"strictTemplates": true,
|
||||
"target": 3.3
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"tests",
|
||||
"coverage",
|
||||
"inertia"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user