Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 100 additions & 5 deletions src/controllers/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import User from '../domain/user.js'
import { sendDataResponse, sendMessageResponse } from '../utils/responses.js'
import bcrypt from 'bcrypt'

export const create = async (req, res) => {
const userToCreate = await User.fromJson(req.body)
Expand Down Expand Up @@ -57,11 +58,105 @@ export const getAll = async (req, res) => {
}

export const updateById = async (req, res) => {
const { cohort_id: cohortId } = req.body
const userId = parseInt(req.params.id)
const {
firstName,
lastName,
email,
biography,
githubUrl,
password,
cohort_id: cohortIdSnake,
cohortId: cohortIdCamel,
role
} = req.body

const cohortId = cohortIdSnake ?? cohortIdCamel

if (!cohortId) {
return sendDataResponse(res, 400, { cohort_id: 'Cohort ID is required' })
}
try {
const userToUpdate = await User.findById(userId)

if (!userToUpdate) {
return sendDataResponse(res, 404, { id: 'User not found' })
}

// Check if user is updating their own profile or is a teacher
const isOwnProfile = req.user.id === userId
const isTeacher = req.user.role === 'TEACHER'

if (!isOwnProfile && !isTeacher) {
return sendDataResponse(res, 403, {
authorization: 'You are not authorized to update this profile'
})
}

// Check if student is trying to update restricted fields
if (!isTeacher && (cohortId || role)) {
return sendDataResponse(res, 403, {
authorization:
'Students cannot modify cohort or role information. Please contact a teacher for these changes.'
})
}

// Create update data object
const updateData = {}

// Helper function to validate and add field if it has a valid value
const addValidField = (field, value) => {
if (
value !== undefined &&
value !== null &&
value !== '' &&
value !== 'string'
) {
updateData[field] = value
}
}

return sendDataResponse(res, 201, { user: { cohort_id: cohortId } })
// Profile fields any user can update on their own profile
if (isOwnProfile) {
if (password && typeof password === 'string') {
updateData.passwordHash = await bcrypt.hash(password, 8)
}
}

// Fields only teachers can update
if (isTeacher) {
if (cohortId !== undefined) {
const cohortIdInt = parseInt(cohortId, 10)
if (!isNaN(cohortIdInt)) {
updateData.cohortId = cohortIdInt
}
}
if (role === 'STUDENT' || role === 'TEACHER') {
updateData.role = role
}
}

// Profile fields any user can update on their own profile
if (isOwnProfile || isTeacher) {
addValidField('firstName', firstName)
addValidField('lastName', lastName)
addValidField('bio', biography)
addValidField('githubUrl', githubUrl)
addValidField('email', email)
}

// If no valid fields to update
if (Object.keys(updateData).length === 0) {
return sendDataResponse(res, 400, {
fields: 'No valid fields provided for update'
})
}

const updatedUser = await userToUpdate.update(updateData)

return sendDataResponse(res, 200, updatedUser)
} catch (error) {
return sendMessageResponse(
res,
500,
`Unable to update user: ${error.message}`
)
}
}
53 changes: 53 additions & 0 deletions src/domain/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,57 @@ export default class User {

return foundUsers.map((user) => User.fromDb(user))
}

async update(data) {
const updateData = {
where: {
id: this.id
},
data: {},
include: {
profile: true
}
}

// Handle user-level updates
if (data.email && data.email !== 'string')
updateData.data.email = data.email
if (data.passwordHash) updateData.data.password = data.passwordHash
if (data.role && (data.role === 'STUDENT' || data.role === 'TEACHER')) {
updateData.data.role = data.role
}
if (typeof data.cohortId === 'number') {
updateData.data.cohortId = data.cohortId
}

// Handle profile-related updates
const profileUpdates = {}
let hasProfileUpdates = false

if (data.firstName && data.firstName !== 'string') {
profileUpdates.firstName = data.firstName
hasProfileUpdates = true
}
if (data.lastName && data.lastName !== 'string') {
profileUpdates.lastName = data.lastName
hasProfileUpdates = true
}
if (data.bio && data.bio !== 'string') {
profileUpdates.bio = data.bio
hasProfileUpdates = true
}
if (data.githubUrl && data.githubUrl !== 'string') {
profileUpdates.githubUrl = data.githubUrl
hasProfileUpdates = true
}

if (hasProfileUpdates) {
updateData.data.profile = {
update: profileUpdates
}
}

const updatedUser = await dbClient.user.update(updateData)
return User.fromDb(updatedUser)
}
}
7 changes: 2 additions & 5 deletions src/routes/user.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Router } from 'express'
import { create, getById, getAll, updateById } from '../controllers/user.js'
import {
validateAuthentication,
validateTeacherRole
} from '../middleware/auth.js'
import { validateAuthentication } from '../middleware/auth.js'

const router = Router()

router.post('/', create)
router.get('/', validateAuthentication, getAll)
router.get('/:id', validateAuthentication, getById)
router.patch('/:id', validateAuthentication, validateTeacherRole, updateById)
router.patch('/:id', validateAuthentication, updateById)

export default router
Loading