Skip to content

Commit

Permalink
added contactRequests
Browse files Browse the repository at this point in the history
  • Loading branch information
westonz7042 committed Jan 11, 2025
1 parent 2a38544 commit 03f0fa7
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 9 deletions.
23 changes: 22 additions & 1 deletion backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"dependencies": {
"dotenv": "^16.4.7",
"express": "^4.21.1",
"mongoose": "^8.8.3"
"mongoose": "^8.8.3",
"nodemailer": "^6.9.16"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/nodemailer": "^6.4.17",
"@typescript-eslint/eslint-plugin": "^8.18.2",
"@typescript-eslint/parser": "^8.18.2",
"eslint": "^8.57.1",
Expand Down
10 changes: 4 additions & 6 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { json } from "body-parser";
import express from "express";

Check warning on line 2 in backend/src/app.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

There should be at least one empty line between import groups

import contactRoute from "./routes/contactRequest";

Check warning on line 3 in backend/src/app.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

`./routes/contactRequest` import should occur after import of `./errors/handler`
import { port } from "./config";

// Initialize Express App
import { errorHandler } from "./errors/handler";
const app = express();

// Provide json body-parser middleware
app.use(json());

// Tell app to listen on our port environment variable
app.use("/api", contactRoute);
app.use(errorHandler);
app.listen(port, () => {
console.log(`> Listening on port ${port}`);
});
2 changes: 2 additions & 0 deletions backend/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ if (!process.env.PORT) throw InternalError.NO_APP_PORT;
const port = process.env.PORT;

export { port };
if (!process.env.EMAIL) throw InternalError.NO_EMAIL;
if (!process.env.EMAIL_PASSWORD) throw InternalError.NO_EMAIL_PASSWORD;
48 changes: 48 additions & 0 deletions backend/src/controllers/contactRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Request, Response } from "express";
import nodemailer from "nodemailer";

export type ContactRequest = {
fullName: string;
email: string;
phoneNumber: string;
message?: string;
};

export const handleContactRequest = (req: Request, res: Response): void => {
const {
fullName,
email,
phoneNumber,
message = "No Message Provided",
} = req.body as ContactRequest;

console.log("Contact request received:", { fullName, email, phoneNumber, message });

const sendContactEmail = async (subject: string, message: string) => {

Check failure on line 21 in backend/src/controllers/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

'message' is already declared in the upper scope on line 16 column 5
const EMAIL_SUBJECT = `Contact Request from ${subject}`;
const EMAIL_BODY = `Name: ${fullName}\nEmail: ${email}\nPhone: ${phoneNumber}\nMessage: ${message}`;
const transporter = nodemailer.createTransport({
service: "Gmail",
host: "smtp.gmail.com",
secure: true,
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});
const mailOptions = {
from: process.env.EMAIL,
to: process.env.EMAIL,
subject: EMAIL_SUBJECT,
text: EMAIL_BODY,
};
try {
await transporter.sendMail(mailOptions);
console.log("Contact email sent successfully");
} catch (error) {
console.error("Error sending email:", error);
}
};
sendContactEmail(fullName, message);

Check failure on line 46 in backend/src/controllers/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
res.status(200).json({ message: "Contact request submitted successfully." });
};
9 changes: 9 additions & 0 deletions backend/src/errors/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CustomError } from "./errors";
const INVALID_FORM = "Name, email, and phone are required fields.";
const INVALID_PHONE = "Invalid phone number format.";
const INVALID_EMAIL = "Invalid email format.";
export class ContactError extends CustomError {
static INVALID_FORM = new ContactError(0, 400, INVALID_FORM);
static INVALID_PHONE = new ContactError(1, 400, INVALID_PHONE);
static INVALID_EMAIL = new ContactError(2, 400, INVALID_EMAIL);
}
1 change: 1 addition & 0 deletions backend/src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./errors";
export * from "./internal";
export * from "./contact";
5 changes: 4 additions & 1 deletion backend/src/errors/internal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CustomError } from "./errors";

const NO_APP_PORT = "Could not find app port env variable";

const NO_EMAIL = "Could not find email";
const NO_EMAIL_PASSWORD = "Could not find email password";
export class InternalError extends CustomError {
static NO_APP_PORT = new InternalError(0, 500, NO_APP_PORT);
static NO_EMAIL = new InternalError(1, 500, NO_EMAIL);
static NO_EMAIL_PASSWORD = new InternalError(2, 500, NO_EMAIL_PASSWORD);
}
8 changes: 8 additions & 0 deletions backend/src/routes/contactRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from "express";

Check warning on line 1 in backend/src/routes/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

There should be at least one empty line between import groups
import { validateContactRequest } from "../validators/contactRequest";
import { handleContactRequest } from "../controllers/contactRequest";

Check warning on line 3 in backend/src/routes/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

`../controllers/contactRequest` import should occur before import of `../validators/contactRequest`
const router = express.Router();

router.post("/contact", validateContactRequest, handleContactRequest);

export default router;
22 changes: 22 additions & 0 deletions backend/src/validators/contactRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import express, { Request, Response, NextFunction } from "express";

Check warning on line 1 in backend/src/validators/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

There should be at least one empty line between import groups

Check failure on line 1 in backend/src/validators/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

'express' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 1 in backend/src/validators/contactRequest.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Member 'NextFunction' of the import declaration should be sorted alphabetically
import { ContactRequest } from "../controllers/contactRequest";
import { ContactError } from "../errors/contact";
export const validateContactRequest = (req: Request, res: Response, next: NextFunction) => {
const { fullName, email, phoneNumber } = req.body as ContactRequest;

if (!fullName || !email || !phoneNumber) {
throw ContactError.INVALID_FORM;
}

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw ContactError.INVALID_EMAIL;
}

const phoneRegex = /^\d{10,15}$/;
if (!phoneRegex.test(phoneNumber)) {
throw ContactError.INVALID_PHONE;
}

next();
};

0 comments on commit 03f0fa7

Please sign in to comment.