From 3df04926a1dd62253bf2af344fb3d29b693b5b00 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 28 May 2024 20:52:50 -0400 Subject: [PATCH 01/57] Updated structure of project and created separate router for projects endpoints --- routes/projects.ts | 114 ++++++++++++++++++++++++++++++++++++++++ server.ts | 128 ++------------------------------------------- utils/logger.ts | 20 +++++++ 3 files changed, 137 insertions(+), 125 deletions(-) create mode 100644 routes/projects.ts create mode 100644 utils/logger.ts diff --git a/routes/projects.ts b/routes/projects.ts new file mode 100644 index 0000000..ed4dd75 --- /dev/null +++ b/routes/projects.ts @@ -0,0 +1,114 @@ +import {Router} from 'express'; +import {readFile} from 'fs'; +import logger from '../utils/logger'; + +const router = Router(); + +router.get("/", (req, res) => { + if (req.query) { + logger.warn("Query parameters ignored"); + } + + readFile("./data.json", "utf8", (error, content) => { + if (error) { + logger.error("Error reading data.json"); + return res.status(500).send("Error reading file"); + } + return res.status(200).json(JSON.parse(content)); + }); +}); + +router.get("/team", (req, res) => { + if (!req.query.team) { + logger.error("Team query parameter missing"); + res.status(400).send("Missing team"); + return; + } + + readFile("./data.json", "utf8", (error, content) => { + if (error) { + logger.error("Error reading data.json"); + return res.status(500).send("Error reading file"); + } + const jsonData = JSON.parse(content); + const filteredData = jsonData.filter((item: any) => { + const itemData = item.team.toString().toLowerCase().split(","); + const queryData = req.query.team + ?.toString() + .toLowerCase() + .split(","); + return queryData?.every((query) => itemData.includes(query)); + }); + if (filteredData.length === 0) { + logger.warn("No projects found"); + return res + .status(404) + .send("The data you are looking for does not exist"); + } + return res.status(200).send(filteredData); + }); +}); + +router.get("/cohort", (req, res) => { + if (!req.query.cohort) { + logger.error("Cohort query parameter missing"); + res.send("Missing cohort").status(400); + return; + } + + readFile("data.json", "utf8", (err, data) => { + if (err) { + logger.error("Error reading data.json"); + res.send("Error reading file").status(500); + } + const jsonData = JSON.parse(data); + const filteredData = jsonData.filter((item: any) => { + const itemData = item.cohort.toString().toLowerCase().split(","); + const queryData = req.query.cohort + ?.toString() + .toLowerCase() + .split(","); + console.log(itemData, queryData); + return itemData.some((item: any) => queryData?.includes(item)); + }); + + if (filteredData.length === 0) { + logger.warn("No projects found"); + res.send("No projects found").status(404); + return; + } + res.send(filteredData).status(200); + }); +}); + +router.get("/name", (req, res) => { + if (!req.query.name) { + logger.error("Name query parameter missing"); + res.send("Missing project name").status(400); + return; + } + + readFile("data.json", "utf8", (err, data) => { + if (err) { + logger.error("Error reading data.json"); + res.send("Error reading file").status(500); + } + const jsonData = JSON.parse(data); + const filteredData = jsonData.filter((item: any) => { + const itemData = item.name.toString().toLowerCase(); + const queryData = req.query.name + ?.toString() + .toLowerCase() + .split(","); + return queryData?.some((query) => itemData.includes(query)); + }); + if (filteredData.length === 0) { + logger.warn("No projects found"); + res.send("No projects found").status(404); + return; + } + res.send(filteredData).status(200); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/server.ts b/server.ts index 31cb022..89463c3 100644 --- a/server.ts +++ b/server.ts @@ -1,28 +1,11 @@ import express from "express"; import activateDb from "./db"; -import { readFile } from "fs"; -import winston from "winston"; +import logger from "./utils/logger"; // activateDb(); const app = express(); const PORT = 3000; - -const logger = winston.createLogger({ - // Log only if level is less than (meaning more severe) or equal to this - level: "info", - // Use timestamp and printf to create a standard log format - format: winston.format.combine( - winston.format.timestamp(), - winston.format.printf( - (info) => `${info.timestamp} ${info.level}: ${info.message}` - ) - ), - // Log to the console and a file - transports: [ - new winston.transports.Console(), - new winston.transports.File({ filename: "logs/app.log" }), - ], -}); +const projectsRoute = require("./routes/projects"); app.use((req, res, next) => { // Log an info message for each incoming request @@ -34,112 +17,7 @@ app.get("/", (req, res) => { res.send("BYTE @ CCNY").status(200); }); -app.get("/projects", (req, res) => { - if (req.query) { - logger.warn("Query parameters ignored"); - } - - readFile("./data.json", "utf8", (error, content) => { - if (error) { - logger.error("Error reading data.json"); - return res.status(500).send("Error reading file"); - } - return res.status(200).json(JSON.parse(content)); - }); -}); - -app.get("/projects/team", (req, res) => { - if (!req.query.team) { - logger.error("Team query parameter missing"); - res.status(400).send("Missing team"); - return; - } - - readFile("./data.json", "utf8", (error, content) => { - if (error) { - logger.error("Error reading data.json"); - return res.status(500).send("Error reading file"); - } - const jsonData = JSON.parse(content); - const filteredData = jsonData.filter((item: any) => { - const itemData = item.team.toString().toLowerCase().split(","); - const queryData = req.query.team - ?.toString() - .toLowerCase() - .split(","); - return queryData?.every((query) => itemData.includes(query)); - }); - if (filteredData.length === 0) { - logger.warn("No projects found"); - return res - .status(404) - .send("The data you are looking for does not exist"); - } - return res.status(200).send(filteredData); - }); -}); - -app.get("/projects/cohort", (req, res) => { - if (!req.query.cohort) { - logger.error("Cohort query parameter missing"); - res.send("Missing cohort").status(400); - return; - } - - readFile("data.json", "utf8", (err, data) => { - if (err) { - logger.error("Error reading data.json"); - res.send("Error reading file").status(500); - } - const jsonData = JSON.parse(data); - const filteredData = jsonData.filter((item: any) => { - const itemData = item.cohort.toString().toLowerCase().split(","); - const queryData = req.query.cohort - ?.toString() - .toLowerCase() - .split(","); - console.log(itemData, queryData); - return itemData.some((item: any) => queryData?.includes(item)); - }); - - if (filteredData.length === 0) { - logger.warn("No projects found"); - res.send("No projects found").status(404); - return; - } - res.send(filteredData).status(200); - }); -}); - -app.get("/projects/name", (req, res) => { - if (!req.query.name) { - logger.error("Name query parameter missing"); - res.send("Missing project name").status(400); - return; - } - - readFile("data.json", "utf8", (err, data) => { - if (err) { - logger.error("Error reading data.json"); - res.send("Error reading file").status(500); - } - const jsonData = JSON.parse(data); - const filteredData = jsonData.filter((item: any) => { - const itemData = item.name.toString().toLowerCase(); - const queryData = req.query.name - ?.toString() - .toLowerCase() - .split(","); - return queryData?.some((query) => itemData.includes(query)); - }); - if (filteredData.length === 0) { - logger.warn("No projects found"); - res.send("No projects found").status(404); - return; - } - res.send(filteredData).status(200); - }); -}); +app.use("/projects", projectsRoute); app.listen(PORT, () => { console.log(`listening on port ${PORT}`); diff --git a/utils/logger.ts b/utils/logger.ts new file mode 100644 index 0000000..dd2f5ac --- /dev/null +++ b/utils/logger.ts @@ -0,0 +1,20 @@ +import winston from "winston"; + +const logger = winston.createLogger({ + // Log only if level is less than (meaning more severe) or equal to this + level: "info", + // Use timestamp and printf to create a standard log format + format: winston.format.combine( + winston.format.timestamp(), + winston.format.printf( + (info) => `${info.timestamp} ${info.level}: ${info.message}` + ) + ), + // Log to the console and a file + transports: [ + new winston.transports.Console(), + new winston.transports.File({ filename: "logs/app.log" }), + ], +}); + +export default logger; \ No newline at end of file From cb2fc29f9b02c198f85329d736c61c0cdda36618 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 30 May 2024 00:08:20 -0400 Subject: [PATCH 02/57] Added router when using database instead of local JSON and attempt to connect DB --- routes/projectsDB.ts | 9 +++++++++ routes/{projects.ts => projectsLocal.ts} | 0 server.ts | 17 +++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 routes/projectsDB.ts rename routes/{projects.ts => projectsLocal.ts} (100%) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts new file mode 100644 index 0000000..9f341c5 --- /dev/null +++ b/routes/projectsDB.ts @@ -0,0 +1,9 @@ +import { Router } from 'express'; + +const router = Router(); + +router.get("/", (req, res) => { + return res.status(200).send("Retrieving projects from DB"); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/projects.ts b/routes/projectsLocal.ts similarity index 100% rename from routes/projects.ts rename to routes/projectsLocal.ts diff --git a/server.ts b/server.ts index 89463c3..efac7ec 100644 --- a/server.ts +++ b/server.ts @@ -5,7 +5,18 @@ import logger from "./utils/logger"; // activateDb(); const app = express(); const PORT = 3000; -const projectsRoute = require("./routes/projects"); +const projectsLocal = require("./routes/projectsLocal"); +const projectsDB = require("./routes/projectsDB"); + +const connectDB = async (req: any, res: any, next: any) => { + try { + await activateDb(); + next(); + } catch (err: any) { + logger.info(`Error connecting to database: ${err.message} \n Switching to local data.`); + next("route"); + } +} app.use((req, res, next) => { // Log an info message for each incoming request @@ -17,7 +28,9 @@ app.get("/", (req, res) => { res.send("BYTE @ CCNY").status(200); }); -app.use("/projects", projectsRoute); +app.use("/projects", connectDB, projectsDB); + +app.use("/projects", projectsLocal); app.listen(PORT, () => { console.log(`listening on port ${PORT}`); From 684bae8bf93f5360824e3a015f523d0e2261e0dc Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 31 May 2024 20:16:50 -0400 Subject: [PATCH 03/57] Updated imports for bun environment --- db.ts | 1 + routes/projectsDB.ts | 5 ++--- routes/projectsLocal.ts | 22 +++++++++++----------- server.ts | 7 +++---- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/db.ts b/db.ts index b58010e..3bdf2d5 100644 --- a/db.ts +++ b/db.ts @@ -13,4 +13,5 @@ const activateDb = async () => { } } + export default activateDb; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 9f341c5..679930e 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,6 +1,5 @@ -import { Router } from 'express'; - -const router = Router(); +const express = require("express"); +const router = express.Router(); router.get("/", (req, res) => { return res.status(200).send("Retrieving projects from DB"); diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index ed4dd75..65835f0 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -1,15 +1,15 @@ -import {Router} from 'express'; -import {readFile} from 'fs'; import logger from '../utils/logger'; -const router = Router(); +const express = require("express"); +const router = express.Router(); +const fs = require("fs"); -router.get("/", (req, res) => { +router.get("/", (req: any, res: any) => { if (req.query) { logger.warn("Query parameters ignored"); } - readFile("./data.json", "utf8", (error, content) => { + fs.readFile("./data.json", "utf8", (error, content) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -18,14 +18,14 @@ router.get("/", (req, res) => { }); }); -router.get("/team", (req, res) => { +router.get("/team", (req: any, res: any) => { if (!req.query.team) { logger.error("Team query parameter missing"); res.status(400).send("Missing team"); return; } - readFile("./data.json", "utf8", (error, content) => { + fs.readFile("./data.json", "utf8", (error, content) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -49,14 +49,14 @@ router.get("/team", (req, res) => { }); }); -router.get("/cohort", (req, res) => { +router.get("/cohort", (req: any, res: any) => { if (!req.query.cohort) { logger.error("Cohort query parameter missing"); res.send("Missing cohort").status(400); return; } - readFile("data.json", "utf8", (err, data) => { + fs.readFile("data.json", "utf8", (err, data) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); @@ -81,14 +81,14 @@ router.get("/cohort", (req, res) => { }); }); -router.get("/name", (req, res) => { +router.get("/name", (req: any, res: any) => { if (!req.query.name) { logger.error("Name query parameter missing"); res.send("Missing project name").status(400); return; } - readFile("data.json", "utf8", (err, data) => { + fs.readFile("data.json", "utf8", (err, data) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); diff --git a/server.ts b/server.ts index efac7ec..200faa8 100644 --- a/server.ts +++ b/server.ts @@ -1,8 +1,7 @@ -import express from "express"; import activateDb from "./db"; import logger from "./utils/logger"; -// activateDb(); +const express = require("express"); const app = express(); const PORT = 3000; const projectsLocal = require("./routes/projectsLocal"); @@ -18,13 +17,13 @@ const connectDB = async (req: any, res: any, next: any) => { } } -app.use((req, res, next) => { +app.use((req: any, res: any, next: any) => { // Log an info message for each incoming request logger.info(`Received a ${req.method} request for ${req.url}`); next(); }); -app.get("/", (req, res) => { +app.get("/", (req: any, res: any) => { res.send("BYTE @ CCNY").status(200); }); From 9c36e0539cff222f2a954cc9ac5ca832da7ebc6f Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sat, 1 Jun 2024 13:02:37 -0400 Subject: [PATCH 04/57] updated import path to work with bun --- routes/projectsLocal.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index 65835f0..6a441f1 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -1,5 +1,4 @@ -import logger from '../utils/logger'; - +const logger = require("../utils/logger"); const express = require("express"); const router = express.Router(); const fs = require("fs"); From c17c7900e18a26c925abab86dbd2c9e5ffd05626 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sat, 1 Jun 2024 16:59:47 -0400 Subject: [PATCH 05/57] Handled error for multiple attempts of connecting to DB and allowed for crud operations in projectsDB --- db.ts | 3 --- routes/projectsDB.ts | 6 ++++-- routes/projectsLocal.ts | 12 ++++++------ server.ts | 9 ++++++++- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/db.ts b/db.ts index 3bdf2d5..f480509 100644 --- a/db.ts +++ b/db.ts @@ -6,12 +6,9 @@ const activateDb = async () => { try { await client.connect(); console.log("Database connected"); - - return client; } catch (err: any) { throw new Error(`Database connection error\n ${err.message}`); } } - export default activateDb; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 679930e..9613367 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,8 +1,10 @@ +import client from "../db.config"; + const express = require("express"); const router = express.Router(); -router.get("/", (req, res) => { - return res.status(200).send("Retrieving projects from DB"); +router.get("/", (req: any, res: any) => { + res.status(200).send("Projects Database"); }); module.exports = router; \ No newline at end of file diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index 6a441f1..6ed9ba3 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -8,7 +8,7 @@ router.get("/", (req: any, res: any) => { logger.warn("Query parameters ignored"); } - fs.readFile("./data.json", "utf8", (error, content) => { + fs.readFile("../data.json", "utf8", (error: any, content: any) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -24,7 +24,7 @@ router.get("/team", (req: any, res: any) => { return; } - fs.readFile("./data.json", "utf8", (error, content) => { + fs.readFile("../data.json", "utf8", (error: any, content: any) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -36,7 +36,7 @@ router.get("/team", (req: any, res: any) => { ?.toString() .toLowerCase() .split(","); - return queryData?.every((query) => itemData.includes(query)); + return queryData?.every((query: any) => itemData.includes(query)); }); if (filteredData.length === 0) { logger.warn("No projects found"); @@ -55,7 +55,7 @@ router.get("/cohort", (req: any, res: any) => { return; } - fs.readFile("data.json", "utf8", (err, data) => { + fs.readFile("../data.json", "utf8", (err: any, data: any) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); @@ -87,7 +87,7 @@ router.get("/name", (req: any, res: any) => { return; } - fs.readFile("data.json", "utf8", (err, data) => { + fs.readFile("../data.json", "utf8", (err: any, data: any) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); @@ -99,7 +99,7 @@ router.get("/name", (req: any, res: any) => { ?.toString() .toLowerCase() .split(","); - return queryData?.some((query) => itemData.includes(query)); + return queryData?.some((query: any) => itemData.includes(query)); }); if (filteredData.length === 0) { logger.warn("No projects found"); diff --git a/server.ts b/server.ts index 200faa8..8ffaa98 100644 --- a/server.ts +++ b/server.ts @@ -7,12 +7,19 @@ const PORT = 3000; const projectsLocal = require("./routes/projectsLocal"); const projectsDB = require("./routes/projectsDB"); +let isActive = false; + const connectDB = async (req: any, res: any, next: any) => { try { + if (isActive) { + logger.info("Database already connected"); + return next(); + } await activateDb(); + isActive = true; next(); } catch (err: any) { - logger.info(`Error connecting to database: ${err.message} \n Switching to local data.`); + logger.info(err.message); next("route"); } } From 58d052cd85dc8cbb63126ff1d4bc87a3cd5c2405 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sat, 1 Jun 2024 18:07:56 -0400 Subject: [PATCH 06/57] Updated previous mistake on client, can now use connected client for crud operations in projectsDB --- db.ts | 6 +++++- routes/projectsDB.ts | 2 +- server.ts | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/db.ts b/db.ts index f480509..6509d5f 100644 --- a/db.ts +++ b/db.ts @@ -1,14 +1,18 @@ import client from './db.config'; +let connectedClient: any; + const activateDb = async () => { console.log("Connecting to Database ..."); try { await client.connect(); console.log("Database connected"); + connectedClient = client; } catch (err: any) { throw new Error(`Database connection error\n ${err.message}`); } } -export default activateDb; \ No newline at end of file +export default activateDb; +export { connectedClient }; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 9613367..d922feb 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,4 +1,4 @@ -import client from "../db.config"; +import { connectedClient } from "../db"; const express = require("express"); const router = express.Router(); diff --git a/server.ts b/server.ts index 8ffaa98..55722f7 100644 --- a/server.ts +++ b/server.ts @@ -17,7 +17,7 @@ const connectDB = async (req: any, res: any, next: any) => { } await activateDb(); isActive = true; - next(); + next(); } catch (err: any) { logger.info(err.message); next("route"); @@ -33,7 +33,7 @@ app.use((req: any, res: any, next: any) => { app.get("/", (req: any, res: any) => { res.send("BYTE @ CCNY").status(200); }); - + app.use("/projects", connectDB, projectsDB); app.use("/projects", projectsLocal); From 85d71938d1d1e162fd1e2a4bdd96098e15a3708c Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 3 Jun 2024 12:25:57 -0400 Subject: [PATCH 07/57] removing common js --- db.ts | 7 ++----- routes/projectsDB.ts | 21 +++++++++++++++------ server.ts | 25 +++++++++++++------------ 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/db.ts b/db.ts index 6509d5f..19fe821 100644 --- a/db.ts +++ b/db.ts @@ -1,18 +1,15 @@ import client from './db.config'; -let connectedClient: any; - const activateDb = async () => { console.log("Connecting to Database ..."); try { await client.connect(); console.log("Database connected"); - connectedClient = client; + return client; } catch (err: any) { throw new Error(`Database connection error\n ${err.message}`); } } -export default activateDb; -export { connectedClient }; \ No newline at end of file +export default activateDb; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index d922feb..9d1d3e4 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,10 +1,19 @@ -import { connectedClient } from "../db"; +import { Client } from "pg"; +import activateDb from "../db"; +import {Router} from "express"; +const router: Router = Router(); -const express = require("express"); -const router = express.Router(); +router.get("/", async (req: any, res: any) => { + let client: Client | undefined; -router.get("/", (req: any, res: any) => { - res.status(200).send("Projects Database"); + try{ + client = await activateDb(); + await client.end() + } catch (e) { + return res.status(500).send("Internal Server Error"); + } + + return res.status(200).send("Projects Database"); }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/server.ts b/server.ts index 55722f7..df05b64 100644 --- a/server.ts +++ b/server.ts @@ -1,22 +1,19 @@ +import { Client } from "pg"; import activateDb from "./db"; import logger from "./utils/logger"; +import projectsLocal from "./routes/projectsLocal"; +import projectsDB from "./routes/projectsDB"; const express = require("express"); const app = express(); const PORT = 3000; -const projectsLocal = require("./routes/projectsLocal"); -const projectsDB = require("./routes/projectsDB"); -let isActive = false; +let client: Client | undefined; const connectDB = async (req: any, res: any, next: any) => { try { - if (isActive) { - logger.info("Database already connected"); - return next(); - } - await activateDb(); - isActive = true; + client = await activateDb(); + await client.end(); next(); } catch (err: any) { logger.info(err.message); @@ -33,10 +30,14 @@ app.use((req: any, res: any, next: any) => { app.get("/", (req: any, res: any) => { res.send("BYTE @ CCNY").status(200); }); - -app.use("/projects", connectDB, projectsDB); -app.use("/projects", projectsLocal); + +// if (!client) { +// app.use("/projects", projectsLocal); +// } else { + app.use("/projects", connectDB, projectsDB); +// } + app.listen(PORT, () => { console.log(`listening on port ${PORT}`); From 58f91525d0730f01e14b41a90430232f7ee48dc7 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 3 Jun 2024 12:52:11 -0400 Subject: [PATCH 08/57] removed all commonjs imports --- routes/projectsDB.ts | 16 +++++++--------- routes/projectsLocal.ts | 8 ++++---- server.ts | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 9d1d3e4..a91482a 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,18 +1,16 @@ import { Client } from "pg"; import activateDb from "../db"; -import {Router} from "express"; -const router: Router = Router(); +import express from "express"; +let router = express.Router(); +let client: Client | undefined; router.get("/", async (req: any, res: any) => { - let client: Client | undefined; - - try{ + try { client = await activateDb(); - await client.end() - } catch (e) { - return res.status(500).send("Internal Server Error"); + await client.end(); + } catch (err: any) { + return res.status(500).send(err.message); } - return res.status(200).send("Projects Database"); }); diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index 6ed9ba3..654e861 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -1,7 +1,7 @@ -const logger = require("../utils/logger"); -const express = require("express"); +import logger from "../utils/logger"; +import express from "express"; +import fs from "fs"; const router = express.Router(); -const fs = require("fs"); router.get("/", (req: any, res: any) => { if (req.query) { @@ -110,4 +110,4 @@ router.get("/name", (req: any, res: any) => { }); }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/server.ts b/server.ts index df05b64..3c1f79c 100644 --- a/server.ts +++ b/server.ts @@ -3,8 +3,8 @@ import activateDb from "./db"; import logger from "./utils/logger"; import projectsLocal from "./routes/projectsLocal"; import projectsDB from "./routes/projectsDB"; +import express from "express"; -const express = require("express"); const app = express(); const PORT = 3000; From c34fed03f314777507d4e8aaf43c4b8f01896f6d Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 3 Jun 2024 12:55:07 -0400 Subject: [PATCH 09/57] made router constant --- routes/projectsDB.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index a91482a..bb60268 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,17 +1,18 @@ import { Client } from "pg"; import activateDb from "../db"; import express from "express"; -let router = express.Router(); +const router = express.Router(); + let client: Client | undefined; router.get("/", async (req: any, res: any) => { try { client = await activateDb(); await client.end(); + return res.status(200).send("No errors!!"); } catch (err: any) { return res.status(500).send(err.message); } - return res.status(200).send("Projects Database"); }); export default router; \ No newline at end of file From d985a35cb17b70e5114dac7eecd570c04860f767 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 3 Jun 2024 12:55:11 -0400 Subject: [PATCH 10/57] added logging --- db.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db.config.ts b/db.config.ts index 96c7940..4dd8373 100644 --- a/db.config.ts +++ b/db.config.ts @@ -10,4 +10,7 @@ const client = new Client({ port: process.env.POSTGRESQL_DB_PORT ? parseInt(process.env.POSTGRESQL_DB_PORT) : 5432 }); +client.on('end', () => console.log('Client has disconnected')); +client.on('error', (err) => console.error('Unexpected error on idle client', err)); + export default client; From e3178d97994ecb026bab7e56651585147841210c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Mon, 3 Jun 2024 13:12:51 -0400 Subject: [PATCH 11/57] Removed commonJS --- routes/projectsDB.ts | 5 +++-- routes/projectsLocal.ts | 19 +++++++++++-------- server.ts | 1 - 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index bb60268..c21b6aa 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,7 +1,8 @@ import { Client } from "pg"; import activateDb from "../db"; -import express from "express"; -const router = express.Router(); +import { Router } from "express"; + +const router = Router(); let client: Client | undefined; diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index 654e861..2e8bca2 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -1,14 +1,17 @@ import logger from "../utils/logger"; -import express from "express"; -import fs from "fs"; -const router = express.Router(); +import { Router } from "express"; +import { readFile } from "fs"; +import path from "path"; + +const router = Router(); +const FILE_PATH: string = path.resolve(__dirname, "../data.json"); router.get("/", (req: any, res: any) => { if (req.query) { logger.warn("Query parameters ignored"); } - - fs.readFile("../data.json", "utf8", (error: any, content: any) => { + + readFile(FILE_PATH, "utf8", (error: any, content: any) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -24,7 +27,7 @@ router.get("/team", (req: any, res: any) => { return; } - fs.readFile("../data.json", "utf8", (error: any, content: any) => { + readFile(FILE_PATH, "utf8", (error: any, content: any) => { if (error) { logger.error("Error reading data.json"); return res.status(500).send("Error reading file"); @@ -55,7 +58,7 @@ router.get("/cohort", (req: any, res: any) => { return; } - fs.readFile("../data.json", "utf8", (err: any, data: any) => { + readFile(FILE_PATH, "utf8", (err: any, data: any) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); @@ -87,7 +90,7 @@ router.get("/name", (req: any, res: any) => { return; } - fs.readFile("../data.json", "utf8", (err: any, data: any) => { + readFile(FILE_PATH, "utf8", (err: any, data: any) => { if (err) { logger.error("Error reading data.json"); res.send("Error reading file").status(500); diff --git a/server.ts b/server.ts index 3c1f79c..acb261a 100644 --- a/server.ts +++ b/server.ts @@ -1,7 +1,6 @@ import { Client } from "pg"; import activateDb from "./db"; import logger from "./utils/logger"; -import projectsLocal from "./routes/projectsLocal"; import projectsDB from "./routes/projectsDB"; import express from "express"; From e2b1210ff8bc3fc76301f96b415f37c96f08e299 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 3 Jun 2024 14:59:53 -0400 Subject: [PATCH 12/57] moved middleware to projectsDB file & handled multiple connection error --- connectDB.ts | 14 ++++++++++++++ db.ts | 20 ++++++++++---------- routes/projectsDB.ts | 12 +++++------- server.ts | 22 +++------------------- 4 files changed, 32 insertions(+), 36 deletions(-) create mode 100644 connectDB.ts diff --git a/connectDB.ts b/connectDB.ts new file mode 100644 index 0000000..47c9894 --- /dev/null +++ b/connectDB.ts @@ -0,0 +1,14 @@ +import getDB from "./db"; +import logger from "./utils/logger"; + +const connectDB = async (req: any, res: any, next: any) => { + try { + req.client = await getDB(); + next(); + } catch (err: any) { + logger.info(err.message); + next("route"); + } +} + +export default connectDB; diff --git a/db.ts b/db.ts index 19fe821..bb3e853 100644 --- a/db.ts +++ b/db.ts @@ -1,15 +1,15 @@ import client from './db.config'; -const activateDb = async () => { - console.log("Connecting to Database ..."); +const getDB = async () => { + console.log("Connecting to Database ..."); - try { - await client.connect(); - console.log("Database connected"); - return client; - } catch (err: any) { - throw new Error(`Database connection error\n ${err.message}`); - } + try { + await client.connect(); + console.log("Database connected"); + return client; + } catch (err: any) { + return client; + } } -export default activateDb; \ No newline at end of file +export default getDB; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index c21b6aa..3f74ece 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,15 +1,13 @@ -import { Client } from "pg"; -import activateDb from "../db"; -import { Router } from "express"; +import {Router} from "express"; +import connectDB from "../connectDB"; -const router = Router(); +const router: Router = Router(); -let client: Client | undefined; +router.use(connectDB); router.get("/", async (req: any, res: any) => { try { - client = await activateDb(); - await client.end(); + await req.client.query('SELECT * FROM projects'); return res.status(200).send("No errors!!"); } catch (err: any) { return res.status(500).send(err.message); diff --git a/server.ts b/server.ts index acb261a..199f480 100644 --- a/server.ts +++ b/server.ts @@ -3,25 +3,12 @@ import activateDb from "./db"; import logger from "./utils/logger"; import projectsDB from "./routes/projectsDB"; import express from "express"; +import connectDB from "./connectDB"; const app = express(); const PORT = 3000; -let client: Client | undefined; - -const connectDB = async (req: any, res: any, next: any) => { - try { - client = await activateDb(); - await client.end(); - next(); - } catch (err: any) { - logger.info(err.message); - next("route"); - } -} - app.use((req: any, res: any, next: any) => { - // Log an info message for each incoming request logger.info(`Received a ${req.method} request for ${req.url}`); next(); }); @@ -31,11 +18,8 @@ app.get("/", (req: any, res: any) => { }); -// if (!client) { -// app.use("/projects", projectsLocal); -// } else { - app.use("/projects", connectDB, projectsDB); -// } +// app.use("/projects", projectsLocal); +app.use("/projects", projectsDB); app.listen(PORT, () => { From eed74a039d69076eec9e1b53cdf7750cdd3b303c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Mon, 3 Jun 2024 20:04:41 -0400 Subject: [PATCH 13/57] Create and read operation to the database --- routes/projectsDB.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 3f74ece..573d6cc 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,9 +1,12 @@ import {Router} from "express"; import connectDB from "../connectDB"; +import express from "express"; +import logger from "../utils/logger"; const router: Router = Router(); router.use(connectDB); +router.use(express.json()); router.get("/", async (req: any, res: any) => { try { @@ -14,4 +17,34 @@ router.get("/", async (req: any, res: any) => { } }); +router.get("/get", (req: any, res: any) => { + req.client.query('SELECT * FROM projects', (err: any, result: any) => { + if (err) { + logger.error("Error reading from database"); + return res.status(500).send(err.message); + } + return res.status(200).send(result.rows); + }); +}); + +router.post("/add" , (req: any, res: any) => { + const keys = Object.keys(req.body); + const values = Object.values(req.body); + + const query = `INSERT INTO projects (${keys.map(key => { + if (key.includes("-")) { + return `"${key}"`; + } + return key; + }).join(", ")}) VALUES (${keys.map((key, i) => `$${i + 1}`).join(", ")})`; + + req.client.query(query, values, (err: any, result: any) => { + if (err) { + logger.error("Error adding to database"); + return res.status(500).send(err.message); + } + return res.status(200).send("Added new project"); + }); +}); + export default router; \ No newline at end of file From cc47ac2677f1bc80ea68aebbbf724bdb175039c8 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 3 Jun 2024 20:36:44 -0400 Subject: [PATCH 14/57] added filter to return an error message for non-existant routes --- server.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server.ts b/server.ts index 199f480..cbe737d 100644 --- a/server.ts +++ b/server.ts @@ -17,10 +17,13 @@ app.get("/", (req: any, res: any) => { res.send("BYTE @ CCNY").status(200); }); - // app.use("/projects", projectsLocal); app.use("/projects", projectsDB); +// any other route will return a 404 +app.get("*", (req: any, res: any) => { + res.status(404).json({ message: "Page not found" }); +}); app.listen(PORT, () => { console.log(`listening on port ${PORT}`); From c326f08a86ba78f2311fe003b01c4b0c695196f1 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 3 Jun 2024 20:36:56 -0400 Subject: [PATCH 15/57] implemented filters for /get route. --- routes/projectsDB.ts | 68 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 573d6cc..c6615df 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,4 +1,4 @@ -import {Router} from "express"; +import { Router } from "express"; import connectDB from "../connectDB"; import express from "express"; import logger from "../utils/logger"; @@ -10,7 +10,7 @@ router.use(express.json()); router.get("/", async (req: any, res: any) => { try { - await req.client.query('SELECT * FROM projects'); + await req.client.query("SELECT * FROM projects"); return res.status(200).send("No errors!!"); } catch (err: any) { return res.status(500).send(err.message); @@ -18,25 +18,60 @@ router.get("/", async (req: any, res: any) => { }); router.get("/get", (req: any, res: any) => { - req.client.query('SELECT * FROM projects', (err: any, result: any) => { + let baseQuery = "SELECT * FROM projects"; + const filters: string[] = []; + const values: (string | number)[] = []; + + // if the name filter was provided + if (req.query.name) { + filters.push(`name ILIKE $${filters.length + 1}`); + values.push(`%${req.query.name}%`); + } + + // if the cohort filter was provided + if (req.query.cohort) { + filters.push(`cohort ILIKE $${filters.length + 1}`); + values.push(`%${req.query.cohort}%`); + } + // if the team filter was provided + if (req.query.team) { + filters.push(`team ILIKE $${filters.length + 1}`); + values.push(`%${req.query.team}%`); + } + + // combine all the filters into a single query + if (filters.length > 0) { + baseQuery += " WHERE " + filters.join(" AND "); + } + + // execute the query, making sure to provide the values for the filters + req.client.query(baseQuery, values, (err: any, result: any) => { if (err) { - logger.error("Error reading from database"); - return res.status(500).send(err.message); + logger.error(`Error reading from database: ${err.message}`); + return res + .status(500) + .json({ message: "Error reading from database" }); + } + if (result.rows.length === 0) { + logger.warn("No projects found"); + return res.status(404).json({"message": "No projects found"}); } return res.status(200).send(result.rows); }); }); -router.post("/add" , (req: any, res: any) => { +router.post("/add", (req: any, res: any) => { const keys = Object.keys(req.body); const values = Object.values(req.body); - - const query = `INSERT INTO projects (${keys.map(key => { - if (key.includes("-")) { - return `"${key}"`; - } - return key; - }).join(", ")}) VALUES (${keys.map((key, i) => `$${i + 1}`).join(", ")})`; + + const query = `INSERT INTO projects (${keys + .map((key) => { + if (key.includes("-")) { + return `"${key}"`; + } + return key; + }) + .join(", ")}) VALUES (${keys.map((key, i) => `$${i + 1}`).join(", ")})`; req.client.query(query, values, (err: any, result: any) => { if (err) { @@ -47,4 +82,9 @@ router.post("/add" , (req: any, res: any) => { }); }); -export default router; \ No newline at end of file +router.post("/update", (req: any, res: any) => { + // TODO + +}); + +export default router; From 06133523d23df331f8122322e77593d7590d3865 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:30:05 -0400 Subject: [PATCH 16/57] adding database function abstraction --- routes/databaseFunctions.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 routes/databaseFunctions.ts diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts new file mode 100644 index 0000000..fd57326 --- /dev/null +++ b/routes/databaseFunctions.ts @@ -0,0 +1,11 @@ +import { Client } from 'pg'; + +// todo: wrap everything in a try catch block and pass the exception to the user +export function addToDB(client: Client, valuesToQuery: any) { + const query = ` + INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; + + const values = [valuesToQuery.name, valuesToQuery.shortDescription, valuesToQuery.longDescription, valuesToQuery.team, valuesToQuery.link, valuesToQuery.image, valuesToQuery.techStack, valuesToQuery.cohort, valuesToQuery.topic]; + client.query(query, values); +} \ No newline at end of file From 53a3ace11ac9aeaa33795f926477385f446264c7 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:31:28 -0400 Subject: [PATCH 17/57] adding import --- routes/projectsDB.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index c6615df..8fbbd84 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -2,6 +2,7 @@ import { Router } from "express"; import connectDB from "../connectDB"; import express from "express"; import logger from "../utils/logger"; +import { addToDB } from "./databaseFunctions"; const router: Router = Router(); @@ -64,6 +65,8 @@ router.post("/add", (req: any, res: any) => { const keys = Object.keys(req.body); const values = Object.values(req.body); + + const query = `INSERT INTO projects (${keys .map((key) => { if (key.includes("-")) { From 282b8f5ef16b1d64b139fac99bc955973a4b7705 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:32:39 -0400 Subject: [PATCH 18/57] formatting --- routes/databaseFunctions.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index fd57326..58b852a 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -1,11 +1,21 @@ -import { Client } from 'pg'; +import { Client } from "pg"; // todo: wrap everything in a try catch block and pass the exception to the user export function addToDB(client: Client, valuesToQuery: any) { - const query = ` + const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - const values = [valuesToQuery.name, valuesToQuery.shortDescription, valuesToQuery.longDescription, valuesToQuery.team, valuesToQuery.link, valuesToQuery.image, valuesToQuery.techStack, valuesToQuery.cohort, valuesToQuery.topic]; - client.query(query, values); -} \ No newline at end of file + const values = [ + valuesToQuery.name, + valuesToQuery.shortDescription, + valuesToQuery.longDescription, + valuesToQuery.team, + valuesToQuery.link, + valuesToQuery.image, + valuesToQuery.techStack, + valuesToQuery.cohort, + valuesToQuery.topic, + ]; + client.query(query, values); +} From 66a92e7f9659d469c9ef45f45d11f93ea0cc549e Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:34:54 -0400 Subject: [PATCH 19/57] added error handling --- routes/databaseFunctions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 58b852a..de81b87 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -1,6 +1,5 @@ import { Client } from "pg"; -// todo: wrap everything in a try catch block and pass the exception to the user export function addToDB(client: Client, valuesToQuery: any) { const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) @@ -17,5 +16,10 @@ export function addToDB(client: Client, valuesToQuery: any) { valuesToQuery.cohort, valuesToQuery.topic, ]; - client.query(query, values); + + try { + return client.query(query, values); + } catch (e: any) { + throw Error(e.toString()); + } } From a26406eae3b97f5f092abad068b68a8532614252 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:39:47 -0400 Subject: [PATCH 20/57] finished add endpoint --- routes/databaseFunctions.ts | 14 +---- routes/projectsDB.ts | 113 ++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 77 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index de81b87..57b4eac 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -1,22 +1,10 @@ import { Client } from "pg"; -export function addToDB(client: Client, valuesToQuery: any) { +export function addToDB(client: Client, values: Array) { const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - const values = [ - valuesToQuery.name, - valuesToQuery.shortDescription, - valuesToQuery.longDescription, - valuesToQuery.team, - valuesToQuery.link, - valuesToQuery.image, - valuesToQuery.techStack, - valuesToQuery.cohort, - valuesToQuery.topic, - ]; - try { return client.query(query, values); } catch (e: any) { diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 8fbbd84..7969097 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -10,84 +10,69 @@ router.use(connectDB); router.use(express.json()); router.get("/", async (req: any, res: any) => { - try { - await req.client.query("SELECT * FROM projects"); - return res.status(200).send("No errors!!"); - } catch (err: any) { - return res.status(500).send(err.message); - } + try { + await req.client.query("SELECT * FROM projects"); + return res.status(200).send("No errors!!"); + } catch (err: any) { + return res.status(500).send(err.message); + } }); router.get("/get", (req: any, res: any) => { - let baseQuery = "SELECT * FROM projects"; - const filters: string[] = []; - const values: (string | number)[] = []; + let baseQuery = "SELECT * FROM projects"; + const filters: string[] = []; + const values: (string | number)[] = []; - // if the name filter was provided - if (req.query.name) { - filters.push(`name ILIKE $${filters.length + 1}`); - values.push(`%${req.query.name}%`); - } + // if the name filter was provided + if (req.query.name) { + filters.push(`name ILIKE $${filters.length + 1}`); + values.push(`%${req.query.name}%`); + } - // if the cohort filter was provided - if (req.query.cohort) { - filters.push(`cohort ILIKE $${filters.length + 1}`); - values.push(`%${req.query.cohort}%`); - } - // if the team filter was provided - if (req.query.team) { - filters.push(`team ILIKE $${filters.length + 1}`); - values.push(`%${req.query.team}%`); - } + // if the cohort filter was provided + if (req.query.cohort) { + filters.push(`cohort ILIKE $${filters.length + 1}`); + values.push(`%${req.query.cohort}%`); + } + // if the team filter was provided + if (req.query.team) { + filters.push(`team ILIKE $${filters.length + 1}`); + values.push(`%${req.query.team}%`); + } - // combine all the filters into a single query - if (filters.length > 0) { - baseQuery += " WHERE " + filters.join(" AND "); - } + // combine all the filters into a single query + if (filters.length > 0) { + baseQuery += " WHERE " + filters.join(" AND "); + } - // execute the query, making sure to provide the values for the filters - req.client.query(baseQuery, values, (err: any, result: any) => { - if (err) { - logger.error(`Error reading from database: ${err.message}`); - return res - .status(500) - .json({ message: "Error reading from database" }); - } - if (result.rows.length === 0) { - logger.warn("No projects found"); - return res.status(404).json({"message": "No projects found"}); - } - return res.status(200).send(result.rows); - }); + // execute the query, making sure to provide the values for the filters + req.client.query(baseQuery, values, (err: any, result: any) => { + if (err) { + logger.error(`Error reading from database: ${err.message}`); + return res.status(500).json({ message: "Error reading from database" }); + } + if (result.rows.length === 0) { + logger.warn("No projects found"); + return res.status(404).json({ message: "No projects found" }); + } + return res.status(200).send(result.rows); + }); }); router.post("/add", (req: any, res: any) => { - const keys = Object.keys(req.body); - const values = Object.values(req.body); - - - - const query = `INSERT INTO projects (${keys - .map((key) => { - if (key.includes("-")) { - return `"${key}"`; - } - return key; - }) - .join(", ")}) VALUES (${keys.map((key, i) => `$${i + 1}`).join(", ")})`; + const keys: Array = Object.keys(req.body); + const values: Array = Object.values(req.body); - req.client.query(query, values, (err: any, result: any) => { - if (err) { - logger.error("Error adding to database"); - return res.status(500).send(err.message); - } - return res.status(200).send("Added new project"); - }); + try { + addToDB(req.client, values); + return res.status(200).send("Project added successfully"); + } catch (err: any) { + return res.status(500).send(err.message); + } }); router.post("/update", (req: any, res: any) => { - // TODO - + // TODO }); export default router; From a121f671d592c5e5e7a5cebfce18794589450e41 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:44:37 -0400 Subject: [PATCH 21/57] added doc --- routes/databaseFunctions.ts | 6 +++++- routes/projectsDB.ts | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 57b4eac..7c6115e 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -1,5 +1,9 @@ import { Client } from "pg"; +/** + * Function to add all items from values to database + * Assumes values array correctly maps to the database schema (no empty values, etc.) + */ export function addToDB(client: Client, values: Array) { const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) @@ -8,6 +12,6 @@ export function addToDB(client: Client, values: Array) { try { return client.query(query, values); } catch (e: any) { - throw Error(e.toString()); + throw Error(e); } } diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 7969097..0d10464 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -60,7 +60,6 @@ router.get("/get", (req: any, res: any) => { }); router.post("/add", (req: any, res: any) => { - const keys: Array = Object.keys(req.body); const values: Array = Object.values(req.body); try { From 91fd2515655a8b6b419b001460d0dedc147f2da4 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Tue, 4 Jun 2024 12:45:33 -0400 Subject: [PATCH 22/57] added null fn --- routes/databaseFunctions.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 7c6115e..546e9f8 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -15,3 +15,7 @@ export function addToDB(client: Client, values: Array) { throw Error(e); } } + +export function getFromDB(client: Client, parameters: Array) { + return null; +} From b5d6bce0dc1efcbcce0bfbd8aeefe42985d793d3 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 Jun 2024 14:23:47 -0400 Subject: [PATCH 23/57] Boolean switch for database that is already connected --- connectDB.ts | 7 +++++++ routes/projectsDB.ts | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/connectDB.ts b/connectDB.ts index 47c9894..ccee4fd 100644 --- a/connectDB.ts +++ b/connectDB.ts @@ -1,9 +1,16 @@ import getDB from "./db"; import logger from "./utils/logger"; +let isActive = false; + const connectDB = async (req: any, res: any, next: any) => { try { + if (isActive) { + logger.info("Database already connected"); + return next(); + } req.client = await getDB(); + isActive = true; next(); } catch (err: any) { logger.info(err.message); diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 0d10464..86ce8f3 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -11,7 +11,6 @@ router.use(express.json()); router.get("/", async (req: any, res: any) => { try { - await req.client.query("SELECT * FROM projects"); return res.status(200).send("No errors!!"); } catch (err: any) { return res.status(500).send(err.message); @@ -66,7 +65,7 @@ router.post("/add", (req: any, res: any) => { addToDB(req.client, values); return res.status(200).send("Project added successfully"); } catch (err: any) { - return res.status(500).send(err.message); + return res.status(400).send(err.message); } }); From 98b39b57f2671d677083703911921c7171a3c4d4 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 Jun 2024 19:10:30 -0400 Subject: [PATCH 24/57] Updated connected db issue in db.ts --- db.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/db.ts b/db.ts index bb3e853..124a989 100644 --- a/db.ts +++ b/db.ts @@ -1,15 +1,20 @@ -import client from './db.config'; +import client from './db.config'; -const getDB = async () => { - console.log("Connecting to Database ..."); - - try { - await client.connect(); - console.log("Database connected"); - return client; - } catch (err: any) { - return client; +let isActive = false; +const getDB = async () => { + console.log("Connecting to Database ..."); + if (isActive) { + console.log("Database already connected"); + return client; + } + try { + await client.connect(); + console.log("Database connected"); + isActive = true; + return client; + } catch (err: any) { + return client; } -} +} export default getDB; \ No newline at end of file From 3822fb35f27a7c1d7c90c10925f6249091e46b50 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Tue, 4 Jun 2024 19:25:49 -0400 Subject: [PATCH 25/57] query database refactor --- routes/databaseFunctions.ts | 20 ++++++++++++-------- routes/projectsDB.ts | 27 ++++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 546e9f8..9f9b54b 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -5,17 +5,21 @@ import { Client } from "pg"; * Assumes values array correctly maps to the database schema (no empty values, etc.) */ export function addToDB(client: Client, values: Array) { - const query = ` + const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - try { - return client.query(query, values); - } catch (e: any) { - throw Error(e); - } + try { + return client.query(query, values); + } catch (e: any) { + throw Error(e); + } } -export function getFromDB(client: Client, parameters: Array) { - return null; +export function getFromDB(client: Client, query: string, values: Array) { + try { + return client.query(query, values); + } catch (e: any) { + throw Error(e); + } } diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 86ce8f3..bce293d 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -2,7 +2,8 @@ import { Router } from "express"; import connectDB from "../connectDB"; import express from "express"; import logger from "../utils/logger"; -import { addToDB } from "./databaseFunctions"; +import { addToDB, getFromDB } from "./databaseFunctions"; +import { QueryResult } from "pg"; const router: Router = Router(); @@ -13,11 +14,11 @@ router.get("/", async (req: any, res: any) => { try { return res.status(200).send("No errors!!"); } catch (err: any) { - return res.status(500).send(err.message); + return res.status(500).json({ "message": err.message }); } }); -router.get("/get", (req: any, res: any) => { +router.get("/get", async (req: any, res: any) => { let baseQuery = "SELECT * FROM projects"; const filters: string[] = []; const values: (string | number)[] = []; @@ -45,17 +46,12 @@ router.get("/get", (req: any, res: any) => { } // execute the query, making sure to provide the values for the filters - req.client.query(baseQuery, values, (err: any, result: any) => { - if (err) { - logger.error(`Error reading from database: ${err.message}`); - return res.status(500).json({ message: "Error reading from database" }); - } - if (result.rows.length === 0) { - logger.warn("No projects found"); - return res.status(404).json({ message: "No projects found" }); - } - return res.status(200).send(result.rows); - }); + try { + const data: QueryResult = await getFromDB(req.client, baseQuery, values); + return res.status(200).send(data.rows); + } catch { + return res.status(500).json({"message": "Error retrieving data"}); + } }); router.post("/add", (req: any, res: any) => { @@ -70,7 +66,8 @@ router.post("/add", (req: any, res: any) => { }); router.post("/update", (req: any, res: any) => { - // TODO + // TODO: Implement this endpoint + }); export default router; From d3e25ce03d5ffe2462ef995c803c6a6b80dbd3a7 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 Jun 2024 19:27:57 -0400 Subject: [PATCH 26/57] Removed extra code in connectDB causing connection issues --- connectDB.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/connectDB.ts b/connectDB.ts index ccee4fd..47c9894 100644 --- a/connectDB.ts +++ b/connectDB.ts @@ -1,16 +1,9 @@ import getDB from "./db"; import logger from "./utils/logger"; -let isActive = false; - const connectDB = async (req: any, res: any, next: any) => { try { - if (isActive) { - logger.info("Database already connected"); - return next(); - } req.client = await getDB(); - isActive = true; next(); } catch (err: any) { logger.info(err.message); From 6abf3b333043383e4d17ed0c79071ebf1977cf39 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Tue, 4 Jun 2024 19:57:45 -0400 Subject: [PATCH 27/57] implemented update endpoint and database function --- routes/databaseFunctions.ts | 6 +---- routes/projectsDB.ts | 44 +++++++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 9f9b54b..557370f 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -4,11 +4,7 @@ import { Client } from "pg"; * Function to add all items from values to database * Assumes values array correctly maps to the database schema (no empty values, etc.) */ -export function addToDB(client: Client, values: Array) { - const query = ` - INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - +export function addToDB(client: Client, query: string, values: Array) { try { return client.query(query, values); } catch (e: any) { diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index bce293d..9998a49 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -56,18 +56,54 @@ router.get("/get", async (req: any, res: any) => { router.post("/add", (req: any, res: any) => { const values: Array = Object.values(req.body); - + const query = ` + INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; try { - addToDB(req.client, values); + addToDB(req.client, query, values); return res.status(200).send("Project added successfully"); } catch (err: any) { return res.status(400).send(err.message); } }); -router.post("/update", (req: any, res: any) => { - // TODO: Implement this endpoint +router.put("/update", async (req: any, res: any) => { + const projectName = req.query.name; + + if (!projectName) { + return res.status(400).json({ message: "Project name is required" }); + } + + const fields = req.body; + if (!fields || Object.keys(fields).length === 0) { + return res.status(400).json({ message: "No fields to update provided" }); + } + + const setClauses: string[] = []; + const values: (string | number)[] = []; + // Construct the set clauses and values array + Object.keys(fields).forEach((key, index) => { + setClauses.push(`"${key}" = $${index + 1}`); + values.push(fields[key]); + }); + + // Add the project name to the values array for the WHERE clause + values.push(projectName); + + const query = ` + UPDATE projects + SET ${setClauses.join(", ")} + WHERE name = $${values.length}`; + console.log(query); + console.log(values); + try { + await addToDB(req.client, query, values); + return res.status(200).send("Project updated successfully"); + } catch (err: any) { + return res.status(400).json({ message: err.message }); + } }); + export default router; From e09dec869f6cfcb9e43909d824f0df99582e4f76 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 Jun 2024 21:16:56 -0400 Subject: [PATCH 28/57] Updated README.md for db actions and local JSON file queries --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 870f431..01fc2d5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,58 @@ -# byte-server +# Byte-server Server for byte # Description -Server is built with Node.js and Express. Provides endpoints for managing a collection of projects, including creating, reading and updating. Exists both a json file and database backup for storing and quering projects +Server is built with Node.js and Express. Provides endpoints for managing a collection of projects, including creating, reading and updating. Exists both database and a local JSON file backup for storing and quering projects +# How to use +Creating, Reading and Updating can be done from the database (default approach) endpoints. Only reading can be done for the local JSON file +## Endpoints for DB + +### Get projects +`/projects/get` + +Retrieves all projects in the database and supports additional query params for filtering + +### Post new projects +`/projects/add` + +**Please Fill Out All Fields!!!** + +The schema for projects is defined as follows: +- **name**: TYPE string(s) +- **"short-desc"**: TYPE string(s) +- **"long-desc"**: TYPE string(s) +- **team**: TYPE array of strings +- **link**: TYPE string(s) +- **Image**: TYPE string(s) +- **"tech-stack"**: TYPE array of strings +- **cohort**: TYPE string(s) +- **topic**: TYPE array of strings + +### UPDATE projects +`/projects/update?name={project_name}` + +Replace `{project_name}` with the desired project name you want to update + +## Endpoints for Local File + +### Get all projects +`/projects` + +Retrieves all projects from the local JSON file and also supports additional query params for filtering + +### Get projects based on team members +`/projects?team={member_name}` + +Replace `{member_name}` with a member that is in the project you would like to query + +### Get projects based on cohort +`/projects?cohort={cohort}` + +Replace `{cohort}` with the desired cohort to filter projects + +### Get projects based on name +`/projects?name={project_name}` + +Replace `{project_name}` with the desired project name to filter projects \ No newline at end of file From 27ec86cdcd1125e75713c8273210d8d7b9ec1f0d Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Wed, 5 Jun 2024 09:36:54 -0400 Subject: [PATCH 29/57] updated readme with more info --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 01fc2d5..d89a9cb 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,33 @@ # Byte-server Server for byte -# Description -Server is built with Node.js and Express. Provides endpoints for managing a collection of projects, including creating, reading and updating. Exists both database and a local JSON file backup for storing and quering projects +## Description +Server is built with Node.js and Express. Provides endpoints for managing a collection of projects, including creating, reading and updating. Exists both database and a local JSON file backup for storing and quering projects. -# How to use -Creating, Reading and Updating can be done from the database (default approach) endpoints. Only reading can be done for the local JSON file +## How to use +Creating, Reading and Updating can be done from the database (default approach) endpoints. Only reading can be done for the local JSON file. ## Endpoints for DB ### Get projects `/projects/get` -Retrieves all projects in the database and supports additional query params for filtering +Retrieves all projects in the database and supports additional query params for filtering. + +Filters include: +- **team**: TYPE string(s) +- **cohort**: TYPE string(s) +- **name**: TYPE string(s) + +Filters are stackable, meaning you can do something similar to: +`/projects/get?team=John&cohort=1&team=Jane` +This will return all projects that have John **AND** Jane in their team and are in cohort 1. You can stack as many filters as you want but be aware that the more filters you add, the more specific the query will be, meaning it might return no results. ### Post new projects `/projects/add` -**Please Fill Out All Fields!!!** +**Please Fill Out All Fields!!!** +These fields must be provided as JSON in the body of the request. The schema for projects is defined as follows: - **name**: TYPE string(s) @@ -31,28 +41,48 @@ The schema for projects is defined as follows: - **topic**: TYPE array of strings ### UPDATE projects +`/projects/update` + +You **MUST** provide the project name you want to update as a query parameter like so: `/projects/update?name={project_name}` -Replace `{project_name}` with the desired project name you want to update +You can provide any key value pair you want to update in the body of the request, but it **HAS** to be JSON. This doesn't have to be all the fields, only the ones you want to update. + +Reminder that the schema for projects is defined as follows: +- **name**: TYPE string(s) +- **"short-desc"**: TYPE string(s) +- **"long-desc"**: TYPE string(s) +- **team**: TYPE array of strings +- **link**: TYPE string(s) +- **Image**: TYPE string(s) +- **"tech-stack"**: TYPE array of strings +- **cohort**: TYPE string(s) +- **topic**: TYPE array of strings + +Any mismatch of the schema types (i.e. providing a string when we expect an array) will return an error. ## Endpoints for Local File ### Get all projects `/projects` -Retrieves all projects from the local JSON file and also supports additional query params for filtering +Retrieves all projects from the local JSON file and also supports additional query params for filtering. ### Get projects based on team members `/projects?team={member_name}` -Replace `{member_name}` with a member that is in the project you would like to query +Replace `{member_name}` with a member that is in the project you would like to query. This filter is stackable, meaning you can do something similar to: +`/projects?team=John&team=Jane` ### Get projects based on cohort `/projects?cohort={cohort}` -Replace `{cohort}` with the desired cohort to filter projects +Replace `{cohort}` with the desired cohort to filter projects. This filter is stackable, meaning you can do something similar to: +`/projects?cohort=1&cohort=2` ### Get projects based on name `/projects?name={project_name}` -Replace `{project_name}` with the desired project name to filter projects \ No newline at end of file +Replace `{project_name}` with the desired project name to filter projects. +This filter is stackable, meaning you can do something similar to: +`/projects?name=Website&name=Server` \ No newline at end of file From 27b2e1bd2f64fc55f094240b25d38e00d045eb59 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Wed, 5 Jun 2024 17:17:10 -0400 Subject: [PATCH 30/57] created universal query function to query database --- routes/databaseFunctions.ts | 10 +--------- routes/projectsDB.ts | 8 ++++---- server.ts | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 557370f..8245fa5 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -4,15 +4,7 @@ import { Client } from "pg"; * Function to add all items from values to database * Assumes values array correctly maps to the database schema (no empty values, etc.) */ -export function addToDB(client: Client, query: string, values: Array) { - try { - return client.query(query, values); - } catch (e: any) { - throw Error(e); - } -} - -export function getFromDB(client: Client, query: string, values: Array) { +export function queryDatabase(client: Client, query: string, values: Array) { try { return client.query(query, values); } catch (e: any) { diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 9998a49..c335b39 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -2,7 +2,7 @@ import { Router } from "express"; import connectDB from "../connectDB"; import express from "express"; import logger from "../utils/logger"; -import { addToDB, getFromDB } from "./databaseFunctions"; +import { queryDatabase } from "./databaseFunctions"; import { QueryResult } from "pg"; const router: Router = Router(); @@ -47,7 +47,7 @@ router.get("/get", async (req: any, res: any) => { // execute the query, making sure to provide the values for the filters try { - const data: QueryResult = await getFromDB(req.client, baseQuery, values); + const data: QueryResult = await queryDatabase(req.client, baseQuery, values); return res.status(200).send(data.rows); } catch { return res.status(500).json({"message": "Error retrieving data"}); @@ -60,7 +60,7 @@ router.post("/add", (req: any, res: any) => { INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; try { - addToDB(req.client, query, values); + queryDatabase(req.client, query, values); return res.status(200).send("Project added successfully"); } catch (err: any) { return res.status(400).send(err.message); @@ -98,7 +98,7 @@ router.put("/update", async (req: any, res: any) => { console.log(query); console.log(values); try { - await addToDB(req.client, query, values); + await queryDatabase(req.client, query, values); return res.status(200).send("Project updated successfully"); } catch (err: any) { return res.status(400).json({ message: err.message }); diff --git a/server.ts b/server.ts index cbe737d..1f1061f 100644 --- a/server.ts +++ b/server.ts @@ -22,7 +22,7 @@ app.use("/projects", projectsDB); // any other route will return a 404 app.get("*", (req: any, res: any) => { - res.status(404).json({ message: "Page not found" }); + res.status(404).json({ message: "Page not found. Invalid path or method provided to make this request." }); }); app.listen(PORT, () => { From 7cb85baf537e6aa4262c49cbe5ca6e987239ab99 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 6 Jun 2024 14:12:57 -0400 Subject: [PATCH 31/57] Validate middleware before inserting new data into DB --- middlewares/connectDB.ts | 14 ++++++ middlewares/validate.ts | 102 +++++++++++++++++++++++++++++++++++++++ routes/projectsDB.ts | 7 ++- 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 middlewares/connectDB.ts create mode 100644 middlewares/validate.ts diff --git a/middlewares/connectDB.ts b/middlewares/connectDB.ts new file mode 100644 index 0000000..f1f8b98 --- /dev/null +++ b/middlewares/connectDB.ts @@ -0,0 +1,14 @@ +import getDB from "../db"; +import logger from "../utils/logger"; + +const connectDB = async (req: any, res: any, next: any) => { + try { + req.client = await getDB(); + next(); + } catch (err: any) { + logger.info(err.message); + next("route"); + } +} + +export default connectDB; diff --git a/middlewares/validate.ts b/middlewares/validate.ts new file mode 100644 index 0000000..334b213 --- /dev/null +++ b/middlewares/validate.ts @@ -0,0 +1,102 @@ +//so the error that is occurring is due to the fact that initially it would return a field is a invalid field but hten it would give me the error saying that we try to write after the response ahs ended +function validating(keys: string[], values: any[], requiredFields: string[], res: any) { + for (let index = 0; index < keys.length; index++) { + //check if the field is a required field, if not return a 400 status code + if (!requiredFields.includes(keys[index])) { + return res.status(400).json({ message: `${keys[index]} is a invalid field` }); + }; + + //at this rate field(s) provided are required field(s) therefore now checking type of its value based on index + switch (keys[index]) { + case "name": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "short-desc": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "long-desc": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "team": + if (!Array.isArray(values[index])) { + return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); + } + break; + case "link": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "image": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "tech-stack": + if (!Array.isArray(values[index])) { + return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); + } + break; + case "cohort": + if (typeof values[index] !== "string") { + return res.status(400).json({ message: `${keys[index]} field must be a string` }); + } + break; + case "topic": + if (!Array.isArray(values[index])) { + return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); + } + break; + }; + }; + //at this point of code all fields are required and have correct typings, meaning no JSON was sent. if no response is sent, return false at the end of the function + return false; +}; + +const validate = (req: any, res: any, next: any) => { + + const requiredFields = ["name", "short-desc", "long-desc", "team", "link", "image", "tech-stack", "cohort", "topic"]; + const values = Object.values(req.body); + const keys = Object.keys(req.body).toString().toLowerCase().split(","); + + if (req.method === "POST") { + if (Object.keys(req.body).length === 0) { + return res.status(400).json({ message: "Please insert a object with all required fields!" }); + } + //there should be 9 fields so if one is missing return a 400 status code indicating missing fields + if (keys.length !== 9) { + return res.status(400).json({ message: "Please insert all required fields, you are missing some fields!" }); + } + else { + /**check if all fields are required fields and have the correct typings, + * if validating sends a response, return to stop the execution of validate + **/ + if (validating(keys, values, requiredFields, res)) { + return; + } + } + } + // the request method is PUT + else { + if (Object.keys(req.body).length === 0) { + return res.status(400).json({ message: "Please insert a object to update" }); + } + /**check if all fields are required fields and have the correct typings, + * if validating sends a response, return to stop the execution of validate + **/ + if (validating(keys, values, requiredFields, res)) { + return; + } + } + + //at this rate all fields are filled with the correct typings so pass control to the next middleware to insert into DB + next(); +}; + +export default validate; \ No newline at end of file diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index c335b39..088b684 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -4,6 +4,7 @@ import express from "express"; import logger from "../utils/logger"; import { queryDatabase } from "./databaseFunctions"; import { QueryResult } from "pg"; +import validate from "../middlewares/validate"; const router: Router = Router(); @@ -54,7 +55,7 @@ router.get("/get", async (req: any, res: any) => { } }); -router.post("/add", (req: any, res: any) => { +router.post("/add", validate, (req: any, res: any) => { const values: Array = Object.values(req.body); const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) @@ -67,7 +68,7 @@ router.post("/add", (req: any, res: any) => { } }); -router.put("/update", async (req: any, res: any) => { +router.put("/update", validate, async (req: any, res: any) => { const projectName = req.query.name; if (!projectName) { @@ -95,8 +96,6 @@ router.put("/update", async (req: any, res: any) => { UPDATE projects SET ${setClauses.join(", ")} WHERE name = $${values.length}`; - console.log(query); - console.log(values); try { await queryDatabase(req.client, query, values); return res.status(200).send("Project updated successfully"); From 3efa7168707d36d4981196fa08058a126d99d890 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Thu, 6 Jun 2024 16:02:57 -0400 Subject: [PATCH 32/57] small tweaks to responses --- routes/projectsDB.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index c335b39..56b51fd 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -12,7 +12,7 @@ router.use(express.json()); router.get("/", async (req: any, res: any) => { try { - return res.status(200).send("No errors!!"); + return res.status(200).json({"message": "API is operational."}); } catch (err: any) { return res.status(500).json({ "message": err.message }); } @@ -61,9 +61,9 @@ router.post("/add", (req: any, res: any) => { VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; try { queryDatabase(req.client, query, values); - return res.status(200).send("Project added successfully"); + return res.status(200).json({"message": "Project added successfully"}); } catch (err: any) { - return res.status(400).send(err.message); + return res.status(400).json({"message": err.message}); } }); From 2c4f6f5435f96048783886e9e7e8a86bf5694949 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Thu, 6 Jun 2024 16:11:22 -0400 Subject: [PATCH 33/57] fixed incorrect logging issue for update --- routes/projectsDB.ts | 152 +++++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index b18d661..6ed01d2 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -12,97 +12,107 @@ router.use(connectDB); router.use(express.json()); router.get("/", async (req: any, res: any) => { - try { - return res.status(200).json({"message": "API is operational."}); - } catch (err: any) { - return res.status(500).json({ "message": err.message }); - } + try { + return res.status(200).json({ message: "API is operational." }); + } catch (err: any) { + return res.status(500).json({ message: err.message }); + } }); router.get("/get", async (req: any, res: any) => { - let baseQuery = "SELECT * FROM projects"; - const filters: string[] = []; - const values: (string | number)[] = []; - - // if the name filter was provided - if (req.query.name) { - filters.push(`name ILIKE $${filters.length + 1}`); - values.push(`%${req.query.name}%`); - } - - // if the cohort filter was provided - if (req.query.cohort) { - filters.push(`cohort ILIKE $${filters.length + 1}`); - values.push(`%${req.query.cohort}%`); - } - // if the team filter was provided - if (req.query.team) { - filters.push(`team ILIKE $${filters.length + 1}`); - values.push(`%${req.query.team}%`); - } - - // combine all the filters into a single query - if (filters.length > 0) { - baseQuery += " WHERE " + filters.join(" AND "); - } - - // execute the query, making sure to provide the values for the filters - try { - const data: QueryResult = await queryDatabase(req.client, baseQuery, values); - return res.status(200).send(data.rows); - } catch { - return res.status(500).json({"message": "Error retrieving data"}); - } + let baseQuery = "SELECT * FROM projects"; + const filters: string[] = []; + const values: (string | number)[] = []; + + // if the name filter was provided + if (req.query.name) { + filters.push(`name ILIKE $${filters.length + 1}`); + values.push(`%${req.query.name}%`); + } + + // if the cohort filter was provided + if (req.query.cohort) { + filters.push(`cohort ILIKE $${filters.length + 1}`); + values.push(`%${req.query.cohort}%`); + } + // if the team filter was provided + if (req.query.team) { + filters.push(`team ILIKE $${filters.length + 1}`); + values.push(`%${req.query.team}%`); + } + + // combine all the filters into a single query + if (filters.length > 0) { + baseQuery += " WHERE " + filters.join(" AND "); + } + + // execute the query, making sure to provide the values for the filters + try { + const data: QueryResult = await queryDatabase( + req.client, + baseQuery, + values + ); + return res.status(200).send(data.rows); + } catch { + return res.status(500).json({ message: "Error retrieving data" }); + } }); router.post("/add", validate, (req: any, res: any) => { - const values: Array = Object.values(req.body); - const query = ` + const values: Array = Object.values(req.body); + const query = ` INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - try { - queryDatabase(req.client, query, values); - return res.status(200).json({"message": "Project added successfully"}); - } catch (err: any) { - return res.status(400).json({"message": err.message}); - } + try { + queryDatabase(req.client, query, values); + return res.status(200).json({ message: "Project added successfully" }); + } catch (err: any) { + return res.status(400).json({ message: err.message }); + } }); router.put("/update", validate, async (req: any, res: any) => { - const projectName = req.query.name; + const projectName = req.query.name; - if (!projectName) { - return res.status(400).json({ message: "Project name is required" }); - } + if (!projectName) { + return res.status(400).json({ message: "Project name is required" }); + } - const fields = req.body; - if (!fields || Object.keys(fields).length === 0) { - return res.status(400).json({ message: "No fields to update provided" }); - } + const fields = req.body; + if (!fields || Object.keys(fields).length === 0) { + return res + .status(400) + .json({ message: "No fields to update provided" }); + } - const setClauses: string[] = []; - const values: (string | number)[] = []; + const setClauses: string[] = []; + const values: (string | number)[] = []; - // Construct the set clauses and values array - Object.keys(fields).forEach((key, index) => { - setClauses.push(`"${key}" = $${index + 1}`); - values.push(fields[key]); - }); + // Construct the set clauses and values array + Object.keys(fields).forEach((key, index) => { + setClauses.push(`"${key}" = $${index + 1}`); + values.push(fields[key]); + }); - // Add the project name to the values array for the WHERE clause - values.push(projectName); + // Add the project name to the values array for the WHERE clause + values.push(projectName); - const query = ` + const query = ` UPDATE projects SET ${setClauses.join(", ")} WHERE name = $${values.length}`; - try { - await queryDatabase(req.client, query, values); - return res.status(200).send("Project updated successfully"); - } catch (err: any) { - return res.status(400).json({ message: err.message }); - } -}); + try { + const result = await queryDatabase(req.client, query, values); + + if (result.rowCount === 0) { + return res.status(404).json({ message: "Project not found" }); + } + return res.status(200).send("Project updated successfully"); + } catch (err: any) { + return res.status(400).json({ message: err.message }); + } +}); export default router; From 38e4415fbbe4bde4179bd16c18ea6edf32a18ffb Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 6 Jun 2024 20:59:58 -0400 Subject: [PATCH 34/57] Significantly reduced lines of code while retaining the logic of validate middleware --- middlewares/validate.ts | 97 ++++++++++------------------------------- 1 file changed, 23 insertions(+), 74 deletions(-) diff --git a/middlewares/validate.ts b/middlewares/validate.ts index 334b213..580e81e 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,101 +1,50 @@ -//so the error that is occurring is due to the fact that initially it would return a field is a invalid field but hten it would give me the error saying that we try to write after the response ahs ended -function validating(keys: string[], values: any[], requiredFields: string[], res: any) { - for (let index = 0; index < keys.length; index++) { - //check if the field is a required field, if not return a 400 status code - if (!requiredFields.includes(keys[index])) { - return res.status(400).json({ message: `${keys[index]} is a invalid field` }); - }; - - //at this rate field(s) provided are required field(s) therefore now checking type of its value based on index - switch (keys[index]) { - case "name": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "short-desc": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "long-desc": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "team": - if (!Array.isArray(values[index])) { - return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); - } - break; - case "link": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "image": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "tech-stack": - if (!Array.isArray(values[index])) { - return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); - } - break; - case "cohort": - if (typeof values[index] !== "string") { - return res.status(400).json({ message: `${keys[index]} field must be a string` }); - } - break; - case "topic": - if (!Array.isArray(values[index])) { - return res.status(400).json({ message: `${keys[index]} field must be a array of strings` }); - } - break; - }; - }; - //at this point of code all fields are required and have correct typings, meaning no JSON was sent. if no response is sent, return false at the end of the function +//validate all fields and their types +function validating(keys: string[], values: any[], requiredFields: any, res: any) { + for (let i = 0; i < values.length; i++) { + //initial check if the field is a required field + if (!(keys[i] in requiredFields)) { + return res.status(400).json({ message: `Please insert a valid field name, ${keys[i]} is invalid`}); + } + //check for the correct typing + if (typeof values[i] !== requiredFields[keys[i]]) { + return res.status(400).json({ message: `Please insert the correct typing for ${keys[i]}, it should be a ${requiredFields[keys[i]] === "object" ? "array of strings" : requiredFields[keys[i]]}!`}); + } + } + // if no response is sent meaning all validations passed, return false at the end of the function return false; -}; - +} + const validate = (req: any, res: any, next: any) => { - const requiredFields = ["name", "short-desc", "long-desc", "team", "link", "image", "tech-stack", "cohort", "topic"]; + const requiredFields = {name: "string", "short-desc": "string", "long-desc": "string", team: "object", link: "string", image: "string", "tech-stack": "object", cohort: "string", topic: "object"}; const values = Object.values(req.body); const keys = Object.keys(req.body).toString().toLowerCase().split(","); if (req.method === "POST") { + //initial check for empty request body if (Object.keys(req.body).length === 0) { return res.status(400).json({ message: "Please insert a object with all required fields!" }); } - //there should be 9 fields so if one is missing return a 400 status code indicating missing fields if (keys.length !== 9) { return res.status(400).json({ message: "Please insert all required fields, you are missing some fields!" }); } else { - /**check if all fields are required fields and have the correct typings, - * if validating sends a response, return to stop the execution of validate - **/ if (validating(keys, values, requiredFields, res)) { return; } } } - // the request method is PUT else { + //initial check for empty request body if (Object.keys(req.body).length === 0) { - return res.status(400).json({ message: "Please insert a object to update" }); + return res.status(400).json({ message: "Please insert a object to update!" }); } - /**check if all fields are required fields and have the correct typings, - * if validating sends a response, return to stop the execution of validate - **/ - if (validating(keys, values, requiredFields, res)) { - return; + else { + if (validating(keys, values, requiredFields, res)) { + return; + } } } - - //at this rate all fields are filled with the correct typings so pass control to the next middleware to insert into DB next(); }; From dfdc0507628006f299018b595f53faeb338bda49 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sat, 8 Jun 2024 17:52:18 -0400 Subject: [PATCH 35/57] adding initial frakework for parallel processing the database check --- app.ts | 0 dbCheck.ts | 6 ++++++ dbMiddleware.ts | 28 ++++++++++++++++++++++++++++ routes/databaseFunctions.ts | 6 +++++- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 app.ts create mode 100644 dbCheck.ts create mode 100644 dbMiddleware.ts diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..e69de29 diff --git a/dbCheck.ts b/dbCheck.ts new file mode 100644 index 0000000..c5b523a --- /dev/null +++ b/dbCheck.ts @@ -0,0 +1,6 @@ +function checkDB() { + console.log("checking db"); + process.exit(1); +} + +checkDB(); diff --git a/dbMiddleware.ts b/dbMiddleware.ts new file mode 100644 index 0000000..0dcfc1a --- /dev/null +++ b/dbMiddleware.ts @@ -0,0 +1,28 @@ +// import { Client } from "pg"; +// import activateDb from "./db"; +import { spawn } from "child_process"; + +// IDEA: +// have a thread running parrallel to the server that will check every 1hr +// if the database is still contected, changing the server to use local instead untill the +// new connection is established +// +// fn will try to establish a connection to the db in 30s intervals + +function secondsToMs(d: number) { + return d * 1000; +} + +function checkDB() { + const database = spawn("bun", ["dbCheck.ts"]); + database.on("exit", (code) => { + if (code === 1) { + console.log("DB is down"); + } else { + console.log("DB is all good"); + } + }); +} +const INTERVAL = secondsToMs(1); + +setInterval(checkDB, INTERVAL); diff --git a/routes/databaseFunctions.ts b/routes/databaseFunctions.ts index 8245fa5..3863ea8 100644 --- a/routes/databaseFunctions.ts +++ b/routes/databaseFunctions.ts @@ -4,7 +4,11 @@ import { Client } from "pg"; * Function to add all items from values to database * Assumes values array correctly maps to the database schema (no empty values, etc.) */ -export function queryDatabase(client: Client, query: string, values: Array) { +export function queryDatabase( + client: Client, + query: string, + values: Array, +) { try { return client.query(query, values); } catch (e: any) { From bcb3a466f3325c3c9a6ebba614fabf28b0ac7391 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sat, 8 Jun 2024 18:22:59 -0400 Subject: [PATCH 36/57] fn dbCheck.ts will try to connect to db with a given timeout --- dbCheck.ts | 24 ++++++++++++++++++++---- dbMiddleware.ts => dbChecker.ts | 9 +++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) rename dbMiddleware.ts => dbChecker.ts (75%) diff --git a/dbCheck.ts b/dbCheck.ts index c5b523a..795d705 100644 --- a/dbCheck.ts +++ b/dbCheck.ts @@ -1,6 +1,22 @@ -function checkDB() { - console.log("checking db"); - process.exit(1); +import client from "./db.config"; + +async function getDB() { + await client.connect(); +} + +async function checkDB(timeout: number) { + setTimeout(async () => { + try { + await getDB(); + process.exit(0); + } catch (e: any) { + console.error("Error:", e.message); + process.exit(1); + } + }, timeout); } -checkDB(); +const args: string[] = process.argv; +const timeout: number = parseInt(args[2]); + +checkDB(timeout); diff --git a/dbMiddleware.ts b/dbChecker.ts similarity index 75% rename from dbMiddleware.ts rename to dbChecker.ts index 0dcfc1a..f6e32b2 100644 --- a/dbMiddleware.ts +++ b/dbChecker.ts @@ -12,9 +12,15 @@ import { spawn } from "child_process"; function secondsToMs(d: number) { return d * 1000; } +const INTERVAL = secondsToMs(1); +// threading should happen at top level of server "setInterval" function checkDB() { - const database = spawn("bun", ["dbCheck.ts"]); + const database = spawn("bun", ["dbCheck.ts", INTERVAL.toString()]); + database.stdout.on("data", (data) => { + console.log(data.toString()); + }); + database.on("exit", (code) => { if (code === 1) { console.log("DB is down"); @@ -23,6 +29,5 @@ function checkDB() { } }); } -const INTERVAL = secondsToMs(1); setInterval(checkDB, INTERVAL); From b2382519b5e171231b0fd96333bef5d88bb0879f Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sat, 8 Jun 2024 18:30:30 -0400 Subject: [PATCH 37/57] updating time settings on timeout and interval --- dbChecker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dbChecker.ts b/dbChecker.ts index f6e32b2..186758e 100644 --- a/dbChecker.ts +++ b/dbChecker.ts @@ -12,11 +12,12 @@ import { spawn } from "child_process"; function secondsToMs(d: number) { return d * 1000; } -const INTERVAL = secondsToMs(1); +const INTERVAL = secondsToMs(60 * 60); +const TIMEOUT = secondsToMs(30); // threading should happen at top level of server "setInterval" function checkDB() { - const database = spawn("bun", ["dbCheck.ts", INTERVAL.toString()]); + const database = spawn("bun", ["dbCheck.ts", TIMEOUT.toString()]); database.stdout.on("data", (data) => { console.log(data.toString()); }); From 07eb0b6c45f661a81151fd3ff0a5dfadd934723f Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 01:38:56 -0400 Subject: [PATCH 38/57] checkDB now runs parallel to server --- db.config.ts | 14 +++++++++----- dbCheck.ts | 4 ++-- dbChecker.ts | 48 ++++++++++++++++++++++++------------------------ server.ts | 27 +++++++++++++++++++++------ 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/db.config.ts b/db.config.ts index 4dd8373..28e6f15 100644 --- a/db.config.ts +++ b/db.config.ts @@ -1,5 +1,5 @@ -import dotenv from 'dotenv'; -import {Client} from 'pg'; +import dotenv from "dotenv"; +import { Client } from "pg"; dotenv.config(); const client = new Client({ @@ -7,10 +7,14 @@ const client = new Client({ user: process.env.POSTGRESQL_DB_USER, password: process.env.POSTGRESQL_DB_PASSWORD, database: process.env.POSTGRESQL_DB, - port: process.env.POSTGRESQL_DB_PORT ? parseInt(process.env.POSTGRESQL_DB_PORT) : 5432 + port: process.env.POSTGRESQL_DB_PORT + ? parseInt(process.env.POSTGRESQL_DB_PORT) + : 5432, }); -client.on('end', () => console.log('Client has disconnected')); -client.on('error', (err) => console.error('Unexpected error on idle client', err)); +client.on("end", () => console.log("Client has disconnected")); +client.on("error", (err) => + console.error("Unexpected error on idle client", err), +); export default client; diff --git a/dbCheck.ts b/dbCheck.ts index 795d705..62eb457 100644 --- a/dbCheck.ts +++ b/dbCheck.ts @@ -10,7 +10,7 @@ async function checkDB(timeout: number) { await getDB(); process.exit(0); } catch (e: any) { - console.error("Error:", e.message); + console.error("Error Connecting to DB:", e.message); process.exit(1); } }, timeout); @@ -19,4 +19,4 @@ async function checkDB(timeout: number) { const args: string[] = process.argv; const timeout: number = parseInt(args[2]); -checkDB(timeout); +await checkDB(timeout); diff --git a/dbChecker.ts b/dbChecker.ts index 186758e..99868d0 100644 --- a/dbChecker.ts +++ b/dbChecker.ts @@ -1,34 +1,34 @@ -// import { Client } from "pg"; -// import activateDb from "./db"; import { spawn } from "child_process"; -// IDEA: -// have a thread running parrallel to the server that will check every 1hr -// if the database is still contected, changing the server to use local instead untill the -// new connection is established -// -// fn will try to establish a connection to the db in 30s intervals - -function secondsToMs(d: number) { +export function secondsToMs(d: number) { return d * 1000; } -const INTERVAL = secondsToMs(60 * 60); -const TIMEOUT = secondsToMs(30); // threading should happen at top level of server "setInterval" -function checkDB() { - const database = spawn("bun", ["dbCheck.ts", TIMEOUT.toString()]); - database.stdout.on("data", (data) => { - console.log(data.toString()); - }); +async function checkDB(TIMEOUT: number): Promise { + return new Promise((resolve, reject) => { + let dbAval: boolean = false; + + const database = spawn("bun", ["dbCheck.ts", TIMEOUT.toString()]); + + database.stdout.on("data", (data) => { + console.log("Output from dbCheck.ts:", data.toString()); + }); + + database.on("exit", (code) => { + if (code === 0) { + dbAval = true; + } else { + dbAval = false; + } + resolve(dbAval); + }); - database.on("exit", (code) => { - if (code === 1) { - console.log("DB is down"); - } else { - console.log("DB is all good"); - } + database.on("error", (error) => { + console.error(error); + reject(error); + }); }); } -setInterval(checkDB, INTERVAL); +export default checkDB; diff --git a/server.ts b/server.ts index 1f1061f..6d4baf4 100644 --- a/server.ts +++ b/server.ts @@ -4,9 +4,22 @@ import logger from "./utils/logger"; import projectsDB from "./routes/projectsDB"; import express from "express"; import connectDB from "./connectDB"; +import checkDB, { secondsToMs } from "./dbChecker"; const app = express(); const PORT = 3000; +const INTERVAL = secondsToMs(5); +const TIMEOUT = secondsToMs(2); +let dbAval: boolean = false; + +setInterval(async () => { + try { + dbAval = await checkDB(TIMEOUT); + } catch (e: any) { + console.error("Error:", e.message); + dbAval = false; + } +}, INTERVAL); app.use((req: any, res: any, next: any) => { logger.info(`Received a ${req.method} request for ${req.url}`); @@ -14,16 +27,18 @@ app.use((req: any, res: any, next: any) => { }); app.get("/", (req: any, res: any) => { - res.send("BYTE @ CCNY").status(200); + res.send( + `BYTE @ CCNY. The database is ${dbAval ? "available" : "not available"}`, + ).status(200); }); // app.use("/projects", projectsLocal); -app.use("/projects", projectsDB); +// app.use("/projects", projectsDB); -// any other route will return a 404 -app.get("*", (req: any, res: any) => { - res.status(404).json({ message: "Page not found. Invalid path or method provided to make this request." }); -}); +// // any other route will return a 404 +// app.get("*", (req: any, res: any) => { +// res.status(404).json({ message: "Page not found. Invalid path or method provided to make this request." }); +// }); app.listen(PORT, () => { console.log(`listening on port ${PORT}`); From 300adc7f3ce32078756e05516068daf4a164c868 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 01:52:54 -0400 Subject: [PATCH 39/57] adding logging --- routes/projectsDB.ts | 8 ++++++-- routes/projectsLocal.ts | 9 +++++++-- server.ts | 27 +++++++++++++++++---------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 6ed01d2..08333dd 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -8,8 +8,12 @@ import validate from "../middlewares/validate"; const router: Router = Router(); -router.use(connectDB); +// router.use(connectDB); router.use(express.json()); +router.use((req: any, res: any, next: any) => { + logger.info(`Received ${req.url} request for database projects`); + next(); +}); router.get("/", async (req: any, res: any) => { try { @@ -51,7 +55,7 @@ router.get("/get", async (req: any, res: any) => { const data: QueryResult = await queryDatabase( req.client, baseQuery, - values + values, ); return res.status(200).send(data.rows); } catch { diff --git a/routes/projectsLocal.ts b/routes/projectsLocal.ts index 2e8bca2..3e1a15c 100644 --- a/routes/projectsLocal.ts +++ b/routes/projectsLocal.ts @@ -6,11 +6,16 @@ import path from "path"; const router = Router(); const FILE_PATH: string = path.resolve(__dirname, "../data.json"); +router.use((req: any, res: any, next: any) => { + logger.info(`Received ${req.url} request for local projects`); + next(); +}); + router.get("/", (req: any, res: any) => { if (req.query) { logger.warn("Query parameters ignored"); } - + readFile(FILE_PATH, "utf8", (error: any, content: any) => { if (error) { logger.error("Error reading data.json"); @@ -113,4 +118,4 @@ router.get("/name", (req: any, res: any) => { }); }); -export default router; \ No newline at end of file +export default router; diff --git a/server.ts b/server.ts index 6d4baf4..cdcfddb 100644 --- a/server.ts +++ b/server.ts @@ -1,9 +1,7 @@ -import { Client } from "pg"; -import activateDb from "./db"; import logger from "./utils/logger"; +import projectsLocal from "./routes/projectsLocal"; import projectsDB from "./routes/projectsDB"; import express from "express"; -import connectDB from "./connectDB"; import checkDB, { secondsToMs } from "./dbChecker"; const app = express(); @@ -23,22 +21,31 @@ setInterval(async () => { app.use((req: any, res: any, next: any) => { logger.info(`Received a ${req.method} request for ${req.url}`); + next(); }); +app.use("/projects", (req: any, res: any, next: any) => { + if (dbAval) { + projectsDB(req, res, next); + } else { + projectsLocal(req, res, next); + } +}); + app.get("/", (req: any, res: any) => { res.send( `BYTE @ CCNY. The database is ${dbAval ? "available" : "not available"}`, ).status(200); }); -// app.use("/projects", projectsLocal); -// app.use("/projects", projectsDB); - -// // any other route will return a 404 -// app.get("*", (req: any, res: any) => { -// res.status(404).json({ message: "Page not found. Invalid path or method provided to make this request." }); -// }); +// any other route will return a 404 +app.get("*", (req: any, res: any) => { + res.status(404).json({ + message: + "Page not found. Invalid path or method provided to make this request.", + }); +}); app.listen(PORT, () => { console.log(`listening on port ${PORT}`); From 24cb6d9806cbdb815578f885073011f2ea649407 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 02:09:51 -0400 Subject: [PATCH 40/57] finishing up merging with main and adding db connection to projectsDatabase --- package.json | 2 ++ routes/projectsDB.ts | 12 ++++++------ server.ts | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 23c27dd..5b851c1 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,14 @@ "dependencies": { "@types/chai": "^4.3.16", "@types/chai-http": "^4.2.0", + "@types/cors": "^2.8.17", "@types/dotenv": "^8.2.0", "@types/express": "^4.17.21", "@types/mocha": "^10.0.6", "@types/pg": "^8.11.6", "chai": "^5.1.1", "chai-http": "^4.4.0", + "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", "nodemon": "^3.1.1", diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 08333dd..85e4db3 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,14 +1,14 @@ import { Router } from "express"; -import connectDB from "../connectDB"; import express from "express"; import logger from "../utils/logger"; import { queryDatabase } from "./databaseFunctions"; -import { QueryResult } from "pg"; +import { Client, QueryResult } from "pg"; import validate from "../middlewares/validate"; +import getDB from "../db"; const router: Router = Router(); +const client: Client = await getDB(); -// router.use(connectDB); router.use(express.json()); router.use((req: any, res: any, next: any) => { logger.info(`Received ${req.url} request for database projects`); @@ -53,7 +53,7 @@ router.get("/get", async (req: any, res: any) => { // execute the query, making sure to provide the values for the filters try { const data: QueryResult = await queryDatabase( - req.client, + client, baseQuery, values, ); @@ -69,7 +69,7 @@ router.post("/add", validate, (req: any, res: any) => { INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; try { - queryDatabase(req.client, query, values); + queryDatabase(client, query, values); return res.status(200).json({ message: "Project added successfully" }); } catch (err: any) { return res.status(400).json({ message: err.message }); @@ -107,7 +107,7 @@ router.put("/update", validate, async (req: any, res: any) => { SET ${setClauses.join(", ")} WHERE name = $${values.length}`; try { - const result = await queryDatabase(req.client, query, values); + const result = await queryDatabase(client, query, values); if (result.rowCount === 0) { return res.status(404).json({ message: "Project not found" }); diff --git a/server.ts b/server.ts index cdcfddb..8a9c9db 100644 --- a/server.ts +++ b/server.ts @@ -3,11 +3,12 @@ import projectsLocal from "./routes/projectsLocal"; import projectsDB from "./routes/projectsDB"; import express from "express"; import checkDB, { secondsToMs } from "./dbChecker"; +import cors from "cors"; -const app = express(); const PORT = 3000; const INTERVAL = secondsToMs(5); const TIMEOUT = secondsToMs(2); +const app = express(); let dbAval: boolean = false; setInterval(async () => { @@ -17,8 +18,10 @@ setInterval(async () => { console.error("Error:", e.message); dbAval = false; } + logger.info(`Database is ${dbAval ? "available" : "not available"}`); }, INTERVAL); +app.use(cors()); app.use((req: any, res: any, next: any) => { logger.info(`Received a ${req.method} request for ${req.url}`); From 0434d7cfdc46531372805191715d9d1317325a3e Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 02:15:38 -0400 Subject: [PATCH 41/57] removed app.use(express.json()) since it caused errors --- routes/projectsDB.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 85e4db3..01ed6f8 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -9,7 +9,7 @@ import getDB from "../db"; const router: Router = Router(); const client: Client = await getDB(); -router.use(express.json()); +// router.use(express.json()); router.use((req: any, res: any, next: any) => { logger.info(`Received ${req.url} request for database projects`); next(); From 5e8f0ec71183a2be0977790910918d5ddc94be90 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 02:19:00 -0400 Subject: [PATCH 42/57] changed db checker interval from 5 sec to 1 hr --- server.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.ts b/server.ts index 8a9c9db..b9cf968 100644 --- a/server.ts +++ b/server.ts @@ -6,10 +6,10 @@ import checkDB, { secondsToMs } from "./dbChecker"; import cors from "cors"; const PORT = 3000; -const INTERVAL = secondsToMs(5); -const TIMEOUT = secondsToMs(2); +const INTERVAL = secondsToMs(60 * 60); // 1 hr +const TIMEOUT = secondsToMs(10); const app = express(); -let dbAval: boolean = false; +let dbAval: boolean = true; setInterval(async () => { try { From 42eb3c7ea972dfd28df4601019fc99d395c8e34a Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 02:40:13 -0400 Subject: [PATCH 43/57] formatting validation middleware --- middlewares/validate.ts | 62 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/middlewares/validate.ts b/middlewares/validate.ts index 580e81e..e0b0ef2 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,45 +1,75 @@ //validate all fields and their types -function validating(keys: string[], values: any[], requiredFields: any, res: any) { +function validating( + keys: string[], + values: any[], + requiredFields: any, + res: any, +) { for (let i = 0; i < values.length; i++) { //initial check if the field is a required field if (!(keys[i] in requiredFields)) { - return res.status(400).json({ message: `Please insert a valid field name, ${keys[i]} is invalid`}); + return res + .status(400) + .json({ + message: `Please insert a valid field name, ${keys[i]} is invalid`, + }); } //check for the correct typing if (typeof values[i] !== requiredFields[keys[i]]) { - return res.status(400).json({ message: `Please insert the correct typing for ${keys[i]}, it should be a ${requiredFields[keys[i]] === "object" ? "array of strings" : requiredFields[keys[i]]}!`}); + return res + .status(400) + .json({ + message: `Please insert the correct typing for ${keys[i]}, it should be a ${requiredFields[keys[i]] === "object" ? "array of strings" : requiredFields[keys[i]]}!`, + }); } } // if no response is sent meaning all validations passed, return false at the end of the function return false; } - -const validate = (req: any, res: any, next: any) => { - const requiredFields = {name: "string", "short-desc": "string", "long-desc": "string", team: "object", link: "string", image: "string", "tech-stack": "object", cohort: "string", topic: "object"}; +const validate = (req: any, res: any, next: any) => { + const requiredFields = { + name: "string", + "short-desc": "string", + "long-desc": "string", + team: "object", + link: "string", + image: "string", + "tech-stack": "object", + cohort: "string", + topic: "object", + }; const values = Object.values(req.body); const keys = Object.keys(req.body).toString().toLowerCase().split(","); if (req.method === "POST") { //initial check for empty request body if (Object.keys(req.body).length === 0) { - return res.status(400).json({ message: "Please insert a object with all required fields!" }); + return res + .status(400) + .json({ + message: "Please insert a object with all required fields!", + }); } if (keys.length !== 9) { - return res.status(400).json({ message: "Please insert all required fields, you are missing some fields!" }); - } - else { + return res + .status(400) + .json({ + message: + "Please insert all required fields, you are missing some fields!", + }); + } else { if (validating(keys, values, requiredFields, res)) { return; } } - } - else { + } else { //initial check for empty request body if (Object.keys(req.body).length === 0) { - return res.status(400).json({ message: "Please insert a object to update!" }); - } - else { + return res + .status(400) + .json({ message: "Please insert a object to update!" }); + } else { if (validating(keys, values, requiredFields, res)) { return; } @@ -48,4 +78,4 @@ const validate = (req: any, res: any, next: any) => { next(); }; -export default validate; \ No newline at end of file +export default validate; From ae4e545f9abe3245385362f1b5d521b49839719d Mon Sep 17 00:00:00 2001 From: Fahad Faruqi <81727522+LordFarquaadtheCreator@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:53:59 -0400 Subject: [PATCH 44/57] Delete app.ts --- app.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app.ts diff --git a/app.ts b/app.ts deleted file mode 100644 index e69de29..0000000 From c0f85a89eb0478bbc516be9c431384a5fbd6c479 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sun, 9 Jun 2024 17:11:04 -0400 Subject: [PATCH 45/57] Removed redundant connectDB.tsx file and updated the typing for validation middleware --- connectDB.ts | 14 -------------- middlewares/validate.ts | 4 ++-- routes/projectsDB.ts | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 connectDB.ts diff --git a/connectDB.ts b/connectDB.ts deleted file mode 100644 index 47c9894..0000000 --- a/connectDB.ts +++ /dev/null @@ -1,14 +0,0 @@ -import getDB from "./db"; -import logger from "./utils/logger"; - -const connectDB = async (req: any, res: any, next: any) => { - try { - req.client = await getDB(); - next(); - } catch (err: any) { - logger.info(err.message); - next("route"); - } -} - -export default connectDB; diff --git a/middlewares/validate.ts b/middlewares/validate.ts index e0b0ef2..19f958e 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,8 +1,8 @@ //validate all fields and their types function validating( keys: string[], - values: any[], - requiredFields: any, + values: (string | number)[], + requiredFields: { [key: string]: string }, res: any, ) { for (let i = 0; i < values.length; i++) { diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 6ed01d2..3892b13 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import connectDB from "../connectDB"; +import connectDB from "../middlewares/connectDB"; import express from "express"; import logger from "../utils/logger"; import { queryDatabase } from "./databaseFunctions"; From f87ed21307c071fe558e7d466948c5429c76cc3b Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 10 Jun 2024 19:18:21 -0400 Subject: [PATCH 46/57] adding http and https to server --- server.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server.ts b/server.ts index b9cf968..1581776 100644 --- a/server.ts +++ b/server.ts @@ -4,6 +4,12 @@ import projectsDB from "./routes/projectsDB"; import express from "express"; import checkDB, { secondsToMs } from "./dbChecker"; import cors from "cors"; +import http from 'http'; +import https from 'https'; + +// const privateKey = fs.readFileSync('sslcert/server.key', 'utf8'); +// const certificate = fs.readFileSync('sslcert/server.crt', 'utf8'); +// const credentials = {key: privateKey, cert: certificate}; const PORT = 3000; const INTERVAL = secondsToMs(60 * 60); // 1 hr @@ -54,4 +60,8 @@ app.listen(PORT, () => { console.log(`listening on port ${PORT}`); }); -export default app; +const httpServer = http.createServer(app); +// const httpsServer = https.createServer(credentials, app); + +httpServer.listen(PORT); +// httpsServer.listen(PORT+1); \ No newline at end of file From 4128c12e3f6ebd35d932680d662a275390f203b3 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 10 Jun 2024 19:50:09 -0400 Subject: [PATCH 47/57] removed duplicate listner --- server.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server.ts b/server.ts index 1581776..74d57c4 100644 --- a/server.ts +++ b/server.ts @@ -56,10 +56,6 @@ app.get("*", (req: any, res: any) => { }); }); -app.listen(PORT, () => { - console.log(`listening on port ${PORT}`); -}); - const httpServer = http.createServer(app); // const httpsServer = https.createServer(credentials, app); From 3971498557834dec107e0c65f3b33a132ec32586 Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 10 Jun 2024 20:01:40 -0400 Subject: [PATCH 48/57] im gonna touch abrar --- routes/projectsDB.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index 01ed6f8..fe8a866 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -7,11 +7,12 @@ import validate from "../middlewares/validate"; import getDB from "../db"; const router: Router = Router(); -const client: Client = await getDB(); +let client: Client; // router.use(express.json()); -router.use((req: any, res: any, next: any) => { +router.use(async (req: any, res: any, next: any) => { logger.info(`Received ${req.url} request for database projects`); + client = await getDB(); next(); }); From feab096c146c48a2a0f62200c55a284acc1a0d95 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 10 Jun 2024 20:09:45 -0400 Subject: [PATCH 49/57] removed the app.listen when we have a httpserver doing that job --- server.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server.ts b/server.ts index 74d57c4..18d681e 100644 --- a/server.ts +++ b/server.ts @@ -56,6 +56,10 @@ app.get("*", (req: any, res: any) => { }); }); +// app.listen(PORT, () => { +// console.log(`listening on port ${PORT}`); +// }); + const httpServer = http.createServer(app); // const httpsServer = https.createServer(credentials, app); From 8aa04c0cbd2ff63cb1e9b9baa76cc49000c1de39 Mon Sep 17 00:00:00 2001 From: Abrar Habib Date: Mon, 10 Jun 2024 20:10:00 -0400 Subject: [PATCH 50/57] moved all routes under a single async function so we can use the client. --- routes/projectsDB.ts | 219 ++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 106 deletions(-) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index fe8a866..c3e4e55 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -7,117 +7,124 @@ import validate from "../middlewares/validate"; import getDB from "../db"; const router: Router = Router(); -let client: Client; -// router.use(express.json()); -router.use(async (req: any, res: any, next: any) => { - logger.info(`Received ${req.url} request for database projects`); - client = await getDB(); - next(); -}); - -router.get("/", async (req: any, res: any) => { - try { - return res.status(200).json({ message: "API is operational." }); - } catch (err: any) { - return res.status(500).json({ message: err.message }); - } -}); - -router.get("/get", async (req: any, res: any) => { - let baseQuery = "SELECT * FROM projects"; - const filters: string[] = []; - const values: (string | number)[] = []; - - // if the name filter was provided - if (req.query.name) { - filters.push(`name ILIKE $${filters.length + 1}`); - values.push(`%${req.query.name}%`); - } - - // if the cohort filter was provided - if (req.query.cohort) { - filters.push(`cohort ILIKE $${filters.length + 1}`); - values.push(`%${req.query.cohort}%`); - } - // if the team filter was provided - if (req.query.team) { - filters.push(`team ILIKE $${filters.length + 1}`); - values.push(`%${req.query.team}%`); - } - - // combine all the filters into a single query - if (filters.length > 0) { - baseQuery += " WHERE " + filters.join(" AND "); - } - - // execute the query, making sure to provide the values for the filters - try { - const data: QueryResult = await queryDatabase( - client, - baseQuery, - values, - ); - return res.status(200).send(data.rows); - } catch { - return res.status(500).json({ message: "Error retrieving data" }); - } -}); - -router.post("/add", validate, (req: any, res: any) => { - const values: Array = Object.values(req.body); - const query = ` - INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - try { - queryDatabase(client, query, values); - return res.status(200).json({ message: "Project added successfully" }); - } catch (err: any) { - return res.status(400).json({ message: err.message }); - } -}); - -router.put("/update", validate, async (req: any, res: any) => { - const projectName = req.query.name; - if (!projectName) { - return res.status(400).json({ message: "Project name is required" }); - } - - const fields = req.body; - if (!fields || Object.keys(fields).length === 0) { - return res - .status(400) - .json({ message: "No fields to update provided" }); - } - - const setClauses: string[] = []; - const values: (string | number)[] = []; - - // Construct the set clauses and values array - Object.keys(fields).forEach((key, index) => { - setClauses.push(`"${key}" = $${index + 1}`); - values.push(fields[key]); +async function startServer() { + const client: Client = await getDB(); + + router.use((req: any, res: any, next: any) => { + logger.info(`Received ${req.url} request for database projects`); + next(); }); - - // Add the project name to the values array for the WHERE clause - values.push(projectName); - - const query = ` - UPDATE projects - SET ${setClauses.join(", ")} - WHERE name = $${values.length}`; - try { - const result = await queryDatabase(client, query, values); - - if (result.rowCount === 0) { - return res.status(404).json({ message: "Project not found" }); + + router.get("/", async (req: any, res: any) => { + try { + return res.status(200).json({ message: "API is operational." }); + } catch (err: any) { + return res.status(500).json({ message: err.message }); + } + }); + + router.get("/get", async (req: any, res: any) => { + let baseQuery = "SELECT * FROM projects"; + const filters: string[] = []; + const values: (string | number)[] = []; + + // if the name filter was provided + if (req.query.name) { + filters.push(`name ILIKE $${filters.length + 1}`); + values.push(`%${req.query.name}%`); + } + + // if the cohort filter was provided + if (req.query.cohort) { + filters.push(`cohort ILIKE $${filters.length + 1}`); + values.push(`%${req.query.cohort}%`); + } + // if the team filter was provided + if (req.query.team) { + filters.push(`team ILIKE $${filters.length + 1}`); + values.push(`%${req.query.team}%`); + } + + // combine all the filters into a single query + if (filters.length > 0) { + baseQuery += " WHERE " + filters.join(" AND "); + } + + // execute the query, making sure to provide the values for the filters + try { + const data: QueryResult = await queryDatabase( + client, + baseQuery, + values, + ); + return res.status(200).send(data.rows); + } catch { + return res.status(500).json({ message: "Error retrieving data" }); } + }); + + router.post("/add", validate, (req: any, res: any) => { + const values: Array = Object.values(req.body); + const query = ` + INSERT INTO projects (name, "short-desc", "long-desc", team, link, image, "tech-stack", cohort, topic) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; + try { + queryDatabase(client, query, values); + return res.status(200).json({ message: "Project added successfully" }); + } catch (err: any) { + return res.status(400).json({ message: err.message }); + } + }); + + router.put("/update", validate, async (req: any, res: any) => { + const projectName = req.query.name; + + if (!projectName) { + return res.status(400).json({ message: "Project name is required" }); + } + + const fields = req.body; + if (!fields || Object.keys(fields).length === 0) { + return res + .status(400) + .json({ message: "No fields to update provided" }); + } + + const setClauses: string[] = []; + const values: (string | number)[] = []; + + // Construct the set clauses and values array + Object.keys(fields).forEach((key, index) => { + setClauses.push(`"${key}" = $${index + 1}`); + values.push(fields[key]); + }); + + // Add the project name to the values array for the WHERE clause + values.push(projectName); + + const query = ` + UPDATE projects + SET ${setClauses.join(", ")} + WHERE name = $${values.length}`; + try { + const result = await queryDatabase(client, query, values); + + if (result.rowCount === 0) { + return res.status(404).json({ message: "Project not found" }); + } + + return res.status(200).send("Project updated successfully"); + } catch (err: any) { + return res.status(400).json({ message: err.message }); + } + }); +} - return res.status(200).send("Project updated successfully"); - } catch (err: any) { - return res.status(400).json({ message: err.message }); - } +startServer().catch((err) => { + logger.error(err); + console.log("Failed to start server"); }); export default router; From 4b7631d995a180bcbf8afb75e51ef1721c37873f Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Mon, 10 Jun 2024 20:24:19 -0400 Subject: [PATCH 51/57] switched express.json() to projectsDB so no issues are caused in server.ts --- routes/projectsDB.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/routes/projectsDB.ts b/routes/projectsDB.ts index c3e4e55..adf48aa 100644 --- a/routes/projectsDB.ts +++ b/routes/projectsDB.ts @@ -8,6 +8,7 @@ import getDB from "../db"; const router: Router = Router(); +router.use(express.json()); async function startServer() { const client: Client = await getDB(); From 366c1615177aa747b0a611c968c8acdfe38087bd Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Mon, 10 Jun 2024 20:53:34 -0400 Subject: [PATCH 52/57] misc --- server.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server.ts b/server.ts index 18d681e..55d6990 100644 --- a/server.ts +++ b/server.ts @@ -13,7 +13,7 @@ import https from 'https'; const PORT = 3000; const INTERVAL = secondsToMs(60 * 60); // 1 hr -const TIMEOUT = secondsToMs(10); +const TIMEOUT = secondsToMs(30); const app = express(); let dbAval: boolean = true; @@ -30,7 +30,6 @@ setInterval(async () => { app.use(cors()); app.use((req: any, res: any, next: any) => { logger.info(`Received a ${req.method} request for ${req.url}`); - next(); }); From be99702c97958df38b6d30721354b98a107e437a Mon Sep 17 00:00:00 2001 From: Fahad Faruqi Date: Sun, 9 Jun 2024 02:40:13 -0400 Subject: [PATCH 53/57] formatting validation middleware --- middlewares/validate.ts | 62 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/middlewares/validate.ts b/middlewares/validate.ts index 580e81e..e0b0ef2 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,45 +1,75 @@ //validate all fields and their types -function validating(keys: string[], values: any[], requiredFields: any, res: any) { +function validating( + keys: string[], + values: any[], + requiredFields: any, + res: any, +) { for (let i = 0; i < values.length; i++) { //initial check if the field is a required field if (!(keys[i] in requiredFields)) { - return res.status(400).json({ message: `Please insert a valid field name, ${keys[i]} is invalid`}); + return res + .status(400) + .json({ + message: `Please insert a valid field name, ${keys[i]} is invalid`, + }); } //check for the correct typing if (typeof values[i] !== requiredFields[keys[i]]) { - return res.status(400).json({ message: `Please insert the correct typing for ${keys[i]}, it should be a ${requiredFields[keys[i]] === "object" ? "array of strings" : requiredFields[keys[i]]}!`}); + return res + .status(400) + .json({ + message: `Please insert the correct typing for ${keys[i]}, it should be a ${requiredFields[keys[i]] === "object" ? "array of strings" : requiredFields[keys[i]]}!`, + }); } } // if no response is sent meaning all validations passed, return false at the end of the function return false; } - -const validate = (req: any, res: any, next: any) => { - const requiredFields = {name: "string", "short-desc": "string", "long-desc": "string", team: "object", link: "string", image: "string", "tech-stack": "object", cohort: "string", topic: "object"}; +const validate = (req: any, res: any, next: any) => { + const requiredFields = { + name: "string", + "short-desc": "string", + "long-desc": "string", + team: "object", + link: "string", + image: "string", + "tech-stack": "object", + cohort: "string", + topic: "object", + }; const values = Object.values(req.body); const keys = Object.keys(req.body).toString().toLowerCase().split(","); if (req.method === "POST") { //initial check for empty request body if (Object.keys(req.body).length === 0) { - return res.status(400).json({ message: "Please insert a object with all required fields!" }); + return res + .status(400) + .json({ + message: "Please insert a object with all required fields!", + }); } if (keys.length !== 9) { - return res.status(400).json({ message: "Please insert all required fields, you are missing some fields!" }); - } - else { + return res + .status(400) + .json({ + message: + "Please insert all required fields, you are missing some fields!", + }); + } else { if (validating(keys, values, requiredFields, res)) { return; } } - } - else { + } else { //initial check for empty request body if (Object.keys(req.body).length === 0) { - return res.status(400).json({ message: "Please insert a object to update!" }); - } - else { + return res + .status(400) + .json({ message: "Please insert a object to update!" }); + } else { if (validating(keys, values, requiredFields, res)) { return; } @@ -48,4 +78,4 @@ const validate = (req: any, res: any, next: any) => { next(); }; -export default validate; \ No newline at end of file +export default validate; From e064eedb55fc6198b029c12bee1883f6f86274e5 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sun, 9 Jun 2024 17:11:04 -0400 Subject: [PATCH 54/57] Removed redundant connectDB.tsx file and updated the typing for validation middleware --- connectDB.ts | 14 -------------- middlewares/validate.ts | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 connectDB.ts diff --git a/connectDB.ts b/connectDB.ts deleted file mode 100644 index 47c9894..0000000 --- a/connectDB.ts +++ /dev/null @@ -1,14 +0,0 @@ -import getDB from "./db"; -import logger from "./utils/logger"; - -const connectDB = async (req: any, res: any, next: any) => { - try { - req.client = await getDB(); - next(); - } catch (err: any) { - logger.info(err.message); - next("route"); - } -} - -export default connectDB; diff --git a/middlewares/validate.ts b/middlewares/validate.ts index e0b0ef2..19f958e 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,8 +1,8 @@ //validate all fields and their types function validating( keys: string[], - values: any[], - requiredFields: any, + values: (string | number)[], + requiredFields: { [key: string]: string }, res: any, ) { for (let i = 0; i < values.length; i++) { From 39d73ac20819e97c7a5025d2d684b6d27ff2a046 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 11 Jun 2024 13:48:42 -0400 Subject: [PATCH 55/57] Updated keys from body to be case sensitive and updated typing of values due to TypeError --- middlewares/validate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middlewares/validate.ts b/middlewares/validate.ts index 19f958e..646e52a 100644 --- a/middlewares/validate.ts +++ b/middlewares/validate.ts @@ -1,7 +1,7 @@ //validate all fields and their types function validating( keys: string[], - values: (string | number)[], + values: any, requiredFields: { [key: string]: string }, res: any, ) { @@ -40,7 +40,7 @@ const validate = (req: any, res: any, next: any) => { topic: "object", }; const values = Object.values(req.body); - const keys = Object.keys(req.body).toString().toLowerCase().split(","); + const keys = Object.keys(req.body); if (req.method === "POST") { //initial check for empty request body From 02c5a4ece7d09a8c2c70897996a6262500543a2c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 11 Jun 2024 14:24:08 -0400 Subject: [PATCH 56/57] Added UUID to each project --- data.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/data.json b/data.json index 653e16c..026a8e4 100644 --- a/data.json +++ b/data.json @@ -18,7 +18,8 @@ "topic": [ "AI", "ML" - ] + ], + "uuid": "80eb8a48-9172-4f6f-87ca-4ae89de1938c" }, { "name": "Stockbros", @@ -40,7 +41,8 @@ "topic": [ "Data Science", "Finance" - ] + ], + "uuid": "5dca7d6e-5131-434f-9eb9-5670a820a260" }, { "name": "TrackLeet", @@ -65,7 +67,8 @@ "Chrome Extension", "Web Development", "Authentication" - ] + ], + "uuid": "e00bfd77-58a4-4d2f-a56e-6fc5b8280454" }, { "name": "Don't Be Alarmed", @@ -87,7 +90,8 @@ "cohort": "Spring 2024", "topic": [ "Android App Development" - ] + ], + "uuid": "9fe8f025-d0a4-4a88-abb5-184da85db827" }, { "name": "BYTE Website", @@ -111,7 +115,8 @@ "cohort": "Summer 2024", "topic": [ "Web Development" - ] + ], + "uuid": "d78df4b2-4ad6-4c0e-82b1-31473c4ef9f2" }, { "name": "BYTE Server", @@ -140,6 +145,7 @@ "Server Development", "API", "Postgres" - ] + ], + "uuid": "52a901bf-685a-4320-9336-5ce7785e4f18" } ] \ No newline at end of file From ff79c0421e7ee0a0a6a1e6a8bad839ea6c657ca8 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 11 Jun 2024 14:43:59 -0400 Subject: [PATCH 57/57] Changed timeout and interval back to its original values --- server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.ts b/server.ts index 55d6990..42cf0b9 100644 --- a/server.ts +++ b/server.ts @@ -13,7 +13,7 @@ import https from 'https'; const PORT = 3000; const INTERVAL = secondsToMs(60 * 60); // 1 hr -const TIMEOUT = secondsToMs(30); +const TIMEOUT = secondsToMs(10); const app = express(); let dbAval: boolean = true;