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.
- 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.
- 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
- 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
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.
- 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
- 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
- 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
Add Device.ManagementServer.PeriodicInformInterval and
Device.ManagementServer.PeriodicInformEnable to the GPV performed
during CriticalReadsState (applies to both TR-181 and TR-098).
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.
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.
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.
- 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
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).
- 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)
- 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
- 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
- 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)