Add cron endpoint for automatic round open/close scheduling
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

New /api/cron/round-scheduler endpoint that:
- Activates DRAFT rounds whose windowOpenAt has arrived
- Closes ACTIVE rounds whose windowCloseAt has passed
- Uses existing activateRound/closeRound from round-engine
- Protected by CRON_SECRET header like other cron endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-18 11:33:25 +01:00
parent 7b16873b9c
commit 9b1b319362

View File

@@ -0,0 +1,82 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { prisma } from '@/lib/prisma'
import { activateRound, closeRound } from '@/server/services/round-engine'
export async function GET(request: NextRequest): Promise<NextResponse> {
const cronSecret = request.headers.get('x-cron-secret')
if (!cronSecret || cronSecret !== process.env.CRON_SECRET) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
try {
const now = new Date()
const results: Array<{ roundId: string; action: string; success: boolean; errors?: string[] }> = []
// Find a SUPER_ADMIN to use as the actor for audit logging
const systemActor = await prisma.user.findFirst({
where: { role: 'SUPER_ADMIN' },
select: { id: true },
})
if (!systemActor) {
return NextResponse.json({
ok: false,
error: 'No SUPER_ADMIN user found for system actions',
}, { status: 500 })
}
// 1. Activate DRAFT rounds whose windowOpenAt has arrived
const roundsToOpen = await prisma.round.findMany({
where: {
status: 'ROUND_DRAFT',
windowOpenAt: { lte: now },
competition: { status: { not: 'ARCHIVED' } },
},
select: { id: true, name: true },
})
for (const round of roundsToOpen) {
const result = await activateRound(round.id, systemActor.id, prisma)
results.push({
roundId: round.id,
action: `activate: ${round.name}`,
success: result.success,
errors: result.errors,
})
}
// 2. Close ACTIVE rounds whose windowCloseAt has passed
const roundsToClose = await prisma.round.findMany({
where: {
status: 'ROUND_ACTIVE',
windowCloseAt: { lte: now },
},
select: { id: true, name: true },
})
for (const round of roundsToClose) {
const result = await closeRound(round.id, systemActor.id, prisma)
results.push({
roundId: round.id,
action: `close: ${round.name}`,
success: result.success,
errors: result.errors,
})
}
return NextResponse.json({
ok: true,
processed: results.length,
results,
timestamp: now.toISOString(),
})
} catch (error) {
console.error('Cron round-scheduler failed:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}