// app/beach/actions.ts 'use server'; export async function submitRegistration(prevState: unknown, formData: FormData) { // 1. Spara all rådata direkt för att kunna skicka tillbaka den vid fel const rawData = { name: formData.get('name') as string, email: formData.get('email') as string, tel: formData.get('tel') as string, team: formData.get('team') as string, count: formData.get('count') as string, level: formData.get('level') as string, message: formData.get('message') as string, }; const honeypot = formData.get('website'); if (honeypot) { return { success: false, message: 'Spam detekterat.', values: rawData }; } // 2. Hämta Turnstile Token och Secret const turnstileToken = formData.get('cf-turnstile-response'); const secretKey = process.env.TURNSTILE_SECRET; if (!secretKey) { console.error("Saknar TURNSTILE_SECRET i miljön."); return { success: false, message: 'Serverkonfiguration saknas. Kontakta administratör.', values: rawData }; } // 3. Verifiera mot Cloudflare try { const turnstileRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ secret: secretKey, response: String(turnstileToken || ''), }), }); const turnstileData = await turnstileRes.json(); if (!turnstileData.success) { console.error("Turnstile failed:", turnstileData); return { success: false, message: 'Säkerhetsverifieringen misslyckades. Ladda om sidan och försök igen.', values: rawData }; } } catch (error) { console.error("Turnstile fetch error:", error); return { success: false, message: 'Kunde inte nå verifieringsservern.', values: rawData }; } // 4. Formatera telefonnummer let cleanTel = rawData.tel.replace(/\D/g, ''); if (cleanTel.startsWith('46')) cleanTel = cleanTel.substring(2); else if (cleanTel.startsWith('0')) cleanTel = cleanTel.substring(1); const formattedTel = cleanTel.length === 9 ? `+46 (0)${cleanTel.substring(0, 2)}-${cleanTel.substring(2, 5)} ${cleanTel.substring(5, 7)} ${cleanTel.substring(7, 9)}` : rawData.tel; // 5. Stalwart JMAP Setup const jmapUrl = process.env.STALWART_JMAP_URL; const jmapToken = process.env.STALWART_API_TOKEN; const accountId = process.env.STALWART_ACCOUNT_ID; if (!jmapUrl || !jmapToken || !accountId) { console.error("Saknar JMAP-konfiguration i miljön."); return { success: false, message: 'E-postserver är inte konfigurerad. Kontakta administratör.', values: rawData }; } async function sendMailJMAP(toEmail: string, replyToEmail: string, subject: string, htmlContent: string, fromName: string, fromEmail: string) { // 1. Hämta Mapp-ID (Drafts/Utkast) och Identitets-ID från Stalwart const setupPayload = { using: ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"], methodCalls: [ ["Mailbox/query", { accountId: accountId, filter: { role: "drafts" } }, "getMailbox"], ["Identity/get", { accountId: accountId }, "getIdentity"] ] }; const setupRes = await fetch(jmapUrl as string, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${jmapToken}` }, body: JSON.stringify(setupPayload) }); const setupData = await setupRes.json(); const mailboxRes = setupData.methodResponses.find((r: any) => r[0] === "Mailbox/query"); const identityRes = setupData.methodResponses.find((r: any) => r[0] === "Identity/get"); let mailboxId = mailboxRes && mailboxRes[1].ids && mailboxRes[1].ids.length > 0 ? mailboxRes[1].ids[0] : null; // ---- NY LOGIK HÄR: Hitta rätt identitet baserat på fromEmail ---- let identityId = null; let actualFromEmail = fromEmail; if (identityRes && identityRes[1].list) { // Leta efter identiteten som matchar adressen vi vill skicka från const matchedIdentity = identityRes[1].list.find((i: any) => i.email === fromEmail); if (matchedIdentity) { identityId = matchedIdentity.id; } else if (identityRes[1].list.length > 0) { // Fallback om den exakta adressen inte fanns i listan identityId = identityRes[1].list[0].id; actualFromEmail = identityRes[1].list[0].email; } } // Om ingen mapp med rollen "drafts" finns, hämta första bästa mapp if (!mailboxId) { const fallbackRes = await fetch(jmapUrl as string, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${jmapToken}` }, body: JSON.stringify({ using: ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"], methodCalls: [["Mailbox/query", { accountId: accountId }, "0"]] }) }); const fallbackData = await fallbackRes.json(); mailboxId = fallbackData.methodResponses[0][1].ids[0]; } if (!mailboxId || !identityId) { throw new Error("Kunde inte hitta mailboxId eller identityId i Stalwart."); } // 2. Skapa och skicka mailet const payload = { using: [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission" ], methodCalls: [ [ "Email/set", { accountId: accountId, create: { "draft-1": { mailboxIds: { [mailboxId as string]: true }, from: [{ email: actualFromEmail, name: fromName }], // Nu använder vi rätt adress to: [{ email: toEmail }], replyTo: [{ email: replyToEmail }], subject: subject, bodyValues: { "body-html": { value: htmlContent, isTruncated: false, isEncodingProblem: false } }, htmlBody: [{ partId: "body-html", type: "text/html" }] } } }, "0" ], [ "EmailSubmission/set", { accountId: accountId, create: { "sub-1": { emailId: "#draft-1", identityId: identityId // Nu använder vi rätt ID } } }, "1" ] ] }; const res = await fetch(jmapUrl as string, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${jmapToken}` }, body: JSON.stringify(payload) }); const data = await res.json(); const hasErrors = data.methodResponses.some( (response: any) => response[0] === "error" || (response[1] && response[1].notCreated) ); if (hasErrors) { console.error("--- JMAP METHOD ERROR ---"); console.error(JSON.stringify(data.methodResponses, null, 2)); console.error("-------------------------"); throw new Error("JMAP accepterade anropet, men vägrade skapa/skicka mailet."); } return data; } // 6. Skapa och skicka mail try { const dateStr = new Date().toLocaleString('sv-SE', { timeZone: 'Europe/Stockholm', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); const cleanMessage = rawData.message ? rawData.message.replace(/[\r\n;]/g, " ") : ""; // HTML för mail till klubben const mailTillKlubbHtml = `

