this repo has no description
1CREATE TYPE notification_channel AS ENUM ('email', 'discord', 'telegram', 'signal');
2CREATE TYPE notification_status AS ENUM ('pending', 'processing', 'sent', 'failed');
3CREATE TYPE notification_type AS ENUM (
4 'welcome',
5 'email_verification',
6 'password_reset',
7 'email_update',
8 'account_deletion',
9 'admin_email'
10);
11
12CREATE TABLE IF NOT EXISTS users (
13 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
14 handle TEXT NOT NULL UNIQUE,
15 email TEXT NOT NULL UNIQUE,
16 did TEXT NOT NULL UNIQUE,
17 password_hash TEXT NOT NULL,
18 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
19 updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
20
21 -- status & moderation
22 deactivated_at TIMESTAMPTZ,
23 invites_disabled BOOLEAN DEFAULT FALSE,
24 takedown_ref TEXT,
25
26 -- notifs
27 preferred_notification_channel notification_channel NOT NULL DEFAULT 'email',
28
29 -- auth & verification
30 password_reset_code TEXT,
31 password_reset_code_expires_at TIMESTAMPTZ,
32
33 email_pending_verification TEXT,
34 email_confirmation_code TEXT,
35 email_confirmation_code_expires_at TIMESTAMPTZ
36);
37
38CREATE INDEX IF NOT EXISTS idx_users_password_reset_code ON users(password_reset_code) WHERE password_reset_code IS NOT NULL;
39CREATE INDEX IF NOT EXISTS idx_users_email_confirmation_code ON users(email_confirmation_code) WHERE email_confirmation_code IS NOT NULL;
40
41CREATE TABLE IF NOT EXISTS invite_codes (
42 code TEXT PRIMARY KEY,
43 available_uses INT NOT NULL DEFAULT 1,
44 created_by_user UUID NOT NULL REFERENCES users(id),
45 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
46 disabled BOOLEAN DEFAULT FALSE
47);
48
49CREATE TABLE IF NOT EXISTS invite_code_uses (
50 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
51 code TEXT NOT NULL REFERENCES invite_codes(code),
52 used_by_user UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
53 used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
54 UNIQUE(code, used_by_user)
55);
56
57-- TODO: encrypt at rest!
58CREATE TABLE IF NOT EXISTS user_keys (
59 user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
60 key_bytes BYTEA NOT NULL,
61 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
62);
63
64CREATE TABLE IF NOT EXISTS repos (
65 user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
66 repo_root_cid TEXT NOT NULL,
67 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
68 updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
69);
70
71-- content addressable storage
72CREATE TABLE IF NOT EXISTS blocks (
73 cid BYTEA PRIMARY KEY,
74 data BYTEA NOT NULL,
75 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
76);
77
78-- denormalized index for fast queries
79CREATE TABLE IF NOT EXISTS records (
80 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
81 repo_id UUID NOT NULL REFERENCES repos(user_id) ON DELETE CASCADE,
82 collection TEXT NOT NULL,
83 rkey TEXT NOT NULL,
84 record_cid TEXT NOT NULL,
85 takedown_ref TEXT,
86 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
87 UNIQUE(repo_id, collection, rkey)
88);
89
90CREATE TABLE IF NOT EXISTS blobs (
91 cid TEXT PRIMARY KEY,
92 mime_type TEXT NOT NULL,
93 size_bytes BIGINT NOT NULL,
94 created_by_user UUID NOT NULL REFERENCES users(id),
95 storage_key TEXT NOT NULL,
96 takedown_ref TEXT,
97 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
98);
99
100CREATE TABLE IF NOT EXISTS sessions (
101 access_jwt TEXT PRIMARY KEY,
102 refresh_jwt TEXT NOT NULL UNIQUE,
103 did TEXT NOT NULL REFERENCES users(did) ON DELETE CASCADE,
104 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
105);
106
107CREATE TABLE IF NOT EXISTS app_passwords (
108 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
109 user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
110 name TEXT NOT NULL,
111 password_hash TEXT NOT NULL,
112 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
113 privileged BOOLEAN NOT NULL DEFAULT FALSE,
114 UNIQUE(user_id, name)
115);
116
117-- naughty list
118CREATE TABLE reports (
119 id BIGINT PRIMARY KEY,
120 reason_type TEXT NOT NULL,
121 reason TEXT,
122 subject_json JSONB NOT NULL,
123 reported_by_did TEXT NOT NULL,
124 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
125);
126
127CREATE TABLE IF NOT EXISTS account_deletion_requests (
128 token TEXT PRIMARY KEY,
129 did TEXT NOT NULL REFERENCES users(did) ON DELETE CASCADE,
130 expires_at TIMESTAMPTZ NOT NULL,
131 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
132);
133
134CREATE TABLE IF NOT EXISTS notification_queue (
135 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
136 user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
137 channel notification_channel NOT NULL DEFAULT 'email',
138 notification_type notification_type NOT NULL,
139 status notification_status NOT NULL DEFAULT 'pending',
140 recipient TEXT NOT NULL,
141 subject TEXT,
142 body TEXT NOT NULL,
143 metadata JSONB,
144 attempts INT NOT NULL DEFAULT 0,
145 max_attempts INT NOT NULL DEFAULT 3,
146 last_error TEXT,
147 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
148 updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
149 scheduled_for TIMESTAMPTZ NOT NULL DEFAULT NOW(),
150 processed_at TIMESTAMPTZ
151);
152
153CREATE INDEX idx_notification_queue_status_scheduled
154 ON notification_queue(status, scheduled_for)
155 WHERE status = 'pending';
156
157CREATE INDEX idx_notification_queue_user_id ON notification_queue(user_id);