All files / web/src/app/api/vision-training/sync/history route.ts

0% Statements 0/102
0% Branches 0/1
0% Functions 0/1
0% Lines 0/102

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' }
)