88 Commits
Author SHA1 Message Date
Kevin L'Hereec 76e75132d5 test: raise business-logic unit test coverage across core services
Build & Deploy / build (push) Successful in 34s
Build & Deploy / docker (push) Successful in 1m19s
Build & Deploy / deploy (push) Successful in 37s
Adds targeted unit tests to cover previously untested branches/functions
in the business-critical (non-protocol) services:

- device_service.ts: 35.8% -> 98.76% (CRUD, executeCommand, refreshDevice,
  calculatePagination, IPv4/IPv6 Connection Request fallback chain)
- domain/device/device.ts: 76.7% -> 96.59% (GPN circuit breaker state
  machine, session lifecycle, canExecuteCommands, detectDataModelFromParams)
- services/references/reference_engine.ts: 78.66% -> 100% (provider
  loading edge cases, rejection handling, singleton lifecycle)
- services/configuration_engine.ts: 70.96% -> 98.7% (matchesCriteria
  branches, shouldApplyOnce policy variants, dryRun, complianceReport)
- services/cwmp/state_machine_manager.ts: 70.9% -> 87.27% (getHashIdForRemoteIp,
  ensureSessionForRemote IP fallback, task callback error handling)
- services/cwmp/main_state_machine.ts: 57.79% -> 94.29% (ExecutingCommandsState
  full command-type dispatch, MAX_STATE_TRANSITIONS guard, error->
  PartialOperationalState transitions)

Also fixes:
- reference_engine_full.spec.ts previously depended on the real
  config/references.json (production config, wassup provider legitimately
  disabled there) instead of a test fixture — now mocks config_loader
  for deterministic, isolated assertions
- main_state_machine.spec.ts CommandModel.getCollection mock returned a
  new object per call instead of a shared instance, breaking
  setupCommandMock() for the new command-dispatch tests

Overall project coverage (excluding protocol/): 72.76% -> 82.34% statements.
Full suite: 1005/1005 tests passing.
2026-07-22 11:45:50 +02:00
Kevin L'Hereec 576b068cf4 chore: relax naming-convention/filename-case lint rules for legacy CWMP protocol files
app/protocol/cwmp-1-*.ts and app/utils/cwmp-events.ts mirror TR-069 spec
version numbers in their type/class names (e.g. Cwmp_1_0, EventCode_1_1),
which trips @unicorn/filename-case and @typescript-eslint/naming-convention.
Renaming these would be a large, risky refactor across the protocol layer
for no functional benefit, so the two rules are disabled for this specific
file set via an eslint.config.js override.

This unblocks 'eslint app/' from reporting pre-existing, unfixable errors,
allowing a pre-push git hook (lint app/ + tsc --noEmit) to be enabled
without a large unrelated renaming effort.
2026-07-22 11:45:35 +02:00
Kevin L'Hereec f9e56c78b1 refactor: migrate all internal imports to package.json subpath aliases
- Add #domain, #protocol, #utils aliases to package.json imports map
- Replace all relative imports (./, ../, ../..) in app/ and
  database/mongo_migrations/ with the corresponding #alias imports,
  eliminating relative path traversal across the codebase
- Remove dead code: ConfigurationVersionModel.findByConfigurationIdentifier
  (unused, superseded by paginateVersions) and unused exception classes
  (NotFoundException, UnprocessableEntityException, DatabaseException)
  from app/exceptions/index.ts, keeping only BadRequestException
- Fix pre-existing syntax error in configuration_version.ts (mismatched
  parenthesis) that broke tsc
- Fix eqeqeq violation in configuration_service.ts and isNaN ->
  Number.isNaN in configuration_engine.ts / cwmp-1-0.ts / exchange_controller.ts
- Fix no-shadow violation in cwmp-1-0.ts EventStruct constructor
- Prettier formatting pass on touched files

Validated: tsc --noEmit clean, eslint app/ clean, full test suite green.
2026-07-22 11:45:14 +02:00
Kevin L'Hereec 4177f1adbd fix: eslint/prettier formatting and type-only imports
- Use type-only import for HttpContext in exceptions/handler.ts
- Use #services alias instead of relative path in device_record.ts
- Reformat api_key_middleware and cwmp_logger_middleware per prettier
2026-07-22 10:35:49 +02:00
Kevin L'Hereec 9cceb139b7 fix: eslint consistent-type-imports and eqeqeq violations
- Convert value imports used only as types to 'import type' across
  all modified files (cwmp_message_builder, task_queue, device_action_executor,
  main_state_machine, immediate_command_state_machine, configuration_evaluation_state,
  cwmp_controller, cwmp_middleware, device.ts, soap.ts, soap_envelope.ts, tr069_types.ts)
- Replace != null with !== null && !== undefined in main_state_machine and cwmp_controller
2026-07-20 17:14:12 +02:00
Kevin L'Hereec ab45275a89 fix: SetParameterAttributes XML emits 0/1 for boolean fields and drops xsi:type on Notification
NotificationChange and AccessListChange were emitting 'true'/'false' (JS booleans)
instead of '1'/'0' as required by TR-069 spec.
Notification xsi:type='xsd:int' attribute removed — consistent with old builder output.
2026-07-20 16:39:53 +02:00
Kevin L'Hereec b643077e8d fix: correct orphan references after TR-069 typing refactor
- tests/device.spec.ts: fix 6× organizeParameters() calls passing wrapped
  response object instead of flat ParameterInfoStruct[] (runtime break)
