feat(server): operator removal of stale sessions/machines (SPEC-004 Task 5, server)
All checks were successful
All checks were successful
Admin-gated soft-delete + purge so operators can clear ghost machines/sessions (the ~15-rows-for-one-host accumulation) from the console. - migration 009: deleted_at on connect_sessions + connect_machines, with partial indexes WHERE deleted_at IS NULL. - DELETE /api/machines/:agent_id?purge=true and DELETE /api/sessions/:id?purge=true soft-delete the row and purge the in-memory session (remove_session); the non-purge path keeps the legacy hard-delete / live-only disconnect. POST /api/machines/bulk-remove handles multi-select (batch cap 500). All admin-gated (AdminUser -> 403; tightens the prior any-user delete) and audited to connect_session_events (actor + target + trusted client IP). - list/get queries filter deleted_at IS NULL so removed units leave the console; upsert revives (deleted_at = NULL) a genuinely-reconnecting machine. The keyed-reattach identity resolver (get_machine_by_id) is intentionally unfiltered. Dashboard removal UI is the A3b follow-up. 86 server tests pass; fmt/clippy/test clean. Implements specs/v2-stable-identity/plan.md Task 5 (server portion). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
43
server/migrations/009_session_machine_soft_delete.sql
Normal file
43
server/migrations/009_session_machine_soft_delete.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
-- Migration: 009_session_machine_soft_delete.sql
|
||||
-- Purpose: Give connect_machines and connect_sessions a soft-delete marker
|
||||
-- (deleted_at) so an operator can PURGE a stale machine/session —
|
||||
-- removing it from the live console — without destroying its audit
|
||||
-- history (SPEC-004 / v2-stable-identity Task 5).
|
||||
--
|
||||
-- Task 5 is the operator-removal mechanism that finally purges the ~14 live
|
||||
-- ghost connect_machines rows left by the duplicate-registration bug. The
|
||||
-- live-only admin "disconnect" (DELETE /api/sessions/:id) and the legacy hard
|
||||
-- DELETE /api/machines/:agent_id stay as they were; the NEW purge path
|
||||
-- (`?purge=true`) sets deleted_at, drops the in-memory session via
|
||||
-- SessionManager::remove_session, and writes an audit row. A soft delete keeps
|
||||
-- the row (and its connect_session_events history) for the audit trail per the
|
||||
-- project convention "prefer deleted_at over hard deletes" (CLAUDE.md), while
|
||||
-- every list/get query filters `deleted_at IS NULL` so the purged unit
|
||||
-- disappears from the dashboard and the startup reconcile never restores it.
|
||||
--
|
||||
-- Idempotent: ADD COLUMN IF NOT EXISTS. The columns are NULLABLE with no default,
|
||||
-- so adding them is a metadata-only change on Postgres (no table rewrite, no row
|
||||
-- locks held for a scan) — safe to apply online. A NULL deleted_at means "live";
|
||||
-- a non-null timestamp means "removed at that instant". Applied on server startup
|
||||
-- by sqlx::migrate!(); never pre-applied via psql. Ordered after 008.
|
||||
-- See .claude/standards/gururmm/sqlx-migrations.md.
|
||||
|
||||
-- 1. connect_sessions.deleted_at: when set, the session was operator-purged and is
|
||||
-- excluded from every list/get query. NULL = live.
|
||||
ALTER TABLE connect_sessions ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
-- 2. connect_machines.deleted_at: when set, the machine was operator-purged. This
|
||||
-- is the marker that hides the ghost duplicate rows from /api/machines and the
|
||||
-- startup reconcile. NULL = live.
|
||||
ALTER TABLE connect_machines ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
-- 3. Partial indexes so the hot "live rows only" filter (deleted_at IS NULL) used
|
||||
-- by every list query stays an index scan as the soft-deleted set grows. The
|
||||
-- predicate matches the WHERE clause the queries use.
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_machines_live
|
||||
ON connect_machines (hostname)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_sessions_live
|
||||
ON connect_sessions (started_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
Reference in New Issue
Block a user