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:
@@ -89,19 +89,6 @@ function createTables(db: Database.Database): void {
|
|||||||
created_at INTEGER NOT NULL
|
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 (
|
CREATE TABLE IF NOT EXISTS dm_channels (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
owner_id TEXT,
|
owner_id TEXT,
|
||||||
@@ -125,6 +112,22 @@ function createTables(db: Database.Database): void {
|
|||||||
created_at INTEGER NOT NULL
|
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 (
|
CREATE TABLE IF NOT EXISTS friends (
|
||||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
friend_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
friend_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
|||||||
@@ -372,6 +372,9 @@ export function runMigrations(db: Database.Database): void {
|
|||||||
// ─── Add category_overrides table ────────────────────────────────────────
|
// ─── Add category_overrides table ────────────────────────────────────────
|
||||||
migrateCategoryOverrides(db);
|
migrateCategoryOverrides(db);
|
||||||
|
|
||||||
|
// ─── Add FK constraint to attachments.dm_message_id ─────────────────────
|
||||||
|
migrateAttachmentsDmMessageFk(db);
|
||||||
|
|
||||||
console.log('Migrations complete.');
|
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
|
* Async backfill: generate thumbnails for all existing image attachments that
|
||||||
* don't have one yet. Runs once after server startup, gated by a persistent
|
* don't have one yet. Runs once after server startup, gated by a persistent
|
||||||
|
|||||||
Reference in New Issue
Block a user