From ed2cb775bfd11be2ed801233994f57a00f8c8eb5 Mon Sep 17 00:00:00 2001 From: JeffEmmett <20747463-JeffEmmett@users.noreply.replit.com> Date: Fri, 23 May 2025 07:36:34 +0000 Subject: [PATCH] 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 --- package-lock.json | 20 ++++++++++++++++++++ package.json | 2 ++ server/email.ts | 45 +++++++++++++++++++++++++++++++++++++++++++++ server/routes.ts | 34 +++++++++++++++++++++++++++------- 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 server/email.ts diff --git a/package-lock.json b/package-lock.json index 871df4c..6be374e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index e240c62..1d34a8b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/server/email.ts b/server/email.ts new file mode 100644 index 0000000..d3ac914 --- /dev/null +++ b/server/email.ts @@ -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 { + 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; + } +} \ No newline at end of file diff --git a/server/routes.ts b/server/routes.ts index 1558961..8f67402 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -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 { 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: ` +
+

New Message from Pilates with Fadia Website

+

From: ${contactData.name} (${contactData.email})

+

Subject: ${contactData.subject || "No subject"}

+
+

${contactData.message}

+
+

+ This message was sent from the contact form on your Pilates with Fadia website. +

+
+ ` + }); + 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) {