9029d48584
- TERDUT_OPERATOR_KEY creates or re-keys the instance-scoped service account
"terdut-operator" at every start, so terdut-operator needs no bootstrap
handshake. An instance-scoped account now acts as owner of every team's
configuration, but is not a member of any team.
- POST /api/teams takes an external_id (instance service accounts only) and
is idempotent on it, so automation finds its own team again after a crash
instead of adopting by display name. GET /api/teams?name= is removed.
- Integration and dead man's switch names are unique per team (409). The
escalation PUT accepts usernames and resolves them itself.
- The 18 migrations are squashed into 001_schema.sql, with no Default team.
TERDUT_DEADMAN_* and the env seeding of switches are removed: teams carry
their own. Existing development databases must be recreated.
Security and robustness:
- GET /api/users no longer returns other people's email or ntfy topic to
non-admins.
- The access log records the route pattern, so integration keys and ack
tokens in the path are not written to the log. Server errors are logged.
- Rate limits take the client address TERDUT_TRUSTED_PROXIES hops from the
right of X-Forwarded-For instead of trusting the first, forgeable entry.
- /api/bootstrap runs in a transaction under an advisory lock, so two
concurrent calls cannot both create an administrator.
- API key last_used_at is written at most every five minutes.
Cleanup: remove GET /api/incidents/{id}/alerts, unused exports, SQLite
remnants in comments and config.
Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
588 lines
22 KiB
SQL
588 lines
22 KiB
SQL
-- Terdut Server schema. One baseline: the project has not shipped, so the
|
|
-- migration history that led here (SQLite import, a Default team, per-team
|
|
-- deadman configs later replaced by switches) is not carried. Later changes are
|
|
-- new numbered files after this one.
|
|
--
|
|
-- Timestamps are Unix epoch seconds in BIGINT columns throughout.
|
|
|
|
CREATE TABLE alerts (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
fingerprint text NOT NULL,
|
|
name text NOT NULL,
|
|
status text NOT NULL,
|
|
labels jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
annotations jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
starts_at bigint NOT NULL,
|
|
ends_at bigint,
|
|
generator_url text DEFAULT ''::text NOT NULL,
|
|
received_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
archived_at bigint,
|
|
resolution_source text,
|
|
team_id bigint NOT NULL,
|
|
integration_id bigint,
|
|
CONSTRAINT alerts_status_check CHECK ((status = ANY (ARRAY['firing'::text, 'resolved'::text])))
|
|
);
|
|
|
|
CREATE TABLE api_keys (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
key_hash text NOT NULL,
|
|
name text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
last_used_at bigint,
|
|
expires_at bigint
|
|
);
|
|
|
|
CREATE TABLE deadman_switches (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
team_id bigint NOT NULL,
|
|
name text NOT NULL,
|
|
matcher text NOT NULL,
|
|
timeout_seconds bigint NOT NULL,
|
|
severity text DEFAULT 'critical'::text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
CONSTRAINT deadman_switches_timeout_seconds_check CHECK ((timeout_seconds > 0))
|
|
);
|
|
|
|
CREATE TABLE device_logins (
|
|
device_hash text NOT NULL,
|
|
user_code text NOT NULL,
|
|
status text DEFAULT 'pending'::text NOT NULL,
|
|
user_id bigint,
|
|
expires_at bigint NOT NULL,
|
|
last_polled_at bigint DEFAULT 0 NOT NULL,
|
|
CONSTRAINT device_logins_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'approved'::text, 'denied'::text])))
|
|
);
|
|
|
|
CREATE TABLE escalation_levels (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
team_id bigint NOT NULL,
|
|
"position" bigint NOT NULL,
|
|
timeout_seconds bigint NOT NULL,
|
|
CONSTRAINT escalation_levels_timeout_seconds_check CHECK ((timeout_seconds > 0))
|
|
);
|
|
|
|
CREATE TABLE escalation_policies (
|
|
team_id bigint NOT NULL,
|
|
repeat_count bigint DEFAULT 0 NOT NULL,
|
|
fallback_topic text DEFAULT ''::text NOT NULL,
|
|
updated_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
CONSTRAINT escalation_policies_repeat_count_check CHECK (((repeat_count >= 0) AND (repeat_count <= 10)))
|
|
);
|
|
|
|
CREATE TABLE escalation_targets (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
level_id bigint NOT NULL,
|
|
kind text NOT NULL,
|
|
user_id bigint,
|
|
CONSTRAINT escalation_targets_check CHECK ((((kind = 'user'::text) AND (user_id IS NOT NULL)) OR ((kind = 'oncall'::text) AND (user_id IS NULL)))),
|
|
CONSTRAINT escalation_targets_kind_check CHECK ((kind = ANY (ARRAY['user'::text, 'oncall'::text])))
|
|
);
|
|
|
|
CREATE TABLE incident_ack_tokens (
|
|
token_hash text NOT NULL,
|
|
incident_id bigint NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
created_at bigint NOT NULL,
|
|
expires_at bigint NOT NULL
|
|
);
|
|
|
|
CREATE TABLE incident_alerts (
|
|
incident_id bigint NOT NULL,
|
|
alert_id bigint NOT NULL,
|
|
added_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL
|
|
);
|
|
|
|
CREATE TABLE incident_events (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
incident_id bigint NOT NULL,
|
|
type text NOT NULL,
|
|
user_id bigint,
|
|
alert_id bigint,
|
|
detail text,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
service_account_id bigint,
|
|
actor_user_id bigint,
|
|
actor_service_account_id bigint,
|
|
CONSTRAINT incident_events_actor_xor_chk CHECK (((user_id IS NULL) OR (service_account_id IS NULL))),
|
|
CONSTRAINT incident_events_assign_actor_xor_chk CHECK (((actor_user_id IS NULL) OR (actor_service_account_id IS NULL)))
|
|
);
|
|
|
|
CREATE TABLE incidents (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
group_key text NOT NULL,
|
|
title text NOT NULL,
|
|
group_labels jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
status text NOT NULL,
|
|
severity text,
|
|
triggered_at bigint NOT NULL,
|
|
acknowledged_by bigint,
|
|
acknowledged_at bigint,
|
|
assigned_to bigint,
|
|
snoozed_until bigint,
|
|
resolved_at bigint,
|
|
resolution_source text,
|
|
archived_at bigint,
|
|
team_id bigint NOT NULL,
|
|
escalation_level bigint DEFAULT 0 NOT NULL,
|
|
escalation_level_at bigint,
|
|
escalation_round bigint DEFAULT 0 NOT NULL,
|
|
signature text DEFAULT ''::text NOT NULL,
|
|
acknowledged_by_service_account_id bigint,
|
|
CONSTRAINT incidents_ack_actor_xor_chk CHECK (((acknowledged_by IS NULL) OR (acknowledged_by_service_account_id IS NULL))),
|
|
CONSTRAINT incidents_status_check CHECK ((status = ANY (ARRAY['triggered'::text, 'acknowledged'::text, 'resolved'::text])))
|
|
);
|
|
|
|
CREATE TABLE integrations (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
team_id bigint NOT NULL,
|
|
kind text NOT NULL,
|
|
name text NOT NULL,
|
|
key_hash text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
last_used_at bigint,
|
|
CONSTRAINT integrations_kind_check CHECK ((kind = 'alertmanager'::text))
|
|
);
|
|
|
|
CREATE TABLE invites (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
token_hash text NOT NULL,
|
|
team_id bigint NOT NULL,
|
|
role text NOT NULL,
|
|
created_by bigint,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
expires_at bigint NOT NULL,
|
|
max_uses bigint DEFAULT 1 NOT NULL,
|
|
uses bigint DEFAULT 0 NOT NULL,
|
|
revoked_at bigint,
|
|
CONSTRAINT invites_max_uses_check CHECK (((max_uses > 0) AND (max_uses <= 100))),
|
|
CONSTRAINT invites_role_check CHECK ((role = ANY (ARRAY['owner'::text, 'member'::text])))
|
|
);
|
|
|
|
CREATE TABLE notifications (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
incident_id bigint NOT NULL,
|
|
user_id bigint,
|
|
topic text NOT NULL,
|
|
kind text NOT NULL,
|
|
created_at bigint NOT NULL,
|
|
send_after bigint NOT NULL,
|
|
attempts bigint DEFAULT 0 NOT NULL,
|
|
sent_at bigint,
|
|
last_error text,
|
|
CONSTRAINT notifications_kind_check CHECK ((kind = ANY (ARRAY['triggered'::text, 'reminder'::text, 'resolved'::text, 'escalated'::text])))
|
|
);
|
|
|
|
CREATE TABLE oidc_logins (
|
|
state_hash text NOT NULL,
|
|
nonce text NOT NULL,
|
|
pkce_verifier text NOT NULL,
|
|
expires_at bigint NOT NULL,
|
|
next text DEFAULT '/'::text NOT NULL
|
|
);
|
|
|
|
CREATE TABLE rate_limit_counters (
|
|
key text NOT NULL,
|
|
window_start bigint NOT NULL,
|
|
count integer NOT NULL
|
|
);
|
|
|
|
CREATE TABLE schedule_entries (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
date text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
team_id bigint NOT NULL
|
|
);
|
|
|
|
CREATE TABLE service_account_keys (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
service_account_id bigint NOT NULL,
|
|
key_hash text NOT NULL,
|
|
name text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
last_used_at bigint
|
|
);
|
|
|
|
CREATE TABLE service_accounts (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
name text NOT NULL,
|
|
scope text NOT NULL,
|
|
team_id bigint,
|
|
created_by bigint,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
CONSTRAINT service_accounts_scope_check CHECK ((scope = ANY (ARRAY['instance'::text, 'team'::text]))),
|
|
CONSTRAINT service_accounts_scope_team_id_chk CHECK ((((scope = 'team'::text) AND (team_id IS NOT NULL)) OR ((scope = 'instance'::text) AND (team_id IS NULL))))
|
|
);
|
|
|
|
CREATE TABLE sessions (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
token_hash text NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
created_at bigint NOT NULL,
|
|
last_seen_at bigint NOT NULL,
|
|
expires_at bigint NOT NULL,
|
|
user_agent text,
|
|
max_expires_at bigint
|
|
);
|
|
|
|
CREATE TABLE settings (
|
|
key text NOT NULL,
|
|
value text NOT NULL,
|
|
updated_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL
|
|
);
|
|
|
|
CREATE TABLE team_members (
|
|
team_id bigint NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
role text NOT NULL,
|
|
joined_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
source text DEFAULT 'manual'::text NOT NULL,
|
|
CONSTRAINT team_members_role_check CHECK ((role = ANY (ARRAY['owner'::text, 'member'::text]))),
|
|
CONSTRAINT team_members_source_check CHECK ((source = ANY (ARRAY['manual'::text, 'oidc'::text])))
|
|
);
|
|
|
|
CREATE TABLE teams (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
name text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
oidc_member_group text,
|
|
oidc_owner_group text,
|
|
-- A stable identity for a team managed by automation (terdut-operator:
|
|
-- "<namespace>/<name>" of its TerdutTeam), so it can find or recreate its own
|
|
-- team without trusting a display name. NULL for a team a person made.
|
|
external_id text
|
|
);
|
|
|
|
CREATE TABLE user_identities (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
user_id bigint NOT NULL,
|
|
issuer text NOT NULL,
|
|
subject text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
last_login_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL
|
|
);
|
|
|
|
CREATE TABLE users (
|
|
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
username text NOT NULL,
|
|
email text NOT NULL,
|
|
created_at bigint DEFAULT (floor(EXTRACT(epoch FROM now())))::bigint NOT NULL,
|
|
ntfy_topic text,
|
|
password_hash text,
|
|
is_admin boolean DEFAULT false NOT NULL,
|
|
disabled_at bigint,
|
|
invited_via bigint,
|
|
onboarding_dismissed_at bigint,
|
|
admin_source text DEFAULT 'manual'::text NOT NULL,
|
|
CONSTRAINT users_admin_source_check CHECK ((admin_source = ANY (ARRAY['manual'::text, 'oidc'::text])))
|
|
);
|
|
|
|
ALTER TABLE alerts
|
|
ADD CONSTRAINT alerts_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE api_keys
|
|
ADD CONSTRAINT api_keys_key_hash_key UNIQUE (key_hash);
|
|
|
|
ALTER TABLE api_keys
|
|
ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE deadman_switches
|
|
ADD CONSTRAINT deadman_switches_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE device_logins
|
|
ADD CONSTRAINT device_logins_pkey PRIMARY KEY (device_hash);
|
|
|
|
ALTER TABLE device_logins
|
|
ADD CONSTRAINT device_logins_user_code_key UNIQUE (user_code);
|
|
|
|
ALTER TABLE escalation_levels
|
|
ADD CONSTRAINT escalation_levels_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE escalation_levels
|
|
ADD CONSTRAINT escalation_levels_team_id_position_key UNIQUE (team_id, "position");
|
|
|
|
ALTER TABLE escalation_policies
|
|
ADD CONSTRAINT escalation_policies_pkey PRIMARY KEY (team_id);
|
|
|
|
ALTER TABLE escalation_targets
|
|
ADD CONSTRAINT escalation_targets_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE incident_ack_tokens
|
|
ADD CONSTRAINT incident_ack_tokens_pkey PRIMARY KEY (token_hash);
|
|
|
|
ALTER TABLE incident_alerts
|
|
ADD CONSTRAINT incident_alerts_pkey PRIMARY KEY (incident_id, alert_id);
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE integrations
|
|
ADD CONSTRAINT integrations_key_hash_key UNIQUE (key_hash);
|
|
|
|
ALTER TABLE integrations
|
|
ADD CONSTRAINT integrations_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE invites
|
|
ADD CONSTRAINT invites_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE invites
|
|
ADD CONSTRAINT invites_token_hash_key UNIQUE (token_hash);
|
|
|
|
ALTER TABLE notifications
|
|
ADD CONSTRAINT notifications_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE oidc_logins
|
|
ADD CONSTRAINT oidc_logins_pkey PRIMARY KEY (state_hash);
|
|
|
|
ALTER TABLE rate_limit_counters
|
|
ADD CONSTRAINT rate_limit_counters_pkey PRIMARY KEY (key);
|
|
|
|
ALTER TABLE schedule_entries
|
|
ADD CONSTRAINT schedule_entries_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE service_account_keys
|
|
ADD CONSTRAINT service_account_keys_key_hash_key UNIQUE (key_hash);
|
|
|
|
ALTER TABLE service_account_keys
|
|
ADD CONSTRAINT service_account_keys_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE service_accounts
|
|
ADD CONSTRAINT service_accounts_name_key UNIQUE (name);
|
|
|
|
ALTER TABLE service_accounts
|
|
ADD CONSTRAINT service_accounts_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE sessions
|
|
ADD CONSTRAINT sessions_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE sessions
|
|
ADD CONSTRAINT sessions_token_hash_key UNIQUE (token_hash);
|
|
|
|
ALTER TABLE settings
|
|
ADD CONSTRAINT settings_pkey PRIMARY KEY (key);
|
|
|
|
ALTER TABLE team_members
|
|
ADD CONSTRAINT team_members_pkey PRIMARY KEY (team_id, user_id);
|
|
|
|
ALTER TABLE teams
|
|
ADD CONSTRAINT teams_name_key UNIQUE (name);
|
|
|
|
ALTER TABLE teams
|
|
ADD CONSTRAINT teams_external_id_key UNIQUE (external_id);
|
|
|
|
ALTER TABLE teams
|
|
ADD CONSTRAINT teams_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE user_identities
|
|
ADD CONSTRAINT user_identities_issuer_subject_key UNIQUE (issuer, subject);
|
|
|
|
ALTER TABLE user_identities
|
|
ADD CONSTRAINT user_identities_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE users
|
|
ADD CONSTRAINT users_email_key UNIQUE (email);
|
|
|
|
ALTER TABLE users
|
|
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
|
|
|
ALTER TABLE users
|
|
ADD CONSTRAINT users_username_key UNIQUE (username);
|
|
|
|
CREATE INDEX alerts_archived_at_idx ON alerts USING btree (archived_at);
|
|
|
|
CREATE INDEX alerts_integration_idx ON alerts USING btree (integration_id, received_at) WHERE (integration_id IS NOT NULL);
|
|
|
|
CREATE INDEX alerts_name_idx ON alerts USING btree (name);
|
|
|
|
CREATE INDEX alerts_received_at_idx ON alerts USING btree (received_at DESC);
|
|
|
|
CREATE INDEX alerts_status_idx ON alerts USING btree (status);
|
|
|
|
CREATE UNIQUE INDEX alerts_team_fingerprint_idx ON alerts USING btree (team_id, fingerprint);
|
|
|
|
CREATE INDEX alerts_team_received_idx ON alerts USING btree (team_id, received_at DESC);
|
|
|
|
CREATE INDEX deadman_switches_team_idx ON deadman_switches USING btree (team_id);
|
|
|
|
CREATE INDEX device_logins_expires_idx ON device_logins USING btree (expires_at);
|
|
|
|
CREATE INDEX escalation_targets_level_idx ON escalation_targets USING btree (level_id);
|
|
|
|
CREATE INDEX idx_sessions_user ON sessions USING btree (user_id);
|
|
|
|
CREATE INDEX incident_ack_tokens_expires_idx ON incident_ack_tokens USING btree (expires_at);
|
|
|
|
CREATE INDEX incident_alerts_alert_id_idx ON incident_alerts USING btree (alert_id);
|
|
|
|
CREATE INDEX incident_events_actor_service_account_id_idx ON incident_events USING btree (actor_service_account_id);
|
|
|
|
CREATE INDEX incident_events_actor_user_id_idx ON incident_events USING btree (actor_user_id);
|
|
|
|
CREATE INDEX incident_events_incident_idx ON incident_events USING btree (incident_id, created_at);
|
|
|
|
CREATE INDEX incident_events_service_account_id_idx ON incident_events USING btree (service_account_id);
|
|
|
|
CREATE INDEX incidents_acknowledged_by_service_account_id_idx ON incidents USING btree (acknowledged_by_service_account_id);
|
|
|
|
CREATE INDEX incidents_archived_at_idx ON incidents USING btree (archived_at);
|
|
|
|
CREATE INDEX incidents_escalation_idx ON incidents USING btree (escalation_level_at) WHERE ((resolved_at IS NULL) AND (status = 'triggered'::text));
|
|
|
|
CREATE UNIQUE INDEX incidents_open_group_key_idx ON incidents USING btree (team_id, group_key) WHERE (resolved_at IS NULL);
|
|
|
|
CREATE INDEX incidents_signature_idx ON incidents USING btree (team_id, signature, triggered_at DESC);
|
|
|
|
CREATE INDEX incidents_status_idx ON incidents USING btree (status);
|
|
|
|
CREATE INDEX incidents_team_triggered_idx ON incidents USING btree (team_id, triggered_at DESC);
|
|
|
|
CREATE INDEX incidents_triggered_at_idx ON incidents USING btree (triggered_at DESC);
|
|
|
|
CREATE INDEX integrations_team_idx ON integrations USING btree (team_id);
|
|
|
|
-- A name identifies an integration (and a switch) within its team, so a client
|
|
-- that manages them declaratively can look one up by name instead of listing
|
|
-- and matching.
|
|
CREATE UNIQUE INDEX integrations_team_name_key ON integrations (team_id, name);
|
|
CREATE UNIQUE INDEX deadman_switches_team_name_key ON deadman_switches (team_id, name);
|
|
|
|
CREATE INDEX invites_team_idx ON invites USING btree (team_id);
|
|
|
|
CREATE INDEX notifications_incident_idx ON notifications USING btree (incident_id, id DESC);
|
|
|
|
CREATE INDEX notifications_pending_idx ON notifications USING btree (send_after) WHERE (sent_at IS NULL);
|
|
|
|
CREATE INDEX oidc_logins_expires_idx ON oidc_logins USING btree (expires_at);
|
|
|
|
CREATE INDEX schedule_entries_date_idx ON schedule_entries USING btree (date);
|
|
|
|
CREATE UNIQUE INDEX schedule_entries_team_date_idx ON schedule_entries USING btree (team_id, date);
|
|
|
|
CREATE INDEX service_account_keys_service_account_id_idx ON service_account_keys USING btree (service_account_id);
|
|
|
|
CREATE INDEX service_accounts_team_id_idx ON service_accounts USING btree (team_id);
|
|
|
|
CREATE INDEX team_members_user_idx ON team_members USING btree (user_id);
|
|
|
|
CREATE INDEX user_identities_user_idx ON user_identities USING btree (user_id);
|
|
|
|
CREATE INDEX users_is_admin_idx ON users USING btree (is_admin) WHERE is_admin;
|
|
|
|
ALTER TABLE alerts
|
|
ADD CONSTRAINT alerts_integration_id_fkey FOREIGN KEY (integration_id) REFERENCES integrations(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE alerts
|
|
ADD CONSTRAINT alerts_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE api_keys
|
|
ADD CONSTRAINT api_keys_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE deadman_switches
|
|
ADD CONSTRAINT deadman_switches_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE device_logins
|
|
ADD CONSTRAINT device_logins_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE escalation_levels
|
|
ADD CONSTRAINT escalation_levels_team_id_fkey FOREIGN KEY (team_id) REFERENCES escalation_policies(team_id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE escalation_policies
|
|
ADD CONSTRAINT escalation_policies_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE escalation_targets
|
|
ADD CONSTRAINT escalation_targets_level_id_fkey FOREIGN KEY (level_id) REFERENCES escalation_levels(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE escalation_targets
|
|
ADD CONSTRAINT escalation_targets_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_ack_tokens
|
|
ADD CONSTRAINT incident_ack_tokens_incident_id_fkey FOREIGN KEY (incident_id) REFERENCES incidents(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_ack_tokens
|
|
ADD CONSTRAINT incident_ack_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_alerts
|
|
ADD CONSTRAINT incident_alerts_alert_id_fkey FOREIGN KEY (alert_id) REFERENCES alerts(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_alerts
|
|
ADD CONSTRAINT incident_alerts_incident_id_fkey FOREIGN KEY (incident_id) REFERENCES incidents(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_actor_service_account_id_fkey FOREIGN KEY (actor_service_account_id) REFERENCES service_accounts(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_actor_user_id_fkey FOREIGN KEY (actor_user_id) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_alert_id_fkey FOREIGN KEY (alert_id) REFERENCES alerts(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_incident_id_fkey FOREIGN KEY (incident_id) REFERENCES incidents(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_service_account_id_fkey FOREIGN KEY (service_account_id) REFERENCES service_accounts(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incident_events
|
|
ADD CONSTRAINT incident_events_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_acknowledged_by_fkey FOREIGN KEY (acknowledged_by) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_acknowledged_by_service_account_id_fkey FOREIGN KEY (acknowledged_by_service_account_id) REFERENCES service_accounts(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_assigned_to_fkey FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE incidents
|
|
ADD CONSTRAINT incidents_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE integrations
|
|
ADD CONSTRAINT integrations_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE invites
|
|
ADD CONSTRAINT invites_created_by_fkey FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE invites
|
|
ADD CONSTRAINT invites_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE notifications
|
|
ADD CONSTRAINT notifications_incident_id_fkey FOREIGN KEY (incident_id) REFERENCES incidents(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE notifications
|
|
ADD CONSTRAINT notifications_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE schedule_entries
|
|
ADD CONSTRAINT schedule_entries_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE schedule_entries
|
|
ADD CONSTRAINT schedule_entries_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE service_account_keys
|
|
ADD CONSTRAINT service_account_keys_service_account_id_fkey FOREIGN KEY (service_account_id) REFERENCES service_accounts(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE service_accounts
|
|
ADD CONSTRAINT service_accounts_created_by_fkey FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE service_accounts
|
|
ADD CONSTRAINT service_accounts_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE sessions
|
|
ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE team_members
|
|
ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE team_members
|
|
ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE user_identities
|
|
ADD CONSTRAINT user_identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
ALTER TABLE users
|
|
ADD CONSTRAINT users_invited_via_fkey FOREIGN KEY (invited_via) REFERENCES invites(id) ON DELETE SET NULL;
|