- tests/device.spec.ts: fix setTmpParamValues([{x:1}]) with valid shape
- immediate_command_state_machine.ts: type handleResponse message param as
  CWMPTaskResultData (was any, gap in typing contract)
- configuration_evaluation_state.ts: replace fragile ../../../app/ dynamic
  import paths with proper static imports from ../../protocol/tr069_types.js
- main_state_machine.ts: replace all inline dynamic import() type casts with
  static imports (GetParameterNamesResponse, GetParameterValuesResponse,
  ParameterValueStruct, SetParameterAttribute, DownloadParameter, etc.)
2026-07-20 16:27:38 +02:00
Kevin L'Hereec f3eb8e1650 feat: restore strong typing on TR-069/CWMP protocol layer
- Copy cwmp-1-0..4.ts source classes from cwmp-connector (papaye) into
  app/protocol/ as local files (ts-nocheck, external dependency)
- Add soap_envelope.ts: SoapEnvelope, SoapEnvelopeHeader, CwmpID, NAMESPACES
- Add tr069_types.ts: re-exports all CWMP classes + CwmpMessage discriminated
  union + typed task payload interfaces (CWMPTaskType, CWMPTaskPayload, etc.)
- Rewrite cwmp_message_builder.ts to use class-based builders instead of raw
  XML template strings; add transferCompleteResponse() and soapFault()
- soap.ts: extractCwmpMessage returns CwmpMessage union, adds runtime allowlist
  guard against unknown CWMP method names from CPE
- cwmp_middleware.ts: envelope typed SoapEnvelope|null, message CwmpMessage|null
- cwmp_controller.ts: handleInform(informData: Inform), remove any, migrate
  TransferComplete and fault to new builder path, drop buildSoapEnvelope import
- task_queue.ts: CWMPTask.type → CWMPTaskType literal union, payload →
  CWMPTaskPayload, result → CWMPTaskResultData
- device_action_executor.ts: GPV/GPN/GPA responses typed, no more any
- main_state_machine.ts: DeviceContext.message.data → CWMPTaskResultData,
  organizeParameters receives ParameterInfoStruct[] directly
- device.ts: tmpParamValues typed ParameterValueStruct[], organizeParameters
  signature updated, updateFromValues typed
- device_id.ts: fromInform typed with DeviceIdStruct
- soap_envelope.ts: remove unconditional <cwmp:noMoreRequests> header
  (CPE→ACS only per TR-069 §3.7.1.4)
2026-07-20 15:25:26 +02:00
Kevin L'Hereec 8969355688 feat: improve critical reads, device list and modelPaths bug fix
Build & Deploy / build (push) Successful in 34s
Build & Deploy / docker (push) Successful in 1m4s
Build & Deploy / deploy (push) Successful in 32s
- main_state_machine.ts: add to CriticalReadsState GPV:
  ProvisioningCode (confirm CPE persisted value), UpTime (detect reboots),
  ManagementServer.URL (verify ACS URL), ConnectionRequestUsername/Password
  (ensure wake credentials are always available)
- Home.vue: add Last Seen column (device.updatedAt)
- pages_controller.ts: fix modelPaths bug — was never passed to DeviceDetail,
  now auto-detected from dataModel and passed as predefined TR-181/TR-098 paths
2026-07-17 16:57:08 +02:00
Kevin L'Hereec 7d603da0ca fix: Connection Request fallback to IPv4 when CPE has IPv6-only URL
Build & Deploy / build (push) Successful in 30s
Build & Deploy / docker (push) Successful in 57s
Build & Deploy / deploy (push) Successful in 24s
- device_record.ts: add lastRemoteIp field to store the CPE public IP
  from each Inform (X-Forwarded-For or TCP remote IP)
- cwmp_controller.ts: persist lastRemoteIp on every Inform alongside
  lastPeriodicInformAt (single  operation)
- device_service.ts: sendWakeOnWlan() now tries the original URL first
  (may be IPv6), then falls back to a rebuilt URL using lastRemoteIp
  (IPv4 public IP) if the ACS server has no IPv6 connectivity
2026-07-17 16:45:30 +02:00
Kevin L'Hereec c3bc2902f0 fix: add missing /internal/references routes and clean isGpnAllowed()
Build & Deploy / build (push) Successful in 38s
Build & Deploy / docker (push) Successful in 1m4s
Build & Deploy / deploy (push) Successful in 31s
- start/routes.ts: mirror references routes into /internal group so the
  DeviceDetail.vue resolve buttons work (were returning 404)
- device.ts: isGpnAllowed() now clears cooldownUntil when transitioning
  open→half-open, avoiding stale date in persisted snapshots
