letsbe-hub/src/app/api/cron/collect-stats/route.ts

111 lines
4.2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { statsCollectionService } from '@/lib/services/stats-collection-service'
import { logScanningService } from '@/lib/services/log-scanning-service'
import { containerHealthService } from '@/lib/services/container-health-service'
/**
* GET /api/cron/collect-stats
* Collect stats, scan logs for errors, and check container health for all enterprise servers
*
* This endpoint is designed to be called by a cron job or scheduled task.
* It should be protected by a secret token in production.
*
* Example cron schedule: Every 5 minutes
* Vercel cron config in vercel.json:
* {
* "crons": [
* { "path": "/api/cron/collect-stats", "schedule": "*\/5 * * * *" }
* ]
* }
*
* What this cron does:
* 1. Collects performance stats from Netcup + Portainer for all active servers
* 2. Scans container logs for errors matching client-defined rules
* 3. Checks container health and detects crashes/OOM kills
*/
export async function GET(request: NextRequest) {
// Verify cron secret (for security in production)
const cronSecret = process.env.CRON_SECRET
const authHeader = request.headers.get('authorization')
if (cronSecret && authHeader !== `Bearer ${cronSecret}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const startTime = Date.now()
const results = {
stats: { collected: 0, failed: 0 },
logScan: { totalServers: 0, scannedServers: 0, failedServers: 0, totalErrorsFound: 0, duration: 0 },
healthCheck: { totalServers: 0, checkedServers: 0, failedServers: 0, eventsDetected: 0, crashes: 0, oomKills: 0, duration: 0 },
errors: [] as string[],
}
// 1. Collect performance stats (existing functionality)
try {
results.stats = await statsCollectionService.collectAllStats()
console.log(`[Cron] Stats collection: ${results.stats.collected} servers, ${results.stats.failed} failed`)
} catch (error) {
console.error('[Cron] Stats collection failed:', error)
results.errors.push(`Stats collection: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
// 2. Scan container logs for errors
try {
results.logScan = await logScanningService.scanAllServers()
console.log(`[Cron] Log scan: ${results.logScan.scannedServers}/${results.logScan.totalServers} servers, ${results.logScan.totalErrorsFound} errors found`)
} catch (error) {
console.error('[Cron] Log scanning failed:', error)
results.errors.push(`Log scanning: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
// 3. Check container health (crash detection)
try {
results.healthCheck = await containerHealthService.checkAllServers()
console.log(`[Cron] Health check: ${results.healthCheck.checkedServers}/${results.healthCheck.totalServers} servers, ${results.healthCheck.eventsDetected} events detected`)
} catch (error) {
console.error('[Cron] Health check failed:', error)
results.errors.push(`Health check: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
const totalDuration = Date.now() - startTime
const hasErrors = results.errors.length > 0
return NextResponse.json({
success: !hasErrors,
timestamp: new Date().toISOString(),
duration: `${totalDuration}ms`,
// Stats collection results
stats: {
serversCollected: results.stats.collected,
serversFailed: results.stats.failed,
},
// Log scanning results
logScan: {
serversScanned: results.logScan.scannedServers,
serversFailed: results.logScan.failedServers,
errorsFound: results.logScan.totalErrorsFound,
duration: `${results.logScan.duration}ms`,
},
// Health check results
healthCheck: {
serversChecked: results.healthCheck.checkedServers,
serversFailed: results.healthCheck.failedServers,
eventsDetected: results.healthCheck.eventsDetected,
crashes: results.healthCheck.crashes,
oomKills: results.healthCheck.oomKills,
duration: `${results.healthCheck.duration}ms`,
},
// Any errors that occurred
...(hasErrors && { errors: results.errors }),
}, { status: hasErrors ? 207 : 200 }) // 207 Multi-Status if partial failure
}
// Also support POST for flexibility
export async function POST(request: NextRequest) {
return GET(request)
}