Files
mb-grid-solutions.com/server.ts
Marc Mintel 102a24d6a3
Some checks are pending
ci/woodpecker/manual/woodpecker Pipeline is pending
woodpecker
2026-01-15 19:27:41 +01:00

112 lines
2.9 KiB
TypeScript

import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import nodemailer from 'nodemailer';
import cors from 'cors';
import dotenv from 'dotenv';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
dotenv.config();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = process.env.PORT || 3000;
// Security
app.use(helmet({
contentSecurityPolicy: {
directives: {
...helmet.contentSecurityPolicy.getDefaultDirectives(),
"img-src": ["'self'", "data:"],
},
},
}));
app.use(cors());
app.use(express.json());
// Rate limiting for API
const apiLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Zu viele Anfragen von dieser IP, bitte versuchen Sie es später erneut.',
standardHeaders: true,
legacyHeaders: false,
});
// API Endpoint
app.post('/api/contact', apiLimiter, async (req, res) => {
const { name, email, company, message, website } = req.body;
// Honeypot check
if (website) {
console.log('Spam detected (honeypot)');
return res.status(200).json({ message: 'Ok' }); // Generic success
}
// Validation
if (!name || name.length < 2 || name.length > 100) {
return res.status(400).json({ error: 'Ungültiger Name' });
}
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
return res.status(400).json({ error: 'Ungültige E-Mail' });
}
if (!message || message.length < 20 || message.length > 4000) {
return res.status(400).json({ error: 'Nachricht zu kurz oder zu lang' });
}
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,
replyTo: email,
subject: `Kontaktanfrage von ${name}`,
text: `
Name: ${name}
Firma: ${company || 'Nicht angegeben'}
E-Mail: ${email}
Zeitpunkt: ${new Date().toISOString()}
Nachricht:
${message}
`,
});
res.status(200).json({ message: 'Ok' });
} catch (error) {
console.error('SMTP Error:', error);
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
// Health check
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
// Serve static files from the React app
const distPath = path.join(__dirname, 'dist/frontend');
app.use(express.static(distPath));
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});