Enable website to send email notifications for new contact form submissions

Adds nodemailer to send contact form messages to hello@pilateswithfadia.com.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: d004b9e1-f9be-46e2-acda-f440ccd644a9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/af8dabca-e746-4e53-9c29-d8d4d9cf30f5/326fe4d1-4762-4d4b-a391-c6c35a1a7590.jpg
This commit is contained in:
JeffEmmett 2025-05-23 07:36:34 +00:00
parent 82d58348e7
commit ed2cb775bf
4 changed files with 94 additions and 7 deletions

20
package-lock.json generated
View File

@ -41,6 +41,7 @@
"@radix-ui/react-tooltip": "^1.2.0",
"@tailwindcss/vite": "^4.1.3",
"@tanstack/react-query": "^5.60.5",
"@types/nodemailer": "^6.4.17",
"axios": "^1.9.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@ -57,6 +58,7 @@
"lucide-react": "^0.453.0",
"memorystore": "^1.6.7",
"next-themes": "^0.4.6",
"nodemailer": "^7.0.3",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
"react": "^18.3.1",
@ -3445,6 +3447,15 @@
"undici-types": "~6.19.2"
}
},
"node_modules/@types/nodemailer": {
"version": "6.4.17",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz",
"integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/passport": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz",
@ -6384,6 +6395,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/nodemailer": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz",
"integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",

View File

@ -43,6 +43,7 @@
"@radix-ui/react-tooltip": "^1.2.0",
"@tailwindcss/vite": "^4.1.3",
"@tanstack/react-query": "^5.60.5",
"@types/nodemailer": "^6.4.17",
"axios": "^1.9.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@ -59,6 +60,7 @@
"lucide-react": "^0.453.0",
"memorystore": "^1.6.7",
"next-themes": "^0.4.6",
"nodemailer": "^7.0.3",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
"react": "^18.3.1",

45
server/email.ts Normal file
View File

@ -0,0 +1,45 @@
import nodemailer from 'nodemailer';
interface EmailOptions {
to: string;
from: string;
subject: string;
text: string;
html?: string;
}
// Create a transporter
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com', // You may need to change this depending on your email provider
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.EMAIL_USER, // email address used for sending
pass: process.env.EMAIL_PASSWORD, // email password or app-specific password
},
tls: {
rejectUnauthorized: false // only for development
}
});
export async function sendEmail(options: EmailOptions): Promise<boolean> {
try {
// Set sender if not specified
const mailOptions = {
from: options.from || `"Pilates with Fadia" <${process.env.EMAIL_USER}>`,
to: options.to,
subject: options.subject,
text: options.text,
html: options.html,
replyTo: options.from // Set reply-to as the sender's email
};
// Send the email
const info = await transporter.sendMail(mailOptions);
console.log('Email sent:', info.messageId);
return true;
} catch (error) {
console.error('Error sending email:', error);
return false;
}
}

View File

@ -3,6 +3,7 @@ import { createServer, type Server } from "http";
import { storage } from "./storage";
import { setupAuth } from "./auth";
import { subscribeToMailchimp } from "./mailchimp";
import { sendEmail } from "./email";
import {
insertNewsletterSchema,
insertContactMessageSchema,
@ -122,13 +123,32 @@ export async function registerRoutes(app: Express): Promise<Server> {
const contactData = insertContactMessageSchema.parse(req.body);
const message = await storage.createContactMessage(contactData);
// In a real app, we'd send an email here
// sendEmail({
// to: "hello@pilateswithfadia.com",
// from: contactData.email,
// subject: contactData.subject || "New contact form submission",
// text: `Name: ${contactData.name}\nEmail: ${contactData.email}\nMessage: ${contactData.message}`
// });
// Send email to hello@pilateswithfadia.com
try {
await sendEmail({
to: "hello@pilateswithfadia.com",
from: contactData.email,
subject: contactData.subject || "New contact form submission from Pilates with Fadia website",
text: `You received a new message from your website contact form:\n\nName: ${contactData.name}\nEmail: ${contactData.email}\n\nMessage:\n${contactData.message}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #eaeaea; border-radius: 5px;">
<h2 style="color: #0c8991; border-bottom: 1px solid #eaeaea; padding-bottom: 10px;">New Message from Pilates with Fadia Website</h2>
<p><strong>From:</strong> ${contactData.name} (${contactData.email})</p>
<p><strong>Subject:</strong> ${contactData.subject || "No subject"}</p>
<div style="background-color: #f9f9f9; padding: 15px; border-radius: 4px; margin-top: 20px;">
<p style="white-space: pre-line;">${contactData.message}</p>
</div>
<p style="color: #666; font-size: 12px; margin-top: 30px; border-top: 1px solid #eaeaea; padding-top: 10px;">
This message was sent from the contact form on your Pilates with Fadia website.
</p>
</div>
`
});
console.log('Contact form email sent successfully');
} catch (emailError) {
console.error('Failed to send contact form email:', emailError);
// Continue with response even if email sending fails
}
res.status(201).json({ message: "Message sent successfully" });
} catch (error) {