modbus: add explicit cascade delete (device + readings + toggles + HA cleanup)
frontend / frontend (push) Successful in 2m4s
pytest / test (push) Successful in 7m32s

A disabled device with historical readings still cannot be deleted by default
(409 guard stays, to prevent accidental data loss). A new explicit opt-in lets
the user remove it for real:

- DELETE /api/modbus/devices/{uuid}?cascade=true removes the device's readings
  and exposed_entity_toggle rows, then the device, in one transaction (FK order:
  readings before device). Best-effort clears the HA discovery configs first
  (MQTT not connected -> no-op, never blocks the delete). Default (no cascade)
  unchanged. Returns 200 with deletion counts.
- Frontend: a 'Force Delete (all data)' button appears only after the 409, with
  a red irreversible warning; cascade is sent only on that explicit action.
- Also corrected a stale comment: FK RESTRICT IS enforced at runtime now.
This commit is contained in:
2026-06-24 17:38:52 +02:00
parent d7f0731d1c
commit 682e06d256
11 changed files with 499 additions and 42 deletions
+31 -3
View File
@@ -1332,16 +1332,37 @@ paths:
description: 'Delete a Modbus device.
**Default behaviour (cascade=false)**:
Returns 409 Conflict if the device has any associated readings; use
``enabled=false`` to disable it instead.
**Application-layer safety**: the check is performed via an explicit
**Cascade delete (cascade=true)**:
SELECT COUNT query — not by relying on SQLite''s FK RESTRICT constraint,
Permanently deletes the device together with all its readings and any
which is not enforced at runtime in this project (no PRAGMA foreign_keys=ON).'
``ExposedEntityToggle`` rows whose key matches ``modbus.<uuid>.*``.
Also makes a best-effort attempt to clear the device''s HA Discovery
config topics from MQTT (empty retained payload) before the DB rows
are removed. MQTT failures are swallowed — the DB deletion proceeds
regardless.
Returns HTTP 200 with a ``ModbusDeleteResponse`` JSON body on success.
**Application-layer safety**: the 409 guard uses an explicit SELECT COUNT
query to return a friendly message. FK RESTRICT is enforced at runtime
(the app sets ``PRAGMA foreign_keys=ON``), so the cascade path deletes
readings before the device.'
operationId: delete_device_api_modbus_devices__uuid__delete
parameters:
- name: uuid
@@ -1350,6 +1371,13 @@ paths:
schema:
type: string
title: Uuid
- name: cascade
in: query
required: false
schema:
type: boolean
default: false
title: Cascade
- name: X-CSRF-Token
in: header
required: false