-- AZ-535 + AZ-533 (Epic AZ-529): logout/revocation surface + mission tokens. -- -- AZ-535 adds `revoked_by_user_id` so admin / system revocations can be audited. -- AZ-533 adds `class` and `aircraft_id` so mission tokens (long-lived, no refresh, -- bound to a specific aircraft) live in the same row store as interactive sessions -- and the auto-revoke-on-reconnect middleware can index them cheaply. -- -- The two columns ride together because mission token revocation reuses the same -- code path the logout endpoints exercise; splitting them into separate migrations -- would just force duplicate ALTER TABLE round-trips in the test bootstrap. ALTER TABLE public.sessions ADD COLUMN IF NOT EXISTS revoked_by_user_id uuid NULL REFERENCES public.users(id) ON DELETE SET NULL, ADD COLUMN IF NOT EXISTS class varchar(32) NOT NULL DEFAULT 'interactive', ADD COLUMN IF NOT EXISTS aircraft_id uuid NULL REFERENCES public.users(id) ON DELETE SET NULL, -- AZ-533: mission tokens have no refresh value, so refresh_hash is now nullable. -- Interactive rows continue to set it; the unique index already ignores nulls -- by default in Postgres. ALTER COLUMN refresh_hash DROP NOT NULL; -- AZ-533: cheap "find every mission session belonging to UAV-X that hasn't been -- revoked yet" — driven on every successful auth call so it must be O(active rows -- for that aircraft), not O(all sessions). Partial index excludes the cold pile of -- already-revoked rows. CREATE INDEX IF NOT EXISTS sessions_aircraft_active_idx ON public.sessions (aircraft_id, class) WHERE revoked_at IS NULL AND aircraft_id IS NOT NULL; -- AZ-535: snapshot endpoint pulls every revocation since ; index on revoked_at -- makes the "since" filter O(matching rows) instead of full-table scan. CREATE INDEX IF NOT EXISTS sessions_revoked_at_idx ON public.sessions (revoked_at) WHERE revoked_at IS NOT NULL; -- AZ-535: session-revoke and snapshot endpoints both write/read these columns; -- existing grants in 08_sessions.sql already cover the table, so no new grants -- needed.