From c81b31653036c36e36c45a6c3fb002e4b0bffde9 Mon Sep 17 00:00:00 2001 From: Calvin Lu Date: Mon, 1 Jan 2024 21:43:54 -0800 Subject: [PATCH 1/4] Basic info route setup --- src/api/index.ts | 9 +++++++++ src/api/routes/info.ts | 13 +++++++++++++ src/app.ts | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 src/api/index.ts create mode 100644 src/api/routes/info.ts diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..55f3c2e --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,9 @@ +import express from "express" + +import infoRouter from "./routes/info"; + +const router = express.Router(); + +router.use("/info", ) + +export default router; \ No newline at end of file diff --git a/src/api/routes/info.ts b/src/api/routes/info.ts new file mode 100644 index 0000000..68e78cf --- /dev/null +++ b/src/api/routes/info.ts @@ -0,0 +1,13 @@ +import express from "express"; + +const router = express.Router(); + +router.get("/users", (req, res) => { + +}); + +router.get("/users/:id", (req, res) => { + // req.params.id +}); + +export default router; \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 0c4240d..9994b98 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,9 +3,12 @@ require("dotenv").config() import express from "express"; import logger from "./utils/logger"; +import api from "./api"; const app = express(); +app.use("/api/v1", api); + app.listen(process.env.PORT, () => { logger.info(`Listening on port ${process.env.PORT}!`); }) \ No newline at end of file From 46ea9a49f4403793f63691264eec5fc8f6dd2cef Mon Sep 17 00:00:00 2001 From: Calvin Lu Date: Thu, 4 Jan 2024 22:34:48 -0800 Subject: [PATCH 2/4] Create layout for info.ts --- src/api/routes/info.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/api/routes/info.ts b/src/api/routes/info.ts index 68e78cf..1dd2135 100644 --- a/src/api/routes/info.ts +++ b/src/api/routes/info.ts @@ -2,12 +2,18 @@ import express from "express"; const router = express.Router(); +// Return all users router.get("/users", (req, res) => { - + // Return user_id, first_name, last_name, signed_in, last_signed_in, total_time for all users }); +// Return user given valid ID router.get("/users/:id", (req, res) => { // req.params.id + + // Is user ID valid? + + // Return user_id, first_name, last_name, signed_in, last_signed_in, total_time of given user }); export default router; \ No newline at end of file From e7c7a899a1f4f13df54a0d36f611e92fa4133d41 Mon Sep 17 00:00:00 2001 From: Calvin Lu Date: Thu, 4 Jan 2024 22:35:11 -0800 Subject: [PATCH 3/4] Add info router to api --- src/api/index.ts | 2 +- src/app.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index 55f3c2e..37da112 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -4,6 +4,6 @@ import infoRouter from "./routes/info"; const router = express.Router(); -router.use("/info", ) +router.use("/info", infoRouter); export default router; \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 9994b98..5adec14 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,12 @@ -require("dotenv").config() +import dotenv from "dotenv"; +dotenv.config(); import express from "express"; import logger from "./utils/logger"; import api from "./api"; + const app = express(); app.use("/api/v1", api); From 47240c07d5b19c71157b1ebd5974884713ff97cb Mon Sep 17 00:00:00 2001 From: Calvin Lu Date: Wed, 10 Jan 2024 14:37:44 -0800 Subject: [PATCH 4/4] Complete both info routes --- package.json | 2 +- src/api/routes/info.ts | 48 ++++++++++++++++++++++++++++++------- src/models/Config.model.ts | 2 +- src/models/Session.model.ts | 2 ++ src/models/User.model.ts | 30 ++++++++++++++++------- src/utils/errors.ts | 4 ++++ src/utils/logger.ts | 8 +++---- 7 files changed, 74 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index e015ad0..d6ad260 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "rimraf": "./node_modules/rimraf/bin.js", "start": "nodemon app.ts", "build": "rimraf ./dist/ && tsc --project basic.tsconfig.json", - "test": "rimraf ./test/ && tsc --project test.tsconfig.json && cross-env NODE_ENV=testing mocha ./test/tests/**/*.spec.js --exit --timeout 10000" + "test": "rimraf ./test/ && tsc --project test.tsconfig.json && cross-env NODE_ENV=testing mocha ./test/tests/**/*.spec.js ./test/e2e/**/*.spec.js --exit --timeout 10000" }, "repository": { "type": "git", diff --git a/src/api/routes/info.ts b/src/api/routes/info.ts index 1dd2135..8736b8e 100644 --- a/src/api/routes/info.ts +++ b/src/api/routes/info.ts @@ -1,19 +1,51 @@ import express from "express"; +import { getAllUsers, getUserById } from "../../models/User.model"; +import { RowNotFoundError } from "../../utils/errors"; + const router = express.Router(); -// Return all users -router.get("/users", (req, res) => { - // Return user_id, first_name, last_name, signed_in, last_signed_in, total_time for all users -}); +// Returns user_id, first_name, last_name, signed_in, last_signed_in, total_time for all users +router.get("/users", async (req, res) => { + const users = await getAllUsers(); + users.map((user: any) => {delete user.password}); // Remove password field -// Return user given valid ID -router.get("/users/:id", (req, res) => { - // req.params.id + return res.status(200).json({ + description: "Returning all users", + users: users, + }) +}); +// Return user_id, first_name, last_name, signed_in, last_signed_in, total_time of user with given valid ID +router.get("/users/:id", async (req, res) => { // Is user ID valid? + if (isNaN(Number(req.params.id))) { + return res.status(404).json({ + description: `User of id: ${req.params.id} not found`, + }); + } + + let user; + + try { + user = await getUserById(Number(req.params.id)); + } catch (err) { + if (err instanceof RowNotFoundError) { + return res.status(404).json({ + description: err.message, + }); + } + + return res.sendStatus(500); + } + - // Return user_id, first_name, last_name, signed_in, last_signed_in, total_time of given user + delete (user as any).password; // Remove password field + + return res.status(200).json({ + description: "User found", + user: user, + }); }); export default router; \ No newline at end of file diff --git a/src/models/Config.model.ts b/src/models/Config.model.ts index 8ab202c..e125dcd 100644 --- a/src/models/Config.model.ts +++ b/src/models/Config.model.ts @@ -1,7 +1,7 @@ -import { config } from "dotenv"; import { ResultSetHeader, RowDataPacket } from "mysql2"; import database from "../database"; +import { RowNotFoundError } from "../utils/errors"; interface Config extends RowDataPacket { name: string; diff --git a/src/models/Session.model.ts b/src/models/Session.model.ts index b96af1a..1e4b913 100644 --- a/src/models/Session.model.ts +++ b/src/models/Session.model.ts @@ -1,5 +1,7 @@ import { ResultSetHeader, RowDataPacket } from "mysql2"; + import database from "../database"; +import { RowNotFoundError } from "../utils/errors"; interface Session extends RowDataPacket { session_id: number; diff --git a/src/models/User.model.ts b/src/models/User.model.ts index f04f2be..f6cb39b 100644 --- a/src/models/User.model.ts +++ b/src/models/User.model.ts @@ -1,11 +1,13 @@ import { ResultSetHeader, RowDataPacket } from "mysql2"; + import database from "../database"; +import { RowNotFoundError } from "../utils/errors"; interface User extends RowDataPacket { user_id: number; first_name: string; last_name: string; - password: number; + password: string; signed_in: boolean; last_signed_in: bigint; total_time: bigint; @@ -18,12 +20,23 @@ async function getAllUsers(): Promise { return users; } -async function getUserByPassword(password: number): Promise { +async function getUserById(userId: number): Promise { + const sql = "SELECT * FROM `users` WHERE user_id = ?"; + const [users] = await database.query(sql, [userId]); + + if (users.length < 1) { + throw new RowNotFoundError(`User of id: ${userId} not found in table: users`); + } + + return users[0]; +} + +async function getUserByPassword(password: string): Promise { const sql = "SELECT * FROM `users` WHERE password = ?"; const [users] = await database.query(sql, [password]); if (users.length < 1) { - throw new RowNotFoundError("User not found in table: users"); + throw new RowNotFoundError(`User of password: ${password} not found in table: users`); } return users[0]; @@ -32,7 +45,7 @@ async function getUserByPassword(password: number): Promise { async function createUser( first_name: string, last_name: string, - password: number + password: string ): Promise { const sql = "INSERT INTO `users` (first_name, last_name, password) VALUES (?, ?, ?); SELECT user_id, first_name, last_name FROM users where password = ?"; @@ -46,7 +59,7 @@ async function createUser( return users[0]; } -async function updateUserTotalTime(password: number): Promise { +async function updateUserTotalTime(password: string): Promise { const sql = "UPDATE users SET total_time = (SELECT SUM(CASE WHEN end_time - start_time < 43200000 THEN end_time - start_time ELSE 0 END) FROM sessions WHERE user_id = (SELECT user_id FROM users WHERE password = ?)); SELECT user_id, first_name, last_name from users where password = ?"; const [resHeader] = await database.query(sql, [ @@ -58,7 +71,7 @@ async function updateUserTotalTime(password: number): Promise { } async function updateUserLastSignedIn( - password: number, + password: string, newValue: bigint ): Promise { const sql = @@ -72,7 +85,7 @@ async function updateUserLastSignedIn( return resHeader.affectedRows == 1; } -async function deleteUser(password: number): Promise { +async function deleteUserByPassword(password: string): Promise { const sql = "DELETE FROM `users` WHERE password = ?"; const [resHeader] = await database.query(sql, [password]); @@ -82,9 +95,10 @@ async function deleteUser(password: number): Promise { export default User; export { getAllUsers, + getUserById, getUserByPassword, createUser, updateUserTotalTime, updateUserLastSignedIn, - deleteUser, + deleteUserByPassword, }; diff --git a/src/utils/errors.ts b/src/utils/errors.ts index c334f96..b60de5a 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -3,4 +3,8 @@ class RowNotFoundError extends Error { super(msg); Object.setPrototypeOf(this, RowNotFoundError.prototype); } +} + +export { + RowNotFoundError, } \ No newline at end of file diff --git a/src/utils/logger.ts b/src/utils/logger.ts index c2d1581..5d3c160 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -23,17 +23,17 @@ function newLogFileName(type: string): string { pathSegments.push("logs"); if (process.env.NODE_ENV === "testing") { pathSegments.push("tests") } pathSegments.push(type); - pathSegments.push(type + "_" + dateToYYYYMMDD()); + pathSegments.push(type + "_" + dateToTimestamp()); - return pathSegments.join("/"); + return pathSegments.join("/") + ".log"; } -function dateToYYYYMMDD(date?: Date): string { +function dateToTimestamp(date?: Date): string { if (!date) { date = new Date(); } - return date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate(); + return date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate() + "-" + date.getTime(); } export default logger; \ No newline at end of file