CREATE OR REPLACE FUNCTION update_updated_on_column()
RETURNS TRIGGER AS $$
BEGIN
IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
NEW.updated_on = NOW() AT TIME ZONE 'UTC';
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ language 'plpgsql';
CREATE TYPE domain_level AS ENUM (
'SYSTEM',
'USER'
);
CREATE TYPE domain_service AS ENUM (
'LINKS',
'SHORT',
'LIST'
);
CREATE TYPE domain_status AS ENUM (
'PENDING',
'APPROVED',
'ERROR'
);
CREATE TYPE invoice_status AS ENUM (
'PENDING',
'PAID',
'REFUNDED',
'PARTIAL_REFUND'
);
CREATE TYPE org_link_visibility AS ENUM (
'PUBLIC',
'PRIVATE',
'RESTRICTED'
);
CREATE TYPE org_link_type AS ENUM (
'LINK',
'NOTE'
);
CREATE TYPE org_link_perm AS ENUM (
'READ',
'WRITE',
'ADMIN_WRITE'
);
CREATE TYPE org_type AS ENUM (
'USER',
'NORMAL'
);
CREATE TYPE subscription_type AS ENUM (
'PERSONAL',
'BUSINESS'
);
CREATE TYPE qr_code_type AS ENUM (
'LIST',
'SHORT'
);
CREATE TYPE totals_meta_type AS ENUM (
'REFERRER',
'COUNTRY',
'CITY',
'DEVICE'
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
full_name VARCHAR ( 150 ) NOT NULL,
password VARCHAR ( 1024 ) NOT NULL,
email VARCHAR ( 255 ) UNIQUE NOT NULL,
picture VARCHAR(1024) DEFAULT '',
settings JSONB DEFAULT '{}',
is_verified BOOLEAN DEFAULT FALSE,
is_superuser BOOLEAN DEFAULT FALSE,
is_staff BOOLEAN DEFAULT FALSE,
is_locked BOOLEAN DEFAULT FALSE,
lock_reason VARCHAR (255) DEFAULT '',
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMPTZ DEFAULT NULL
);
CREATE INDEX users_id_idx ON users (id);
CREATE TABLE organizations (
id SERIAL PRIMARY KEY,
owner_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
org_type org_type DEFAULT 'USER',
name VARCHAR ( 150 ) NOT NULL,
slug VARCHAR ( 150 ) UNIQUE NOT NULL,
image VARCHAR(1024) DEFAULT '',
settings JSONB DEFAULT '{}',
timezone VARCHAR(20) NOT NULL DEFAULT 'UTC',
is_active BOOLEAN DEFAULT TRUE,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX organizations_id_idx ON organizations (id);
CREATE INDEX organizations_owner_id_idx ON organizations (owner_id);
CREATE INDEX organizations_slug_idx ON organizations (slug);
CREATE TRIGGER update_organizations_modtime BEFORE UPDATE ON organizations FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE sessions (
token TEXT PRIMARY KEY,
data BYTEA NOT NULL,
expiry TIMESTAMPTZ NOT NULL
);
CREATE INDEX sessions_expiry_idx ON sessions (expiry);
CREATE TABLE confirmations (
id SERIAL PRIMARY KEY,
"type" INT NOT NULL, -- email, password reset
confirmation_target VARCHAR DEFAULT NULL,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
key VARCHAR NOT NULL UNIQUE,
confirm_time TIMESTAMPTZ DEFAULT NULL,
expire_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP + interval '24 hours',
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE email_notifications (
id SERIAL PRIMARY KEY,
email VARCHAR ( 255 ) UNIQUE NOT NULL,
count INT NOT NULL DEFAULT 0,
notifications JSON NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX email_notifications_id_idx on email_notifications (id);
CREATE TRIGGER update_email_notifications_modtime BEFORE UPDATE ON email_notifications FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE base_urls (
id SERIAL PRIMARY KEY,
title VARCHAR ( 150 ) DEFAULT '',
url TEXT UNIQUE NOT NULL,
public_ready BOOLEAN DEFAULT FALSE,
hash VARCHAR(128) UNIQUE NOT NULL,
data JSONB DEFAULT '{}',
counter INT DEFAULT 0,
parse_attempts INT DEFAULT 0,
last_parse_attempt TIMESTAMPTZ,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX base_urls_id_idx ON base_urls (id);
CREATE TRIGGER base_urls_modtime BEFORE UPDATE ON base_urls FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE org_links (
id SERIAL PRIMARY KEY,
title VARCHAR ( 150 ) NOT NULL,
url TEXT NOT NULL,
description TEXT DEFAULT '',
"type" org_link_type default 'LINK',
hash VARCHAR(128) UNIQUE NOT NULL,
base_url_id INT REFERENCES base_urls (id) ON DELETE CASCADE,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE SET NULL,
visibility org_link_visibility DEFAULT 'PUBLIC',
unread BOOLEAN DEFAULT FALSE NOT NULL,
starred BOOLEAN DEFAULT FALSE NOT NULL,
archive_url TEXT DEFAULT '',
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
CONSTRAINT unique_base_url_org UNIQUE (base_url_id, org_id)
);
CREATE INDEX org_links_id_idx ON org_links (id);
CREATE INDEX org_links_base_url_id_idx ON org_links (base_url_id);
CREATE INDEX org_links_org_id_idx ON org_links (org_id);
CREATE INDEX org_links_user_id_idx ON org_links (user_id);
CREATE INDEX org_links_search_idx ON org_links USING GIN (to_tsvector('simple', title || ' ' || description || ' ' || url));
CREATE TRIGGER update_org_links_modtime BEFORE UPDATE ON org_links FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE organization_users (
id SERIAL PRIMARY KEY,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
permission org_link_perm default 'READ',
is_active BOOLEAN DEFAULT TRUE,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_user_org UNIQUE (org_id, user_id)
);
CREATE INDEX organization_users_id_idx ON organization_users (id);
CREATE INDEX organization_users_org_id_idx ON organization_users (org_id);
CREATE INDEX organization_users_user_id_idx ON organization_users (user_id);
CREATE TRIGGER update_organization_users_modtime BEFORE UPDATE ON organization_users FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE domains (
id SERIAL PRIMARY KEY,
name VARCHAR (255) NOT NULL,
lookup_name VARCHAR (500) NOT NULL, -- padded in case...
org_id INT REFERENCES organizations (id) ON DELETE CASCADE,
level domain_level NOT NULL,
service domain_service NOT NULL,
status domain_status DEFAULT 'PENDING' NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
last_action TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_lookup_name_active UNIQUE (lookup_name, is_active)
);
CREATE INDEX domains_id_idx ON domains (id);
CREATE INDEX domains_org_id_idx ON domains (org_id);
CREATE TRIGGER update_domains_modtime BEFORE UPDATE ON domains FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE oauth2_clients (
id serial PRIMARY KEY,
owner_id integer NOT NULL REFERENCES users (id) ON DELETE CASCADE NOT NULL,
name character varying(256) NOT NULL,
description character varying,
key character varying NOT NULL UNIQUE,
secret_hash character varying(128) NOT NULL,
secret_partial character varying(8) NOT NULL,
redirect_url character varying,
client_url character varying,
revoked boolean DEFAULT false NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX oauth2_clients_id_idx ON oauth2_clients (id);
CREATE INDEX oauth2_clients_owner_id_idx ON oauth2_clients (owner_id);
CREATE TRIGGER update_oauth2_clients_modtime BEFORE UPDATE ON oauth2_clients FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE oauth2_grants (
id serial PRIMARY KEY,
issued TIMESTAMPTZ NOT NULL,
expires TIMESTAMPTZ NOT NULL,
comment character varying,
token_hash character varying(128) NOT NULL,
refresh_token_hash character varying(128) NOT NULL,
grants character varying default '',
user_id integer NOT NULL REFERENCES users (id) ON DELETE CASCADE,
client_id integer REFERENCES oauth2_clients (id) ON DELETE CASCADE
);
CREATE INDEX oauth2_grants_id_idx ON oauth2_grants (id);
CREATE INDEX oauth2_grants_user_id_idx ON oauth2_grants (user_id);
CREATE INDEX oauth2_grants_client_id_idx ON oauth2_grants (client_id);
CREATE TABLE oauth2_authorizations (
id serial PRIMARY KEY,
code character varying(128) NOT NULL,
payload character varying NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX oauth2_authorizations_id_idx ON oauth2_authorizations (id);
CREATE INDEX oauth2_authorizations_code_idx ON oauth2_authorizations (code);
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR ( 50 ) NOT NULL,
slug VARCHAR ( 50 ) UNIQUE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX tags_id_idx ON tags (id);
CREATE INDEX tags_id_slug ON tags (slug);
CREATE TABLE tag_links (
id SERIAL PRIMARY KEY,
org_link_id INT REFERENCES org_links (id) ON DELETE CASCADE NOT NULL,
tag_id INT REFERENCES tags (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_tag_link UNIQUE (org_link_id, tag_id)
);
CREATE INDEX tag_links_id_idx ON tag_links (id);
CREATE INDEX tag_links_org_link_id_idx ON tag_links (org_link_id);
CREATE INDEX tag_links_tag_id_idx ON tag_links (tag_id);
CREATE TABLE slack_connections (
id SERIAL PRIMARY KEY,
token TEXT NOT NULL,
team_id TEXT UNIQUE NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL UNIQUE,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX slack_connections_id_idx ON slack_connections (id);
CREATE INDEX slack_connections_org_id_idx ON slack_connections (org_id);
CREATE TABLE slack_users (
id SERIAL PRIMARY KEY,
slack_conn_id INT REFERENCES slack_connections (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
slack_user TEXT NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_slack_conn_user UNIQUE (slack_conn_id, user_id)
);
CREATE INDEX slack_users_id_idx ON slack_users (id);
CREATE INDEX slack_users_slack_conn_id_idx ON slack_users (slack_conn_id);
CREATE TABLE mattermost_connections (
id SERIAL PRIMARY KEY,
team_id TEXT UNIQUE NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL UNIQUE,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX mattermost_connections_id_idx ON mattermost_connections (id);
CREATE INDEX mattermost_connections_org_id_idx ON mattermost_connections (org_id);
CREATE TABLE mattermost_users (
id SERIAL PRIMARY KEY,
mattermost_conn_id INT REFERENCES mattermost_connections (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
mattermost_user TEXT NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_mattermost_conn_user UNIQUE (mattermost_conn_id, user_id)
);
CREATE INDEX mattermost_users_id_idx ON mattermost_users (id);
CREATE INDEX mattermost_users_mattermost_conn_id_idx ON mattermost_users (mattermost_conn_id);
CREATE TABLE link_shorts (
id SERIAL PRIMARY KEY,
title VARCHAR ( 150 ) DEFAULT '',
url TEXT NOT NULL,
short_code TEXT DEFAULT '',
domain_id INT REFERENCES domains (id) ON DELETE RESTRICT NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE SET NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_short_code_domain UNIQUE (short_code, domain_id)
);
CREATE INDEX link_shorts_id_idx ON link_shorts (id);
CREATE INDEX link_shorts_domain_id_idx ON link_shorts (domain_id);
CREATE INDEX link_shorts_org_id_idx ON link_shorts (org_id);
CREATE INDEX link_shorts_user_id_idx ON link_shorts (user_id);
CREATE TRIGGER update_link_shorts_modtime BEFORE UPDATE ON link_shorts FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE tag_link_shorts (
id SERIAL PRIMARY KEY,
link_short_id INT REFERENCES link_shorts (id) ON DELETE CASCADE NOT NULL,
tag_id INT REFERENCES tags (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_tag_link_short UNIQUE (link_short_id, tag_id)
);
CREATE INDEX tag_link_shorts_id_idx ON tag_link_shorts (id);
CREATE INDEX tag_link_shorts_link_short_id_idx ON tag_link_shorts (link_short_id);
CREATE INDEX tag_link_shorts_tag_id_idx ON tag_link_shorts (tag_id);
CREATE TABLE listings (
id SERIAL PRIMARY KEY,
title VARCHAR ( 150 ) NOT NULL,
slug VARCHAR ( 150 ) NOT NULL,
image VARCHAR(1024) DEFAULT '',
metadata JSONB DEFAULT '{}',
domain_id INT REFERENCES domains (id) ON DELETE RESTRICT NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
user_id INT REFERENCES users (id) ON DELETE SET NULL,
is_default BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_slug_domain UNIQUE (slug, domain_id)
);
CREATE INDEX listings_id_idx ON listings (id);
CREATE INDEX listings_domain_id_idx ON listings (domain_id);
CREATE INDEX listings_org_id_idx ON listings (org_id);
CREATE INDEX listings_domain_user_id_idx ON listings (user_id);
CREATE TRIGGER update_listings_modtime BEFORE UPDATE ON link_shorts FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE tag_listings (
id SERIAL PRIMARY KEY,
listing_id INT REFERENCES listings (id) ON DELETE CASCADE NOT NULL,
tag_id INT REFERENCES tags (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_tag_listing UNIQUE (listing_id, tag_id)
);
CREATE INDEX tag_listings_id_idx ON tag_listings (id);
CREATE INDEX tag_listings_listing_id_idx ON tag_listings (listing_id);
CREATE INDEX tag_listings_tag_id_idx ON tag_listings (tag_id);
CREATE TABLE listing_links (
id SERIAL PRIMARY KEY,
title VARCHAR ( 150 ) NOT NULL,
listing_id INT REFERENCES listings (id) ON DELETE CASCADE NOT NULL,
link_order INT DEFAULT 0,
url TEXT NOT NULL,
description TEXT DEFAULT '',
user_id INT REFERENCES users (id) ON DELETE SET NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX listing_links_id_idx ON listing_links (id);
CREATE INDEX listing_links_listing_id_idx ON listing_links (listing_id);
CREATE INDEX listing_links_user_id_idx ON listing_links (user_id);
CREATE TRIGGER update_listing_links_modtime BEFORE UPDATE ON listing_links FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE qr_codes (
id SERIAL PRIMARY KEY,
title VARCHAR (150) NOT NULL,
hash_id VARCHAR(10) UNIQUE NOT NULL,
url TEXT NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
code_type qr_code_type NOT NULL,
image_path TEXT DEFAULT '',
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX qr_codes_id_idx ON qr_codes (id);
CREATE INDEX qr_codes_hash_id_idx ON qr_codes (hash_id);
CREATE INDEX qr_codes_org_id_idx ON qr_codes (org_id);
CREATE INDEX qr_codes_user_id_idx ON qr_codes (user_id);
CREATE TRIGGER update_qr_codes_modtime BEFORE UPDATE ON qr_codes FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE qr_codes_shorts (
id SERIAL PRIMARY KEY,
qr_code_id INT REFERENCES qr_codes (id) ON DELETE CASCADE NOT NULL,
short_id INT REFERENCES link_shorts (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX qr_codes_shorts_id_idx ON qr_codes_shorts (id);
CREATE INDEX qr_codes_shorts_code_id_idx ON qr_codes_shorts (qr_code_id);
CREATE INDEX qr_codes_shorts_short_id_idx ON qr_codes_shorts (short_id);
CREATE TRIGGER update_qr_codes_shorts_modtime BEFORE UPDATE ON qr_codes_shorts FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE qr_codes_lists (
id SERIAL PRIMARY KEY,
qr_code_id INT REFERENCES qr_codes (id) ON DELETE CASCADE NOT NULL,
list_id INT REFERENCES listings (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX qr_codes_lists_id_idx ON qr_codes_lists (id);
CREATE INDEX qr_codes_lists_code_id_idx ON qr_codes_lists (qr_code_id);
CREATE INDEX qr_codes_lists_list_id_idx ON qr_codes_lists (list_id);
CREATE TABLE daily_totals (
id SERIAL PRIMARY KEY,
clicks INT DEFAULT 0,
unique_clicks INT DEFAULT 0,
"date" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
link_short_id INT REFERENCES link_shorts (id) ON DELETE CASCADE DEFAULT NULL,
list_id INT REFERENCES listings (id) ON DELETE CASCADE DEFAULT NULL,
listing_link_id INT REFERENCES listing_links (id) ON DELETE CASCADE DEFAULT NULL,
qr_id INT REFERENCES qr_codes (id) ON DELETE CASCADE DEFAULT NULL,
-- Check constraint to ensure only one of the three columns is NOT NULL
CONSTRAINT only_one_not_null_constraint
CHECK (
(listing_link_id IS NOT NULL AND link_short_id IS NULL AND list_id IS NULL AND qr_id IS NULL) OR
(listing_link_id IS NULL AND link_short_id IS NOT NULL AND list_id IS NULL AND qr_id IS NULL) OR
(listing_link_id IS NULL AND link_short_id IS NULL AND list_id IS NOT NULL AND qr_id IS NULL) OR
(listing_link_id IS NULL AND link_short_id IS NULL AND list_id IS NULL AND qr_id IS NOT NULL)
)
);
CREATE INDEX daily_totals_id_idx ON daily_totals (id);
CREATE INDEX daily_totals_link_short_id_idx ON daily_totals (link_short_id);
CREATE INDEX daily_totals_list_id_idx ON daily_totals (list_id);
CREATE INDEX daily_totals_listing_link_id_idx ON daily_totals (listing_link_id);
CREATE INDEX daily_totals_qr_id_idx ON daily_totals (qr_id);
CREATE TABLE totals_meta (
id SERIAL PRIMARY KEY,
daily_total_id INT REFERENCES daily_totals (id) ON DELETE CASCADE NOT NULL,
meta_type totals_meta_type DEFAULT 'REFERRER',
meta_value VARCHAR(1024) DEFAULT '',
clicks INT DEFAULT 0,
unique_clicks INT DEFAULT 0,
CONSTRAINT unique_daily_total_meta UNIQUE (daily_total_id, meta_type, meta_value)
);
CREATE INDEX totals_meta_id_idx ON totals_meta (id);
CREATE INDEX totals_meta_daily_total_id_idx ON totals_meta (daily_total_id);
CREATE TABLE subscription_plans (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL,
plan_id VARCHAR(150) NOT NULL,
stripe_price_id VARCHAR(150) NOT NULL,
price INT NOT NULL CHECK (price > 0),
"type" subscription_type NOT NULL DEFAULT 'PERSONAL',
is_active BOOLEAN DEFAULT true,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX subscription_plans_id_idx ON subscription_plans (id);
CREATE TRIGGER update_subscription_plans_modtime BEFORE UPDATE ON subscription_plans FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
stripe_id VARCHAR(150) UNIQUE NOT NULL,
subscription_plan_id INT REFERENCES subscription_plans (id) ON DELETE RESTRICT DEFAULT NULL,
start_date TIMESTAMPTZ DEFAULT NULL,
end_date TIMESTAMPTZ DEFAULT NULL,
cancel_end BOOLEAN DEFAULT false,
is_active BOOLEAN DEFAULT true,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX subscriptions_id_idx ON subscriptions (id);
CREATE INDEX subscriptions_user_id_idx ON subscriptions (user_id);
CREATE INDEX subscriptions_org_id_idx ON subscriptions (org_id);
CREATE TRIGGER update_subscriptions_modtime BEFORE UPDATE ON subscriptions FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE invoices (
id SERIAL PRIMARY KEY,
status invoice_status DEFAULT 'PENDING',
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
stripe_id VARCHAR(150),
subscription_id INT REFERENCES subscriptions (id) ON DELETE RESTRICT DEFAULT NULL,
currency VARCHAR(10) DEFAULT 'USD',
amount INT NOT NULL,
amount_paid INT DEFAULT 0,
amount_net INT DEFAULT 0,
amount_refunded INT DEFAULT 0,
payment_fee INT DEFAULT 0,
hosted_invoice_url VARCHAR(1024),
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX invoices_id_idx ON invoices (id);
CREATE INDEX invoices_user_id_idx ON invoices (user_id);
CREATE INDEX invoices_org_id_idx ON invoices (org_id);
CREATE TRIGGER update_invoices_modtime BEFORE UPDATE ON invoices FOR EACH ROW EXECUTE PROCEDURE update_updated_on_column();
CREATE TABLE followers (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL,
org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL,
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_followers UNIQUE (user_id, org_id)
);
CREATE INDEX followers_id_idx ON followers (id);
CREATE INDEX followers_user_id_idx ON followers (user_id);
CREATE INDEX followers_org_id_idx ON followers (org_id);
CREATE TABLE audit_log (
id serial PRIMARY KEY,
user_id INTEGER REFERENCES users (id) ON DELETE CASCADE,
ip_address character varying(50) NOT NULL,
event_type character varying(256) NOT NULL,
details character varying(512),
metadata JSONB DEFAULT '{}',
created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX audit_log_id_idx ON audit_log (id);
CREATE INDEX audit_log_created_on_idx ON audit_log (created_on);