1CREATE TABLE users (
2 id TEXT PRIMARY KEY CHECK (length(id) = 36),
3 username TEXT NOT NULL UNIQUE
4 CHECK (
5 length(username) BETWEEN 1 AND 32
6 AND username = lower(username)
7 AND substr(username, 1, 1) GLOB '[a-z0-9]'
8 AND username NOT GLOB '*[^a-z0-9._-]*'
9 ),
10 email TEXT NOT NULL UNIQUE
11 CHECK (length(email) > 0 AND email = lower(email)),
12 email_verified INTEGER NOT NULL CHECK (email_verified IN (0, 1)),
13 display_name TEXT NOT NULL
14 CHECK (length(CAST(display_name AS BLOB)) <= 128),
15 password_hash BLOB NOT NULL CHECK (length(password_hash) > 0),
16 disabled INTEGER NOT NULL CHECK (disabled IN (0, 1)),
17 uid_number INTEGER NOT NULL UNIQUE
18 CHECK (uid_number BETWEEN 10000 AND 19999),
19 created_at INTEGER NOT NULL,
20 last_login_at INTEGER
21) STRICT;
22
23CREATE TABLE groups (
24 id TEXT PRIMARY KEY CHECK (length(id) = 36),
25 name TEXT NOT NULL UNIQUE
26 CHECK (
27 length(name) BETWEEN 1 AND 32
28 AND name = lower(name)
29 AND substr(name, 1, 1) GLOB '[a-z0-9]'
30 AND name NOT GLOB '*[^a-z0-9_-]*'
31 ),
32 description TEXT NOT NULL,
33 gid_number INTEGER NOT NULL UNIQUE
34 CHECK (gid_number BETWEEN 20000 AND 2147483647),
35 created_at INTEGER NOT NULL
36) STRICT;
37
38CREATE TABLE memberships (
39 user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
40 group_id TEXT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
41 PRIMARY KEY (user_id, group_id)
42) STRICT;
43
44CREATE INDEX memberships_group_id_idx ON memberships(group_id, user_id);
45
46CREATE TABLE google_links (
47 user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
48 google_sub TEXT NOT NULL UNIQUE CHECK (length(google_sub) > 0),
49 google_email_snapshot TEXT NOT NULL
50 CHECK (
51 length(google_email_snapshot) > 0
52 AND google_email_snapshot = lower(google_email_snapshot)
53 ),
54 linked_at INTEGER NOT NULL
55) STRICT;
56
57CREATE TABLE invitations (
58 id TEXT PRIMARY KEY CHECK (length(id) = 36),
59 token_hash BLOB NOT NULL UNIQUE CHECK (length(token_hash) > 0),
60 email TEXT CHECK (email IS NULL OR (length(email) > 0 AND email = lower(email))),
61 created_by TEXT REFERENCES users(id) ON DELETE SET NULL,
62 created_at INTEGER NOT NULL,
63 expires_at INTEGER NOT NULL,
64 used_by TEXT REFERENCES users(id) ON DELETE SET NULL,
65 used_at INTEGER,
66 revoked_at INTEGER,
67 CHECK (used_at IS NULL OR revoked_at IS NULL)
68) STRICT;
69
70CREATE INDEX invitations_created_at_idx ON invitations(created_at DESC, id);
71
72CREATE TABLE audit_log (
73 id INTEGER PRIMARY KEY AUTOINCREMENT,
74 actor TEXT NOT NULL CHECK (length(actor) > 0),
75 action TEXT NOT NULL CHECK (length(action) > 0),
76 subject TEXT NOT NULL CHECK (length(subject) > 0),
77 occurred_at INTEGER NOT NULL,
78 detail TEXT NOT NULL
79) STRICT;
80
81CREATE INDEX audit_log_occurred_at_idx ON audit_log(occurred_at DESC, id DESC);
82CREATE INDEX audit_log_actor_idx ON audit_log(actor, occurred_at DESC, id DESC);
83CREATE INDEX audit_log_action_idx ON audit_log(action, occurred_at DESC, id DESC);
84CREATE INDEX audit_log_subject_idx ON audit_log(subject, occurred_at DESC, id DESC);
85
86CREATE TABLE forward_sessions (
87 id TEXT PRIMARY KEY CHECK (length(id) > 0),
88 user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
89 app_domain TEXT NOT NULL CHECK (length(app_domain) > 0),
90 created_at INTEGER NOT NULL,
91 expires_at INTEGER NOT NULL,
92 revoked INTEGER NOT NULL CHECK (revoked IN (0, 1))
93) STRICT;
94
95CREATE INDEX forward_sessions_user_id_idx
96 ON forward_sessions(user_id, created_at DESC, id);
97
98CREATE TABLE settings (
99 id INTEGER PRIMARY KEY CHECK (id = 1),
100 config_commit TEXT CHECK (config_commit IS NULL OR length(config_commit) > 0),
101 userbase_commit TEXT CHECK (userbase_commit IS NULL OR length(userbase_commit) > 0),
102 next_uid_number INTEGER NOT NULL
103 CHECK (next_uid_number BETWEEN 10000 AND 20000),
104 next_gid_number INTEGER NOT NULL
105 CHECK (next_gid_number BETWEEN 20000 AND 2147483648)
106) STRICT;
107
108INSERT INTO settings (
109 id,
110 config_commit,
111 userbase_commit,
112 next_uid_number,
113 next_gid_number
114) VALUES (1, NULL, NULL, 10000, 20000);