feat: add device deletion endpoint with cascade cleanup and admin UI integration

Add DELETE /admin/devices/{id} endpoint with cascade deletion of device records, WireGuard peers, IP allocations, and device access profile settings. Update device status to 'deleted' and set deleted_at timestamp while preserving revoked_at if already set.

Add deleteDevice API method and delete button to devices page with query invalidation for both devices and device-profile lists. Record admin.device.deleted audit
This commit is contained in:
2026-03-19 22:59:07 +01:00
parent a8a88140af
commit b199b58840
7 changed files with 82 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ type Repository interface {
GetSelectedProfileID(ctx context.Context, deviceID uuid.UUID) (*uuid.UUID, error)
SetSelectedProfileID(ctx context.Context, deviceID uuid.UUID, profileID uuid.UUID) error
Revoke(ctx context.Context, deviceID uuid.UUID) error
Delete(ctx context.Context, deviceID uuid.UUID) error
Rotate(ctx context.Context, deviceID uuid.UUID) error
}
@@ -292,6 +293,44 @@ func (r *PGRepository) Revoke(ctx context.Context, deviceID uuid.UUID) error {
return tx.Commit(ctx)
}
func (r *PGRepository) Delete(ctx context.Context, deviceID uuid.UUID) error {
tx, err := r.db.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
if _, err := tx.Exec(ctx, `
update devices
set status = 'deleted', deleted_at = now(), revoked_at = coalesce(revoked_at, now()), updated_at = now()
where id = $1 and deleted_at is null
`, deviceID); err != nil {
return err
}
if _, err := tx.Exec(ctx, `
update wireguard_peers
set deleted_at = now(), updated_at = now()
where device_id = $1 and deleted_at is null
`, deviceID); err != nil {
return err
}
if _, err := tx.Exec(ctx, `
update ip_allocations
set status = 'released', released_at = coalesce(released_at, now()), updated_at = now()
where device_id = $1 and status = 'allocated'
`, deviceID); err != nil {
return err
}
if _, err := tx.Exec(ctx, `
delete from settings
where category = 'device_access_profile' and key = $1
`, deviceID.String()); err != nil {
return err
}
return tx.Commit(ctx)
}
func (r *PGRepository) Rotate(ctx context.Context, deviceID uuid.UUID) error {
_, err := r.db.Exec(ctx, `
update wireguard_peers