async function ensureFaceScanAutomation(db) { const triggerNames = [ 'photos_face_enqueue_insert', 'photos_face_enqueue_update', 'photos_face_cleanup_update', 'photos_face_cleanup_delete', ]; for (const triggerName of triggerNames) { await db.raw( `DROP TRIGGER IF EXISTS ${triggerName}`, ); } await db.raw(` CREATE TRIGGER photos_face_enqueue_insert AFTER INSERT ON photos WHEN NEW.deleted_at IS NULL AND NEW.path IS NOT NULL AND NEW.path != '' AND ( NEW.mime_type IS NULL OR NEW.mime_type LIKE 'image/%' ) BEGIN INSERT INTO face_scan_jobs ( photo_id, status, attempts, rescan_requested, created_at, updated_at ) VALUES ( NEW.id, 'pending', 0, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ) ON CONFLICT(photo_id) DO UPDATE SET status = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.status ELSE 'pending' END, rescan_requested = CASE WHEN face_scan_jobs.status = 'processing' THEN 1 ELSE 0 END, faces_detected = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.faces_detected ELSE NULL END, execution_ms = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.execution_ms ELSE NULL END, applied_rotation = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.applied_rotation ELSE NULL END, last_error = NULL, started_at = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.started_at ELSE NULL END, completed_at = NULL, updated_at = CURRENT_TIMESTAMP; END `); await db.raw(` CREATE TRIGGER photos_face_enqueue_update AFTER UPDATE OF path, fast_hash, mtimeMs, size_bytes, rotation, mime_type, deleted_at ON photos WHEN NEW.deleted_at IS NULL AND NEW.path IS NOT NULL AND NEW.path != '' AND ( NEW.mime_type IS NULL OR NEW.mime_type LIKE 'image/%' ) AND ( OLD.path IS NOT NEW.path OR OLD.fast_hash IS NOT NEW.fast_hash OR OLD.mtimeMs IS NOT NEW.mtimeMs OR OLD.size_bytes IS NOT NEW.size_bytes OR OLD.rotation IS NOT NEW.rotation OR OLD.mime_type IS NOT NEW.mime_type OR OLD.deleted_at IS NOT NEW.deleted_at ) BEGIN INSERT INTO face_scan_jobs ( photo_id, status, attempts, rescan_requested, created_at, updated_at ) VALUES ( NEW.id, 'pending', 0, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ) ON CONFLICT(photo_id) DO UPDATE SET status = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.status ELSE 'pending' END, rescan_requested = CASE WHEN face_scan_jobs.status = 'processing' THEN 1 ELSE 0 END, faces_detected = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.faces_detected ELSE NULL END, execution_ms = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.execution_ms ELSE NULL END, applied_rotation = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.applied_rotation ELSE NULL END, last_error = NULL, started_at = CASE WHEN face_scan_jobs.status = 'processing' THEN face_scan_jobs.started_at ELSE NULL END, completed_at = NULL, updated_at = CURRENT_TIMESTAMP; END `); await db.raw(` CREATE TRIGGER photos_face_cleanup_update AFTER UPDATE OF path, mime_type, deleted_at ON photos WHEN NEW.deleted_at IS NOT NULL OR NEW.path IS NULL OR NEW.path = '' OR ( NEW.mime_type IS NOT NULL AND NEW.mime_type NOT LIKE 'image/%' ) BEGIN DELETE FROM faces WHERE photo_id = NEW.id; DELETE FROM face_scan_jobs WHERE photo_id = NEW.id; END `); await db.raw(` CREATE TRIGGER photos_face_cleanup_delete AFTER DELETE ON photos BEGIN DELETE FROM faces WHERE photo_id = OLD.id; DELETE FROM face_scan_jobs WHERE photo_id = OLD.id; END `); console.log( '✔ trigger automatici scansione volti creati', ); } module.exports = { ensureFaceScanAutomation, };