todo.sr.ht/schema.sql

425 lines
15 KiB
SQL

CREATE TYPE auth_method AS ENUM (
'OAUTH_LEGACY',
'OAUTH2',
'COOKIE',
'INTERNAL',
'WEBHOOK'
);
CREATE TYPE ticket_webhook_event AS ENUM (
'TICKET_UPDATE',
'EVENT_CREATED',
'TICKET_DELETED'
);
CREATE TYPE tracker_webhook_event AS ENUM (
'TRACKER_UPDATE',
'TRACKER_DELETED',
'TICKET_CREATED',
'TICKET_UPDATE',
'LABEL_CREATED',
'LABEL_UPDATE',
'LABEL_DELETED',
'EVENT_CREATED',
'TICKET_DELETED'
);
CREATE TYPE visibility AS ENUM (
'PUBLIC',
'UNLISTED',
'PRIVATE'
);
CREATE TYPE webhook_event AS ENUM (
'TRACKER_CREATED',
'TRACKER_UPDATE',
'TRACKER_DELETED',
'TICKET_CREATED'
);
CREATE TABLE "user" (
id integer PRIMARY KEY,
username character varying(256) UNIQUE,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
oauth_token character varying(256),
oauth_token_expires timestamp without time zone,
oauth_token_scopes character varying,
email character varying(256) NOT NULL,
user_type character varying DEFAULT 'active_non_paying'::character varying NOT NULL,
url character varying(256),
location character varying(256),
bio character varying(4096),
oauth_revocation_token character varying(256),
suspension_notice character varying(4096),
notify_self boolean DEFAULT false NOT NULL
);
CREATE INDEX ix_user_username ON "user" USING btree (username);
CREATE TABLE participant (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
participant_type character varying NOT NULL,
user_id integer UNIQUE REFERENCES "user"(id) ON DELETE CASCADE,
email character varying UNIQUE,
email_name character varying,
external_id character varying UNIQUE,
external_url character varying
);
CREATE TABLE tracker (
id serial PRIMARY KEY,
owner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
name character varying(1024),
description character varying(8192),
default_access integer DEFAULT 7 NOT NULL,
next_ticket_id integer DEFAULT 1 NOT NULL,
import_in_progress boolean DEFAULT false NOT NULL,
visibility visibility NOT NULL,
CONSTRAINT tracker_owner_id_name_unique UNIQUE (owner_id, name)
);
CREATE TABLE user_access (
id serial PRIMARY KEY,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
permissions integer NOT NULL,
created timestamp without time zone NOT NULL,
CONSTRAINT idx_useraccess_tracker_user_unique UNIQUE (tracker_id, user_id)
);
CREATE TABLE label (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
name text NOT NULL,
color text NOT NULL,
text_color text NOT NULL,
CONSTRAINT idx_tracker_name_unique UNIQUE (tracker_id, name)
);
CREATE TABLE ticket (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
dupe_of_id integer REFERENCES ticket(id) ON DELETE SET NULL,
title character varying(2048) NOT NULL,
description character varying(16384),
status integer DEFAULT 0 NOT NULL,
resolution integer DEFAULT 0 NOT NULL,
scoped_id integer NOT NULL,
submitter_id integer NOT NULL REFERENCES participant(id) ON DELETE CASCADE,
authenticity integer DEFAULT 0 NOT NULL,
comment_count integer DEFAULT 0 NOT NULL,
CONSTRAINT uq_ticket_scoped_id_tracker_id UNIQUE (scoped_id, tracker_id),
CONSTRAINT uq_ticket_tracker_id_scoped_id UNIQUE (tracker_id, scoped_id)
);
CREATE INDEX ix_ticket_comment_count ON ticket USING btree (comment_count);
CREATE INDEX ix_ticket_scoped_id ON ticket USING btree (scoped_id);
CREATE INDEX ticket_scoped_id ON ticket USING btree (scoped_id);
CREATE INDEX ticket_tracker_id ON ticket USING btree (tracker_id);
CREATE INDEX ticket_dupe_of_id ON ticket USING btree (dupe_of_id);
CREATE TABLE ticket_assignee (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
assignee_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
assigner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
CONSTRAINT idx_ticket_assignee_unique UNIQUE (ticket_id, assignee_id)
);
CREATE INDEX ticket_assignee_ticket_id ON ticket_assignee USING btree (ticket_id);
CREATE TABLE ticket_comment (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
text character varying(16384),
submitter_id integer NOT NULL REFERENCES participant(id),
authenticity integer DEFAULT 0 NOT NULL,
superceeded_by_id integer REFERENCES ticket_comment(id) ON DELETE SET NULL
);
CREATE INDEX ticket_comment_submitter_id ON ticket_comment USING btree (submitter_id);
CREATE INDEX ticket_comment_superceeded_by_id ON ticket_comment USING btree (superceeded_by_id);
CREATE INDEX ticket_comment_ticket_id ON ticket_comment USING btree (ticket_id);
CREATE TABLE ticket_label (
ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
label_id integer NOT NULL REFERENCES label(id) ON DELETE CASCADE,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
created timestamp without time zone NOT NULL,
PRIMARY KEY (ticket_id, label_id)
);
CREATE INDEX ticket_label_ticket_id ON ticket_label USING btree (ticket_id);
CREATE TABLE ticket_subscription (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
tracker_id integer REFERENCES tracker(id) ON DELETE CASCADE,
participant_id integer REFERENCES participant(id) ON DELETE CASCADE,
CONSTRAINT subscription_ticket_participant_uq UNIQUE (ticket_id, participant_id),
CONSTRAINT subscription_tracker_participant_uq UNIQUE (tracker_id, participant_id)
);
CREATE TABLE event (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
event_type integer NOT NULL,
old_status integer,
old_resolution integer,
new_status integer,
new_resolution integer,
ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
comment_id integer REFERENCES ticket_comment(id) ON DELETE CASCADE,
label_id integer REFERENCES label(id) ON DELETE CASCADE,
from_ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
participant_id integer REFERENCES participant(id) ON DELETE CASCADE,
by_participant_id integer REFERENCES participant(id) ON DELETE CASCADE
);
CREATE INDEX event_comment_id ON event USING btree (comment_id);
CREATE INDEX event_from_ticket_id ON event USING btree (from_ticket_id);
CREATE INDEX event_label_id ON event USING btree (label_id);
CREATE INDEX event_participant_id ON event USING btree (participant_id);
CREATE INDEX event_ticket_id ON event USING btree (ticket_id);
CREATE TABLE event_notification (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
event_id integer NOT NULL REFERENCES event(id) ON DELETE CASCADE,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE
);
CREATE INDEX event_notification_event_id ON event_notification USING btree (event_id);
-- GraphQL webhooks
CREATE TABLE gql_user_wh_sub (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
events webhook_event[] NOT NULL,
url character varying NOT NULL,
query character varying NOT NULL,
auth_method auth_method NOT NULL,
token_hash character varying(128),
grants character varying,
client_id uuid,
expires timestamp without time zone,
node_id character varying,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
CONSTRAINT gql_user_wh_sub_auth_method_check
CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
CONSTRAINT gql_user_wh_sub_check
CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
CONSTRAINT gql_user_wh_sub_check1
CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
CONSTRAINT gql_user_wh_sub_check2
CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
CONSTRAINT gql_user_wh_sub_events_check
CHECK ((array_length(events, 1) > 0))
);
CREATE INDEX gql_user_wh_sub_token_hash_idx ON gql_user_wh_sub USING btree (token_hash);
CREATE TABLE gql_user_wh_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
date timestamp without time zone NOT NULL,
event webhook_event NOT NULL,
subscription_id integer NOT NULL REFERENCES gql_user_wh_sub(id) ON DELETE CASCADE,
request_body character varying NOT NULL,
response_body character varying,
response_headers character varying,
response_status integer
);
CREATE TABLE gql_tracker_wh_sub (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
events tracker_webhook_event[] NOT NULL,
url character varying NOT NULL,
query character varying NOT NULL,
auth_method auth_method NOT NULL,
token_hash character varying(128),
grants character varying,
client_id uuid,
expires timestamp without time zone,
node_id character varying,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
CONSTRAINT gql_tracker_wh_sub_auth_method_check
CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
CONSTRAINT gql_tracker_wh_sub_check
CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
CONSTRAINT gql_tracker_wh_sub_check1
CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
CONSTRAINT gql_tracker_wh_sub_check2
CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
CONSTRAINT gql_tracker_wh_sub_events_check
CHECK ((array_length(events, 1) > 0))
);
CREATE INDEX gql_tracker_wh_sub_token_hash_idx ON gql_tracker_wh_sub USING btree (token_hash);
CREATE TABLE gql_tracker_wh_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
date timestamp without time zone NOT NULL,
event tracker_webhook_event NOT NULL,
subscription_id integer NOT NULL REFERENCES gql_tracker_wh_sub(id) ON DELETE CASCADE,
request_body character varying NOT NULL,
response_body character varying,
response_headers character varying,
response_status integer
);
CREATE TABLE gql_ticket_wh_sub (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
events ticket_webhook_event[] NOT NULL,
url character varying NOT NULL,
query character varying NOT NULL,
auth_method auth_method NOT NULL,
token_hash character varying(128),
grants character varying,
client_id uuid,
expires timestamp without time zone,
node_id character varying,
user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
scoped_id integer NOT NULL,
CONSTRAINT gql_ticket_wh_sub_auth_method_check
CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
CONSTRAINT gql_ticket_wh_sub_check
CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
CONSTRAINT gql_ticket_wh_sub_check1
CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
CONSTRAINT gql_ticket_wh_sub_check2
CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
CONSTRAINT gql_ticket_wh_sub_events_check
CHECK ((array_length(events, 1) > 0))
);
CREATE INDEX gql_ticket_wh_sub_token_hash_idx ON gql_ticket_wh_sub USING btree (token_hash);
CREATE TABLE gql_ticket_wh_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
date timestamp without time zone NOT NULL,
event ticket_webhook_event NOT NULL,
subscription_id integer NOT NULL REFERENCES gql_ticket_wh_sub(id) ON DELETE CASCADE,
request_body character varying NOT NULL,
response_body character varying,
response_headers character varying,
response_status integer
);
-- Legacy OAuth (TODO: Remove)
CREATE TABLE oauthtoken (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
updated timestamp without time zone NOT NULL,
expires timestamp without time zone NOT NULL,
token_hash character varying(128) NOT NULL,
token_partial character varying(8) NOT NULL,
scopes character varying(512) NOT NULL,
user_id integer REFERENCES "user"(id) ON DELETE CASCADE
);
-- Legacy webhooks (TODO: Remove)
CREATE TABLE user_webhook_subscription (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
url character varying(2048) NOT NULL,
events character varying NOT NULL,
user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE
);
CREATE TABLE user_webhook_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
created timestamp without time zone NOT NULL,
event character varying(256) NOT NULL,
url character varying(2048) NOT NULL,
payload character varying(65536) NOT NULL,
payload_headers character varying(16384) NOT NULL,
response character varying(65536),
response_status integer NOT NULL,
response_headers character varying(16384),
subscription_id integer NOT NULL REFERENCES user_webhook_subscription(id) ON DELETE CASCADE
);
CREATE TABLE tracker_webhook_subscription (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
url character varying(2048) NOT NULL,
events character varying NOT NULL,
user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE,
tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE
);
CREATE TABLE tracker_webhook_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
created timestamp without time zone NOT NULL,
event character varying(256) NOT NULL,
url character varying(2048) NOT NULL,
payload character varying(65536) NOT NULL,
payload_headers character varying(16384) NOT NULL,
response character varying(65536),
response_status integer NOT NULL,
response_headers character varying(16384),
subscription_id integer NOT NULL REFERENCES tracker_webhook_subscription(id) ON DELETE CASCADE
);
CREATE TABLE ticket_webhook_subscription (
id serial PRIMARY KEY,
created timestamp without time zone NOT NULL,
url character varying(2048) NOT NULL,
events character varying NOT NULL,
user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE,
ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE
);
CREATE TABLE ticket_webhook_delivery (
id serial PRIMARY KEY,
uuid uuid NOT NULL,
created timestamp without time zone NOT NULL,
event character varying(256) NOT NULL,
url character varying(2048) NOT NULL,
payload character varying(65536) NOT NULL,
payload_headers character varying(16384) NOT NULL,
response character varying(65536),
response_status integer NOT NULL,
response_headers character varying(16384),
subscription_id integer NOT NULL REFERENCES ticket_webhook_subscription(id) ON DELETE CASCADE
);
CREATE INDEX ticket_webhook_subscription_ticket_id ON ticket_webhook_subscription USING btree (ticket_id);