A tool for tailing a labelers' firehose, rehydrating, and storing records for future analysis of moderation decisions.

fix: remove foreign key constraint from blobs table

Profile blobs use profile:// URIs that don't exist in posts table. Drop the foreign key constraint to allow storing blobs from both posts and profiles.

Add migration to recreate blobs table without constraint for existing databases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+46 -3
src
database
+46 -3
src/database/schema.ts
··· 39 39 banner_cid TEXT 40 40 ); 41 41 42 - -- Blobs table: stores information about image blobs found in posts 42 + -- Blobs table: stores information about image blobs found in posts and profiles 43 43 CREATE TABLE IF NOT EXISTS blobs ( 44 44 post_uri TEXT NOT NULL, 45 45 blob_cid TEXT NOT NULL, ··· 47 47 phash TEXT, 48 48 storage_path TEXT, 49 49 mimetype TEXT, 50 - PRIMARY KEY (post_uri, blob_cid), 51 - FOREIGN KEY (post_uri) REFERENCES posts(uri) 50 + PRIMARY KEY (post_uri, blob_cid) 52 51 ); 53 52 54 53 -- Indexes for performance ··· 106 105 }); 107 106 } 108 107 108 + async function migrateBlobsTableConstraint(): Promise<void> { 109 + const db = getDatabase(); 110 + 111 + return new Promise((resolve, reject) => { 112 + db.all( 113 + `SELECT constraint_name FROM information_schema.table_constraints 114 + WHERE table_name = 'blobs' AND constraint_type = 'FOREIGN KEY'`, 115 + (err, rows: any[]) => { 116 + if (err) { 117 + logger.error({ err }, "Failed to check blobs table constraints"); 118 + reject(err); 119 + return; 120 + } 121 + 122 + if (rows && rows.length > 0) { 123 + logger.info("Migrating blobs table to remove foreign key constraint"); 124 + 125 + const migration = ` 126 + CREATE TABLE blobs_new AS SELECT * FROM blobs; 127 + DROP TABLE blobs; 128 + ALTER TABLE blobs_new RENAME TO blobs; 129 + CREATE INDEX IF NOT EXISTS idx_blobs_sha256 ON blobs(sha256); 130 + CREATE INDEX IF NOT EXISTS idx_blobs_phash ON blobs(phash); 131 + `; 132 + 133 + db.exec(migration, (err) => { 134 + if (err) { 135 + logger.error({ err }, "Failed to migrate blobs table"); 136 + reject(err); 137 + return; 138 + } 139 + logger.info("Blobs table migration completed"); 140 + resolve(); 141 + }); 142 + } else { 143 + logger.debug("Blobs table already has no foreign key constraint"); 144 + resolve(); 145 + } 146 + } 147 + ); 148 + }); 149 + } 150 + 109 151 export async function initializeSchema(): Promise<void> { 110 152 const db = getDatabase(); 111 153 ··· 120 162 121 163 try { 122 164 await migrateProfilesTable(); 165 + await migrateBlobsTableConstraint(); 123 166 resolve(); 124 167 } catch (migrationErr) { 125 168 reject(migrationErr);