Files
Alarm/database/migrations/004_drop_auto_stop.sql
T
Admin 3a2da52b7a [refactor](alarm): end alerts by disarm or expiry, not a ring timer
An alert now rings from trigger until the owner disarms it; a subscriber silences only its own device. The owner instead sets a persistence cap (default 24h, hard cap 14 days) and the sweeper destroys expired alerts. Disarm ends an armed alert too, and every ending (delivered, expired, disarmed, deleted) converges on the one destruction path so their event-log reasons cannot drift.
2026-10-19 02:26:31 +01:00

52 lines
2.2 KiB
PL/PgSQL

\c dcntrlized;
-- 004_drop_auto_stop.sql — remove the auto-stop window.
--
-- Contract decision (2026-10-18, user ruling): an alert is active from trigger
-- until the owner disarms it. There is no clock-based expiry: "if an owner
-- triggers it and then loses their phone, the alarm should go off until the
-- client dismisses it, with no autostop". A subscriber's ringing ends when that
-- subscriber dismisses locally; the server does not expire the event, and the
-- server-side state does not depend on any client's cooperation.
--
-- Consequence, accepted and documented in API.md: an alert that is never
-- disarmed stays active forever, and only the owner's admin token can end it.
--
-- `alerts.auto_stop_seconds` and its CHECK constraint
-- (alerts_auto_stop_seconds_range) have no remaining reader: `active` is simply
-- `status = 'triggered'` now. `001_alarm_schema.sql` is already applied and is
-- never edited in place, so the column is dropped here; a fresh database creates
-- it in 001 and drops it in this file, keeping the history append-only.
--
-- Idempotent: guarded on the schema existing and DROP COLUMN IF EXISTS, so a
-- re-apply of the whole sequence is a clean no-op (same pattern as 002/003).
-- The fan-out trigger reads the alert row when it builds its pg_notify payload,
-- so it is replaced BEFORE the column goes: leaving the old body behind would
-- make every event INSERT fail with "column auto_stop_seconds does not exist"
-- once the column is gone. The payload is the WS frame's transport form; with no
-- auto-stop it carries alertId/eventId/action/message/createdAtMs and nothing
-- else. CREATE OR REPLACE is idempotent.
CREATE OR REPLACE FUNCTION alarm.notify_alarm_event()
RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('alarm_events', json_build_object(
'alertId', NEW.alert_id::text,
'eventId', NEW.id::text,
'action', NEW.kind::text,
'message', COALESCE(NEW.message, ''),
'createdAtMs', (EXTRACT(EPOCH FROM NEW.created_at) * 1000)::bigint
)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'alarm') THEN
ALTER TABLE alarm.alerts DROP COLUMN IF EXISTS auto_stop_seconds;
END IF;
END
$$;