@@ -10,6 +10,7 @@ import { and, eq } from 'drizzle-orm';
1010import { CryptoService } from './CryptoService' ;
1111import { EmailProviderFactory } from './EmailProviderFactory' ;
1212import { ingestionQueue } from '../jobs/queues' ;
13+ import type { JobType } from 'bullmq' ;
1314import { StorageService } from './StorageService' ;
1415import type { IInitialImportJob , EmailObject } from '@open-archiver/types' ;
1516import { archivedEmails , attachments as attachmentsSchema , emailAttachments } from '../database/schema' ;
@@ -142,11 +143,29 @@ export class IngestionService {
142143
143144 public static async triggerForceSync ( id : string ) : Promise < void > {
144145 const source = await this . findById ( id ) ;
145-
146+ logger . info ( { ingestionSourceId : id } , 'Force syncing started.' ) ;
146147 if ( ! source ) {
147148 throw new Error ( 'Ingestion source not found' ) ;
148149 }
149150
151+ // Clean up existing jobs for this source to break any stuck flows
152+ const jobTypes : JobType [ ] = [ 'active' , 'waiting' , 'failed' , 'delayed' , 'paused' ] ;
153+ const jobs = await ingestionQueue . getJobs ( jobTypes ) ;
154+ for ( const job of jobs ) {
155+ if ( job . data . ingestionSourceId === id ) {
156+ try {
157+ await job . remove ( ) ;
158+ logger . info ( { jobId : job . id , ingestionSourceId : id } , 'Removed stale job during force sync.' ) ;
159+ } catch ( error ) {
160+ logger . error ( { err : error , jobId : job . id } , 'Failed to remove stale job.' ) ;
161+ }
162+ }
163+ }
164+
165+ // Reset status to 'active'
166+ await this . update ( id , { status : 'active' , lastSyncStatusMessage : 'Force sync triggered by user.' } ) ;
167+
168+
150169 await ingestionQueue . add ( 'continuous-sync' , { ingestionSourceId : source . id } ) ;
151170 }
152171
0 commit comments