Phase 2: device registry, dashboard cards, manual lock/unlock, audit

- Device registry: add/edit name, child and platform, persisted in devices.
- Stable per-device topic derived from name + id, unchanged by renames.
- Desired state persisted in state; timed locks expire and are audited.
- Dashboard cards: name, child, platform, status dot, state and time left.
- Manual lock/unlock forms with CSRF; every change appends to audit (FR-15).
- Append-only activity list on the dashboard. No hard deletes (rule 10).
- Store and handler tests; version bumped to 0.2.0-phase2.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-10-13 14:18:02 +11:00
parent c83c9df0a3
commit 7c88d3f52b
15 changed files with 1674 additions and 60 deletions

View File

@@ -112,14 +112,7 @@ func migrationFiles() ([]migration, error) {
// Audit appends one row to the append-only audit log.
func (s *Store) Audit(ctx context.Context, actor, action string, deviceID *int64, detail string) error {
_, err := s.db.ExecContext(ctx,
`INSERT INTO audit (actor, action, device_id, detail) VALUES (?, ?, ?, ?)`,
actor, action, deviceID, detail,
)
if err != nil {
return fmt.Errorf("audit insert: %w", err)
}
return nil
return insertAudit(ctx, s.db, actor, action, deviceID, detail)
}
// CountDevices returns the number of registered devices (0 in Phase 1).