2026-07-17 16:36:43 +02:00
Kevin L'Hereec 19caa1a06f feat: read PeriodicInformInterval and PeriodicInformEnable in critical reads
Build & Deploy / build (push) Successful in 33s
Build & Deploy / docker (push) Successful in 1m4s
Build & Deploy / deploy (push) Successful in 24s
Add Device.ManagementServer.PeriodicInformInterval and
Device.ManagementServer.PeriodicInformEnable to the GPV performed
during CriticalReadsState (applies to both TR-181 and TR-098).
2026-07-17 16:31:33 +02:00
Kevin L'Hereec 26ffcbea6b fix: avoid GPN on root path Device. which returns the full parameter tree
Build & Deploy / build (push) Successful in 33s
Build & Deploy / docker (push) Successful in 57s
Build & Deploy / deploy (push) Successful in 33s
Two complementary fixes:
- IdleState: skip root-level parent paths (Device., InternetGatewayDevice.)
  when building the GPN queue. Params like Device.RootDataModelVersion have
  Device. as parent but sending GPN on Device. with NextLevel=0 returns the
  entire TR-181 tree in one response (potentially several MB).
  These root-level params are classified via deeper subtree GPN or catalog.
- task_queue: use NextLevel=1 for root paths (1 segment) as a safety net,
  NextLevel=0 for deeper paths to get all leaves of that subtree.
2026-07-17 16:29:29 +02:00
Kevin L'Hereec cce14d7039 fix: increase raw body limit to 4mb for CWMP XML payloads
Build & Deploy / build (push) Successful in 35s
Build & Deploy / docker (push) Successful in 1m7s
Build & Deploy / deploy (push) Successful in 36s
GetParameterNamesResponse from CPEs with many parameters exceeds the
default 56kb raw body limit. Add a 'raw' section in bodyparser config
covering text/xml, application/xml and application/soap+xml with a 4mb
limit to handle large TR-069 discovery responses.
2026-07-17 16:19:50 +02:00
Kevin L'Hereec 61bfb4e762 fix: rebind CWMP session when CPE sends empty POST on new TCP port
Build & Deploy / build (push) Successful in 33s
Build & Deploy / docker (push) Successful in 1m1s
Build & Deploy / deploy (push) Successful in 33s
TR-069 CPEs routinely open a new TCP connection (new port) for the empty
POST that follows InformResponse. ensureSessionForRemote() only did an
exact remoteKey match, so hasSession() returned false and the state
machine was never continued — GPN discovery was never triggered.

Add IP-only fallback in ensureSessionForRemote(): when the exact
remoteKey is not found, scan RUNNING machines for a unique IP match,
rebind the new remoteKey, and remap sessionMachines to the new sessionId.
Ambiguous cases (NAT/CGNAT with multiple active devices on same IP)
return false safely.
2026-07-17 16:14:38 +02:00
Kevin L'Hereec 7835f99657 fix: correct CWMP session management side effects
Build & Deploy / build (push) Successful in 29s
Build & Deploy / docker (push) Successful in 1m0s
Build & Deploy / deploy (push) Successful in 34s
- state_machine_manager: add deviceToRemotes reverse index (Map<deviceKeyJson, Set<remoteKey>>)
  so completeMachine/failMachine clean up remoteToDevice in O(1) — fixes unbounded memory leak
- state_machine_manager: getHashIdForRemoteIp() now only matches RUNNING sessions and
  returns undefined when multiple candidates exist (NAT/CGNAT safety)
- state_machine_manager: IPv6 bracket normalization in IP comparison
- cwmp_controller: apply IP fallback to TransferComplete and generic response handler
  (same new-TCP-port scenario as empty POST after firmware reboot)
- cwmp_logger_middleware: reuse request.cwmp.sessionId instead of recomputing
  from socket to avoid formula drift
2026-07-17 15:39:41 +02:00
Kevin L'Hereec d44d896556 fix: persist empty POST in TR-069 trace when CPE uses a new TCP port
Build & Deploy / build (push) Successful in 38s
Build & Deploy / docker (push) Successful in 1m5s
Build & Deploy / deploy (push) Successful in 23s
When a CPE sends an empty POST (polling) on a new TCP connection, the
remoteKey (IP:port) differs from the one registered during Inform.
Add StateMachineManager.getHashIdForRemoteIp() to find the device by
IP alone as a fallback, so cwmpDeviceId is resolved and the exchange
is properly stored in the trace (cwmpMethod: Empty, dashed arrow).
2026-07-17 14:24:44 +02:00
Kevin L'Hereec 3bb85d9031 fix: support alias-based IPv6 interface names and add engine reset utility
Build & Deploy / build (push) Successful in 30s
Build & Deploy / docker (push) Successful in 1m4s
Build & Deploy / deploy (push) Successful in 35s
- Home.vue: extend RE_IPV4/RE_IPV6 to match alias-based interface names
  like Device.IP.Interface.[BR_LAN].IPv6Address.[GUA_LAN].IPAddress
  (used by Sagemcom Livebox 4 with AliasBasedAddressing=1)
- reference_engine.ts: add resetReferenceEngine() to allow config reload
  without restart (wassup provider disabled via config/references.json)
2026-07-17 14:21:02 +02:00
Kevin L'Hereec 1fc9715888 fix: display IPv6 address in device table and disable wassup provider
Build & Deploy / build (push) Successful in 31s
Build & Deploy / docker (push) Successful in 1m3s
Build & Deploy / deploy (push) Successful in 24s
- Home.vue: add getIpAddress() with IPv4/IPv6 fallback chain (TR-098, TR-181)
  hoisted regex constants, prefer interface index 1, format IPv6 with brackets
