@@ -136,4 +136,337 @@ export default async function mcpRegistryRoutes(server: FastifyInstance) {
136136 return reply . status ( 500 ) . type ( 'application/json' ) . send ( jsonString ) ;
137137 }
138138 } ) ;
139+
140+ /**
141+ * Get detailed batch progress
142+ * Provides real-time progress, job status, and error details for a sync batch
143+ */
144+ server . get ( '/admin/mcp-registry/progress/:batchId' , {
145+ preValidation : requirePermission ( 'mcp.registry.sync' ) ,
146+ schema : {
147+ tags : [ 'Admin - MCP Registry' ] ,
148+ summary : 'Get detailed sync progress for batch' ,
149+ description : 'Get real-time progress, job status, and error details for MCP Registry sync batch' ,
150+ security : [ { cookieAuth : [ ] } ] ,
151+
152+ params : {
153+ type : 'object' ,
154+ properties : {
155+ batchId : { type : 'string' , description : 'Job batch ID' }
156+ } ,
157+ required : [ 'batchId' ]
158+ } ,
159+
160+ response : {
161+ 200 : {
162+ type : 'object' ,
163+ properties : {
164+ success : { type : 'boolean' } ,
165+ data : {
166+ type : 'object' ,
167+ properties : {
168+ batch : {
169+ type : 'object' ,
170+ description : 'Batch information'
171+ } ,
172+ progress : {
173+ type : 'object' ,
174+ properties : {
175+ total : { type : 'number' } ,
176+ completed : { type : 'number' } ,
177+ failed : { type : 'number' } ,
178+ pending : { type : 'number' } ,
179+ processing : { type : 'number' } ,
180+ percentage : { type : 'number' }
181+ }
182+ } ,
183+ recentJobs : {
184+ type : 'array' ,
185+ description : 'Recent jobs in batch (last 10)'
186+ } ,
187+ errors : {
188+ type : 'array' ,
189+ description : 'Failed jobs with error details'
190+ } ,
191+ estimatedTimeRemaining : {
192+ type : 'number' ,
193+ nullable : true ,
194+ description : 'Estimated milliseconds until completion'
195+ }
196+ }
197+ }
198+ }
199+ } ,
200+ 500 : {
201+ type : 'object' ,
202+ properties : {
203+ success : { type : 'boolean' } ,
204+ error : { type : 'string' }
205+ }
206+ }
207+ }
208+ }
209+ } , async ( request , reply ) => {
210+ const { batchId } = request . params as { batchId : string } ;
211+
212+ try {
213+ const db = getDb ( ) ;
214+ const jobQueueService = new JobQueueService ( db , request . log ) ;
215+
216+ const batchProgress = await jobQueueService . getBatchProgress ( batchId ) ;
217+
218+ const responseData = {
219+ success : true ,
220+ data : batchProgress
221+ } ;
222+ const jsonString = JSON . stringify ( responseData ) ;
223+ return reply . status ( 200 ) . type ( 'application/json' ) . send ( jsonString ) ;
224+
225+ } catch ( error ) {
226+ request . log . error ( { error, batchId } , 'Failed to get batch progress' ) ;
227+ const errorResponse = {
228+ success : false ,
229+ error : error instanceof Error ? error . message : 'Failed to get batch progress'
230+ } ;
231+ const jsonString = JSON . stringify ( errorResponse ) ;
232+ return reply . status ( 500 ) . type ( 'application/json' ) . send ( jsonString ) ;
233+ }
234+ } ) ;
235+
236+ /**
237+ * Cancel active sync batch
238+ * Cancels all pending jobs in a batch
239+ */
240+ server . post ( '/admin/mcp-registry/cancel/:batchId' , {
241+ preValidation : requirePermission ( 'mcp.registry.sync' ) ,
242+ schema : {
243+ tags : [ 'Admin - MCP Registry' ] ,
244+ summary : 'Cancel active sync batch' ,
245+ description : 'Cancel an active MCP Registry sync batch and all pending jobs' ,
246+ security : [ { cookieAuth : [ ] } ] ,
247+
248+ params : {
249+ type : 'object' ,
250+ properties : {
251+ batchId : { type : 'string' , description : 'Job batch ID to cancel' }
252+ } ,
253+ required : [ 'batchId' ]
254+ } ,
255+
256+ response : {
257+ 200 : {
258+ type : 'object' ,
259+ properties : {
260+ success : { type : 'boolean' } ,
261+ message : { type : 'string' } ,
262+ data : {
263+ type : 'object' ,
264+ properties : {
265+ batchId : { type : 'string' } ,
266+ cancelledJobs : { type : 'number' }
267+ }
268+ }
269+ }
270+ } ,
271+ 500 : {
272+ type : 'object' ,
273+ properties : {
274+ success : { type : 'boolean' } ,
275+ error : { type : 'string' }
276+ }
277+ }
278+ }
279+ }
280+ } , async ( request , reply ) => {
281+ const { batchId } = request . params as { batchId : string } ;
282+
283+ try {
284+ const db = getDb ( ) ;
285+ const jobQueueService = new JobQueueService ( db , request . log ) ;
286+
287+ const cancelledJobs = await jobQueueService . cancelBatchJobs ( batchId ) ;
288+
289+ request . log . info ( {
290+ batchId,
291+ cancelledJobs,
292+ userId : request . user ! . id ,
293+ operation : 'mcp_sync_batch_cancelled'
294+ } , 'MCP Registry sync batch cancelled' ) ;
295+
296+ const responseData = {
297+ success : true ,
298+ message : `Cancelled ${ cancelledJobs } pending jobs in batch` ,
299+ data : { batchId, cancelledJobs }
300+ } ;
301+ const jsonString = JSON . stringify ( responseData ) ;
302+ return reply . status ( 200 ) . type ( 'application/json' ) . send ( jsonString ) ;
303+
304+ } catch ( error ) {
305+ request . log . error ( { error, batchId } , 'Failed to cancel sync batch' ) ;
306+ const errorResponse = {
307+ success : false ,
308+ error : error instanceof Error ? error . message : 'Failed to cancel sync batch'
309+ } ;
310+ const jsonString = JSON . stringify ( errorResponse ) ;
311+ return reply . status ( 500 ) . type ( 'application/json' ) . send ( jsonString ) ;
312+ }
313+ } ) ;
314+
315+ /**
316+ * Retry failed jobs in batch
317+ * Resets and retries all failed jobs in a batch
318+ */
319+ server . post ( '/admin/mcp-registry/retry/:batchId' , {
320+ preValidation : requirePermission ( 'mcp.registry.sync' ) ,
321+ schema : {
322+ tags : [ 'Admin - MCP Registry' ] ,
323+ summary : 'Retry failed jobs in batch' ,
324+ description : 'Retry all failed jobs in an MCP Registry sync batch' ,
325+ security : [ { cookieAuth : [ ] } ] ,
326+
327+ params : {
328+ type : 'object' ,
329+ properties : {
330+ batchId : { type : 'string' , description : 'Job batch ID to retry failed jobs' }
331+ } ,
332+ required : [ 'batchId' ]
333+ } ,
334+
335+ response : {
336+ 200 : {
337+ type : 'object' ,
338+ properties : {
339+ success : { type : 'boolean' } ,
340+ message : { type : 'string' } ,
341+ data : {
342+ type : 'object' ,
343+ properties : {
344+ batchId : { type : 'string' } ,
345+ retriedJobs : { type : 'number' }
346+ }
347+ }
348+ }
349+ } ,
350+ 500 : {
351+ type : 'object' ,
352+ properties : {
353+ success : { type : 'boolean' } ,
354+ error : { type : 'string' }
355+ }
356+ }
357+ }
358+ }
359+ } , async ( request , reply ) => {
360+ const { batchId } = request . params as { batchId : string } ;
361+
362+ try {
363+ const db = getDb ( ) ;
364+ const jobQueueService = new JobQueueService ( db , request . log ) ;
365+
366+ const retriedJobs = await jobQueueService . retryFailedBatchJobs ( batchId ) ;
367+
368+ request . log . info ( {
369+ batchId,
370+ retriedJobs,
371+ userId : request . user ! . id ,
372+ operation : 'mcp_sync_batch_retry'
373+ } , 'Retried failed jobs in MCP Registry sync batch' ) ;
374+
375+ const responseData = {
376+ success : true ,
377+ message : `Retried ${ retriedJobs } failed jobs in batch` ,
378+ data : { batchId, retriedJobs }
379+ } ;
380+ const jsonString = JSON . stringify ( responseData ) ;
381+ return reply . status ( 200 ) . type ( 'application/json' ) . send ( jsonString ) ;
382+
383+ } catch ( error ) {
384+ request . log . error ( { error, batchId } , 'Failed to retry batch jobs' ) ;
385+ const errorResponse = {
386+ success : false ,
387+ error : error instanceof Error ? error . message : 'Failed to retry batch jobs'
388+ } ;
389+ const jsonString = JSON . stringify ( errorResponse ) ;
390+ return reply . status ( 500 ) . type ( 'application/json' ) . send ( jsonString ) ;
391+ }
392+ } ) ;
393+
394+ /**
395+ * Get recent sync batches
396+ * Returns recent MCP registry sync operations for monitoring
397+ */
398+ server . get ( '/admin/mcp-registry/batches' , {
399+ preValidation : requirePermission ( 'mcp.registry.sync' ) ,
400+ schema : {
401+ tags : [ 'Admin - MCP Registry' ] ,
402+ summary : 'Get recent sync batches' ,
403+ description : 'Get recent MCP Registry sync operations with progress information' ,
404+ security : [ { cookieAuth : [ ] } ] ,
405+
406+ querystring : {
407+ type : 'object' ,
408+ properties : {
409+ limit : {
410+ type : 'number' ,
411+ minimum : 1 ,
412+ maximum : 50 ,
413+ default : 10 ,
414+ description : 'Number of batches to return'
415+ }
416+ }
417+ } ,
418+
419+ response : {
420+ 200 : {
421+ type : 'object' ,
422+ properties : {
423+ success : { type : 'boolean' } ,
424+ data : {
425+ type : 'array' ,
426+ items : {
427+ type : 'object' ,
428+ description : 'Batch information with progress'
429+ }
430+ }
431+ }
432+ } ,
433+ 500 : {
434+ type : 'object' ,
435+ properties : {
436+ success : { type : 'boolean' } ,
437+ error : { type : 'string' }
438+ }
439+ }
440+ }
441+ }
442+ } , async ( request , reply ) => {
443+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
444+ const { limit } = ( request . query || { } ) as any ;
445+
446+ try {
447+ const db = getDb ( ) ;
448+ const jobQueueService = new JobQueueService ( db , request . log ) ;
449+
450+ const recentBatches = await jobQueueService . getRecentBatches (
451+ 'mcp_registry_sync' ,
452+ limit || 10
453+ ) ;
454+
455+ const responseData = {
456+ success : true ,
457+ data : recentBatches
458+ } ;
459+ const jsonString = JSON . stringify ( responseData ) ;
460+ return reply . status ( 200 ) . type ( 'application/json' ) . send ( jsonString ) ;
461+
462+ } catch ( error ) {
463+ request . log . error ( { error } , 'Failed to get recent batches' ) ;
464+ const errorResponse = {
465+ success : false ,
466+ error : error instanceof Error ? error . message : 'Failed to get recent batches'
467+ } ;
468+ const jsonString = JSON . stringify ( errorResponse ) ;
469+ return reply . status ( 500 ) . type ( 'application/json' ) . send ( jsonString ) ;
470+ }
471+ } ) ;
139472}
0 commit comments