|
1 | 1 | import { Router, Request, Response } from 'express'; |
2 | | -import { aiDiscoveryService } from '../services/ai-discovery.service.js'; |
| 2 | +import { |
| 3 | + aiDiscoveryService, |
| 4 | + DiscoveryInputError, |
| 5 | + DiscoveryRunConflictError, |
| 6 | + normalizeFullDiscoveryOptions, |
| 7 | +} from '../services/ai-discovery.service.js'; |
3 | 8 | import { favoritesService } from '../services/favorites.service.js'; |
4 | 9 | import { logger } from '../lib/logger.js'; |
5 | 10 | import { asyncHandler } from '../utils/asyncHandler.js'; |
@@ -52,21 +57,47 @@ router.post('/:dbId/quick', asyncHandler(async (req: Request, res: Response) => |
52 | 57 | */ |
53 | 58 | router.post('/:dbId/start', asyncHandler(async (req: Request, res: Response) => { |
54 | 59 | const { dbId } = req.params; |
55 | | - const { batchSize, maxPersons } = req.body; |
56 | | - |
57 | | - const result = await aiDiscoveryService.startDiscovery(dbId, { |
58 | | - batchSize, |
59 | | - maxPersons, |
60 | | - }).catch(err => { |
61 | | - res.status(500).json({ success: false, error: err.message }); |
62 | | - return null; |
63 | | - }); |
| 60 | + let options; |
| 61 | + try { |
| 62 | + options = normalizeFullDiscoveryOptions(req.body ?? {}); |
| 63 | + } catch (err) { |
| 64 | + const message = err instanceof DiscoveryInputError ? err.message : 'Invalid discovery options'; |
| 65 | + logger.warn('ai-discovery', `Rejected full discovery request dbId=${dbId}: ${message}`); |
| 66 | + res.status(400).json({ success: false, error: message }); |
| 67 | + return; |
| 68 | + } |
64 | 69 |
|
65 | | - if (result !== null) { |
| 70 | + try { |
| 71 | + const result = await aiDiscoveryService.startDiscovery(dbId, options); |
66 | 72 | res.json({ success: true, data: result }); |
| 73 | + } catch (err) { |
| 74 | + if (err instanceof DiscoveryRunConflictError) { |
| 75 | + logger.warn('ai-discovery', `Rejected concurrent full discovery dbId=${dbId}, activeRunId=${err.runId}`); |
| 76 | + res.status(409).json({ success: false, error: err.message, data: { runId: err.runId } }); |
| 77 | + return; |
| 78 | + } |
| 79 | + const message = err instanceof Error ? err.message : 'Unable to start discovery'; |
| 80 | + logger.error('ai-discovery', `Failed to start full discovery dbId=${dbId}: ${message}`); |
| 81 | + res.status(500).json({ success: false, error: message }); |
67 | 82 | } |
68 | 83 | })); |
69 | 84 |
|
| 85 | +/** |
| 86 | + * Cancel the active full AI discovery run for a database. |
| 87 | + * POST /api/ai-discovery/:dbId/cancel |
| 88 | + */ |
| 89 | +router.post('/:dbId/cancel', (req: Request, res: Response) => { |
| 90 | + const { dbId } = req.params; |
| 91 | + const cancelled = aiDiscoveryService.cancelDiscovery(dbId); |
| 92 | + |
| 93 | + if (!cancelled) { |
| 94 | + res.status(404).json({ success: false, error: 'No active discovery run for this database' }); |
| 95 | + return; |
| 96 | + } |
| 97 | + |
| 98 | + res.json({ success: true, data: { ...cancelled, message: 'Cancellation requested' } }); |
| 99 | +}); |
| 100 | + |
70 | 101 | /** |
71 | 102 | * Get progress of a discovery run |
72 | 103 | * GET /api/ai-discovery/progress/:runId |
|
0 commit comments