Testing Migrations

This commit is contained in:
Max committed 2026-11-03 12:53:17 -07:00
1 parent d9abeb7fb1
commit 9804771a5b
8 files changed
+1109

No files matched your search

+211
View File
@@ -0,0 +1,211 @@
--
-- Baseline migration: the schema as it existed when Prisma was adopted.
--
-- Generated with:
-- prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script
--
-- HAND-CORRECTED, and it matters: Prisma renders `COLLATE utf8mb4_unicode_ci`
-- for MySQL, but every live table is `utf8mb4_general_ci`. Prisma does not
-- model collation and its own `migrate diff` does NOT report the difference, so
-- without this correction a database rebuilt from empty would silently differ
-- from production in string comparison, sorting and FULLTEXT ranking.
-- If you regenerate this file, re-apply the collation fix. There is a test for
-- it in server/tests/schema/migrations.test.js.
--
-- Things Prisma cannot express, which therefore need handling elsewhere:
-- * the DATABASE-level default collation -- set by migrateDatabase.sh when it
-- creates the database, since tables created later without an explicit
-- COLLATE would otherwise inherit the server default
-- * ENGINE=InnoDB (relies on the server default, which is InnoDB)
-- * AUTO_INCREMENT start values (cosmetic; a fresh database starting at 1 is
-- correct)
-- * the COMMENT on note_raw_text.text
-- * row data -- see prisma/seed.sql for the attachment_type lookup rows
--
-- Context carried over from the migration this replaces: scrape_cache.url_hash
-- carries the UNIQUE index rather than `url` because a UNIQUE on
-- varchar(1000) utf8mb4 is 4000 bytes and exceeds InnoDB's 3072-byte key limit;
-- and attachment.scrape_state DEFAULT 2 ("done") is load-bearing -- it keeps
-- pre-existing attachment rows out of the scraper's boot recovery sweep.
--
-- On environments that already had this schema (dev and production), this file
-- is NOT executed -- it is marked applied with `prisma migrate resolve`.
--
-- CreateTable
CREATE TABLE `attachment` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`note_id` INTEGER UNSIGNED NULL,
`user_id` INTEGER UNSIGNED NOT NULL,
`attachment_type` INTEGER UNSIGNED NULL,
`text` MEDIUMTEXT NULL,
`url` VARCHAR(1000) NULL DEFAULT '',
`last_indexed` INTEGER NULL,
`file_location` VARCHAR(500) NULL,
`visible` TINYINT NULL DEFAULT 1,
`scrape_state` TINYINT NOT NULL DEFAULT 2,
`scrape_attempts` TINYINT NOT NULL DEFAULT 0,
`enqueued_at` INTEGER NULL,
INDEX `attachment_scrape_state`(`scrape_state`, `enqueued_at`),
INDEX `attachment_type`(`attachment_type`),
INDEX `note_id`(`note_id`),
INDEX `user_id`(`user_id`),
FULLTEXT INDEX `attachment_text_index`(`text`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `attachment_type` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`mime_type` VARCHAR(500) NULL,
`extension` VARCHAR(10) NULL,
`name` VARCHAR(50) NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `note` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INTEGER UNSIGNED NULL,
`note_raw_text_id` INTEGER NULL,
`opened` INTEGER NULL,
`created` INTEGER NULL,
`pinned` INTEGER NULL DEFAULT 0,
`archived` INTEGER NULL DEFAULT 0,
`trashed` TINYINT NULL DEFAULT 0,
`color` VARCHAR(500) NULL,
`shared` INTEGER NULL DEFAULT 0,
`share_user_id` INTEGER NULL,
`quick_note` INTEGER NULL DEFAULT 0,
`indexed` TINYINT NULL DEFAULT 0,
`snippet` VARCHAR(1300) NULL,
`snippet_salt` VARCHAR(1300) NULL,
`encrypted_share_password_key` VARCHAR(1300) NULL,
INDEX `user_note`(`user_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `note_raw_text` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`text` LONGTEXT NULL,
`updated` BIGINT NULL,
`salt` VARCHAR(1300) NULL,
`last_decrypted_date` INTEGER NULL,
`decrypt_attempts_count` INTEGER NULL DEFAULT 0,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `note_tag` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`note_id` INTEGER UNSIGNED NULL,
`tag_id` INTEGER UNSIGNED NULL,
`user_id` INTEGER UNSIGNED NULL,
INDEX `note_id`(`note_id`),
INDEX `tag_id`(`tag_id`),
INDEX `user_id`(`user_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `scrape_cache` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`url_hash` CHAR(64) NOT NULL,
`url` VARCHAR(1000) NOT NULL DEFAULT '',
`canonical_url` VARCHAR(1000) NULL,
`status` SMALLINT NOT NULL DEFAULT 0,
`title` VARCHAR(512) NULL,
`site_name` VARCHAR(255) NULL,
`description` MEDIUMTEXT NULL,
`keywords` VARCHAR(512) NULL,
`search_text` MEDIUMTEXT NULL,
`image_url` VARCHAR(1000) NULL,
`file_location` VARCHAR(500) NULL,
`etag` VARCHAR(255) NULL,
`last_modified` VARCHAR(128) NULL,
`content_type` VARCHAR(128) NULL,
`fetch_state` VARCHAR(20) NOT NULL DEFAULT 'pending',
`failure_count` INTEGER NOT NULL DEFAULT 0,
`last_error` VARCHAR(255) NULL,
`fetched_at` INTEGER NULL,
`expires_at` INTEGER NULL,
UNIQUE INDEX `scrape_cache_url_hash`(`url_hash`),
INDEX `scrape_cache_expires_at`(`expires_at`),
INDEX `scrape_cache_fetch_state`(`fetch_state`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `tag` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`text` VARCHAR(400) NULL,
`hash` INTEGER NULL,
INDEX `hash`(`hash`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `user` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(150) NOT NULL DEFAULT '',
`password` TEXT NOT NULL,
`salt` VARCHAR(500) NOT NULL DEFAULT '',
`iterations` INTEGER NOT NULL,
`last_login` DATETIME(0) NULL,
`created` DATETIME(0) NULL,
`two_fa_secret` VARCHAR(50) NULL,
`two_fa_enabled` TINYINT NULL DEFAULT 0,
`two_fa_every_login` TINYINT NULL DEFAULT 0,
`pushkey` VARCHAR(100) NULL,
UNIQUE INDEX `id_UNIQUE`(`id`),
UNIQUE INDEX `username_UNIQUE`(`username`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `user_active_session` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`salt` VARCHAR(100) NULL,
`encrypted_master_password` VARCHAR(500) NULL,
`created` INTEGER NULL,
`uses` INTEGER NULL,
`user_hash` VARCHAR(500) NULL,
`active` TINYINT NULL DEFAULT 1,
`session_id` VARCHAR(100) NULL,
`last_used` INTEGER NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `user_encrypted_search_index` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`user_id` INTEGER NULL,
`salt` VARCHAR(100) NULL,
`index` LONGTEXT NULL,
`last_update` INTEGER NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `user_key` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`user_id` INTEGER NULL,
`salt` VARCHAR(1300) NULL,
`key` VARCHAR(5000) NULL,
`created` INTEGER NULL,
`public_key` VARCHAR(5000) NULL,
`private_key_encrypted` VARCHAR(5000) NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "mysql"
+151
View File
@@ -0,0 +1,151 @@
datasource db {
provider = "mysql"
}
model attachment {
id Int @id @default(autoincrement()) @db.UnsignedInt
note_id Int? @db.UnsignedInt
user_id Int @db.UnsignedInt
attachment_type Int? @db.UnsignedInt
text String? @db.MediumText
url String? @default("") @db.VarChar(1000)
last_indexed Int?
file_location String? @db.VarChar(500)
visible Int? @default(1) @db.TinyInt
scrape_state Int @default(2) @db.TinyInt
scrape_attempts Int @default(0) @db.TinyInt
enqueued_at Int?
@@index([scrape_state, enqueued_at], map: "attachment_scrape_state")
@@index([attachment_type], map: "attachment_type")
@@index([note_id], map: "note_id")
@@index([user_id], map: "user_id")
@@fulltext([text], map: "attachment_text_index")
}
model attachment_type {
id Int @id @default(autoincrement()) @db.UnsignedInt
mime_type String? @db.VarChar(500)
extension String? @db.VarChar(10)
name String? @db.VarChar(50)
}
model note {
id Int @id @default(autoincrement()) @db.UnsignedInt
user_id Int? @db.UnsignedInt
note_raw_text_id Int?
opened Int?
created Int?
pinned Int? @default(0)
archived Int? @default(0)
trashed Int? @default(0) @db.TinyInt
color String? @db.VarChar(500)
shared Int? @default(0)
share_user_id Int?
quick_note Int? @default(0)
indexed Int? @default(0) @db.TinyInt
snippet String? @db.VarChar(1300)
snippet_salt String? @db.VarChar(1300)
encrypted_share_password_key String? @db.VarChar(1300)
@@index([user_id], map: "user_note")
}
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model note_raw_text {
id Int @id @default(autoincrement())
text String? @db.LongText
updated BigInt?
salt String? @db.VarChar(1300)
last_decrypted_date Int?
decrypt_attempts_count Int? @default(0)
}
model note_tag {
id Int @id @default(autoincrement()) @db.UnsignedInt
note_id Int? @db.UnsignedInt
tag_id Int? @db.UnsignedInt
user_id Int? @db.UnsignedInt
@@index([note_id], map: "note_id")
@@index([tag_id], map: "tag_id")
@@index([user_id], map: "user_id")
}
model scrape_cache {
id Int @id @default(autoincrement()) @db.UnsignedInt
url_hash String @unique(map: "scrape_cache_url_hash") @db.Char(64)
url String @default("") @db.VarChar(1000)
canonical_url String? @db.VarChar(1000)
status Int @default(0) @db.SmallInt
title String? @db.VarChar(512)
site_name String? @db.VarChar(255)
description String? @db.MediumText
keywords String? @db.VarChar(512)
search_text String? @db.MediumText
image_url String? @db.VarChar(1000)
file_location String? @db.VarChar(500)
etag String? @db.VarChar(255)
last_modified String? @db.VarChar(128)
content_type String? @db.VarChar(128)
fetch_state String @default("pending") @db.VarChar(20)
failure_count Int @default(0)
last_error String? @db.VarChar(255)
fetched_at Int?
expires_at Int?
@@index([expires_at], map: "scrape_cache_expires_at")
@@index([fetch_state], map: "scrape_cache_fetch_state")
}
model tag {
id Int @id @default(autoincrement()) @db.UnsignedInt
text String? @db.VarChar(400)
hash Int?
@@index([hash], map: "hash")
}
model user {
id Int @id @unique(map: "id_UNIQUE") @default(autoincrement()) @db.UnsignedInt
username String @unique(map: "username_UNIQUE") @default("") @db.VarChar(150)
password String @db.Text
salt String @default("") @db.VarChar(500)
iterations Int
last_login DateTime? @db.DateTime(0)
created DateTime? @db.DateTime(0)
two_fa_secret String? @db.VarChar(50)
two_fa_enabled Int? @default(0) @db.TinyInt
two_fa_every_login Int? @default(0) @db.TinyInt
pushkey String? @db.VarChar(100)
}
model user_active_session {
id Int @id @default(autoincrement())
salt String? @db.VarChar(100)
encrypted_master_password String? @db.VarChar(500)
created Int?
uses Int?
user_hash String? @db.VarChar(500)
active Int? @default(1) @db.TinyInt
session_id String? @db.VarChar(100)
last_used Int?
}
model user_encrypted_search_index {
id Int @id @default(autoincrement())
user_id Int?
salt String? @db.VarChar(100)
index String? @db.LongText
last_update Int?
}
model user_key {
id Int @id @default(autoincrement())
user_id Int?
salt String? @db.VarChar(1300)
key String? @db.VarChar(5000)
created Int?
public_key String? @db.VarChar(5000)
private_key_encrypted String? @db.VarChar(5000)
}
+24
View File
@@ -0,0 +1,24 @@
--
-- Reference data required for a working database.
--
-- Applied by ./migrateDatabase.sh after migrations. Kept out of the migration
-- history on purpose: environments that were baselined with
-- `prisma migrate resolve` never execute 0_init, so seed rows placed inside it
-- would silently never exist there.
--
-- Idempotent, so re-running is harmless.
--
-- attachment_type is a lookup table the application joins against.
-- attachment_type = 1 is hardcoded for scraped URLs, 2 for uploaded images,
-- 3 for PDFs (see server/models/Attachment.js). Without these rows a freshly
-- scaffolded database creates attachments that reference nothing.
INSERT INTO `attachment_type` (`id`, `mime_type`, `extension`, `name`) VALUES
(1, NULL, NULL, 'URL'),
(2, NULL, '.jpg', 'Image - Jpg'),
(3, NULL, '.pdf', 'PDF'),
(4, NULL, '.doc', 'DOC')
ON DUPLICATE KEY UPDATE
`mime_type` = VALUES(`mime_type`),
`extension` = VALUES(`extension`),
`name` = VALUES(`name`);