Contact form logic

This commit is contained in:
becarta
2025-03-04 00:32:39 +01:00
parent 9c61657071
commit e9d3d8a2fb
21 changed files with 2210 additions and 14 deletions

View File

@@ -0,0 +1,72 @@
# Email Handling System
This directory contains the email templates and utilities for the contact form email handling system.
## Features
- **Secure SMTP Authentication**: Uses environment variables for credentials
- **Email Templates**: Customizable templates for both user confirmation and admin notification emails
- **Rate Limiting**: Prevents abuse by limiting the number of submissions per IP address
- **CSRF Protection**: Prevents cross-site request forgery attacks
- **Email Validation**: Ensures valid email addresses are provided
- **Spam Prevention**: Multiple checks to detect and block spam submissions
- **Error Handling**: Proper error handling with client feedback
- **Logging**: Comprehensive logging of email sending attempts
## Configuration
The email system is configured using environment variables in the `.env` file:
```
# SMTP Configuration
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-email@example.com
SMTP_PASS=your-password
# Email Settings
ADMIN_EMAIL=admin@example.com
WEBSITE_NAME=Your Website Name
# Environment
NODE_ENV=development
```
In development mode, emails are logged to the console instead of being sent. Set `NODE_ENV=production` to send actual emails.
## Files
- `admin-notification.ts`: Template for emails sent to the admin
- `user-confirmation.ts`: Template for confirmation emails sent to users
- `../utils/email-handler.ts`: Core email handling functionality
## How It Works
1. When a user submits the contact form, the client-side JavaScript validates the form and sends it to the `/api/contact` endpoint.
2. The endpoint validates the form data, checks for CSRF token validity, and performs rate limiting and spam detection.
3. If all checks pass, two emails are sent:
- A notification email to the admin with the form data
- A confirmation email to the user acknowledging receipt of their message
4. The system logs all email sending attempts for monitoring and debugging.
## Development vs. Production
- In development mode (`NODE_ENV=development`), emails are logged to the console instead of being sent.
- In production mode (`NODE_ENV=production`), emails are sent using the configured SMTP server.
## Security Considerations
- SMTP credentials are stored in environment variables, not in the code
- CSRF tokens are used to prevent cross-site request forgery
- Rate limiting prevents abuse of the contact form
- Form data is validated both on the client and server side
- Spam detection helps prevent unwanted messages
## Testing
To test the email system:
1. Configure the `.env` file with your SMTP settings
2. Submit the contact form on the website
3. Check the logs for email sending attempts
4. In production mode, check your inbox for the actual emails

View File

@@ -0,0 +1,111 @@
interface AdminNotificationProps {
name: string;
email: string;
message: string;
submittedAt: string;
ipAddress?: string;
userAgent?: string;
}
export function getAdminNotificationSubject(): string {
return 'New Contact Form Submission from bergsma.it';
}
export function getAdminNotificationHtml(props: AdminNotificationProps): string {
const { name, email, message, submittedAt, ipAddress, userAgent } = props;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Contact Form Submission</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #f5f5f5;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.content {
background-color: #ffffff;
padding: 15px;
border-radius: 5px;
border: 1px solid #e0e0e0;
}
.message-box {
background-color: #f9f9f9;
padding: 15px;
border-radius: 5px;
border-left: 3px solid #007bff;
margin: 15px 0;
}
.footer {
font-size: 12px;
color: #777;
margin-top: 20px;
padding-top: 10px;
border-top: 1px solid #e0e0e0;
}
.meta {
font-size: 12px;
color: #777;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="header">
<h2>New Contact Form Submission</h2>
</div>
<div class="content">
<p><strong>From:</strong> ${name} (${email})</p>
<p><strong>Submitted on:</strong> ${submittedAt}</p>
<div class="message-box">
<p><strong>Message:</strong></p>
<p>${message.replace(/\n/g, '<br>')}</p>
</div>
<div class="meta">
<p><strong>Additional Information:</strong></p>
<p>IP Address: ${ipAddress || 'Not available'}</p>
<p>User Agent: ${userAgent || 'Not available'}</p>
</div>
</div>
<div class="footer">
<p>This is an automated email from your website contact form.</p>
</div>
</body>
</html>
`;
}
export function getAdminNotificationText(props: AdminNotificationProps): string {
const { name, email, message, submittedAt, ipAddress, userAgent } = props;
return `
New Contact Form Submission
From: ${name} (${email})
Submitted on: ${submittedAt}
Message:
${message}
Additional Information:
IP Address: ${ipAddress || 'Not available'}
User Agent: ${userAgent || 'Not available'}
This is an automated email from your website contact form.
`;
}

View File

@@ -0,0 +1,120 @@
interface UserConfirmationProps {
name: string;
email: string;
message: string;
submittedAt: string;
websiteName?: string;
contactEmail?: string;
}
export function getUserConfirmationSubject(websiteName: string = 'bergsma.it'): string {
return `Thank you for contacting ${websiteName}`;
}
export function getUserConfirmationHtml(props: UserConfirmationProps): string {
const {
name,
message,
submittedAt,
websiteName = 'bergsma.it',
contactEmail = 'richard@bergsma.it'
} = props;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thank you for contacting us</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #f5f5f5;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.content {
background-color: #ffffff;
padding: 15px;
border-radius: 5px;
border: 1px solid #e0e0e0;
}
.message-box {
background-color: #f9f9f9;
padding: 15px;
border-radius: 5px;
border-left: 3px solid #28a745;
margin: 15px 0;
}
.footer {
font-size: 12px;
color: #777;
margin-top: 20px;
padding-top: 10px;
border-top: 1px solid #e0e0e0;
}
</style>
</head>
<body>
<div class="header">
<h2>Thank you for contacting ${websiteName}</h2>
</div>
<div class="content">
<p>Dear ${name},</p>
<p>Thank you for reaching out to us. We have received your message and will get back to you as soon as possible.</p>
<div class="message-box">
<p><strong>Your message (submitted on ${submittedAt}):</strong></p>
<p>${message.replace(/\n/g, '<br>')}</p>
</div>
<p>If you have any additional questions or information to provide, please feel free to reply to this email.</p>
<p>Best regards,<br>
The ${websiteName} Team</p>
</div>
<div class="footer">
<p>If you did not submit this contact form, please disregard this email or contact us at ${contactEmail}.</p>
</div>
</body>
</html>
`;
}
export function getUserConfirmationText(props: UserConfirmationProps): string {
const {
name,
message,
submittedAt,
websiteName = 'bergsma.it',
contactEmail = 'richard@bergsma.it'
} = props;
return `
Thank you for contacting ${websiteName}
Dear ${name},
Thank you for reaching out to us. We have received your message and will get back to you as soon as possible.
Your message (submitted on ${submittedAt}):
${message}
If you have any additional questions or information to provide, please feel free to reply to this email.
Best regards,
The ${websiteName} Team
If you did not submit this contact form, please disregard this email or contact us at ${contactEmail}.
`;
}