longevity: API checkup accetta sessione_id per correggere (#12)

Un sessione_id nel form fa passare da correggiCheckup invece di
salvaCheckup, stesso endpoint. Il client_code per validazioni e
redirect si legge sempre dalla sessione nel DB, mai dal form.

Refs #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkWhSnSLvAyRCoWBuCpXvp
This commit is contained in:
2026-09-27 22:12:00 +02:00
parent 19035e67b0
commit 9e566ad7b1
2 changed files with 106 additions and 6 deletions
@@ -2,7 +2,7 @@ import type { APIRoute } from 'astro';
import { getLongevityDb } from '../../../../lib/longevity/db';
import { etaDelCliente } from '../../../../lib/longevity/anagrafica';
import { testAttivi, type VoceRegistro } from '../../../../lib/longevity/registro';
import { salvaCheckup } from '../../../../lib/longevity/gestionale';
import { salvaCheckup, correggiCheckup } from '../../../../lib/longevity/gestionale';
import type { MisuraIn } from '../../../../lib/longevity/misure';
export const prerender = false;
@@ -54,22 +54,52 @@ export function misureDalForm(
export const POST: APIRoute = async ({ request, locals }) => {
const form = await request.formData();
const clientCode = String(form.get('client_code') ?? '').trim();
const clientCodeForm = String(form.get('client_code') ?? '').trim();
const data = String(form.get('data') ?? '').trim();
// Un sessione_id nel form (issue #12) è una correzione: stesso form, stessa API, solo
// l'hidden field in più — checkup.astro lo mette quando arriva con ?sessione=<id>.
const sessioneIdRaw = String(form.get('sessione_id') ?? '').trim();
const sessioneId = sessioneIdRaw !== '' ? Number(sessioneIdRaw) : null;
// Senza un codice valido non c'è una scheda a cui tornare: l'unico ritorno sensato è
// l'indice del gestionale, non /cliente/undefined/checkup.
if (!REGEX_CODE.test(clientCode)) {
if (!REGEX_CODE.test(clientCodeForm)) {
return redirect(`/longevity/gestionale?errore=${encodeURIComponent('Codice cliente non valido.')}`);
}
const erroreAllaScheda = (testo: string) =>
redirect(`/longevity/gestionale/cliente/${clientCode}/checkup?errore=${encodeURIComponent(testo)}`);
const db = getLongevityDb();
// In correzione il client_code per validazioni e redirect è SEMPRE quello letto dalla
// sessione nel database, mai quello del form (stesso principio di verifica.ts): un
// sessione_id non è un fascicolo che si possa dirottare scrivendo un altro codice cliente.
let clientCode = clientCodeForm;
if (sessioneId !== null) {
if (!Number.isInteger(sessioneId)) {
return redirect(`/longevity/gestionale?errore=${encodeURIComponent('Sessione non valida.')}`);
}
const sessione = db.prepare(`SELECT client_code FROM sessioni WHERE id = ?`).get(sessioneId) as
{ client_code: string } | undefined;
if (!sessione) {
return redirect(`/longevity/gestionale?errore=${encodeURIComponent('Sessione inesistente.')}`);
}
clientCode = sessione.client_code;
if (clientCodeForm !== clientCode) {
return redirect(
`/longevity/gestionale/cliente/${clientCode}?errore=${encodeURIComponent('Codice cliente non coerente con la sessione.')}`
);
}
}
const erroreAllaScheda = (testo: string) => {
const query = new URLSearchParams({ errore: testo });
if (sessioneId !== null) query.set('sessione', String(sessioneId));
return redirect(`/longevity/gestionale/cliente/${clientCode}/checkup?${query.toString()}`);
};
if (!REGEX_DATA.test(data)) return erroreAllaScheda('Data non valida (formato AAAA-MM-GG).');
const oggi = new Date().toISOString().slice(0, 10);
if (data > oggi) return erroreAllaScheda('La data del check-up non può essere nel futuro.');
const db = getLongevityDb();
const campiAttivi = testAttivi(db, data).filter((v) => v.test_id.startsWith('f_'));
const parse = misureDalForm(form, campiAttivi);
@@ -81,6 +111,14 @@ export const POST: APIRoute = async ({ request, locals }) => {
const operatore = locals.user!.username;
const eta = etaDelCliente(clientCode, data);
if (sessioneId !== null) {
const esito = correggiCheckup(db, { sessioneId, data, eta, operatore, misure: parse.misure });
if (!esito.ok) return erroreAllaScheda(esito.errore);
const params = new URLSearchParams({ corretto: String(esito.sessioneId) });
if (esito.fuoriRange.length > 0) params.set('fuori', String(esito.fuoriRange.length));
return redirect(`/longevity/gestionale/cliente/${clientCode}?${params.toString()}`);
}
const esito = salvaCheckup(db, { client_code: clientCode, data, eta, operatore, misure: parse.misure });
if (!esito.ok) return erroreAllaScheda(esito.errore);