From f634f354509886a417e680a08824d8c4420a6c59 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 23 Mar 2026 01:55:26 +0100 Subject: [PATCH] fix: add FK constraint to attachments.dm_message_id via table rebuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebuilds the attachments table with ON DELETE CASCADE on dm_message_id. Dangling records are excluded during copy — their files become standard disk orphans detectable by the storage janitor. Also updates the raw SQL in index.ts to include width/height/duration columns for fresh installs. --- packages/server/src/db/index.ts | 29 +++++++++++--------- packages/server/src/db/migrate.ts | 45 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/packages/server/src/db/index.ts b/packages/server/src/db/index.ts index 20e12f81..7e600a9b 100644 --- a/packages/server/src/db/index.ts +++ b/packages/server/src/db/index.ts @@ -89,19 +89,6 @@ function createTables(db: Database.Database): void { created_at INTEGER NOT NULL ); - CREATE TABLE IF NOT EXISTS attachments ( - id TEXT PRIMARY KEY, - message_id TEXT REFERENCES messages(id) ON DELETE CASCADE, - dm_message_id TEXT, - uploader_id TEXT, - filename TEXT NOT NULL, - original_name TEXT NOT NULL, - mimetype TEXT NOT NULL, - size INTEGER NOT NULL, - thumbnail_filename TEXT, - created_at INTEGER NOT NULL - ); - CREATE TABLE IF NOT EXISTS dm_channels ( id TEXT PRIMARY KEY, owner_id TEXT, @@ -125,6 +112,22 @@ function createTables(db: Database.Database): void { created_at INTEGER NOT NULL ); + CREATE TABLE IF NOT EXISTS attachments ( + id TEXT PRIMARY KEY, + message_id TEXT REFERENCES messages(id) ON DELETE CASCADE, + dm_message_id TEXT REFERENCES dm_messages(id) ON DELETE CASCADE, + uploader_id TEXT, + filename TEXT NOT NULL, + original_name TEXT NOT NULL, + mimetype TEXT NOT NULL, + size INTEGER NOT NULL, + thumbnail_filename TEXT, + width INTEGER, + height INTEGER, + duration REAL, + created_at INTEGER NOT NULL + ); + CREATE TABLE IF NOT EXISTS friends ( user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, friend_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, diff --git a/packages/server/src/db/migrate.ts b/packages/server/src/db/migrate.ts index ece44d54..17079bc5 100644 --- a/packages/server/src/db/migrate.ts +++ b/packages/server/src/db/migrate.ts @@ -372,6 +372,9 @@ export function runMigrations(db: Database.Database): void { // ─── Add category_overrides table ──────────────────────────────────────── migrateCategoryOverrides(db); + // ─── Add FK constraint to attachments.dm_message_id ───────────────────── + migrateAttachmentsDmMessageFk(db); + console.log('Migrations complete.'); } @@ -1027,6 +1030,48 @@ function migrateCategoryOverrides(db: Database.Database): void { `); } +/** Add FK constraint to attachments.dm_message_id (SQLite requires table recreation) */ +function migrateAttachmentsDmMessageFk(db: Database.Database): void { + const tableInfo = db.prepare( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='attachments'" + ).get() as { sql: string } | undefined; + + // Only migrate if dm_message_id exists but has no FK reference + if (!tableInfo) return; + if (!tableInfo.sql.includes('dm_message_id')) return; + if (tableInfo.sql.includes('REFERENCES dm_messages')) return; + + console.log('Migrating: Adding FK constraint to attachments.dm_message_id...'); + + db.exec(` + CREATE TABLE attachments_new ( + id TEXT PRIMARY KEY, + message_id TEXT REFERENCES messages(id) ON DELETE CASCADE, + dm_message_id TEXT REFERENCES dm_messages(id) ON DELETE CASCADE, + uploader_id TEXT, + filename TEXT NOT NULL, + original_name TEXT NOT NULL, + mimetype TEXT NOT NULL, + size INTEGER NOT NULL, + thumbnail_filename TEXT, + width INTEGER, + height INTEGER, + duration REAL, + created_at INTEGER NOT NULL + ); + INSERT INTO attachments_new + SELECT id, message_id, dm_message_id, uploader_id, filename, original_name, + mimetype, size, thumbnail_filename, width, height, duration, created_at + FROM attachments + WHERE dm_message_id IS NULL + OR dm_message_id IN (SELECT id FROM dm_messages); + DROP TABLE attachments; + ALTER TABLE attachments_new RENAME TO attachments; + CREATE INDEX IF NOT EXISTS idx_attachments_message_id ON attachments(message_id); + CREATE INDEX IF NOT EXISTS idx_attachments_dm_message_id ON attachments(dm_message_id); + `); +} + /** * Async backfill: generate thumbnails for all existing image attachments that * don't have one yet. Runs once after server startup, gated by a persistent