Fix contatti + rinomina servizi + orario sabato

- Contatti: rate-limit per IP reale (X-Forwarded-For) invece dell'IP del
  proxy Traefik, condiviso da tutti; controllo spostato dopo la validazione
  così bot/honeypot non consumano la quota. Honeypot rinominato da "website"
  a "hp_field" per non farlo riempire dall'autofill del browser (falsi 400).
- Servizi: "Performance Class" -> "Class" (titolo/card/H1/footer); migrazione
  DB invertita e guardata.
- One to One: migrazione titolo prod "Personal training" -> "One to One".
- Footer: orario sabato 8-17 (non 8-19), seed + migrazione guardata.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 17:58:26 +02:00
parent 22c9cadb43
commit b17b5c831a
9 changed files with 42 additions and 21 deletions
+6 -4
View File
@@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import { validateContact } from '../../lib/contact';
import { sendContactEmail } from '../../lib/mailer';
import { rateLimit } from '../../lib/rate-limit';
import { rateLimit, clientIp } from '../../lib/rate-limit';
export const prerender = false;
@@ -9,13 +9,15 @@ const json = (status: number, body: object) =>
new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } });
export const POST: APIRoute = async ({ request, clientAddress }) => {
if (!rateLimit(`contact:${clientAddress}`, 5, 60 * 60 * 1000)) {
return json(429, { error: 'Troppe richieste, riprova più tardi.' });
}
let data: unknown;
try { data = await request.json(); } catch { return json(400, { error: 'Dati non validi.' }); }
const result = validateContact(data);
if (!result.ok) return json(400, { error: result.error });
// Rate-limit solo sugli invii validi, per IP reale: bot/honeypot non
// consumano la quota di un utente legittimo.
if (!rateLimit(`contact:${clientIp(request, clientAddress)}`, 5, 60 * 60 * 1000)) {
return json(429, { error: 'Troppe richieste, riprova più tardi.' });
}
try {
await sendContactEmail(result.value);
} catch (err) {