Updated to svenskalag.se and new jmap mail server
Publish Docker image / Build and Push Docker image (release) Successful in 33s
Publish Docker image / Build and Push Docker image (release) Successful in 33s
This commit is contained in:
+108
-47
@@ -2,8 +2,6 @@
|
||||
|
||||
'use server';
|
||||
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
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 = {
|
||||
@@ -21,9 +19,9 @@ export async function submitRegistration(prevState: unknown, formData: FormData)
|
||||
return { success: false, message: 'Spam detekterat.', values: rawData };
|
||||
}
|
||||
|
||||
// 2. Hämta Turnstile Token och Secret (MATCHING .env NAME)
|
||||
// 2. Hämta Turnstile Token och Secret
|
||||
const turnstileToken = formData.get('cf-turnstile-response');
|
||||
const secretKey = process.env.TURNSTILE_SECRET; // Matchar din .env exakt
|
||||
const secretKey = process.env.TURNSTILE_SECRET;
|
||||
|
||||
if (!secretKey) {
|
||||
console.error("Saknar TURNSTILE_SECRET i miljön.");
|
||||
@@ -73,43 +71,97 @@ export async function submitRegistration(prevState: unknown, formData: FormData)
|
||||
? `+46 (0)${cleanTel.substring(0, 2)}-${cleanTel.substring(2, 5)} ${cleanTel.substring(5, 7)} ${cleanTel.substring(7, 9)}`
|
||||
: rawData.tel;
|
||||
|
||||
// 5. Nodemailer setup
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: 587,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
});
|
||||
// 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
|
||||
};
|
||||
}
|
||||
|
||||
// Hjälpfunktion för att bygga och skicka via Stalwart JMAP
|
||||
async function sendMailJMAP(toEmail: string, replyToEmail: string, subject: string, htmlContent: string, fromName: string) {
|
||||
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": {
|
||||
from: [{ email: accountId, name: fromName }],
|
||||
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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"1"
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
const res = await fetch(jmapUrl as string, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${jmapToken}`
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`JMAP API Error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// 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'
|
||||
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, " ") : "";
|
||||
|
||||
// Mail till klubben
|
||||
await transporter.sendMail({
|
||||
from: `"Beach-anmälan: ${rawData.name}" <${process.env.SMTP_USER}>`,
|
||||
to: 'beach@lerbergetsvolleyboll.se',
|
||||
replyTo: rawData.email,
|
||||
subject: `Anmälan: ${rawData.team}`,
|
||||
html: `
|
||||
// HTML för mail till klubben
|
||||
const mailTillKlubbHtml = `
|
||||
<html>
|
||||
<body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; padding: 20px;'>
|
||||
<h2 style='color: #39979f;'>Beach-anmälan</h2>
|
||||
|
||||
<table style='width: 100%; max-width: 600px; border-collapse: collapse;'>
|
||||
<tr><td style='padding: 5px; font-weight: bold; width: 150px;'>Lagnamn:</td><td>${rawData.team}</td></tr>
|
||||
<tr><td style='padding: 5px; font-weight: bold;'>Kontakt:</td><td>${rawData.name}</td></tr>
|
||||
@@ -119,14 +171,11 @@ export async function submitRegistration(prevState: unknown, formData: FormData)
|
||||
<tr><td style='padding: 5px; font-weight: bold;'>Nivå:</td><td>${rawData.level}</td></tr>
|
||||
</table>
|
||||
<p><strong>Meddelande:</strong><br>${rawData.message ? rawData.message.replace(/\n/g, '<br>') : '<em>Inget meddelande</em>'}</p>
|
||||
|
||||
<hr style='border: none; border-top: 1px solid #ddd; margin: 30px 0;'>
|
||||
|
||||
<details style="margin-top: 20px;">
|
||||
<summary style="background-color: #fdb84b; color: #fff; padding: 12px 20px; border-radius: 8px; font-weight: bold; cursor: pointer; display: inline-block; font-size: 14px; text-transform: uppercase; letter-spacing: 1px;">
|
||||
Visa kopierbar tabell för Excel
|
||||
</summary>
|
||||
|
||||
<div style="margin-top: 15px; overflow-x: auto;">
|
||||
<table border='1' style='border-collapse: collapse; font-family: Calibri, sans-serif; font-size: 11pt; width: 100%; text-align: left;'>
|
||||
<tr style="background-color: #f9f9f9;">
|
||||
@@ -144,26 +193,38 @@ export async function submitRegistration(prevState: unknown, formData: FormData)
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
});
|
||||
`;
|
||||
|
||||
// Bekräftelse till användaren
|
||||
await transporter.sendMail({
|
||||
from: `"LVS" <${process.env.SMTP_USER}>`,
|
||||
to: rawData.email,
|
||||
replyTo: `"LVS Beach" <beach@lerbergetsvolleyboll.se>`,
|
||||
subject: `Vi har tagit emot din anmälan för ${rawData.team}`,
|
||||
html: `<div style="font-family: sans-serif; padding: 20px; color: #333;">
|
||||
// HTML för bekräftelse till användaren
|
||||
const mailTillAnvandareHtml = `
|
||||
<div style="font-family: sans-serif; padding: 20px; color: #333;">
|
||||
<h2 style="color: #39979f;">Hej ${rawData.name}!</h2>
|
||||
<p>Tack för din anmälan till beachturneringen. Vi återkommer så snart vi har granskat era uppgifter.</p>
|
||||
<p>Ses på stranden!<br><strong style="color: #fdb84b;">Lerbergets Volleybollsällskap</strong></p>
|
||||
</div>`
|
||||
});
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Skicka mailen parallellt för prestanda (frivilligt, men oftast bättre än sekventiellt)
|
||||
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
|
||||
),
|
||||
sendMailJMAP(
|
||||
rawData.email, // To
|
||||
'beach@lerbergetsvolleyboll.se', // ReplyTo
|
||||
`Vi har tagit emot din anmälan för ${rawData.team}`, // Subject
|
||||
mailTillAnvandareHtml, // HTML Content
|
||||
"LVS" // From Name
|
||||
)
|
||||
]);
|
||||
|
||||
// Returnerar INGA värden här, vilket gör att formuläret kan rensas rent
|
||||
return { success: true, message: 'Tack! Din anmälan har skickats. Håll utkik i din inkorg efter bekräftelse.' };
|
||||
} catch (e) {
|
||||
console.error("Nodemailer error:", e);
|
||||
console.error("JMAP Fetch error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: 'Kunde inte skicka mail. Kontakta oss manuellt istället.',
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FaFacebookSquare, FaInstagram } from "react-icons/fa";
|
||||
import { IoIosMail } from "react-icons/io";
|
||||
import Image from 'next/image';
|
||||
import player from "../../public/images/player.svg";
|
||||
import laget from "../../public/images/laget.se.svg";
|
||||
import svenskalag from "../../public/images/svenskalag.se.png";
|
||||
|
||||
export default function HomeClientContent() {
|
||||
const [activeModal, setActiveModal] = useState<'member' | 'subscribe' | null>(null);
|
||||
@@ -42,7 +42,7 @@ export default function HomeClientContent() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden md:flex md:relative w-64 md:w-75 player-animation">
|
||||
<Image src={player} alt="Spelare" className="w-full h-auto" />
|
||||
<Image src={player} alt="Spelare" className="w-full h-auto" loading='eager' />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -52,9 +52,9 @@ export default function HomeClientContent() {
|
||||
<div className="flex flex-col items-center w-full my-7.5 gap-2">
|
||||
<InfoCard
|
||||
title="Träningstider"
|
||||
description="Här hittar du alltid uppdaterade träningstider från Laget.se"
|
||||
description="Här hittar du alltid uppdaterade träningstider från Svenskalag.se"
|
||||
actionLabel="Se nästa träningstid"
|
||||
href="https://www.laget.se/LVS/Event/Month"
|
||||
href="https://www.svenskalag.se/lerbergetsvs/kalender/"
|
||||
/>
|
||||
<InfoCard
|
||||
title="Prenumerera på träningstider"
|
||||
@@ -78,15 +78,15 @@ export default function HomeClientContent() {
|
||||
<FaFacebookSquare className="h-20 w-20" />
|
||||
</SocialIcon>
|
||||
|
||||
<SocialIcon title="Laget.se" href="https://www.laget.se/LVS" aria-label="Kolla träningstider på Laget.se">
|
||||
<Image src={laget} alt="Laget.se" className="h-18 w-18" />
|
||||
<SocialIcon title="Svenskalag.se" href="https://www.svenskalag.se/lerbergetsvs" aria-label="Kolla träningstider på Svenskalag.se">
|
||||
<Image src={svenskalag} alt="Svenskalag.se" className="h-18 w-18" />
|
||||
</SocialIcon>
|
||||
</div>
|
||||
|
||||
{/* Kontaktinformation */}
|
||||
<div className="text-center p-2.5 flex flex-col justify-center items-center">
|
||||
<h3 className="uppercase font-bold text-[1.875rem] leading-[1.2] mb-4">Kontakta oss</h3>
|
||||
<p className="text-[1rem] leading-[1.6] opacity-90">Vi finns på Instagram, Facebook och laget.se</p>
|
||||
<p className="text-[1rem] leading-[1.6] opacity-90">Vi finns på Instagram, Facebook och Svenskalag.se</p>
|
||||
|
||||
{/* Klickbar Adress - Länkar till Google Maps */}
|
||||
<a
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function MemberModal({ onClose }: ModalProps) {
|
||||
<h2 className="text-[#406185] my-2.5 text-2xl font-bold uppercase">Registrera dig</h2>
|
||||
<p className="text-[#406185] my-2.5">Trycka på knappen nedan för att starta registreringen. Du kommer att skickas till laget.se</p>
|
||||
|
||||
<ModalAction href="https://www.laget.se/LVS/Member" className="mt-5">
|
||||
<ModalAction href="https://www.svenskalag.se/lerbergetsvs/formular/borja-trana-i-lerbergets-volleybollsallskap/38396" className="mt-5">
|
||||
Bli medlem
|
||||
</ModalAction>
|
||||
</Modal>
|
||||
|
||||
@@ -11,11 +11,10 @@ export default function SubscribeModal({ onClose }: ModalProps) {
|
||||
<p className="text-[#406185] my-2.5">Välj vilken LEVEL du vill prenumerera på.</p>
|
||||
|
||||
<div className="flex flex-row flex-wrap justify-center gap-6.25 mt-5 max-w-162.5">
|
||||
<ModalAction href="webcal://cal.laget.se/lvs-level-5-6.ics">Level 5-6</ModalAction>
|
||||
<ModalAction href="webcal://cal.laget.se/lvs-level-7-8.ics">Level 7-8</ModalAction>
|
||||
<ModalAction href="webcal://cal.laget.se/lvs-level-8-9.ics">Level 8-9</ModalAction>
|
||||
<ModalAction href="webcal://cal.laget.se/lvs-level-10.ics">Level 10</ModalAction>
|
||||
<ModalAction href="webcal://cal.laget.se/LVS.ics">Hela klubben</ModalAction>
|
||||
<ModalAction href="webcal://cal.svenskalag.se/120351">Level 4-6</ModalAction>
|
||||
<ModalAction href="webcal://cal.svenskalag.se/120353">Level 7-9</ModalAction>
|
||||
<ModalAction href="webcal://cal.svenskalag.se/120355">Level 10</ModalAction>
|
||||
<ModalAction href="webcal://cal.svenskalag.se/120187">Hela klubben</ModalAction>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
Generated
+838
-679
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -11,7 +11,6 @@
|
||||
"dependencies": {
|
||||
"@marsidev/react-turnstile": "^1.4.2",
|
||||
"next": "^16",
|
||||
"nodemailer": "^8.0.1",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
@@ -27,5 +26,9 @@
|
||||
"eslint-config-next": "16.1.6",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"allowScripts": {
|
||||
"sharp@0.34.5": true,
|
||||
"unrs-resolver@1.12.2": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Reference in New Issue
Block a user