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:
parent
82d58348e7
commit
ed2cb775bf
|
|
@ -41,6 +41,7 @@
|
||||||
"@radix-ui/react-tooltip": "^1.2.0",
|
"@radix-ui/react-tooltip": "^1.2.0",
|
||||||
"@tailwindcss/vite": "^4.1.3",
|
"@tailwindcss/vite": "^4.1.3",
|
||||||
"@tanstack/react-query": "^5.60.5",
|
"@tanstack/react-query": "^5.60.5",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
@ -57,6 +58,7 @@
|
||||||
"lucide-react": "^0.453.0",
|
"lucide-react": "^0.453.0",
|
||||||
"memorystore": "^1.6.7",
|
"memorystore": "^1.6.7",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"nodemailer": "^7.0.3",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
|
@ -3445,6 +3447,15 @@
|
||||||
"undici-types": "~6.19.2"
|
"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": {
|
"node_modules/@types/passport": {
|
||||||
"version": "1.0.17",
|
"version": "1.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz",
|
||||||
|
|
@ -6384,6 +6395,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
"@radix-ui/react-tooltip": "^1.2.0",
|
"@radix-ui/react-tooltip": "^1.2.0",
|
||||||
"@tailwindcss/vite": "^4.1.3",
|
"@tailwindcss/vite": "^4.1.3",
|
||||||
"@tanstack/react-query": "^5.60.5",
|
"@tanstack/react-query": "^5.60.5",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
@ -59,6 +60,7 @@
|
||||||
"lucide-react": "^0.453.0",
|
"lucide-react": "^0.453.0",
|
||||||
"memorystore": "^1.6.7",
|
"memorystore": "^1.6.7",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"nodemailer": "^7.0.3",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { createServer, type Server } from "http";
|
||||||
import { storage } from "./storage";
|
import { storage } from "./storage";
|
||||||
import { setupAuth } from "./auth";
|
import { setupAuth } from "./auth";
|
||||||
import { subscribeToMailchimp } from "./mailchimp";
|
import { subscribeToMailchimp } from "./mailchimp";
|
||||||
|
import { sendEmail } from "./email";
|
||||||
import {
|
import {
|
||||||
insertNewsletterSchema,
|
insertNewsletterSchema,
|
||||||
insertContactMessageSchema,
|
insertContactMessageSchema,
|
||||||
|
|
@ -122,13 +123,32 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||||
const contactData = insertContactMessageSchema.parse(req.body);
|
const contactData = insertContactMessageSchema.parse(req.body);
|
||||||
const message = await storage.createContactMessage(contactData);
|
const message = await storage.createContactMessage(contactData);
|
||||||
|
|
||||||
// In a real app, we'd send an email here
|
// Send email to hello@pilateswithfadia.com
|
||||||
// sendEmail({
|
try {
|
||||||
// to: "hello@pilateswithfadia.com",
|
await sendEmail({
|
||||||
// from: contactData.email,
|
to: "hello@pilateswithfadia.com",
|
||||||
// subject: contactData.subject || "New contact form submission",
|
from: contactData.email,
|
||||||
// text: `Name: ${contactData.name}\nEmail: ${contactData.email}\nMessage: ${contactData.message}`
|
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" });
|
res.status(201).json({ message: "Message sent successfully" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue