import authHandler from "../middleware/AuthHandler.js"; import users from "../models/users.js"; import express from "express"; const router = express.Router(); /** @typedef User * @property {number} id - The unique identifier for the user. * @property {string} username - The username of the user. * @property {string} password - The hashed password of the user. */ /** @typedef {import("@types/express").Request} Request */ /** @typedef {import("@types/express").Response} Response */ /** @typedef {import("@types/express").NextFunction} NextFunction */ router.get("/", /** * Fetches all users from the database. * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise} */ async (req, res, next) => { await users.getAllUsers() .then((users) => { res.status(200).json({data: users}); }) .catch((err) => next(err)); } ) router.get("/", /** * adds new user to db as non-admin. only direct db manipulation can make users admins * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise> | void>} */ async (req, res, next) => { if (!req.body || !req.body.username || !req.body.password) { return res.status(400).json({data: null, message: 'Username and password are required'}); } await users.addNewUser(req.body.username, req.body.password) .then((user) => { if (!user) { return res.status(500).json({data: null, message: 'User not found after insertion'}); } res.status(200).json({data: user, message: 'User added successfully'}); }) .catch((err) => next(err)); }); router.get("/:identifier", /** * Fetches a user by ID or username from the database. * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise> | void>} */ async (req, res, next) => { const userIdentifier = req.params.identifier; if (!userIdentifier) return res.status(400).json({data: null, message: 'User identifier is required'}); await users.getUser(userIdentifier) .then(user => { if (!user) return res.status(404).json({data: null, message: 'User not found'}); res.status(200).json({data: user}); }) .catch((err) => { if (err instanceof Error) { if (err.message === 'User not found') { return res.status(404).json({data: null, error: err}); } if (err.message === 'Multiple users found with the same identifier, something has gone wrong') { res.status(500).json({ data: null, error: err }); } else next(err); } else { next(new Error('An unhandled error occurred while fetching the user')); } }) } ) router.post("/login", /** * Fetches a user by ID or username from the database. * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise> | void>} */ async (req, res, next) => { const {username, password} = req.body; if (!username || !password) return res.status(400).json({ data: null, message: 'Username and password are required' }); await users.login(username, password).then(data => { if (!data) return res.status(401).json({data: null, message: 'Invalid username or password'}); res.status(200).json({data: data, message: 'Login successful'}); }) .catch((err) => { if (err instanceof Error) { if (err.message === 'Invalid username or password') { res.status(401).json({data: null, error: err}); } else next(err); } else { next(new Error('An unhandled error occurred while login')); } }) }) router.patch("/:identifier", authHandler.authenticateUser, /** * Updates a user by ID or username in the database. * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise> | void>} */ async (req, res, next) => { const userIdentifier = req.params.id; if (!userIdentifier) return res.status(400).json({data: null, message: 'User Identifier is required'}); await users.updateUser(req.body) .then(user => { if (!user) res.status(404).json({data: null, message: 'User not found'}); else res.status(200).json({data: user, message: 'User updated successfully'}); }) .catch((err) => next(err)) } ) router.delete("/:identifier", authHandler.authenticateUser, /** * deletes a user by ID or username from the database. * @param {Request} req * @param {Response} res * @param {NextFunction} next * @return {Promise> | void>} */ async (req, res, next) => { const userIdentifier = req.params.identifier; if (!userIdentifier) return res.status(400).json({data: null, message: 'User identifier is required'}); await users.deleteUser(userIdentifier) .then(user => { if (!user) return res.status(404).json({data: null, message: 'User not found'}); res.status(200).json({data: user, message: 'User deleted successfully'}); }) .catch((err) => next(err)); } ) export default router