9da913080f
Every incident-mutation handler read userFromContext(ctx) and wrote the result's .ID into acknowledged_by/incident_events.user_id without checking the ok bool. For a team-scoped service-account caller this returned a zero-value user id, which violated the users(id) FK and 500'd on acknowledge, unacknowledge, resolve, snooze, unsnooze and create-note. handleDeleteNote didn't crash but silently matched zero rows instead (WHERE user_id = 0), so a service account could never delete its own note. Add acknowledged_by_service_account_id (incidents) and service_account_id (incident_events) as nullable FKs to service_accounts(id), parallel to and mutually exclusive with the existing human columns (migration 015, with a CHECK enforcing the exclusion). Route every one of the six handlers plus delete-note through a new callerActorIDs() helper that branches on Caller.AsHuman()/ServiceAccountID() instead of assuming a human, and thread a serviceAccountID parameter through logEvent and the new acknowledgeIncidentAs (acknowledgeIncident itself is untouched: its only other caller, the push-notification Acknowledge button, is always human). Render the new actor distinctly from both a human and "the server acted" in the web UI's incident timeline and facts card. handleIncidentAssign, handleIncidentArchive and handleIncidentUnarchive are deliberately not touched here — they track no actor at all today, for anyone, which is a separate pre-existing gap (follow-up issue to come). Fixes #25
40 lines
1.9 KiB
SQL
40 lines
1.9 KiB
SQL
-- Service-account actors on incident mutations (terdut-server#25). A
|
|
-- team-scoped service account acknowledging/resolving/snoozing/noting an
|
|
-- incident is not a users row, so it cannot be written into
|
|
-- acknowledged_by/incident_events.user_id — doing so either violates the
|
|
-- users(id) FK (new rows) or, for incident_events.user_id, silently matches
|
|
-- zero rows on delete. These columns are the service-account-shaped parallel
|
|
-- to the existing human ones: nullable, mutually exclusive with their human
|
|
-- counterpart, ON DELETE SET NULL so a deleted service account doesn't take
|
|
-- the incident history with it.
|
|
ALTER TABLE incidents
|
|
ADD COLUMN acknowledged_by_service_account_id BIGINT
|
|
REFERENCES service_accounts(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD COLUMN service_account_id BIGINT
|
|
REFERENCES service_accounts(id) ON DELETE SET NULL;
|
|
|
|
-- At most one actor kind per row: both NULL ("the server acted") is valid,
|
|
-- exactly one set is valid, both set is a bug this constraint refuses to
|
|
-- store rather than silently accepting.
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_ack_actor_xor_chk CHECK (
|
|
acknowledged_by IS NULL OR acknowledged_by_service_account_id IS NULL
|
|
);
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_actor_xor_chk CHECK (
|
|
user_id IS NULL OR service_account_id IS NULL
|
|
);
|
|
|
|
CREATE INDEX incidents_acknowledged_by_service_account_id_idx
|
|
ON incidents(acknowledged_by_service_account_id);
|
|
CREATE INDEX incident_events_service_account_id_idx
|
|
ON incident_events(service_account_id);
|
|
|
|
-- assigned_to_service_account_id is deliberately not added here: it would sit
|
|
-- unpopulated until handleIncidentAssign itself tracks an actor, which is a
|
|
-- separate, pre-existing gap (it records the assignee today, never the
|
|
-- actor, for humans either) tracked in its own follow-up issue.
|