This commit is contained in:
Jakub Kaniecki
2025-03-30 14:46:32 +02:00
commit 4df1d9f9a6
65 changed files with 62132 additions and 0 deletions

16
backend/.env.example Normal file
View File

@@ -0,0 +1,16 @@
# Server Configuration
PORT=3001
NODE_ENV=development
# Email Configuration
SMTP_HOST=smtp.mail.me.com
SMTP_PORT=587
SMTP_USER=your-email@me.com
SMTP_PASS=your-app-specific-password
SMTP_FROM=your-email@me.com
SMTP_TO=your-email@me.com
# Security
CORS_ORIGIN=https://knck.pl
RATE_LIMIT_WINDOW_MS=900000 # 15 minutes
RATE_LIMIT_MAX_REQUESTS=5 # 5 requests per window

18
backend/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Expose port
EXPOSE 3001
# Start the application
CMD ["npm", "start"]

22
backend/package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "knck-contact-backend",
"version": "1.0.0",
"description": "Contact form backend for knck.pl",
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"express-rate-limit": "^7.1.5",
"helmet": "^7.1.0",
"nodemailer": "^6.9.11"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}

89
backend/src/index.js Normal file
View File

@@ -0,0 +1,89 @@
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import dotenv from 'dotenv';
import nodemailer from 'nodemailer';
// Load environment variables
dotenv.config();
const app = express();
const port = process.env.PORT || 3001;
// Middleware
app.use(helmet());
app.use(express.json());
app.use(cors({
origin: process.env.CORS_ORIGIN,
methods: ['POST']
}));
// Rate limiting
const limiter = rateLimit({
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 900000, // 15 minutes
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 5 // 5 requests per window
});
app.use('/api/contact', limiter);
// Email transporter setup
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT),
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
// Contact form endpoint
app.post('/api/contact', async (req, res) => {
try {
const { name, email, message } = req.body;
// Validate input
if (!name || !email || !message) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Email content
const mailOptions = {
from: process.env.SMTP_FROM,
to: process.env.SMTP_TO,
subject: `New Contact Form Submission from ${name}`,
text: `
Name: ${name}
Email: ${email}
Message:
${message}
`,
html: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Message:</strong></p>
<p>${message.replace(/\n/g, '<br>')}</p>
`
};
// Send email
await transporter.sendMail(mailOptions);
res.status(200).json({ message: 'Email sent successfully' });
} catch (error) {
console.error('Error sending email:', error);
res.status(500).json({ error: 'Failed to send email' });
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
// Start server
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});