69 lines
3.0 KiB
SQL
69 lines
3.0 KiB
SQL
-- Portable SQL: TEXT ids and ISO-8601 timestamps, INTEGER booleans, TEXT JSON.
|
|
CREATE TABLE user_roles (
|
|
user_id TEXT NOT NULL, role TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, role)
|
|
);
|
|
CREATE TABLE groups (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE);
|
|
CREATE TABLE group_members (
|
|
group_id TEXT NOT NULL, user_id TEXT NOT NULL,
|
|
PRIMARY KEY (group_id, user_id)
|
|
);
|
|
CREATE TABLE course_authors (
|
|
course_id TEXT NOT NULL, user_id TEXT NOT NULL,
|
|
PRIMARY KEY (course_id, user_id)
|
|
);
|
|
CREATE TABLE course_acl (
|
|
course_id TEXT NOT NULL,
|
|
principal_type TEXT NOT NULL CHECK (principal_type IN ('user', 'group')),
|
|
principal_id TEXT NOT NULL,
|
|
PRIMARY KEY (course_id, principal_type, principal_id)
|
|
);
|
|
CREATE TABLE progress (
|
|
user_id TEXT NOT NULL, course_id TEXT NOT NULL, lesson_id TEXT NOT NULL,
|
|
state TEXT NOT NULL CHECK (state IN ('not-started', 'in-progress', 'done')),
|
|
position_seconds REAL,
|
|
updated_at TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, course_id, lesson_id)
|
|
);
|
|
CREATE TABLE notes (
|
|
id TEXT PRIMARY KEY, user_id TEXT NOT NULL, course_id TEXT NOT NULL, lesson_id TEXT NOT NULL,
|
|
anchor_kind TEXT NOT NULL CHECK (anchor_kind IN ('none', 'video', 'slide')),
|
|
anchor_value TEXT, deck_id TEXT,
|
|
body TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX notes_by_lesson ON notes (user_id, course_id, lesson_id);
|
|
CREATE TABLE exercise_attempts (
|
|
user_id TEXT NOT NULL, course_id TEXT NOT NULL, lesson_id TEXT NOT NULL, track TEXT NOT NULL,
|
|
code TEXT NOT NULL, result_json TEXT, passed INTEGER NOT NULL, updated_at TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, course_id, lesson_id, track)
|
|
);
|
|
CREATE TABLE user_api_keys (
|
|
user_id TEXT NOT NULL, provider TEXT NOT NULL, ciphertext TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, provider)
|
|
);
|
|
CREATE TABLE ai_settings (
|
|
scope TEXT NOT NULL CHECK (scope IN ('server', 'user')),
|
|
owner_id TEXT NOT NULL, -- '' for server scope
|
|
mentor_off INTEGER NOT NULL, config_json TEXT NOT NULL, updated_at TEXT NOT NULL,
|
|
PRIMARY KEY (scope, owner_id)
|
|
);
|
|
CREATE TABLE ai_conversations (
|
|
id TEXT PRIMARY KEY, user_id TEXT NOT NULL, course_id TEXT NOT NULL, title TEXT, created_at TEXT NOT NULL
|
|
);
|
|
CREATE TABLE ai_messages (
|
|
id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX ai_messages_by_conversation ON ai_messages (conversation_id);
|
|
CREATE TABLE library_settings (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
CREATE TABLE collections (id TEXT PRIMARY KEY, title TEXT NOT NULL, sort INTEGER NOT NULL);
|
|
CREATE TABLE collection_courses (
|
|
collection_id TEXT NOT NULL, course_id TEXT NOT NULL, sort INTEGER NOT NULL,
|
|
PRIMARY KEY (collection_id, course_id)
|
|
);
|
|
CREATE TABLE jobs (
|
|
id TEXT PRIMARY KEY, kind TEXT NOT NULL, status TEXT NOT NULL, progress REAL NOT NULL,
|
|
message TEXT, payload_json TEXT NOT NULL, result_json TEXT, error TEXT,
|
|
created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX jobs_by_status ON jobs (status);
|