···1313const migrations = [
1414 {
1515 version: 1,
1616- name: "Complete schema with class system",
1616+ name: "Initial schema with all tables and constraints",
1717 sql: `
1818 -- Users table
1919 CREATE TABLE IF NOT EXISTS users (
···2424 avatar TEXT DEFAULT 'd',
2525 role TEXT NOT NULL DEFAULT 'user',
2626 last_login INTEGER,
2727+ email_verified BOOLEAN DEFAULT 0,
2828+ email_notifications_enabled BOOLEAN DEFAULT 1,
2729 created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
2830 );
29313032 CREATE INDEX IF NOT EXISTS idx_users_role ON users(role);
3133 CREATE INDEX IF NOT EXISTS idx_users_last_login ON users(last_login);
3434+ CREATE INDEX IF NOT EXISTS idx_users_email_verified ON users(email_verified);
32353336 -- Sessions table
3437 CREATE TABLE IF NOT EXISTS sessions (
···84878588 CREATE INDEX IF NOT EXISTS idx_classes_semester_year ON classes(semester, year);
8689 CREATE INDEX IF NOT EXISTS idx_classes_archived ON classes(archived);
9090+ CREATE INDEX IF NOT EXISTS idx_classes_course_code ON classes(course_code);
87918892 -- Class members table
8993 CREATE TABLE IF NOT EXISTS class_members (
···132136 CREATE INDEX IF NOT EXISTS idx_transcriptions_class_id ON transcriptions(class_id);
133137 CREATE INDEX IF NOT EXISTS idx_transcriptions_status ON transcriptions(status);
134138 CREATE INDEX IF NOT EXISTS idx_transcriptions_whisper_job_id ON transcriptions(whisper_job_id);
135135- `,
136136- },
137137- {
138138- version: 2,
139139- name: "Add section column to classes table",
140140- sql: `
141141- ALTER TABLE classes ADD COLUMN section TEXT;
142142- CREATE INDEX IF NOT EXISTS idx_classes_course_code ON classes(course_code);
143143- `,
144144- },
145145- {
146146- version: 3,
147147- name: "Add class waitlist table",
148148- sql: `
139139+ CREATE INDEX IF NOT EXISTS idx_transcriptions_meeting_time_id ON transcriptions(meeting_time_id);
140140+141141+ -- Class waitlist table
149142 CREATE TABLE IF NOT EXISTS class_waitlist (
150143 id TEXT PRIMARY KEY,
151144 user_id INTEGER NOT NULL,
152145 course_code TEXT NOT NULL,
153146 course_name TEXT NOT NULL,
154147 professor TEXT NOT NULL,
155155- section TEXT,
156148 semester TEXT NOT NULL,
157149 year INTEGER NOT NULL,
150150+ meeting_times TEXT,
158151 additional_info TEXT,
159152 created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
160153 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
···162155163156 CREATE INDEX IF NOT EXISTS idx_waitlist_user_id ON class_waitlist(user_id);
164157 CREATE INDEX IF NOT EXISTS idx_waitlist_course_code ON class_waitlist(course_code);
165165- `,
166166- },
167167- {
168168- version: 4,
169169- name: "Add meeting_times to class_waitlist",
170170- sql: `
171171- ALTER TABLE class_waitlist ADD COLUMN meeting_times TEXT;
172172- `,
173173- },
174174- {
175175- version: 5,
176176- name: "Remove section columns",
177177- sql: `
178178- DROP INDEX IF EXISTS idx_classes_section;
179179- ALTER TABLE classes DROP COLUMN section;
180180- ALTER TABLE class_waitlist DROP COLUMN section;
181181- `,
182182- },
183183- {
184184- version: 6,
185185- name: "Add subscriptions table for Polar integration",
186186- sql: `
158158+187159 -- Subscriptions table
188160 CREATE TABLE IF NOT EXISTS subscriptions (
189161 id TEXT PRIMARY KEY,
···202174 CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
203175 CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
204176 CREATE INDEX IF NOT EXISTS idx_subscriptions_customer_id ON subscriptions(customer_id);
205205- `,
206206- },
207207- {
208208- version: 7,
209209- name: "Create ghost user for deleted accounts",
210210- sql: `
211211- -- Create a ghost user account for orphaned transcriptions
212212- INSERT OR IGNORE INTO users (id, email, password_hash, name, avatar, role, created_at)
213213- VALUES (0, 'ghosty@thistle.internal', NULL, 'Ghosty', '👻', 'user', strftime('%s', 'now'));
214214- `,
215215- },
216216- {
217217- version: 8,
218218- name: "Add email verification system",
219219- sql: `
220220- -- Add email verification flag to users
221221- ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT 0;
222177223178 -- Email verification tokens table
224179 CREATE TABLE IF NOT EXISTS email_verification_tokens (
···245200246201 CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_user_id ON password_reset_tokens(user_id);
247202 CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_token ON password_reset_tokens(token);
248248- `,
249249- },
250250- {
251251- version: 10,
252252- name: "Add email notification preferences",
253253- sql: `
254254- ALTER TABLE users ADD COLUMN email_notifications_enabled BOOLEAN DEFAULT 1;
255255- `,
256256- },
257257- {
258258- version: 9,
259259- name: "Add email change tokens table",
260260- sql: `
203203+261204 -- Email change tokens table
262205 CREATE TABLE IF NOT EXISTS email_change_tokens (
263206 id TEXT PRIMARY KEY,
···271214272215 CREATE INDEX IF NOT EXISTS idx_email_change_tokens_user_id ON email_change_tokens(user_id);
273216 CREATE INDEX IF NOT EXISTS idx_email_change_tokens_token ON email_change_tokens(token);
274274- `,
275275- },
276276- {
277277- version: 11,
278278- name: "Add NOT NULL constraints and missing indexes",
279279- sql: `
280280- -- Note: SQLite doesn't support ALTER COLUMN to add NOT NULL to existing columns
281281- -- These tables were created in migration 6 and 8, but the columns should have been NOT NULL
282282- -- The constraints are enforced at the application level, this migration documents the intent
283283-284284- -- Add missing indexes for performance
285285- CREATE INDEX IF NOT EXISTS idx_users_email_verified ON users(email_verified);
286286- CREATE INDEX IF NOT EXISTS idx_transcriptions_meeting_time_id ON transcriptions(meeting_time_id);
217217+218218+ -- Create ghost user for deleted accounts
219219+ INSERT OR IGNORE INTO users (id, email, password_hash, name, avatar, role, created_at)
220220+ VALUES (0, 'ghosty@thistle.internal', NULL, 'Ghosty', '👻', 'user', strftime('%s', 'now'));
287221 `,
288222 },
289223];