feat: Integrate Directus CMS, add i18n with next-intl, and configure project tooling with pnpm, husky, and commitlint.**

This commit is contained in:
2026-02-05 01:18:06 +01:00
parent 765cfd4c69
commit e80140f7cf
65 changed files with 12793 additions and 5879 deletions

View File

@@ -1,56 +1,122 @@
import { NextResponse } from 'next/server';
import * as nodemailer from 'nodemailer';
import { NextResponse } from "next/server";
import * as nodemailer from "nodemailer";
import directus, { ensureAuthenticated } from "@/lib/directus";
import { createItem } from "@directus/sdk";
import { getServerAppServices } from "@/lib/services/create-services.server";
export async function POST(req: Request) {
const services = getServerAppServices();
const logger = services.logger.child({ action: "contact_submission" });
try {
const { name, email, company, message, website } = await req.json();
// Honeypot check
if (website) {
console.log('Spam detected (honeypot)');
return NextResponse.json({ message: 'Ok' });
logger.info("Spam detected (honeypot)");
return NextResponse.json({ message: "Ok" });
}
// Validation
if (!name || name.length < 2 || name.length > 100) {
return NextResponse.json({ error: 'Ungültiger Name' }, { status: 400 });
return NextResponse.json({ error: "Ungültiger Name" }, { status: 400 });
}
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
return NextResponse.json({ error: 'Ungültige E-Mail' }, { status: 400 });
return NextResponse.json({ error: "Ungültige E-Mail" }, { status: 400 });
}
if (!message || message.length < 20 || message.length > 4000) {
return NextResponse.json({ error: 'Nachricht zu kurz oder zu lang' }, { status: 400 });
if (!message || message.length < 20) {
return NextResponse.json({ error: "message_too_short" }, { status: 400 });
}
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '587'),
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
if (message.length > 4000) {
return NextResponse.json({ error: "message_too_long" }, { status: 400 });
}
await transporter.sendMail({
from: process.env.SMTP_FROM,
to: process.env.CONTACT_RECIPIENT,
replyTo: email,
subject: `Kontaktanfrage von ${name}`,
text: `
// 1. Directus save
let directusSaved = false;
try {
await ensureAuthenticated();
await directus.request(
createItem("contact_submissions", {
name,
email,
company: company || "Nicht angegeben",
message,
}),
);
logger.info("Contact submission saved to Directus");
directusSaved = true;
} catch (directusError) {
logger.error("Failed to save to Directus", { error: directusError });
services.errors.captureException(directusError, {
phase: "directus_save",
});
// We still try to send the email even if Directus fails
}
// 2. Email sending
try {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || "587"),
secure: process.env.SMTP_SECURE === "true",
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
await transporter.sendMail({
from: process.env.SMTP_FROM,
to: process.env.CONTACT_RECIPIENT || "info@mb-grid-solutions.com",
replyTo: email,
subject: `Kontaktanfrage von ${name}`,
text: `
Name: ${name}
Firma: ${company || 'Nicht angegeben'}
Firma: ${company || "Nicht angegeben"}
E-Mail: ${email}
Zeitpunkt: ${new Date().toISOString()}
Nachricht:
${message}
`,
});
`,
});
return NextResponse.json({ message: 'Ok' });
logger.info("Email sent successfully");
// Notify success for important leads
await services.notifications.notify({
title: "📩 Neue Kontaktanfrage",
message: `Anfrage von ${name} (${email}) erhalten.\nFirma: ${company || "Nicht angegeben"}`,
priority: 5,
});
} catch (smtpError) {
logger.error("SMTP Error", { error: smtpError });
services.errors.captureException(smtpError, { phase: "smtp_send" });
// If Directus failed AND SMTP failed, then we really have a problem
if (!directusSaved) {
return NextResponse.json(
{ error: "Systemfehler (Speicherung und Versand fehlgeschlagen)" },
{ status: 500 },
);
}
// If Directus was successful, we tell the user "Ok" but we know internally it was a partial failure
await services.notifications.notify({
title: "🚨 SMTP Fehler (Kontaktformular)",
message: `Anfrage von ${name} (${email}) in Directus gespeichert, aber E-Mail-Versand fehlgeschlagen: ${smtpError instanceof Error ? smtpError.message : String(smtpError)}`,
priority: 8,
});
}
return NextResponse.json({ message: "Ok" });
} catch (error) {
console.error('SMTP Error:', error);
return NextResponse.json({ error: 'Interner Serverfehler' }, { status: 500 });
logger.error("Global API Error", { error });
services.errors.captureException(error, { phase: "api_global" });
return NextResponse.json(
{ error: "Interner Serverfehler" },
{ status: 500 },
);
}
}