diff --git a/server/index.js b/server/index.js index f721054..02ea749 100644 --- a/server/index.js +++ b/server/index.js @@ -6,6 +6,8 @@ const userRoutes = require("./src/users/routes.js"); const auth_userRoutes = require("./src/users/authroutes.js"); const bookmarkRoutes = require("./src/bookmarks/routes.js"); const auth_bookmarkRoutes = require("./src/bookmarks/authroutes.js") +const profileRoutes = require("./src/profiles/routes.js"); +const auth_profileRoutes = require("./src/profiles/authroutes.js") const cors = require("cors"); require("dotenv").config(); @@ -34,6 +36,9 @@ app.use("/api/courses", courseRoutes); app.use('/api/users', userRoutes); app.use('/api/users', auth_userRoutes); +app.use('/api/profiles', profileRoutes); +app.use('/api/profiles', auth_profileRoutes); + // Test the database connection pool.connect((err, client, release) => { if (err) { diff --git a/server/src/profiles/authroutes.js b/server/src/profiles/authroutes.js new file mode 100644 index 0000000..549153d --- /dev/null +++ b/server/src/profiles/authroutes.js @@ -0,0 +1,13 @@ +const { Router } = require("express"); +const controller = require("./controller"); +const requireAuth = require('../middleware/requireAuth'); + +const router = Router(); + +router.use(requireAuth); + +router.post("/", controller.createProfile); +router.get("/", controller.getProfile); +router.put("/", controller.updateProfile); + +module.exports = router; diff --git a/server/src/profiles/controller.js b/server/src/profiles/controller.js new file mode 100644 index 0000000..d008e38 --- /dev/null +++ b/server/src/profiles/controller.js @@ -0,0 +1,68 @@ +const pool = require("../../db.js"); +const queries = require("./queries"); +const { validate, validateParams } = require("../middleware/validate"); +const { profileSchema, profileIdSchema } = require("./validation"); + +const createProfile = async (req, res) => { + try { + const { first_name, last_name, email, profile_pic } = req.body; + const user_id = req.user.id; + + // Check if email already exists + if (email) { + const emailExists = await pool.query("SELECT * FROM profiles WHERE email = $1", [email]); + if (emailExists.rows.length > 0) { + return res.status(400).json({ error: 'Email already exists' }); + } + } + + await pool.query(queries.createProfile, [user_id, first_name, last_name, email, profile_pic]); + res.status(201).json({ message: 'Profile created successfully' }); + } catch (err) { + console.error(err.message); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + +const getProfile = async (req, res) => { + try { + const user_id = req.user.id; + const profile = await pool.query(queries.getProfileByUserId, [user_id]); + + if (profile.rows.length === 0) { + return res.status(404).json({ error: 'Profile not found' }); + } + + res.json(profile.rows[0]); + } catch (err) { + console.error(err.message); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + +const updateProfile = async (req, res) => { + try { + const { first_name, last_name, email, profile_pic } = req.body; + const user_id = req.user.id; + + // Check if email already exists + if (email) { + const emailExists = await pool.query("SELECT * FROM profiles WHERE email = $1 AND user_id != $2", [email, user_id]); + if (emailExists.rows.length > 0) { + return res.status(400).json({ error: 'Email already exists' }); + } + } + + await pool.query(queries.updateProfile, [first_name, last_name, email, profile_pic, user_id]); + res.status(200).json({ message: 'Profile updated successfully' }); + } catch (err) { + console.error(err.message); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + +module.exports = { + createProfile, + getProfile, + updateProfile, +}; \ No newline at end of file diff --git a/server/src/profiles/queries.js b/server/src/profiles/queries.js new file mode 100644 index 0000000..7141dfd --- /dev/null +++ b/server/src/profiles/queries.js @@ -0,0 +1,9 @@ +const createProfile = "INSERT INTO profiles (user_id, first_name, last_name, email, profile_pic) VALUES ($1, $2, $3, $4, $5)"; +const getProfileByUserId = "SELECT * FROM profiles WHERE user_id = $1"; +const updateProfile = "UPDATE profiles SET first_name = $1, last_name = $2, email = $3, profile_pic = $4 WHERE user_id = $5"; + +module.exports = { + createProfile, + getProfileByUserId, + updateProfile +}; diff --git a/server/src/profiles/routes.js b/server/src/profiles/routes.js new file mode 100644 index 0000000..5c177b6 --- /dev/null +++ b/server/src/profiles/routes.js @@ -0,0 +1,15 @@ +const { Router } = require("express"); +const controller = require("./controller"); +const { validate, validateParams } = require("../middleware/validate"); +const { profileSchema, profileIdSchema } = require("./validation"); +const requireAuth = require('../middleware/requireAuth'); + +const router = Router(); + +router.use(requireAuth); + +router.post("/", validate(profileSchema), controller.createProfile); +router.get("/", controller.getProfile); +router.put("/", validate(profileSchema), controller.updateProfile); + +module.exports = router; diff --git a/server/src/profiles/validation.js b/server/src/profiles/validation.js new file mode 100644 index 0000000..0edd0dc --- /dev/null +++ b/server/src/profiles/validation.js @@ -0,0 +1,28 @@ +const Joi = require('joi'); + +const profileSchema = Joi.object({ + first_name: Joi.string().min(1).max(50).optional().messages({ + 'string.max': 'First name should have a maximum length of 50' + }), + last_name: Joi.string().min(1).max(50).optional().messages({ + 'string.max': 'Last name should have a maximum length of 50' + }), + email: Joi.string().email().optional().messages({ + 'string.email': 'Email must be a valid email address' + }), + profile_pic: Joi.string().uri().optional().messages({ + 'string.uri': 'Profile picture must be a valid URL' + }) +}); + +const profileIdSchema = Joi.object({ + id: Joi.number().integer().required().messages({ + 'number.base': 'ID must be an integer', + 'any.required': 'ID is required' + }) +}); + +module.exports = { + profileSchema, + profileIdSchema +}; diff --git a/server/src/users/controller.js b/server/src/users/controller.js index b7fad79..cc757e8 100644 --- a/server/src/users/controller.js +++ b/server/src/users/controller.js @@ -114,25 +114,29 @@ const changeUser = async (req, res) => { const deleteAccount = async (req, res) => { try { - const { id } = req.params; - - // Ensure the authenticated user is the same as the user ID being deleted - if (req.user.id !== parseInt(id)) { - return res.status(403).json({ error: 'Access forbidden: You can only delete your own account' }); - } - - // First, delete all bookmarks associated with this user - await pool.query("DELETE FROM bookmarks WHERE user_id = $1", [id]); - - // Then, delete the user - await pool.query(queries.deleteAccount, [id]); - - res.status(200).send("User and related bookmarks deleted"); + const { id } = req.params; // Assumes this has been validated by middleware + + // Ensure the authenticated user is the same as the user ID being deleted + if (req.user.id !== parseInt(id)) { + return res.status(403).json({ error: 'Access forbidden: You can only delete your own account' }); + } + + // First, delete all bookmarks associated with this user + await pool.query("DELETE FROM bookmarks WHERE user_id = $1", [id]); + + // Delete the profile associated with this user + await pool.query("DELETE FROM profiles WHERE user_id = $1", [id]); + + // Then, delete the user + await pool.query(queries.deleteAccount, [id]); + + res.status(200).send("User and related data deleted"); } catch (err) { - console.error(err.message); - res.status(500).json({ error: 'Internal Server Error' }); + console.error(err.message); + res.status(500).json({ error: 'Internal Server Error' }); } -}; + }; + module.exports = { getUsers, diff --git a/server/src/users/routes.js b/server/src/users/routes.js index 608fe1a..1cd4a4d 100644 --- a/server/src/users/routes.js +++ b/server/src/users/routes.js @@ -6,10 +6,14 @@ const requireAuth = require('../middleware/requireAuth'); const router = Router(); + router.post("/", validate(userSchema), controller.createUser); router.post("/login", validate(userSchema), controller.loginUser); router.get("/:id", validateParams(userIdSchema), requireAuth, controller.getUsersById); router.put("/:id", validateParams(userIdSchema), validate(changeUserSchema), requireAuth, controller.changeUser); router.delete("/:id", validateParams(userIdSchema), requireAuth, controller.deleteAccount); +// Include profile routes +router.use("/profiles", require('../profiles/routes')); + module.exports = router;