feat(db): ownership columns and lesson_subscriptions table
This commit is contained in:
22
packages/backend/drizzle/0002_youthful_storm.sql
Normal file
22
packages/backend/drizzle/0002_youthful_storm.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE `lesson_subscriptions` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` integer NOT NULL,
|
||||
`lesson_id` integer NOT NULL,
|
||||
`created_at` integer DEFAULT (unixepoch()) NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`lesson_id`) REFERENCES `lessons`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `card_progress` ADD `user_id` integer REFERENCES users(id);--> statement-breakpoint
|
||||
ALTER TABLE `lessons` ADD `owner_id` integer REFERENCES users(id);--> statement-breakpoint
|
||||
ALTER TABLE `lessons` ADD `visibility` text DEFAULT 'private' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `lessons` ADD `is_curated` integer DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `lessons` ADD `source_lesson_id` integer REFERENCES lessons(id);--> statement-breakpoint
|
||||
ALTER TABLE `sessions` ADD `user_id` integer REFERENCES users(id);--> statement-breakpoint
|
||||
CREATE INDEX `lesson_subscriptions_user_idx` ON `lesson_subscriptions` (`user_id`);--> statement-breakpoint
|
||||
CREATE INDEX `lesson_subscriptions_lesson_idx` ON `lesson_subscriptions` (`lesson_id`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `lesson_subscriptions_user_lesson_unique` ON `lesson_subscriptions` (`user_id`,`lesson_id`);--> statement-breakpoint
|
||||
CREATE INDEX `card_progress_user_idx` ON `card_progress` (`user_id`,`next_due_at`);--> statement-breakpoint
|
||||
CREATE INDEX `lessons_owner_idx` ON `lessons` (`owner_id`);--> statement-breakpoint
|
||||
CREATE INDEX `lessons_visibility_idx` ON `lessons` (`visibility`,`is_curated`);--> statement-breakpoint
|
||||
CREATE INDEX `sessions_user_idx` ON `sessions` (`user_id`,`status`);
|
||||
975
packages/backend/drizzle/meta/0002_snapshot.json
Normal file
975
packages/backend/drizzle/meta/0002_snapshot.json
Normal file
@@ -0,0 +1,975 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "763fe305-72a6-40e2-bc36-ed330b403044",
|
||||
"prevId": "87d22455-00f9-454b-baac-bc7638274645",
|
||||
"tables": {
|
||||
"attempts": {
|
||||
"name": "attempts",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"session_id": {
|
||||
"name": "session_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"card_id": {
|
||||
"name": "card_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"direction": {
|
||||
"name": "direction",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"shown_at": {
|
||||
"name": "shown_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"result": {
|
||||
"name": "result",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_to_answer_ms": {
|
||||
"name": "time_to_answer_ms",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"attempts_session_idx": {
|
||||
"name": "attempts_session_idx",
|
||||
"columns": [
|
||||
"session_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"attempts_card_idx": {
|
||||
"name": "attempts_card_idx",
|
||||
"columns": [
|
||||
"card_id"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"attempts_session_id_sessions_id_fk": {
|
||||
"name": "attempts_session_id_sessions_id_fk",
|
||||
"tableFrom": "attempts",
|
||||
"tableTo": "sessions",
|
||||
"columnsFrom": [
|
||||
"session_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"attempts_card_id_cards_id_fk": {
|
||||
"name": "attempts_card_id_cards_id_fk",
|
||||
"tableFrom": "attempts",
|
||||
"tableTo": "cards",
|
||||
"columnsFrom": [
|
||||
"card_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"auth_tokens": {
|
||||
"name": "auth_tokens",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"token_hash": {
|
||||
"name": "token_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"purpose": {
|
||||
"name": "purpose",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payload": {
|
||||
"name": "payload",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"used_at": {
|
||||
"name": "used_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"auth_tokens_hash_idx": {
|
||||
"name": "auth_tokens_hash_idx",
|
||||
"columns": [
|
||||
"token_hash"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"auth_tokens_user_purpose_idx": {
|
||||
"name": "auth_tokens_user_purpose_idx",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"purpose"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"auth_tokens_user_id_users_id_fk": {
|
||||
"name": "auth_tokens_user_id_users_id_fk",
|
||||
"tableFrom": "auth_tokens",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"card_progress": {
|
||||
"name": "card_progress",
|
||||
"columns": {
|
||||
"card_id": {
|
||||
"name": "card_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"direction": {
|
||||
"name": "direction",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"box": {
|
||||
"name": "box",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"correct_count": {
|
||||
"name": "correct_count",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"incorrect_count": {
|
||||
"name": "incorrect_count",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"last_shown_at": {
|
||||
"name": "last_shown_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"next_due_at": {
|
||||
"name": "next_due_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"card_progress_pk": {
|
||||
"name": "card_progress_pk",
|
||||
"columns": [
|
||||
"card_id",
|
||||
"direction"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"card_progress_due_idx": {
|
||||
"name": "card_progress_due_idx",
|
||||
"columns": [
|
||||
"next_due_at"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"card_progress_user_idx": {
|
||||
"name": "card_progress_user_idx",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"next_due_at"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"card_progress_card_id_cards_id_fk": {
|
||||
"name": "card_progress_card_id_cards_id_fk",
|
||||
"tableFrom": "card_progress",
|
||||
"tableTo": "cards",
|
||||
"columnsFrom": [
|
||||
"card_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"card_progress_user_id_users_id_fk": {
|
||||
"name": "card_progress_user_id_users_id_fk",
|
||||
"tableFrom": "card_progress",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"cards": {
|
||||
"name": "cards",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"lesson_id": {
|
||||
"name": "lesson_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"question": {
|
||||
"name": "question",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"answer": {
|
||||
"name": "answer",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"hint": {
|
||||
"name": "hint",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"position": {
|
||||
"name": "position",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"cards_lesson_idx": {
|
||||
"name": "cards_lesson_idx",
|
||||
"columns": [
|
||||
"lesson_id"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"cards_lesson_id_lessons_id_fk": {
|
||||
"name": "cards_lesson_id_lessons_id_fk",
|
||||
"tableFrom": "cards",
|
||||
"tableTo": "lessons",
|
||||
"columnsFrom": [
|
||||
"lesson_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"lesson_subscriptions": {
|
||||
"name": "lesson_subscriptions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"lesson_id": {
|
||||
"name": "lesson_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"lesson_subscriptions_user_idx": {
|
||||
"name": "lesson_subscriptions_user_idx",
|
||||
"columns": [
|
||||
"user_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"lesson_subscriptions_lesson_idx": {
|
||||
"name": "lesson_subscriptions_lesson_idx",
|
||||
"columns": [
|
||||
"lesson_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"lesson_subscriptions_user_lesson_unique": {
|
||||
"name": "lesson_subscriptions_user_lesson_unique",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"lesson_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"lesson_subscriptions_user_id_users_id_fk": {
|
||||
"name": "lesson_subscriptions_user_id_users_id_fk",
|
||||
"tableFrom": "lesson_subscriptions",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"lesson_subscriptions_lesson_id_lessons_id_fk": {
|
||||
"name": "lesson_subscriptions_lesson_id_lessons_id_fk",
|
||||
"tableFrom": "lesson_subscriptions",
|
||||
"tableTo": "lessons",
|
||||
"columnsFrom": [
|
||||
"lesson_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"lessons": {
|
||||
"name": "lessons",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"parent_id": {
|
||||
"name": "parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"position": {
|
||||
"name": "position",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"bidirectional": {
|
||||
"name": "bidirectional",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": false
|
||||
},
|
||||
"owner_id": {
|
||||
"name": "owner_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"visibility": {
|
||||
"name": "visibility",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'private'"
|
||||
},
|
||||
"is_curated": {
|
||||
"name": "is_curated",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": false
|
||||
},
|
||||
"source_lesson_id": {
|
||||
"name": "source_lesson_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"lessons_owner_idx": {
|
||||
"name": "lessons_owner_idx",
|
||||
"columns": [
|
||||
"owner_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"lessons_visibility_idx": {
|
||||
"name": "lessons_visibility_idx",
|
||||
"columns": [
|
||||
"visibility",
|
||||
"is_curated"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"lessons_owner_id_users_id_fk": {
|
||||
"name": "lessons_owner_id_users_id_fk",
|
||||
"tableFrom": "lessons",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"owner_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"lessons_source_lesson_id_lessons_id_fk": {
|
||||
"name": "lessons_source_lesson_id_lessons_id_fk",
|
||||
"tableFrom": "lessons",
|
||||
"tableTo": "lessons",
|
||||
"columnsFrom": [
|
||||
"source_lesson_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"sessions": {
|
||||
"name": "sessions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"lesson_id": {
|
||||
"name": "lesson_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"started_at": {
|
||||
"name": "started_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"ended_at": {
|
||||
"name": "ended_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"duration_seconds": {
|
||||
"name": "duration_seconds",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cards_shown": {
|
||||
"name": "cards_shown",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"cards_correct": {
|
||||
"name": "cards_correct",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"cards_incorrect": {
|
||||
"name": "cards_incorrect",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'active'"
|
||||
},
|
||||
"queue_snapshot": {
|
||||
"name": "queue_snapshot",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"sessions_status_idx": {
|
||||
"name": "sessions_status_idx",
|
||||
"columns": [
|
||||
"status"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"sessions_user_idx": {
|
||||
"name": "sessions_user_idx",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"status"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"sessions_lesson_id_lessons_id_fk": {
|
||||
"name": "sessions_lesson_id_lessons_id_fk",
|
||||
"tableFrom": "sessions",
|
||||
"tableTo": "lessons",
|
||||
"columnsFrom": [
|
||||
"lesson_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"sessions_user_id_users_id_fk": {
|
||||
"name": "sessions_user_id_users_id_fk",
|
||||
"tableFrom": "sessions",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"sessions_auth": {
|
||||
"name": "sessions_auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"last_used_at": {
|
||||
"name": "last_used_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"user_agent": {
|
||||
"name": "user_agent",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"ip": {
|
||||
"name": "ip",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"sessions_auth_user_idx": {
|
||||
"name": "sessions_auth_user_idx",
|
||||
"columns": [
|
||||
"user_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"sessions_auth_expires_idx": {
|
||||
"name": "sessions_auth_expires_idx",
|
||||
"columns": [
|
||||
"expires_at"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"sessions_auth_user_id_users_id_fk": {
|
||||
"name": "sessions_auth_user_id_users_id_fk",
|
||||
"tableFrom": "sessions_auth",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"users": {
|
||||
"name": "users",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"password_hash": {
|
||||
"name": "password_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'user'"
|
||||
},
|
||||
"is_active": {
|
||||
"name": "is_active",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": true
|
||||
},
|
||||
"email_verified_at": {
|
||||
"name": "email_verified_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"pending_email": {
|
||||
"name": "pending_email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(unixepoch())"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"users_email_unique": {
|
||||
"name": "users_email_unique",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"users_email_idx": {
|
||||
"name": "users_email_idx",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
"when": 1779309705807,
|
||||
"tag": "0001_fuzzy_silhouette",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1779314536550,
|
||||
"tag": "0002_youthful_storm",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,16 +1,28 @@
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { integer, sqliteTable, text, index } from 'drizzle-orm/sqlite-core';
|
||||
import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core';
|
||||
import { integer, sqliteTable, text, index, uniqueIndex } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
export const lessons = sqliteTable('lessons', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
parentId: integer('parent_id'),
|
||||
name: text('name').notNull(),
|
||||
description: text('description'),
|
||||
position: integer('position').notNull().default(0),
|
||||
bidirectional: integer('bidirectional', { mode: 'boolean' }).notNull().default(false),
|
||||
createdAt: integer('created_at').notNull().default(sql`(unixepoch())`),
|
||||
updatedAt: integer('updated_at').notNull().default(sql`(unixepoch())`),
|
||||
});
|
||||
export const lessons = sqliteTable(
|
||||
'lessons',
|
||||
{
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
parentId: integer('parent_id'),
|
||||
name: text('name').notNull(),
|
||||
description: text('description'),
|
||||
position: integer('position').notNull().default(0),
|
||||
bidirectional: integer('bidirectional', { mode: 'boolean' }).notNull().default(false),
|
||||
ownerId: integer('owner_id').references(() => users.id, { onDelete: 'cascade' }),
|
||||
visibility: text('visibility', { enum: ['private', 'shared'] }).notNull().default('private'),
|
||||
isCurated: integer('is_curated', { mode: 'boolean' }).notNull().default(false),
|
||||
sourceLessonId: integer('source_lesson_id').references((): AnySQLiteColumn => lessons.id, { onDelete: 'set null' }),
|
||||
createdAt: integer('created_at').notNull().default(sql`(unixepoch())`),
|
||||
updatedAt: integer('updated_at').notNull().default(sql`(unixepoch())`),
|
||||
},
|
||||
(t) => ({
|
||||
ownerIdx: index('lessons_owner_idx').on(t.ownerId),
|
||||
visibilityIdx: index('lessons_visibility_idx').on(t.visibility, t.isCurated),
|
||||
})
|
||||
);
|
||||
|
||||
export const cards = sqliteTable(
|
||||
'cards',
|
||||
@@ -37,10 +49,12 @@ export const cardProgress = sqliteTable(
|
||||
incorrectCount: integer('incorrect_count').notNull().default(0),
|
||||
lastShownAt: integer('last_shown_at'),
|
||||
nextDueAt: integer('next_due_at').notNull().default(0),
|
||||
userId: integer('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
||||
},
|
||||
(t) => ({
|
||||
pk: index('card_progress_pk').on(t.cardId, t.direction),
|
||||
dueIdx: index('card_progress_due_idx').on(t.nextDueAt),
|
||||
userIdx: index('card_progress_user_idx').on(t.userId, t.nextDueAt),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -57,8 +71,12 @@ export const sessions = sqliteTable(
|
||||
cardsIncorrect: integer('cards_incorrect').notNull().default(0),
|
||||
status: text('status', { enum: ['active', 'completed', 'abandoned'] }).notNull().default('active'),
|
||||
queueSnapshot: text('queue_snapshot'),
|
||||
userId: integer('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
||||
},
|
||||
(t) => ({ statusIdx: index('sessions_status_idx').on(t.status) })
|
||||
(t) => ({
|
||||
statusIdx: index('sessions_status_idx').on(t.status),
|
||||
userIdx: index('sessions_user_idx').on(t.userId, t.status),
|
||||
})
|
||||
);
|
||||
|
||||
export const attempts = sqliteTable(
|
||||
@@ -130,6 +148,23 @@ export const authTokens = sqliteTable(
|
||||
})
|
||||
);
|
||||
|
||||
export const lessonSubscriptions = sqliteTable(
|
||||
'lesson_subscriptions',
|
||||
{
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
lessonId: integer('lesson_id').notNull().references(() => lessons.id, { onDelete: 'cascade' }),
|
||||
createdAt: integer('created_at').notNull().default(sql`(unixepoch())`),
|
||||
},
|
||||
(t) => ({
|
||||
userIdx: index('lesson_subscriptions_user_idx').on(t.userId),
|
||||
lessonIdx: index('lesson_subscriptions_lesson_idx').on(t.lessonId),
|
||||
userLessonUnique: uniqueIndex('lesson_subscriptions_user_lesson_unique').on(t.userId, t.lessonId),
|
||||
})
|
||||
);
|
||||
|
||||
export type LessonSubscriptionRow = typeof lessonSubscriptions.$inferSelect;
|
||||
|
||||
export type LessonRow = typeof lessons.$inferSelect;
|
||||
export type CardRow = typeof cards.$inferSelect;
|
||||
export type CardProgressRow = typeof cardProgress.$inferSelect;
|
||||
|
||||
Reference in New Issue
Block a user