From 839db3d4ca9e3d16a9b460858adc412e981baa41 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Fri, 28 Aug 2026 15:39:46 +0200 Subject: [PATCH] Invio best-effort il risultato dello scout finalizzato a Strapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/routes/api/public/scout-finale.ts | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/routes/api/public/scout-finale.ts diff --git a/src/routes/api/public/scout-finale.ts b/src/routes/api/public/scout-finale.ts new file mode 100644 index 0000000..33bd071 --- /dev/null +++ b/src/routes/api/public/scout-finale.ts @@ -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 }); + }, + }, + }, +});