fix: add FK constraint to attachments.dm_message_id via table rebuild

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.
This commit is contained in:
Jannis Braun
2026-03-23 01:55:26 +01:00
parent 8bcb5cc977
commit f634f35450
2 changed files with 61 additions and 13 deletions
+45
View File
@@ -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