Contact form logic
This commit is contained in:
72
src/email-templates/README.md
Normal file
72
src/email-templates/README.md
Normal 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
|
111
src/email-templates/admin-notification.ts
Normal file
111
src/email-templates/admin-notification.ts
Normal 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.
|
||||
`;
|
||||
}
|
120
src/email-templates/user-confirmation.ts
Normal file
120
src/email-templates/user-confirmation.ts
Normal 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}.
|
||||
`;
|
||||
}
|
Reference in New Issue
Block a user