Invio best-effort il risultato dello scout finalizzato a Strapi

Nuova route server /api/public/scout-finale: proxy verso il content type
scout-match-finale con un token Strapi di sola scrittura, mai esposto al
client. Il salvataggio in localStorage (già chiamato da finePartita() in
scout.tsx nel commit precedente) resta la fonte per le statistiche; questo
invio è additivo e non bloccante, se fallisce la partita resta comunque
salvata sul device come già accadeva prima.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 15:39:46 +02:00
co-authored by Claude Sonnet 5
parent 953908388d
commit 839db3d4ca
+74
View File
@@ -0,0 +1,74 @@
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
const schema = z.object({
id: z.string().min(1).max(50),
data: z.string().min(1).max(20),
avversario: z.string().min(1).max(80),
casa: z.boolean(),
setNostri: z.number().int().min(0),
setLoro: z.number().int().min(0),
parziali: z.array(z.tuple([z.number().int(), z.number().int()])),
mvp: z.string().max(80),
azioni: z.array(
z.object({
id: z.string(),
tipo: z.enum(["attacco", "ace", "muro", "errore", "punto_avv", "errore_avv"]),
giocatoreId: z.string().optional(),
set: z.number().int(),
ts: z.number(),
}),
),
});
/**
* Proxy verso Strapi per il risultato finale di una partita scoutata: additivo, il localStorage
* del device resta la fonte usata da useRosa/giocatoriConScout. Best-effort: nessun retry, nessun
* upsert (un doppio invio crea al più una entry doppia in Strapi, da cancellare a mano).
*/
export const Route = createFileRoute("/api/public/scout-finale")({
server: {
handlers: {
POST: async ({ request }) => {
const parsed = schema.safeParse(await request.json());
if (!parsed.success) return new Response("Dati non validi", { status: 400 });
const strapiUrl = process.env["VITE_STRAPI_URL"];
const token = process.env["STRAPI_WRITE_TOKEN"];
if (!strapiUrl || !token) {
console.error("scout-finale: STRAPI_URL o STRAPI_WRITE_TOKEN mancanti");
return new Response("CMS non configurato", { status: 503 });
}
const m = parsed.data;
const res = await fetch(`${strapiUrl}/api/scout-match-finales`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
data: {
match_id: m.id,
data: m.data,
avversario: m.avversario,
casa: m.casa,
set_nostri: m.setNostri,
set_loro: m.setLoro,
parziali: m.parziali,
mvp: m.mvp,
azioni: m.azioni,
},
}),
});
if (!res.ok) {
console.error("scout-finale: Strapi ha risposto", res.status, await res.text());
return new Response("Salvataggio su CMS non riuscito", { status: 502 });
}
return Response.json({ ok: true });
},
},
},
});