- config/references.json: disable wassup provider (mock URL unreachable)
2026-07-17 11:57:09 +02:00
kevin.lhereec b12141792f fix: supprimer import logger inutilise dans cwmp_middleware (TS6133)
Build & Deploy / build (push) Successful in 40s
Build & Deploy / docker (push) Successful in 1m6s
Build & Deploy / deploy (push) Successful in 25s
2026-07-17 10:39:48 +02:00
kevin.lhereec 7932b43840 chore: passer LOG_LEVEL en debug pour le prochain deploiement
Build & Deploy / build (push) Failing after 34s
Build & Deploy / docker (push) Skipped
Build & Deploy / deploy (push) Skipped
2026-07-17 10:38:29 +02:00
kevin.lhereec 5ea25d462e feat: logs debug sur les echanges XML CWMP
Build & Deploy / build (push) Failing after 36s
Build & Deploy / docker (push) Skipped
Build & Deploy / deploy (push) Skipped
- cwmp_middleware: log XML brut recu (bodyPreview 500 chars), contentType,
  bodyLength, et message extrait (type, cwmpVersion, cwmpId, messageKeys)
  + log body vide (CPE polling)
- cwmp_logger_middleware: log XML pretty-print complet CPE->ACS et ACS->CPE
  en debug, independamment du statut trace
- cwmp_controller: log routage message (messageType), InformResponse,
  TransferCompleteResponse, reponses state machine avec longueur
