~netlandish/links

ref: 02c85f04f6867069e8f9f8296841f4a4a017b39d links/models/schema.sql -rw-r--r-- 22.0 KiB
02c85f04Peter Sanchez Adding a simple go routine to process BaseURL metadata if they have previously failed. 7 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
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);
Do not follow this link