Implement results sync from football-data.org

This commit is contained in:
2026-06-11 13:30:08 -03:00
parent 22c6f93e78
commit 208bc52af7
+99 -9
View File
@@ -1,4 +1,29 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { calculatePoints } from '@/lib/scoring'
interface FDTeam {
tla: string
name: string
}
interface FDScore {
fullTime: { home: number | null; away: number | null }
}
interface FDMatch {
id: number
status: 'TIMED' | 'SCHEDULED' | 'IN_PLAY' | 'PAUSED' | 'FINISHED' | 'POSTPONED' | 'SUSPENDED' | 'CANCELLED'
homeTeam: FDTeam
awayTeam: FDTeam
score: FDScore
}
function mapStatus(s: FDMatch['status']): 'SCHEDULED' | 'LIVE' | 'FINISHED' {
if (s === 'FINISHED') return 'FINISHED'
if (s === 'IN_PLAY' || s === 'PAUSED') return 'LIVE'
return 'SCHEDULED'
}
export async function POST(req: Request) {
const { searchParams } = new URL(req.url)
@@ -8,15 +33,80 @@ export async function POST(req: Request) {
return NextResponse.json({ error: 'No autorizado' }, { status: 401 })
}
const url = process.env.RESULTS_API_URL
if (!url) {
return NextResponse.json({ error: 'RESULTS_API_URL no configurada' }, { status: 400 })
const apiUrl = process.env.RESULTS_API_URL
const apiKey = process.env.RESULTS_API_KEY
if (!apiUrl || !apiKey) {
return NextResponse.json({ error: 'RESULTS_API_URL o RESULTS_API_KEY no configuradas' }, { status: 400 })
}
// TODO: implementar cuando se provea la URL del sitio externo
// const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.RESULTS_API_KEY}` } })
// const data = await res.json()
// ... procesar y actualizar partidos
return NextResponse.json({ ok: true, message: 'Endpoint listo. Configurar RESULTS_API_URL para activar sync.' })
let fdMatches: FDMatch[]
try {
const res = await fetch(apiUrl, {
headers: { 'X-Auth-Token': apiKey },
cache: 'no-store',
})
if (!res.ok) {
return NextResponse.json({ error: `Error de API externa: ${res.status}` }, { status: 502 })
}
const data = await res.json()
fdMatches = data.matches ?? []
} catch {
return NextResponse.json({ error: 'No se pudo conectar con la API externa' }, { status: 502 })
}
// Precargar equipos una sola vez
const teams = await prisma.team.findMany()
const teamByShortName = Object.fromEntries(teams.map(t => [t.shortName, t]))
let updated = 0
let skipped = 0
for (const fdMatch of fdMatches) {
const newStatus = mapStatus(fdMatch.status)
if (newStatus === 'SCHEDULED') { skipped++; continue }
const homeTla = fdMatch.homeTeam?.tla
const awayTla = fdMatch.awayTeam?.tla
if (!homeTla || !awayTla) { skipped++; continue }
const homeTeam = teamByShortName[homeTla]
const awayTeam = teamByShortName[awayTla]
if (!homeTeam || !awayTeam) { skipped++; continue }
const match = await prisma.match.findFirst({
where: { homeTeamId: homeTeam.id, awayTeamId: awayTeam.id },
})
if (!match) { skipped++; continue }
// Evitar re-procesar partidos ya finalizados
if (match.status === 'FINISHED' && newStatus === 'FINISHED') { skipped++; continue }
const homeScore = fdMatch.score.fullTime.home
const awayScore = fdMatch.score.fullTime.away
const updatedMatch = await prisma.match.update({
where: { id: match.id },
data: {
status: newStatus,
...(homeScore !== null && { homeScore }),
...(awayScore !== null && { awayScore }),
},
})
if (newStatus === 'FINISHED' && homeScore !== null && awayScore !== null) {
const predictions = await prisma.prediction.findMany({ where: { matchId: match.id } })
await Promise.all(
predictions.map(p =>
prisma.prediction.update({
where: { id: p.id },
data: { points: calculatePoints(p.result as 'HOME' | 'DRAW' | 'AWAY', homeScore, awayScore) },
})
)
)
}
updated++
}
return NextResponse.json({ ok: true, updated, skipped, total: fdMatches.length })
}