Beach-anmälan

Lagnamn:${rawData.team}
Kontakt:${rawData.name}
E-post:${rawData.email}
Telefon:${formattedTel}
Spelare:${rawData.count}
Nivå:${rawData.level}

Meddelande:
${rawData.message ? rawData.message.replace(/\n/g, '
') : 'Inget meddelande'}


Visa kopierbar tabell för Excel
${rawData.team} ${rawData.count} ${rawData.level} ${rawData.name} ${formattedTel} ${rawData.email} ${dateStr} ${cleanMessage}
`; // HTML för bekräftelse till användaren const mailTillAnvandareHtml = `

Hej ${rawData.name}!

Tack för din anmälan till beachturneringen. Vi återkommer så snart vi har granskat era uppgifter.

Ses på stranden!
Lerbergets Volleybollsällskap

`; // Skicka mailen parallellt await Promise.all([ sendMailJMAP( 'beach@lerbergetsvolleyboll.se', // To rawData.email, // ReplyTo `Anmälan: ${rawData.team}`, // Subject mailTillKlubbHtml, // HTML Content `Beach-anmälan: ${rawData.name}`,// From Name 'beach@lerbergetsvolleyboll.se' // From Email (VIKTIGT!) ), sendMailJMAP( rawData.email, // To 'beach@lerbergetsvolleyboll.se', // ReplyTo `Vi har tagit emot din anmälan för ${rawData.team}`, // Subject mailTillAnvandareHtml, // HTML Content "LVS Beach", // From Name 'beach@lerbergetsvolleyboll.se' // From Email (VIKTIGT!) ) ]); return { success: true, message: 'Tack! Din anmälan har skickats. Håll utkik i din inkorg efter bekräftelse.' }; } catch (e) { console.error("JMAP Fetch error:", e); return { success: false, message: 'Kunde inte skicka mail. Kontakta oss manuellt istället.', values: rawData }; } }