Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | import { desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { db } from '@/db' import { visionTrainingSyncHistory, toSyncHistorySummary } from '@/db/schema' import { withAuth } from '@/lib/auth/withAuth' // Force dynamic rendering - this route reads from database export const dynamic = 'force-dynamic' type ModelType = 'column-classifier' | 'boundary-detector' /** * GET /api/vision-training/sync/history * * Get sync history for a model type. * * Query params: * - modelType: 'column-classifier' | 'boundary-detector' (default: column-classifier) * - limit: Number of records to return (default: 20, max: 100) */ export const GET = withAuth( async (request: NextRequest) => { try { const searchParams = request.nextUrl.searchParams const modelTypeParam = searchParams.get('modelType') const modelType: ModelType = modelTypeParam === 'boundary-detector' ? 'boundary-detector' : 'column-classifier' const limitParam = searchParams.get('limit') const limit = Math.min(Math.max(parseInt(limitParam || '20', 10) || 20, 1), 100) const records = await db .select() .from(visionTrainingSyncHistory) .where(eq(visionTrainingSyncHistory.modelType, modelType)) .orderBy(desc(visionTrainingSyncHistory.startedAt)) .limit(limit) // Get summary stats const allRecords = await db .select() .from(visionTrainingSyncHistory) .where(eq(visionTrainingSyncHistory.modelType, modelType)) const totalSyncs = allRecords.length const successfulSyncs = allRecords.filter((r) => r.status === 'success').length const failedSyncs = allRecords.filter((r) => r.status === 'failed').length const lastSuccessfulSync = allRecords .filter((r) => r.status === 'success') .sort((a, b) => (b.startedAt?.getTime() || 0) - (a.startedAt?.getTime() || 0))[0] return Response.json({ modelType, history: records.map(toSyncHistorySummary), stats: { totalSyncs, successfulSyncs, failedSyncs, lastSuccessfulSync: lastSuccessfulSync ? { id: lastSuccessfulSync.id, startedAt: lastSuccessfulSync.startedAt, filesTransferred: lastSuccessfulSync.filesTransferred, } : null, }, }) } catch (error) { console.error('[vision-training/sync/history] Error:', error) return Response.json({ error: 'Failed to fetch sync history' }, { status: 500 }) } }, { role: 'admin' } ) /** * DELETE /api/vision-training/sync/history * * Delete a specific sync history record. * * Query params: * - id: Record ID to delete */ export const DELETE = withAuth( async (request: NextRequest) => { try { const searchParams = request.nextUrl.searchParams const id = searchParams.get('id') if (!id) { return Response.json({ error: 'Missing id parameter' }, { status: 400 }) } await db.delete(visionTrainingSyncHistory).where(eq(visionTrainingSyncHistory.id, id)) return Response.json({ success: true }) } catch (error) { console.error('[vision-training/sync/history] DELETE Error:', error) return Response.json({ error: 'Failed to delete sync history record' }, { status: 500 }) } }, { role: 'admin' } ) |