feat(task): support mentor access for task management

This commit is contained in:
Muhammad Zhafran Ilham
2026-06-23 10:18:30 +07:00
parent 3ec431177a
commit a5593e11cc
3 changed files with 129 additions and 20 deletions
@@ -30,14 +30,6 @@ class ProjectService {
const normalizedEmails = this.normalizeEmails(member_emails || []);
if (normalizedEmails.length > 8) {
return error(
new BadRequestError(
"Project member limit exceeded. Maximum member is 8",
),
);
}
const users =
normalizedEmails.length > 0
? await projectRepository.findUsersByEmails(normalizedEmails)
+38 -6
View File
@@ -17,8 +17,8 @@ class TaskService {
return error(new BadRequestError('Project ID must be a valid number'));
}
if (!actor?.id || actor.role !== 'admin') {
return error(new ForbiddenError('Access denied: only admin can create task'));
if (!actor?.id || !this.isMentorOrAdmin(actor)) {
return error(new ForbiddenError('Access denied: only admin or mentor can create task'));
}
const project = await taskRepository.findProjectById(idProject);
@@ -27,6 +27,12 @@ class TaskService {
return error(new NotFoundError('Project not found'));
}
const accessError = await this.validateProjectAccess(idProject, actor);
if (accessError) {
return error(accessError);
}
const deadlineAt = this.buildDeadlineAt(payload.deadline_date, payload.specific_time);
const task = await taskRepository.createTask({
@@ -128,8 +134,8 @@ class TaskService {
return error(new BadRequestError('Task ID must be a valid number'));
}
if (!actor?.id || actor.role !== 'admin') {
return error(new ForbiddenError('Access denied: only admin can update task'));
if (!actor?.id || !this.isMentorOrAdmin(actor)) {
return error(new ForbiddenError('Access denied: only admin or mentor can update task'));
}
if (Object.keys(payload).length === 0) {
@@ -142,6 +148,12 @@ class TaskService {
return error(new NotFoundError('Task not found'));
}
const accessError = await this.validateProjectAccess(existingTask.id_project, actor);
if (accessError) {
return error(accessError);
}
const submissionCount = await taskRepository.countSubmissionsByTask(idTask);
if (
@@ -182,8 +194,8 @@ class TaskService {
return error(new BadRequestError('Task ID must be a valid number'));
}
if (!actor?.id || actor.role !== 'admin') {
return error(new ForbiddenError('Access denied: only admin can delete task'));
if (!actor?.id || !this.isMentorOrAdmin(actor)) {
return error(new ForbiddenError('Access denied: only admin or mentor can delete task'));
}
const task = await taskRepository.findTaskById(idTask);
@@ -192,6 +204,12 @@ class TaskService {
return error(new NotFoundError('Task not found'));
}
const accessError = await this.validateProjectAccess(task.id_project, actor);
if (accessError) {
return error(accessError);
}
const submissionCount = await taskRepository.countSubmissionsByTask(idTask);
if (submissionCount > 0) {
@@ -394,6 +412,16 @@ class TaskService {
return null;
}
if (actor.role === 'mentor') {
const project = await taskRepository.findProjectById(idProject);
if (!project || project.id_admin !== parseInt(actor.id)) {
return new ForbiddenError('Access denied: you are not the mentor of this project');
}
return null;
}
if (actor.role === 'intern') {
const membership = await taskRepository.checkActiveProjectMember(idProject, actor.id);
@@ -407,6 +435,10 @@ class TaskService {
return new ForbiddenError('Access denied: invalid role');
}
isMentorOrAdmin(actor) {
return actor?.role === 'admin' || actor?.role === 'mentor';
}
buildDeadlineAt(deadlineDate, specificTime) {
const dateOnly = this.formatDateOnly(new Date(deadlineDate));
return new Date(`${dateOnly}T${specificTime}:00`);