diff --git a/index.ts b/index.ts index d2795fc..94dd7c4 100644 --- a/index.ts +++ b/index.ts @@ -13,7 +13,8 @@ import swaggerUI from "swagger-ui-express"; import adminRoutes from "./src/routes/adminRoutes"; import userRoutes from "./src/routes/userRoutes"; -import eventRoutes from "./src/routes/eventRoutes"; +import usersRoutes from "./src/routes/usersRoutes"; +import eventsRoutes from "./src/routes/eventsRoutes"; const options = { definition: { @@ -43,8 +44,9 @@ const options = { app.use(bodyParser.json()); app.use("/api/v1/user", userRoutes); +app.use("/api/v1/users", usersRoutes); app.use("/api/v1/admin", adminRoutes); -app.use("/api/v1/events", eventRoutes); +app.use("/api/v1/events", eventsRoutes); const specs = swaggerJsDoc(options); app.use("/api-docs", swaggerUI.serve, swaggerUI.setup(specs)); diff --git a/src/controllers/adminController.ts b/src/controllers/adminController.ts index 0ba6044..283da30 100644 --- a/src/controllers/adminController.ts +++ b/src/controllers/adminController.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import { User } from "../models/user"; import { ADMIN_MAX_USERS_PER_PAGE, - MONGODB_IGNORED_FIELDS, + MONGODB_IGNORED_FIELDS_USER, } from "../utils/constants"; export async function GetAllUsers(req: Request, res: Response) { @@ -24,7 +24,7 @@ export async function GetAllUsers(req: Request, res: Response) { // Query for the current page with limit and skip const users = await User.find({}) .lean() - .select(MONGODB_IGNORED_FIELDS) // Exclude password and other fields + .select(MONGODB_IGNORED_FIELDS_USER) .skip(skip) .limit(pageSize); diff --git a/src/controllers/eventController.ts b/src/controllers/eventController.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/controllers/eventsController.ts b/src/controllers/eventsController.ts new file mode 100644 index 0000000..b97ca14 --- /dev/null +++ b/src/controllers/eventsController.ts @@ -0,0 +1,43 @@ +import { Request, Response } from "express"; +import { Event } from "../models/event"; +import { MONGODB_IGNORED_FIELDS } from "../utils/constants"; + +export async function CreateEvent(req: Request, res: Response) { + try { + const { name, type, latitude, longitude } = req.body; + + if (!name || type === null || !latitude || !longitude) { + console.log("missing fields"); + return res.status(400).json({ status: "err" }); + } + + const event = new Event({ + name: name, + type: type, + latitude: latitude, + longitude: longitude, + }); + + await event + .save() + .then(() => res.status(200).json({ status: "ok" })) + .catch((err) => { + console.log(err); + res.status(500).json({ status: "err" }); + }); + } catch (error) { + console.error("error on create event:", error); + res.status(500).json({ status: "err" }); + } +} + +export async function GetEvents(req: Request, res: Response) { + try { + const events = await Event.find().select(MONGODB_IGNORED_FIELDS).lean(); + + res.status(200).json({ events }); + } catch (error) { + console.error("error on get events:", error); + res.status(500).json({ status: "err" }); + } +} diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index b21ff40..592d2ad 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -5,46 +5,52 @@ import { hashPassword, decodeBase64, matchPassword, + getUserSession, } from "../utils/utils"; -import { MONGODB_IGNORED_FIELDS } from "../utils/constants"; import { isUsernameValid, isPasswordValid } from "../validation/validation"; import { Session } from "../models/session"; +import { MONGODB_IGNORED_FIELDS } from "../utils/constants"; export async function SignUp(req: Request, res: Response) { - const { accountName, username, password } = req.body; + try { + const { accountName, username, password } = req.body; - if (!accountName || !username || !password) { - return res.status(400).json({ status: "err" }); - } + if (!accountName || !username || !password) { + return res.status(400).json({ status: "err" }); + } - const existingUser = await User.findOne({ accountName }) - .select("accountName -_id") - .lean(); + const existingUser = await User.findOne({ accountName }) + .select(`accountName ${MONGODB_IGNORED_FIELDS}`) + .lean(); - if (existingUser) { - return res.status(400).json({ status: 1 }); - } + if (existingUser) { + return res.status(400).json({ status: 1 }); + } - if (!isPasswordValid(password)) { - return res.status(400).json({ status: "err" }); - } + if (!isPasswordValid(password)) { + return res.status(400).json({ status: "err" }); + } - const decodedPassword = decodeBase64(password); - const hashedPassword = await hashPassword(decodedPassword); + const decodedPassword = decodeBase64(password); + const hashedPassword = await hashPassword(decodedPassword); - const user = new User({ - accountName: accountName, - username: username, - password: hashedPassword, - }); - - user - .save() - .then(() => saveSession(res, accountName)) - .catch((err) => { - console.log(err); - res.status(500).json({ status: "err" }); + const user = new User({ + accountName: accountName, + username: username, + password: hashedPassword, }); + + user + .save() + .then(() => saveSession(res, accountName)) + .catch((err) => { + console.log(err); + res.status(500).json({ status: "err" }); + }); + } catch (error) { + console.error("error on signup:", error); + res.status(500).json({ status: "err" }); + } } export async function Login(req: Request, res: Response) { @@ -55,7 +61,9 @@ export async function Login(req: Request, res: Response) { const { accountName, password } = req.body; - const user = await User.findOne({ accountName }).lean(); + const user = await User.findOne({ accountName }) + .select(MONGODB_IGNORED_FIELDS) + .lean(); if (!user) { return res.status(401).json({ status: "err" }); @@ -84,71 +92,65 @@ export async function Login(req: Request, res: Response) { } } -export async function GetUserProfile(req: Request, res: Response) { +export async function ChangeUsername(req: Request, res: Response) { try { - const user = await User.findOne({ - accountName: req.params.accountName, - }) - .select(MONGODB_IGNORED_FIELDS) - .lean(); + const { newUsername } = req.body; - if (!user) { - return res.status(404).json({ status: "err" }); + if (!newUsername || !isUsernameValid(newUsername)) { + return res.status(400).json({ status: "err" }); } - res.json({ - accountName: user.accountName, - username: user.username, - }); - } catch (error) { - console.error("error on get user profile:", error); - res.status(500).json({ status: "err" }); - } -} + const session = await getUserSession(req, "accountName"); -export async function ChangeUsername(req: Request, res: Response) { - const { accountName, newUsername } = req.body; + if (!session) { + return res.status(401).json({ status: "err" }); + } - if (!accountName || !newUsername) { - return res.status(400).json({ status: "err" }); - } - - if (!isUsernameValid(newUsername)) { - return res.status(400).json({ status: "err" }); - } - - try { await User.updateOne( - { accountName: accountName }, + { accountName: session.accountName }, { $set: { username: newUsername } } ); res.status(200).json({ status: "ok" }); } catch (error) { - console.error(error); + console.error("error on changing username", error); res.status(500).json({ status: "err" }); } } export async function ChangePassword(req: Request, res: Response) { - const { accountName, currentPassword, newPassword } = req.body; - - if (!accountName || !currentPassword || !newPassword) { - return res.status(400).json({ status: "err" }); - } - try { - const decodedCurrentPassword = decodeBase64(currentPassword); + // cp = current password + // np = new password + const { cp, np } = req.body; - const existingUser = await User.findOne({ accountName }) - .select("password -_id") + if (!cp || !np) { + return res.status(400).json({ status: "err" }); + } + + console.log(cp, np); + + const session = await getUserSession(req, "accountName"); + + if (!session) { + return res.status(401).json({ status: "err" }); + } + + const accountName = session.accountName; + + const decodedCurrentPassword = decodeBase64(cp); + + const existingUser = await User.findOne({ + accountName: accountName, + }) + .select(`password -_id`) .lean(); if (!existingUser) { return res.status(400).json({ status: 1 }); } - const decodedNewPassword = decodeBase64(newPassword); + const decodedNewPassword = decodeBase64(np); if (!isPasswordValid(decodedCurrentPassword)) { return res.status(400).json({ status: "err" }); @@ -173,7 +175,7 @@ export async function ChangePassword(req: Request, res: Response) { // delete all sessions await Session.deleteMany({ accountName: accountName }); } catch (error) { - console.error(error); + console.error("error on changing password", error); res.status(500).json({ status: "err" }); } } diff --git a/src/controllers/usersController.ts b/src/controllers/usersController.ts new file mode 100644 index 0000000..df94be6 --- /dev/null +++ b/src/controllers/usersController.ts @@ -0,0 +1,22 @@ +import { Request, Response } from "express"; +import { User } from "../models/user"; +import { MONGODB_IGNORED_FIELDS_USER } from "../utils/constants"; + +export async function GetUserProfile(req: Request, res: Response) { + try { + const user = await User.findOne({ + accountName: req.params.accountName, + }) + .select(`-accountName ${MONGODB_IGNORED_FIELDS_USER}`) + .lean(); + + if (!user) { + return res.status(404).json({ status: "err" }); + } + + res.json(user); + } catch (error) { + console.error("error on get user profile:", error); + res.status(500).json({ status: "err" }); + } +} diff --git a/src/middleware/authMiddleware.ts b/src/middleware/authMiddleware.ts index bc04dae..0f2d80f 100644 --- a/src/middleware/authMiddleware.ts +++ b/src/middleware/authMiddleware.ts @@ -2,17 +2,10 @@ import { Request } from "express"; import { Session } from "../models/session"; import { User } from "../models/user"; import { HEADER_X_AUTHORIZATION } from "../utils/constants"; +import { getUserSession } from "../utils/utils"; export async function sessionProtection(req: Request, res: any, next: any) { - const xAuthorization = req.get(HEADER_X_AUTHORIZATION); - - if (!xAuthorization) { - return res.status(401).json({ status: "err" }); - } - - const session = await Session.findOne({ sessionId: xAuthorization }) - .select("sessionId -_id") - .lean(); + const session = await getUserSession(req); if (!session) { return res.status(401).json({ status: "err" }); diff --git a/src/models/event.ts b/src/models/event.ts new file mode 100644 index 0000000..6aa956e --- /dev/null +++ b/src/models/event.ts @@ -0,0 +1,15 @@ +import mongoose, { InferSchemaType, Schema } from "mongoose"; + +export const eventSchema = new Schema({ + name: String, + type: Number, + description: String, + latitude: Number, + longitude: Number, + date: Date, + creator: String, +}); + +export type Event = InferSchemaType; + +export const Event = mongoose.model("Event", eventSchema); diff --git a/src/routes/eventRoutes.ts b/src/routes/eventRoutes.ts deleted file mode 100644 index 11d4ee8..0000000 --- a/src/routes/eventRoutes.ts +++ /dev/null @@ -1,4 +0,0 @@ -import express from "express"; -const router = express.Router(); - -export default router; diff --git a/src/routes/eventsRoutes.ts b/src/routes/eventsRoutes.ts new file mode 100644 index 0000000..979a1d3 --- /dev/null +++ b/src/routes/eventsRoutes.ts @@ -0,0 +1,9 @@ +import express from "express"; +import { sessionProtection } from "../middleware/authMiddleware"; +const router = express.Router(); +import * as eventsController from "../controllers/eventsController"; + +router.post("/", sessionProtection, eventsController.CreateEvent); +router.get("/", sessionProtection, eventsController.GetEvents); + +export default router; diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts index aec0db8..21f58c0 100644 --- a/src/routes/userRoutes.ts +++ b/src/routes/userRoutes.ts @@ -1,4 +1,4 @@ -import express, { Router } from "express"; +import express from "express"; const router = express.Router(); import * as userController from "../controllers/userController"; import { sessionProtection } from "../middleware/authMiddleware"; @@ -127,12 +127,6 @@ router.post("/signup", userController.SignUp); */ router.post("/login", userController.Login); -router.get( - "/profile/:accountName", - sessionProtection, - userController.GetUserProfile -); - router.post("/username", sessionProtection, userController.ChangeUsername); router.post("/password", sessionProtection, userController.ChangePassword); diff --git a/src/routes/usersRoutes.ts b/src/routes/usersRoutes.ts new file mode 100644 index 0000000..449399a --- /dev/null +++ b/src/routes/usersRoutes.ts @@ -0,0 +1,8 @@ +import express from "express"; +const router = express.Router(); +import * as usersController from "../controllers/usersController"; +import { sessionProtection } from "../middleware/authMiddleware"; + +router.get("/:accountName", sessionProtection, usersController.GetUserProfile); + +export default router; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 87f2f37..175c8ca 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -5,7 +5,9 @@ export const DEFAULT_SESSION_EXPIRATION: number = 7 * 24 * 60 * 60 * 1000; export const ADMIN_MAX_USERS_PER_PAGE: number = 10; // Fields to ignore when querying MongoDB -export const MONGODB_IGNORED_FIELDS: string = "-password -_id -__v"; +export const MONGODB_IGNORED_FIELDS: string = "-_id -__v"; +export const MONGODB_IGNORED_FIELDS_USER: string = + MONGODB_IGNORED_FIELDS + " -password -isAdmin"; // Header name for the session ID export const HEADER_X_AUTHORIZATION: string = "x-authorization"; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 08aea41..55bafbe 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,7 +1,8 @@ import crypto from "crypto"; import { Session } from "../models/session"; -import { Response } from "express"; +import { Request, Response } from "express"; import bcrypt from "bcrypt"; +import { HEADER_X_AUTHORIZATION } from "./constants"; export async function saveSession(res: Response, accountName: string) { try { @@ -36,3 +37,21 @@ export async function hashPassword(password: string) { export function decodeBase64(value: string) { return Buffer.from(value, "base64").toString("utf-8"); } + +export async function getUserSession(req: Request, select?: string) { + const sessionId = req.get(HEADER_X_AUTHORIZATION); + + if (!sessionId) { + return null; + } + + const session = await Session.findOne({ sessionId }) + .select(`sessionId -_id ${select}`) + .lean(); + + if (!session) { + return null; + } + + return session; +}