2026-07-17 10:36:57 +02:00
kevin.lhereec ebe088a179 fix: couleur bandeau trace orange -> pink
Build & Deploy / build (push) Successful in 35s
Build & Deploy / docker (push) Successful in 55s
Build & Deploy / deploy (push) Successful in 22s
2026-07-17 10:33:23 +02:00
kevin.lhereec b00c0c94a8 feat: suppression des commandes PENDING depuis /commands
Build & Deploy / build (push) Successful in 40s
Build & Deploy / docker (push) Successful in 1m4s
Build & Deploy / deploy (push) Successful in 26s
- Ajout de destroy() dans CommandsController (DELETE /commands/:id)
- Routes DELETE /commands/:id dans les groupes /api/v1 et /internal
- Bouton supprimer dans Commands.vue visible uniquement sur les commandes PENDING
- Utilise fetchWithCsrf pour la protection CSRF
2026-07-17 10:31:56 +02:00
kevin.lhereec b22d3e9b56 fix: correction subnet IPv6 ULA (fd00:adam:: invalide -> fd00:ad4a::)
Build & Deploy / build (push) Successful in 33s
Build & Deploy / docker (push) Successful in 15s
Build & Deploy / deploy (push) Successful in 4s
2026-07-17 10:22:06 +02:00
kevin.lhereec 8cddf98093 fix: CSRF token sur les requetes mutantes /internal + IPv6 Docker + tests Connection Request
Build & Deploy / build (push) Successful in 30s
Build & Deploy / docker (push) Successful in 1m6s
Build & Deploy / deploy (push) Failing after 33s
- Ajout de fetchWithCsrf() helper qui lit le cookie XSRF-TOKEN et injecte
  X-XSRF-TOKEN sur toutes les requetes POST/PUT/PATCH/DELETE vers /internal/*
- Remplacement de fetch() par fetchWithCsrf() dans tous les composants Vue
  concernes (Tr069Trace, DeviceDetail, SequenceViewer, Home, configurations,
  firmware_catalog)
- Activation IPv6 sur les reseaux Docker (enable_ipv6: true, subnet fd00:adam::/64)
  dans docker-compose.yml et docker-compose.prod.yml
- Ajout tests unitaires DeviceService.sendWakeOnWlan : IPv4, IPv6, ENETUNREACH,
  credentials fallback chain (11 tests)
2026-07-17 10:16:29 +02:00
Kevin L'Hereec 228d382cbf fix: login page standalone sans layout (null layout respecté par Inertia) 2026-07-03 18:42:39 +02:00
Kevin L'Hereec 3693d0c0a5 refactor(base-model): remove unused instance methods save/delete/refresh and instance fields 2026-07-03 15:43:54 +02:00
Kevin L'Hereec 991173aa05 feat(auth): add session login page, API key guard, and remove orphan SQL migrations
- Add session-based authentication for web-UI (Login.vue, AuthController)
- Add AuthSessionMiddleware: redirects unauthenticated users to /login
- Add ApiKeyMiddleware: validates X-Api-Key header on all /api/v1 routes
- Add BasicAuthMiddleware: registered for future M2M use
- Use timingSafeEqual for all credential comparisons (timing attack prevention)
- Regenerate session ID on login (session fixation prevention)
- Validate redirect target after login (open redirect prevention)
- Require WEBUI_USERNAME, WEBUI_PASSWORD, API_KEY as mandatory env vars
- Share auth.username/authenticated via Inertia shared props
- Add user dropdown in navbar (avatar icon + username + logout)
- Add login i18n keys in fr.json and en.json
- Remove orphan SQL migrations (users, auth_access_tokens) and @adonisjs/lucid
2026-07-03 13:59:05 +02:00
Kevin L'Hereec d0a4f8fb30 Merge branch 'master' into atx-result-staging-20260608_080351_ed7fbc81
# Conflicts:
#	tests/unit/services/cwmp/config_eval_extra.spec.ts
#	tests/unit/services/cwmp/main_state_machine_extended.spec.ts
#	tests/unit/services/cwmp/main_state_machine_extra.spec.ts
#	tests/unit/services/cwmp/task_queue.spec.ts
2026-06-08 16:58:33 +02:00
Kevin L'Hereec ec7e66555d fix 2026-06-08 16:54:37 +02:00
Kevin L'Hereec 44f7d9acfc fix 2026-06-08 16:46:38 +02:00
Kevin L'Hereec dae82eeff2 fix 2026-06-08 16:35:33 +02:00
Kevin L'HereecandContinue 08c620ee68 fix: drain task queue on session completion to stop orphan CWMP requests
After OperationalState, the state machine was marked completed but leftover
tasks in the CWMPTaskQueue (queued by ConfigurationEvaluationState callbacks)
were being served to the CPE on subsequent empty POSTs via the continue()
task fallback path, causing spurious GetParameterNames + SetParameterValues
exchanges after the session officially ended.

Fix: call taskQueue.clearPendingTasksForDevice() in completeMachine() so no
tasks bleed into the next session.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 14:20:59 +00:00
Kevin L'HereecandContinue 92a3d992de test: add 2 unit tests for GPN response processing in DiscoveringParametersState
- 'processes GPN response and stores parameters': verifies that writable params
  go to device.parameters, read-only to device.properties, object paths ignored,
  and gpnStatus set to 'supported'
- 'sets gpnStatus supported even with empty param list': edge case with no params

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 14:14:02 +00:00
Kevin L'HereecandContinue 7540327689 fix: process GPN response in DiscoveringParametersState
The GPN response (ParameterInfoStruct list) was received but never
processed — organizeParameters() was never called, so discovered
parameters were lost and gpnStatus was never set to 'supported'.

Now on each successful GPN response:
- device.organizeParameters() is called to store leaf params/props
- device.setGpnStatus('supported') marks the device as GPN-capable

This fixes: parameters not showing in UI, wrong dataModel detection,
and PeriodicInformInterval not appearing after SPV.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 14:13:09 +00:00
Kevin L'HereecandContinue 4a035dbc67 fix: correct 2 failing tests in executing_commands.spec.ts
- 'handles unknown command type': missing state.handle() call before assertions,
  and logger.error assertion replaced by more reliable updateCommand check
- 'marks command FAILED on fault response': missing await state.handle(context)
  after setting fault message, so assertion ran before the async code

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 16:03:10 +02:00
Kevin L'Hereec 08f4bb1ea5 fix 2026-06-08 16:03:03 +02:00
Kevin L'HereecandContinue 5f848bf500 fix: correct 2 failing tests in executing_commands.spec.ts
- 'handles unknown command type': missing state.handle() call before assertions,
  and logger.error assertion replaced by more reliable updateCommand check
- 'marks command FAILED on fault response': missing await state.handle(context)
  after setting fault message

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 14:02:08 +00:00
Kevin L'HereecandContinue 38ab615300 fix: correct 2 failing tests in executing_commands.spec.ts
- 'handles unknown command type': missing state.handle() call before assertions,
  and logger.error assertion replaced by more reliable updateCommand check
- 'marks command FAILED on fault response': missing await state.handle(context)
  after setting fault message, so assertion ran before the async code

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 14:01:39 +00:00
Kevin L'Hereec aa164fd47e fix 2026-06-08 16:00:08 +02:00
Kevin L'HereecandContinue ef30c2159c fix: isolate integration test databases to prevent parallel teardown conflicts
Each integration test file now uses its own MongoDB database:
- ttl_expiration → adam-acs-ttl-test
- device_model   → adam-acs-devices-test
- base_model     → adam-acs-basemodel-test

Prevents dropDatabase() in one afterAll from destroying data
used by another test file running concurrently.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:27:16 +00:00
Kevin L'HereecandContinue b3142a8272 fix: create collection before createIndex in TTL integration test
MongoDB throws 'ns does not exist' when calling indexes() on a collection
that has never had documents or been explicitly created. Added createCollection
if not already present before creating the TTL index.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:25:14 +00:00
Kevin L'HereecandContinue 9f155c5ef4 fix: correct 2 failing tests
- state_machine_manager_coverage: remove duplicate assertion expecting 'idle',
  state can legitimately be 'critical-reads' when gpnStatus=unsupported
- integration/device_model + ttl_expiration: add MongoDB connection guard
  (skip tests when no local DB available, timeout 2s)

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:23:31 +00:00
Kevin L'Hereec b0a3f52df7 fix 2026-06-08 15:15:24 +02:00
Kevin L'HereecandContinue ca24b469d3 fix: allow uppercase letters in configuration identifier regex
Previously /^[a-z0-9]+(-[a-z0-9]+)*$/ rejected identifiers like 'BM632w-standard'.
Updated to /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/ to allow mixed case.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:14:04 +00:00
Kevin L'HereecandContinue 30211d278d fix: map plural API keys (manufacturers/ouis/productClasses) to singular suggestion fields in CriteriaEditor and FirmwareCatalog forms
Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:07:43 +00:00
Kevin L'HereecandContinue aad93911e4 feat: TR-069 CWMP state machine hardening + exchange viewer improvements
- TR-069 CWMP state machine hardening (14 phases): shared event utility,
  multi-event routing, GPN circuit breaker, IdleState GPN guard, expanded
  GPN fault codes, CriticalReadsState, ConfigEval GPN guard, task queue
  race condition fix, explicit error handling, terminal states
  (OperationalState/PartialOperationalState), consolidated reclassify,
  crash pattern detection, removed enqueueImmediate, state flow wiring
- Fix SOAP Fault not processed by state machine (soap-env:Fault detection)
- Fix GPN circuit breaker not persisting after session drops
- Fix CriticalReadsState GPV response parsing (xsi:type normalization)
- Fix CriticalReadsState not processing GPV response values
- Skip SPV when device is already compliant (ProvisioningCode check)
- Add Device.DeviceInfo.ModelName to critical reads GPV
- Fix TypeError on DeviceDetail parameters view (null guards)
- Log empty 204 responses in exchange trace UI
- TR-181 catalog: type corrections and new entries (+6640 lines)
- SequenceViewer: replace session-nav buttons with compact dropdown select
- SequenceViewer: fix CPE/ACS lifeline headers wrapping to 2 lines
- Add 20 unit tests for state machine fixes (SOAP Fault, GPN circuit
  breaker, CriticalReads, stale session, ConfigEval/ComplianceUpdate skips)

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 12:49:00 +00:00
Kevin L'Hereec dabe15839d fix: handle GPN Fault in DiscoveringParametersState
When the CPE responds with a CWMP Fault to GetParameterNames,
mark gpnStatus as 'unsupported' and continue to ConfigurationEvaluationState
instead of hanging the session.
2026-06-03 15:56:33 +02:00
Kevin L'Hereec 0c83a4ccb2 feat: TR-069 Exchange Viewer - structured MongoDB storage with SSE streaming and sequence diagram UI
- 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
2026-06-03 15:52:44 +02:00
Kevin L'Hereec 3fefbf6c40 test: 16 tests for typed compliance comparison (numeric, boolean, string) 2026-06-03 08:56:12 +02:00
Kevin L'Hereec 478439783d feat: catalog type enrichment + typed compliance comparison
- Add type field to all 7727 entries in tr181-catalog.json (xsd:string/unsignedInt/boolean/int)
- reclassifyDeviceFromCatalog applies catalog type when device type is UNKNOWN
- Export getCatalogType() utility
- Compliance comparison: numeric types compared as Number, boolean as bool, fallback to String
- Handle NaN gracefully (fallback to string comparison)
2026-06-03 08:53:31 +02:00
Kevin L'Hereec 046a2b09c2 task: catalog type field is mandatory (per TR-181 norm) 2026-06-02 18:33:30 +02:00
Kevin L'Hereec 665e4a864e task: plan catalog type enrichment (TR-181 xsd types) 2026-06-02 18:32:02 +02:00
Kevin L'Hereec ce4a857bb1 fix: compliance comparison uses String() to handle number vs string mismatch 2026-06-02 18:29:10 +02:00
Kevin L'Hereec 8265fb7f0f ui: references properties all on one row (dynamic col width) 2026-06-02 18:19:04 +02:00
Kevin L'Hereec 670ed860aa feat: initialize lastPeriodicInformAt on BOOTSTRAP too 2026-06-02 18:16:16 +02:00
Kevin L'Hereec d9b3a1b810 fix: resolve flaky pagination integration test (isolated collection) 2026-06-02 18:10:35 +02:00
Kevin L'Hereec f0670224f3 feat: periodic inform utility + 3 dates on same line
- Extract computeNextPeriodicInform to app/utils/periodic_inform.ts
- 11 unit tests for the periodic computation logic
- DeviceDetail: 3 date columns on same row (created, last seen, next estimated)
2026-06-02 18:06:18 +02:00
Kevin L'Hereec 75436ad2f4 style: formatting fixes (prettier compliance) 2026-06-02 18:03:19 +02:00
Kevin L'Hereec 7882a49d71 refactor: move dynamic imports to file headers
- getReferenceEngine import moved to top of cwmp_controller
- Fix makeContext type in configuration_evaluation_state tests
2026-06-02 17:58:07 +02:00
Kevin L'Hereec 31578e368f 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
2026-06-02 17:56:17 +02:00
Kevin L'Hereec e1e8ae5097 refactor: move validator imports to file header 2026-06-02 17:12:24 +02:00
Kevin L'Hereec 0cb8e12466 refactor: wire validators into controllers
- configurations_v1_controller: use createConfigurationValidator/updateConfigurationValidator
- firmware_catalog_controller: use createFirmwareEntryValidator/updateFirmwareEntryValidator/createMigrationRouteValidator
- Remove dead isModeType/parseMode from mode.ts
- Remove dead NAMESPACES from soap.ts
2026-06-02 17:09:15 +02:00
Kevin L'Hereec 823427cdbe refactor: remove dead code
- Remove unused NAMESPACES export from soap.ts
- Remove unused isModeType/parseMode from mode.ts (device_service does inline parsing)
- Remove flaky model unit tests (covered by integration tests)
- Keep validators (to be wired into controllers later)
2026-06-02 17:05:58 +02:00
Kevin L'Hereec 5759809145 test: 80%+ line coverage achieved (512 tests)
- ComplianceUpdateState: provisioning code write, transition to executing-commands
- ConfigurationEvaluationState: full happy path with all phases
- Fix flaky integration tests (isolated collections)
- Coverage: 80.64% lines, 79.26% statements, 512 tests
2026-06-02 16:56:47 +02:00
Kevin L'Hereec 818c661f21 test: 508 tests, 79% line coverage - config eval happy path
- Full happy path test for ConfigurationEvaluationState
- Fix flaky integration tests
- Coverage stable at 79% lines
2026-06-02 16:54:04 +02:00
Kevin L'Hereec 575975e211 test: 507 tests, 79% line coverage — near 80% threshold
- Device domain: full coverage of organizeParameters, updateFromValues
- MainStateMachine: full lifecycle test, error handling, GPN failure
- Coverage: 52% → 79% lines (507 tests)
2026-06-02 16:49:43 +02:00
Kevin L'Hereec 12a1787151 test: 492 tests, 78% line coverage
- Device domain: updateFromValues, organizeParameters, identifier
- BaseModel: full CRUD mock tests
- DeviceModel: all methods mocked
- Other models: collectionName verification
- MainStateMachine: start, handleResponse, state transitions
- ConfigurationEvaluationState: init, GPN flow, GPN-unsupported skip
- MongodbProvider: getMongoDb guard
- device_utils: hash determinism
- vitest.config.ts: coverage scope (excludes HTTP layer)
- 492 tests passing, 78% line coverage
2026-06-02 16:45:19 +02:00
Kevin L'Hereec ab21f11b30 test: CWMP task_queue, device_action_executor, reference_engine tests
- task_queue: task lifecycle, events, all SOAP builders, clear pending
- device_action_executor: all action methods, callbacks (GPV/GPN/SPV)
- reference_engine: resolveAll, resolveOne, trigger matching, numeric events
- Coverage: 52% → 67% statements, 428 tests passing
2026-06-02 16:35:08 +02:00
Kevin L'Hereec 003ef50a8e test: improve coverage — domain, protocol, references, integration DB tests
- Domain: DeviceId, Command, DeviceParameter, DeviceProperty, Mode
- Protocol: SOAP parsing, CWMPMessageBuilder (all methods)
- References: config_loader, WassupProvider (timeout, errors)
- Integration: MongoDB tests (CRUD, pagination, references)
- Fix: Home.vue IP lookup for Device.WANDevice path
- Total: 384 tests passing
2026-06-02 16:29:19 +02:00
Kevin L'Hereec e857b4fbe7 feat: reference providers system with Wassup integration
- Pluggable multi-provider architecture (ReferenceEngine)
- Wassup provider implementation (HTTP GET + X_WASSUP_USER_IP)
- Auto-resolve references on Inform (BOOT/BOOTSTRAP events)
- References displayed in device Identity card
- REST API: GET/POST references per device
- Config via config/references.json
- Fix event extraction for single EventStruct and numeric EventCodes
- Mock Wassup provisioned by home-device-simulator on device creation
2026-06-02 15:52:16 +02:00
Kevin L'Hereec 4eb3f0795a feat: immediate mode (I) commands via Connection Request
- Hide Refresh button when ConnectionRequestURL is unavailable
- Fix ImmediateCommandStateMachine session lifecycle (mark complete when no commands)
- Fix preemption: new CONNECTION REQUEST replaces stale immediate session
- Protect ensureSessionForRemote from rebinding to completed machines
- Normalize numeric event codes from simulators (6 -> 6 CONNECTION REQUEST)
- Extract updatePendingCommandsStatus for clarity
2026-06-02 14:00:31 +02:00
Kevin L'Hereec 713a043625 ui: remove diffs column from compliance table 2026-06-02 10:32:25 +02:00
Kevin L'Hereec bf98c9f405 feat: dry-run modal on non-compliant badge click
Replace the separate Dry Run tab with a modal triggered by clicking
a non-compliant badge in the Compliance tab. Shows parameter diffs,
firmware path, and a link to the device page.
2026-06-02 10:30:23 +02:00
Kevin L'Hereec 0109bca236 fix: show ExternalIPAddress from properties in device table 2026-06-01 17:35:38 +02:00
Kevin L'Hereec bc9ac4f125 fix: state machine timing + reclassification tests
- Fix state machine start timing: don't call start() in startStateMachine,
  let the CPE's first empty body trigger the state machine via continue().
  This ensures GPN is sent at the correct point in the TR-069 session.
- Add unit tests for reclassifyDeviceFromCatalog (10 tests)
- Add unit tests for MainStateMachine transitions and GPN callbacks (11 tests)
- Test both scenarios: Huawei (GPN supported) and Livebox (GPN unsupported + fallback)
- Fix tr069_trace_service test path assertion (config/ → data/)
- Update steering doc with Livebox bootstrap command
2026-06-01 17:31:33 +02:00
Kevin L'Hereec a5b3135396 ok maybe 2026-06-01 16:58:52 +02:00
Kevin L'Hereec ea73e91aa4 trace + backup propertie 2026-06-01 16:20:58 +02:00
Kevin L'Hereec b3b1edec17 ok 2026-06-01 15:49:09 +02:00
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
Kevin L'HereecandCopilot 847da1cdf5 feat: per-device TR-069 SOAP trace logging
- Tr069TraceService: in-memory Map O(1) lookup, Promise-queue per device
  for sequential file writes, 10MB rotation (5 files kept)
- config/tr069-trace.json: persisted list of traced devices (atomic write)
- CwmpLoggerMiddleware: buffers request+response, writes to device log file
  after next() once ctx.cwmpDeviceId is set by controller
- CwmpController: sets ctx.cwmpDeviceId on all code paths (Inform + others)
- StateMachineManager: getHashIdForRemote() for non-Inform messages
- REST API: GET/POST/DELETE /api/v1/tr069-trace
- UI: Tr069Trace Vue page with device search, add/remove tracing
- 8 new unit tests (192 total, all passing)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 12:07:07 +02:00
Kevin L'HereecandCopilot a20aa81cfd chore: add simulator docker-compose overlay
Integrates home-device-simulator as optional Docker services.

Usage:
  docker compose -f docker-compose.yml -f docker-compose.simulator.yml up

Services added:
  - simulator  : backend Go TR-069 sur port 9898, partage mongodb et réseau ACS
  - simulator-ui: UI nginx sur port 3000, env injecté via config/simulator-ui-env.js

Flux:
  simulator → ACS via http://app:8080/cwmp10 (réseau Docker interne)
  ACS → simulator Connection Request via http://simulator:9898/...

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 11:37:51 +02:00
Kevin L'HereecandCopilot 9932f69345 chore: docker-compose update
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 11:32:31 +02:00
Kevin L'HereecandCopilot 3394abd5ec feat(ui): compliance report, configuration list improvements
- DeviceDetail: display compliance status with badge + parameter diffs
- Home: show compliance badge in device list
- ConfigurationDetail: compliance report tab with device list + diffs
- ConfigurationEdit: bumpType selector (patch/minor/major), remove version field
- ConfigurationsList: tag filter, name search, compliance column
- ComplianceBadge: pending/compliant/in_progress/failed/non_compliant states
- CriteriaEditor: parameterCriteria editor with autocomplete on parameter names
- AutocompleteInput: new reusable autocomplete component
- i18n: compliance labels in fr/en

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 11:32:26 +02:00
Kevin L'HereecandCopilot aff8e14a63 feat: compliance report + auto-versioning for configurations
- Add ComplianceStatus.PENDING and configVersion to DeviceComplianceInfo
- Add ComplianceReport + ComplianceDeviceResult types to ConfigurationEngine
- Expose evaluateSpecific() as public (used by compliance + dry-run)
- Add complianceReport() method: filter matching devices, evaluate each
- Fix matchesCriteria: skip parameterCriteria entries with null/empty name
- Fix getDeviceParameterValue: fallback to plain object arrays (DeviceRecord)
- Fix findDevice: support lookup by both identifier and deviceId.id
- ConfigurationService.update: version is now auto-managed (no user input)
  - behavioral changes (criteria/params/policy/priority/…) → patch bump
  - bumpType field allows 'patch' | 'minor' | 'major'
  - cosmetic changes (name/tags) → no bump
- Add sanitizeCriteria: filter null/empty parameterCriteria names silently
- Add isBehavioralChange helper
- Add DeviceModel.resetComplianceForConfiguration call on behavioral update
- Add ConfigurationModel.distinctTags() + controller endpoint GET /tags
- Add controller endpoint GET /configurations/:id/compliance
- Add DeviceModel.getDistinctValues for inventory fields
- Fix unit tests: mock DeviceModel, remove obsolete version-validation test
- Add 14 new unit tests (engine: 5, service: 9)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 11:32:17 +02:00
Kevin L'HereecandCopilot b8ff4b68a7 feat: TR-181 parameter writability resolution
- Add GpnStatus type ('supported' | 'unsupported' | 'unknown') to Device domain
- Persist gpnStatus in DeviceRecord and all 3 persist paths
  (cwmp_controller, main_state_machine, device_action_executor)
- Capture Fault 9000/9001 in getParameterNames → mark device as 'unsupported'
- Fix GPN NextLevel flag: false (non-recursive shallow scan)
- Add static TR-181 catalog (7 727 params, sourced from BBF cwmp-data-models)
- Add parameter_writability service with resolveWritability():
  supported → live data; unsupported → catalog → default R/W;
  unknown → catalog → null (genuinely unknown)
- Add scripts/generate-tr181-catalog.mjs to regenerate catalog from BBF XML
- Add design doc: docs/design-parameter-writability.md
- Add unit tests: 26 tests covering normalizePath and all gpnStatus branches

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 11:25:15 +02:00
Kevin L'HereecandCopilot d1c65dbf1e chore: initial commit — AdonisJS v7 TR-069 ACS
- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim)
- AdonisJS v7 app with Inertia.js + Vue 3
- MongoDB integration
- CWMP/TR-069 state machine engine
- Configuration engine with compliance tracking
- Branding: ADAM ACS (favicon, navbar, locales)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 16:46:37 +02:00