diff --git a/config.js b/config.js index c05f1eb..107dd73 100644 --- a/config.js +++ b/config.js @@ -8,34 +8,34 @@ but either way the npm script doesn't seem to be picking up the .env in root. const dotenv = require("dotenv").config({ path: "./.env" }); enviornmentVariablesChecker(dotenv); module.exports = { - PORT: process.env.PORT, - DB: { - PGHOST: process.env.PGHOST, - PGUSER: process.env.PGUSER, - ORIGIN_URL: process.env.NODE_ENV === "production" ? process.env.LIVE_ORIGIN_URL : process.env.LOCAL_ORIGIN_URL, - LIVE_DATABASE_URL: process.env.LIVE_DATABASE_URL, - PGDATABASE: process.env.PGDATABASE, - PGPASSWORD: process.env.PGPASSWORD, - PGPORT: process.env.PGPORT, - USERS_TABLE: "users", - CATEGORIES_TABLE: "categories", - PRODUCTS_TABLE: "products", - ORDERS_TABLE: "orders", - CARTS_TABLE: "carts", - CART_HAS_PRODUCTS_TABLE: "cart_has_products", - ORDER_HAS_PRODUCTS_TABLE: "order_has_products", - USER_SESSIONS_TABLE: "user_sessions", - }, - SESSION: { - COOKIE: { - //secure: 'auto', - // Week long cookie age - // 24 hours * 60 mins * 60 secs * 1000ms - maxAge: 24 * 60 * 60 * 1000, - // maxAge: 10 * 1000, // max age = 10 secs - //sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", - //sameSite: "strict", - }, - SESSION_SECRET: process.env.SESSION_SECRET, + PORT: process.env.PORT, + DB: { + PGHOST: process.env.PGHOST, + PGUSER: process.env.PGUSER, + ORIGIN_URL: process.env.NODE_ENV === "production" ? process.env.LIVE_ORIGIN_URL : process.env.LOCAL_ORIGIN_URL, + LIVE_DATABASE_URL: process.env.LIVE_DATABASE_URL, + PGDATABASE: process.env.PGDATABASE, + PGPASSWORD: process.env.PGPASSWORD, + PGPORT: process.env.PGPORT, + USERS_TABLE: "users", + CATEGORIES_TABLE: "categories", + PRODUCTS_TABLE: "products", + ORDERS_TABLE: "orders", + CARTS_TABLE: "carts", + CART_HAS_PRODUCTS_TABLE: "cart_has_products", + ORDER_HAS_PRODUCTS_TABLE: "order_has_products", + USER_SESSIONS_TABLE: "user_sessions", + }, + SESSION: { + COOKIE: { + //secure: 'auto', + // Week long cookie age + // 24 hours * 60 mins * 60 secs * 1000ms + maxAge: 24 * 60 * 60 * 1000, + // maxAge: 10 * 1000, // max age = 10 secs + //sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + //sameSite: "strict", }, + SESSION_SECRET: process.env.SESSION_SECRET, + }, }; diff --git a/controllers/authController.js b/controllers/authController.js index 6068af5..d6f9d71 100644 --- a/controllers/authController.js +++ b/controllers/authController.js @@ -30,7 +30,6 @@ const logout = (req, res, next) => { if (req.session) { req.session.destroy(); } - res.clearCookie('connect.sid'); res.status(200).json("Logged out"); }); }; diff --git a/controllers/orderController.js b/controllers/orderController.js index 2cf59d0..77d798e 100644 --- a/controllers/orderController.js +++ b/controllers/orderController.js @@ -1,5 +1,5 @@ require("dotenv").config({ - path: "../.env", + path: "../.env", }); const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); const endpointSecret = process.env.NODE_ENV === "production" ? process.env.STRIPE_WEBHOOK_SECRET_LIVE : process.env.STRIPE_WEBHOOK_SECRET_LOCAL; @@ -7,167 +7,106 @@ const { getCartIdByUserId } = require("../models/cart"); const orderModel = require("../models/order"); const handleStripeEvent = async (req, res, next) => { - const sig = req.headers['stripe-signature']; - - let event; - - try { - event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret); - } catch (err) { - // On error, log and return the error message - console.log(`❌ Error message: ${err.message}`); - res.status(400).send(`Webhook Error: ${err.message}`); - return; - } - - // Only verify the event if you have an endpoint secret defined. - // Otherwise use the basic event deserialized with JSON.parse - - //if (endpointSecret) { - // Get the signature sent by Stripe - // const signature = req.headers["stripe-signature"]; - // try { - // event = stripe.webhooks.constructEvent( - // req.body, - // signature, - // endpointSecret - // ); - //} catch (err) { - // console.log(`⚠️ Webhook signature verification failed.`, err.message); - // console.log(err); - // return res.sendStatus(400); - //} - //} - - // Handle the event - const paymentIntent = event.data.object; - switch (event.type) { - case "payment_intent.succeeded": - console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`); - req.user = { ...req.user, ...paymentIntent.metadata }; - return next(); - // Then define and call a method to handle the successful payment intent. - // handlePaymentIntentSucceeded(paymentIntent); - // break; - // case 'payment_method.attached': - // // const paymentMethod = event.data.object; - // // Then define and call a method to handle the successful attachment of a PaymentMethod. - // // handlePaymentMethodAttached(paymentMethod); - // break; - case "charge.succeeded": - // req.user = { ...req.user, ...paymentIntent.metadata } - console.log("Charge succeeded!"); - break; - // return next(); - default: - // Unexpected event type - console.log(`Unhandled event type ${event.type}.`); - } - - // Return a 200 response to acknowledge receipt of the event - res.send(); + const sig = req.headers['stripe-signature']; + + let event; + + try { + event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret); + } catch (err) { + // On error, log and return the error message + console.log(`❌ Error message: ${err.message}`); + res.status(400).send(`Webhook Error: ${err.message}`); + return; + } + + // Handle the event + const paymentIntent = event.data.object; + switch (event.type) { + case "payment_intent.succeeded": + console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`); + req.user = { ...req.user, ...paymentIntent.metadata }; + return next(); + // case 'payment_method.attached': + case "charge.succeeded": + console.log("Charge succeeded!"); + break; + default: + // Unexpected event type + console.log(`Unhandled event type ${event.type}.`); + } + + // Return a 200 response to acknowledge receipt of the event + res.send(); }; const getOrders = async (req, res, next) => { - const { user_id } = req.user; - try { - const response = await orderModel.getOrders(user_id); - res.status(200).send(response); - } catch (err) { - next(err); - } + const { user_id } = req.user; + try { + const response = await orderModel.getOrders(user_id); + res.status(200).send(response); + } catch (err) { + next(err); + } }; const getOrderById = async (req, res, next) => { - const { order_id } = req.params; - const { user_id } = req.user; - const data = { user_id, order_id }; - try { - const response = await orderModel.getOrderById(data); - console.log(response); - if (response == null) res.sendStatus(204); - else res.status(200).send(response); - } catch (err) { - next(err); - } + const { order_id } = req.params; + const { user_id } = req.user; + const data = { user_id, order_id }; + try { + const response = await orderModel.getOrderById(data); + console.log(response); + if (response == null) res.sendStatus(204); + else res.status(200).send(response); + } catch (err) { + next(err); + } }; const getLatestOrder = async (req, res, next) => { - const { user_id } = req.user; - try { - const response = await orderModel.getLatestOrder(user_id); - res.status(200).send(response); - } catch (err) { - console.error(err); - next(err); - } + const { user_id } = req.user; + try { + const response = await orderModel.getLatestOrder(user_id); + res.status(200).send(response); + } catch (err) { + console.error(err); + next(err); + } }; -// const completeOrder = async (req, res, next) => { -// console.log("Successful payment... completing users order") -// const { user_id, order_id } = req.user; -// const cart_id = await getCartIdByUserId(user_id); -// const date = new Date(Date.now()).toISOString(); -// const status = "COMPLETED"; -// const data = { user_id, order_id, cart_id, date, status } -// try { -// const response = await orderModel.completeOrder(data); -// res.status(200).send(response); -// } catch (err) { -// next(err); -// } -// } - -// const updateOrder = async (req, res, next) => { - -// const { user_id, order_id } = req.user; -// console.log(order_id, "NEW ORDER IDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD") -// const cart_id = await getCartIdByUserId(user_id); -// console.log(`Creating order in DB for user: ${user_id}`.red) -// const date = new Date(Date.now()).toISOString(); -// const status = "COMPLETED"; -// const data = { user_id, cart_id, date, status } -// try { -// const response = await orderModel.createOrder(data); -// res.status(200).send(response); -// } catch (err) { -// next(err); -// } -// }; - const createOrder = async (req, res, next) => { - const { user_id } = req.user; - const date = new Date(Date.now()).toISOString(); - const cart_id = await getCartIdByUserId(user_id); - const status = "COMPLETE"; - const data = { user_id, status, date, cart_id }; - try { - const response = await orderModel.createOrder(data); - // req.user.new_order_id = response.order_id; - res.status(200).send(response); - } catch (err) { - console.error(err); - next(err); - } + const { user_id } = req.user; + const date = new Date(Date.now()).toISOString(); + const cart_id = await getCartIdByUserId(user_id); + const status = "COMPLETE"; + const data = { user_id, status, date, cart_id }; + try { + const response = await orderModel.createOrder(data); + // req.user.new_order_id = response.order_id; + res.status(200).send(response); + } catch (err) { + console.error(err); + next(err); + } }; const updateOrderStatus = async (req, res, next) => { - const { order_id } = req.params; - const { status } = req.body; - try { - const response = await orderModel.updateOrderStatus(order_id, status); - res.status(200).send(response); - } catch (err) { - next(err); - } + const { order_id } = req.params; + const { status } = req.body; + try { + const response = await orderModel.updateOrderStatus(order_id, status); + res.status(200).send(response); + } catch (err) { + next(err); + } }; module.exports = { - getOrders, - getOrderById, - // completeOrder, - getLatestOrder, - createOrder, - updateOrderStatus, - handleStripeEvent, + getOrders, + getOrderById, + getLatestOrder, + createOrder, + updateOrderStatus, + handleStripeEvent, }; diff --git a/db/index.js b/db/index.js index c789aa8..ec205da 100644 --- a/db/index.js +++ b/db/index.js @@ -6,59 +6,59 @@ const Pool = require("pg").Pool; const isProduction = checkIfBuildIsProduction(); const pool = - isProduction - ? new Pool({ - connectionString: DB.LIVE_DATABASE_URL, - ssl: { - rejectUnauthorized: false - } - }) : - new Pool({ - user: DB.PGUSER, - host: DB.PGHOST, - database: DB.PGDATABASE, - password: DB.PGPASSWORD, - port: DB.PGPORT, - }); + isProduction + ? new Pool({ + connectionString: DB.LIVE_DATABASE_URL, + ssl: { + rejectUnauthorized: false + } + }) : + new Pool({ + user: DB.PGUSER, + host: DB.PGHOST, + database: DB.PGDATABASE, + password: DB.PGPASSWORD, + port: DB.PGPORT, + }); module.exports = { - async query(text, params) { - const start = Date.now(); - const res = await pool.query(text, params); - const duration = Date.now() - start; - if (process.env.NODE_ENV !== "test") { - console.log("executed query", { text, duration, rows: res.rowCount, params: params }); - } - return res; - }, - async getClient() { - const client = await pool.connect(); - const query = client.query; - const release = client.release; - // set a timeout of 5 seconds, after which we will log this client's last query - const timeout = setTimeout(() => { - console.error("A client has been checked out for more than 5 seconds!"); - console.error( - `The last executed query on this client was: ${client.lastQuery}` - ); - }, 5000); - // monkey patch the query method to keep track of the last query executed - client.query = (...args) => { - if (process.env.NODE_ENV !== "test") { - console.log("executed query", args); - } - client.lastQuery = args; - return query.apply(client, args); - }; - client.release = () => { - // clear our timeout - clearTimeout(timeout); - // set the methods back to their old un-monkey-patched version - client.query = query; - client.release = release; - return release.apply(client); - }; - return client; - }, - pool: pool, + async query(text, params) { + const start = Date.now(); + const res = await pool.query(text, params); + const duration = Date.now() - start; + if (process.env.NODE_ENV !== "test") { + console.log("executed query", { text, duration, rows: res.rowCount, params: params }); + } + return res; + }, + async getClient() { + const client = await pool.connect(); + const query = client.query; + const release = client.release; + // set a timeout of 5 seconds, after which we will log this client's last query + const timeout = setTimeout(() => { + console.error("A client has been checked out for more than 5 seconds!"); + console.error( + `The last executed query on this client was: ${client.lastQuery}` + ); + }, 5000); + // monkey patch the query method to keep track of the last query executed + client.query = (...args) => { + if (process.env.NODE_ENV !== "test") { + console.log("executed query", args); + } + client.lastQuery = args; + return query.apply(client, args); + }; + client.release = () => { + // clear our timeout + clearTimeout(timeout); + // set the methods back to their old un-monkey-patched version + client.query = query; + client.release = release; + return release.apply(client); + }; + return client; + }, + pool: pool, }; diff --git a/helperFunctions.js b/helperFunctions.js index c35f842..9eeb99a 100644 --- a/helperFunctions.js +++ b/helperFunctions.js @@ -4,32 +4,32 @@ will determine if it's being properly parsed without revealing the secret details of the env file */ require("dotenv").config({ - path: "../.env" + path: "../.env" }); require("colors"); function enviornmentVariablesChecker(dotenv) { - let baseScript = ` + let baseScript = ` -------- Environment Variables: Is Empty: `; - if (dotenv.parsed == undefined) { - console.log(baseScript + "true".red); - return; - } else { - console.log(baseScript + "false".green); - } + if (dotenv.parsed == undefined) { + console.log(baseScript + "true".red); + return; + } else { + console.log(baseScript + "false".green); + } } const checkIfBuildIsProduction = () => { - return process.env.NODE_ENV === "production"; + return process.env.NODE_ENV === "production"; } class ExpressErrorHandler extends Error { - constructor(status, message) { - super(message); - this.status = status; - this.name = "ExpressErrorHandler" - } + constructor(status, message) { + super(message); + this.status = status; + this.name = "ExpressErrorHandler" + } }; module.exports = { enviornmentVariablesChecker, ExpressErrorHandler, checkIfBuildIsProduction }; diff --git a/loaders/express.js b/loaders/express.js index 7febc02..79d2905 100644 --- a/loaders/express.js +++ b/loaders/express.js @@ -7,78 +7,63 @@ const config = require("../config"); const express = require("express"); module.exports = (app) => { - // origin: "http://localhost:5173", - // origin: config.DB.ORIGIN_URL, - // origin: [config.DB.ORIGIN_URL, "ecommerce-ttgf.onrender.com", "http://localhost:5173"], - app.use( - cors({ - origin: process.env.NODE_ENV === "production" ? /onrender\.com$/ : "http://localhost:5173", - credentials: true, - }) - ); + app.use( + cors({ + origin: process.env.NODE_ENV === "production" ? /onrender\.com$/ : "http://localhost:5173", + credentials: true, + }) + ); - // Logging - //app.use(morgan("dev")); // Normal dev logging - //app.use(morgan(':method :url :status :response-time :date')); // Custom logger to include date + // Logging + //app.use(morgan("dev")); // Normal dev logging + //app.use(morgan(':method :url :status :response-time :date')); // Custom logger to include date - // Disable logger when testing with Jest - // This is done to prevent errors in Jest. - // Also disable logger when build is production - if ( - process.env.NODE_ENV !== "test" || - process.env.NODE_ENV !== "production" - ) { - const logger = require("morgan"); - app.use( - logger(":method :url :status :response-time :date", { - skip: () => process.env.NODE_ENV === "test", - }) - ); - } + // Disable logger when testing with Jest + // This is done to prevent errors in Jest. + // Also disable logger when build is production + if ( + process.env.NODE_ENV !== "test" || + process.env.NODE_ENV !== "production" + ) { + const logger = require("morgan"); + app.use( + logger(":method :url :status :response-time :date", { + skip: () => process.env.NODE_ENV === "test", + }) + ); + } - //app.use((req, res, next) => { - // If the request is for the webhook, the body - // must be a string and not parsed as JSON. - // https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/express/main.ts - //if (req.originalUrl === '/orders/webhook') { - //next(); - //} else { - // Transforms raw string of req.body into JSON - //bodyParser.json()(req, res, next); - //} - //} - //); - app.use(express.json({ - limit: '5mb', - verify: (req, res, buf) => { - req.rawBody = buf.toString(); - }, - })); + // use req.RawBody for Stripe Webhook processing + app.use(express.json({ + limit: '5mb', + verify: (req, res, buf) => { + req.rawBody = buf.toString(); + }, + })); - // Parses urlencoded bodies - app.use(express.urlencoded({ extended: true })); + // Parses urlencoded bodies + app.use(express.urlencoded({ extended: true })); - // Creates a session - app.use( - session({ - store: new pgSession({ - pool: pgPool, - tableName: config.DB.USER_SESSIONS_TABLE, - }), - secret: config.SESSION.SESSION_SECRET, - resave: false, - cookie: { - secure: process.env.NODE_ENV === "production" ? true : false, - // Week long cookie age - // 24 hours * 60 mins * 60 secs * 1000ms - maxAge: 7 * 24 * 60 * 60 * 1000, - sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", - //sameSite: "strict" - }, - saveUninitialized: false, - proxy: process.env.NODE_ENV === "production" ? true : false, - }) - ); + // Creates a session + app.use( + session({ + store: new pgSession({ + pool: pgPool, + tableName: config.DB.USER_SESSIONS_TABLE, + }), + secret: config.SESSION.SESSION_SECRET, + resave: false, + cookie: { + secure: process.env.NODE_ENV === "production" ? true : false, + // Week long cookie age + maxAge: 7 * 24 * 60 * 60 * 1000, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + //sameSite: "strict" + }, + saveUninitialized: false, + proxy: process.env.NODE_ENV === "production" ? true : false, + }) + ); - return app; + return app; }; diff --git a/middleware/auth.js b/middleware/auth.js index b231bf3..a4e2424 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,48 +1,48 @@ const isLoggedIn = async (req, res, next) => { - console.log(req.isAuthenticated(), "is authenticated"); - console.log(req.user, "req.user"); - if (req.isAuthenticated() && req?.user != null) { - return next(); - } + console.log(req.isAuthenticated(), "is authenticated"); + console.log(req.user, "req.user"); + if (req.isAuthenticated() && req?.user != null) { + return next(); + } - // If the user still has a session attached - if (req.session) { - req.session.destroy(); - } - // If the user is not logged in, - // redirect them to the login page. - res.status(200).json({ - success: false, - redirectUrl: "/auth/login", - error: "Unauthenticated" - }); + // If the user still has a session attached + if (req.session) { + req.session.destroy(); + } + // If the user is not logged in, + // redirect them to the login page. + res.status(200).json({ + success: false, + redirectUrl: "/auth/login", + error: "Unauthenticated" + }); }; const isAdmin = async (req, res, next) => { - if (req.user?.is_admin) { - return next(); - } - res.status(200).json({ - success: false, - redirectUrl: "/auth/login", - error: "Unauthenticated" - }); + if (req.user?.is_admin) { + return next(); + } + res.status(200).json({ + success: false, + redirectUrl: "/auth/login", + error: "Unauthenticated" + }); }; const isAlreadyLoggedIn = async (req, res) => { - if (req.isAuthenticated()) { - console.log("you're already logged in.. redirecting to root page"); - res.status(200).json({ - success: true, - redirectUrl: "/", - }); - } else { - res.sendStatus(200); - } + if (req.isAuthenticated()) { + console.log("you're already logged in.. redirecting to root page"); + res.status(200).json({ + success: true, + redirectUrl: "/", + }); + } else { + res.sendStatus(200); + } }; module.exports = { - isLoggedIn, - isAdmin, - isAlreadyLoggedIn, + isLoggedIn, + isAdmin, + isAlreadyLoggedIn, }; diff --git a/postman_collection/postman_environment b/postman_collection/postman_environment index c4b88c4..e1fd5a3 100644 --- a/postman_collection/postman_environment +++ b/postman_collection/postman_environment @@ -18,4 +18,4 @@ "_postman_variable_scope": "environment", "_postman_exported_at": "2023-04-16T18:43:15.435Z", "_postman_exported_using": "Postman/10.12.11" -} \ No newline at end of file +} diff --git a/postman_collection/postman_routes_collection b/postman_collection/postman_routes_collection index 4fcb104..19c5108 100644 --- a/postman_collection/postman_routes_collection +++ b/postman_collection/postman_routes_collection @@ -156,4 +156,4 @@ "value": "3" } ] -} \ No newline at end of file +} diff --git a/resources/createTables.sql b/resources/createTables.sql index 33e9ea1..c1813f1 100644 --- a/resources/createTables.sql +++ b/resources/createTables.sql @@ -122,4 +122,4 @@ ALTER TABLE IF EXISTS public.products ON UPDATE CASCADE ON DELETE NO ACTION; -END; \ No newline at end of file +END;