Files
thermcool/app/zgloszenie-awarii/actions.ts
T
MichalC d11808b471 fix: standaryzacja i bezpieczeństwo formularzy
- Ujednolicono kontakt/actions.ts (rate limit, escapeHtml, getMailClient, usunięto bezpośrednie Resend)
- Usunięto nieużywany plik lib/recaptcha.ts
- Naprawiono brak obsługi błędów API (Resend error) w zgloszenie-awarii, zamowienie i zapytanie-multisplit
- Dodano logowanie błędów w catch dla tych formularzy
- Posprzątano stare skrypty robocze z roota
2026-08-02 21:10:24 +02:00

95 lines
4.4 KiB
TypeScript

'use server';
import { z } from 'zod';
import { headers } from "next/headers";
import { escapeHtml, rateLimit, verifyRecaptcha, baseContactSchema } from "@/lib/security";
import { getMailClient } from "@/lib/mail";
const awariaFormSchema = baseContactSchema.omit({ consent: true }).extend({
isUrgent: z.boolean().optional(),
brand: z.string().min(1, "Wymagana nazwa urządzenia"),
model: z.string().optional(),
errorCode: z.string().optional(),
problemType: z.string().optional(),
description: z.string().optional(),
warranty: z.string().optional(),
lastService: z.string().optional(),
city: z.string().min(1, "Wymagane miasto"),
preferredDate: z.string().optional(),
preferredTime: z.string().optional(),
recaptchaToken: z.string().optional(),
});
export async function submitAwariaForm(data: Record<string, unknown>) {
try {
const ip = (await headers()).get('x-forwarded-for') || 'unknown';
if (!rateLimit(ip)) {
return { success: false, message: 'Wysłano zbyt wiele zapytań. Spróbuj ponownie później.' };
}
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
if (!data.recaptchaToken || typeof data.recaptchaToken !== 'string') {
return { success: false, message: "Weryfikacja bezpieczeństwa nie powiodła się. Odśwież stronę i spróbuj ponownie." };
}
const success = await verifyRecaptcha(data.recaptchaToken);
if (!success) {
return { success: false, message: "Weryfikacja bezpieczeństwa nie powiodła się. Odśwież stronę i spróbuj ponownie." };
}
}
const mailClient = getMailClient();
if (!mailClient) {
return { success: false, message: "Funkcja wysyłania wiadomości jest tymczasowo niedostępna." };
}
// Default missing fields for zod validation
const parsedData = awariaFormSchema.parse({
...data,
email: data.email || '[email protected]',
});
const { data: emailData, error } = await mailClient.resend.emails.send({
from: mailClient.fromEmail,
to: mailClient.toEmail,
subject: `${parsedData.isUrgent ? '🚨 PILNE — ' : ''}Zgłoszenie serwisowe: ${escapeHtml(parsedData.brand)} — ${escapeHtml(parsedData.city)}`,
html: `
<div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;">
<div style="background:${parsedData.isUrgent ? '#ef4444' : '#0F2A47'};padding:20px;text-align:center;">
<h1 style="color:#fff;margin:0;font-size:20px;">
${parsedData.isUrgent ? '🚨 PILNE — ' : ''}Zgłoszenie serwisowe ThermCool
</h1>
</div>
<div style="padding:24px;">
<h2 style="font-size:16px;margin-top:0;">Dane klienta</h2>
<p><strong>Imię:</strong> ${escapeHtml(parsedData.name)}</p>
<p><strong>Telefon:</strong> <a href="tel:${escapeHtml(parsedData.phone)}">${escapeHtml(parsedData.phone)}</a></p>
<p><strong>Miasto:</strong> ${escapeHtml(parsedData.city)}</p>
${parsedData.preferredDate ? `<p><strong>Preferowany termin:</strong> ${escapeHtml(parsedData.preferredDate)} ${escapeHtml(parsedData.preferredTime || '')}</p>` : ''}
<h2 style="font-size:16px;">Urządzenie</h2>
<p><strong>Marka:</strong> ${escapeHtml(parsedData.brand)}</p>
${parsedData.model ? `<p><strong>Model:</strong> ${escapeHtml(parsedData.model)}</p>` : ''}
${parsedData.warranty ? `<p><strong>Gwarancja:</strong> ${escapeHtml(parsedData.warranty)}</p>` : ''}
${parsedData.lastService ? `<p><strong>Ostatni serwis:</strong> ${escapeHtml(parsedData.lastService)}</p>` : ''}
<h2 style="font-size:16px;">Problem</h2>
<p><strong>Typ problemu:</strong> ${escapeHtml(parsedData.problemType || '')}</p>
${parsedData.errorCode ? `<p><strong>Kod błędu:</strong> ${escapeHtml(parsedData.errorCode)}</p>` : ''}
${parsedData.description ? `<p><strong>Opis:</strong> ${escapeHtml(parsedData.description)}</p>` : ''}
</div>
</div>
`,
});
if (error) {
console.error("Resend Error:", error);
return { success: false, message: "Nie udało się wysłać wiadomości ze względu na błąd serwera pocztowego." };
}
console.log("Wysłano e-mail (ID):", emailData?.id);
return { success: true };
} catch (error) {
console.error("Błąd formularza:", error);
return { success: false, message: 'Wystąpił błąd podczas wysyłania.' };
}
}