feat: nambahin API route untuk generate certificate bagi Admin/Mentor

This commit is contained in:
Rafi Athallah
2026-07-01 15:33:22 +07:00
parent 2c5d77c22e
commit 8d34e443a5
7 changed files with 540 additions and 1 deletions
@@ -80,6 +80,102 @@ class CertificateService {
}
}
async generateCertificates(payload, actor) {
try {
if (!actor?.id) {
return error(new BadRequestError('User ID is required'));
}
if (actor.role !== 'admin' && actor.role !== 'mentor') {
return error(
new ForbiddenError('Access denied: only admins and mentors can generate certificates'),
);
}
const { id_project, id_users } = payload;
const project = await certificateRepository.findProjectById(id_project);
if (!project) {
return error(new NotFoundError('Project not found'));
}
// Authorization check:
if (actor.role === 'mentor' && project.id_admin !== parseInt(actor.id)) {
return error(
new ForbiddenError('Access denied: you are not the mentor of this project'),
);
}
const validatedUsers = [];
for (const userId of id_users) {
const existingCert = await certificateRepository.findByUserAndProject(userId, id_project);
if (existingCert) {
return error(
new ConflictError(`Certificate already generated/claimed for user ID ${userId}`),
);
}
const projectInfo = await certificateRepository.getProjectTasksAndSubmissions(id_project, userId);
if (!projectInfo) {
return error(new NotFoundError('Project not found'));
}
if (!projectInfo.members || projectInfo.members.length === 0) {
return error(
new ForbiddenError(`User ID ${userId} is not an active member of this project`),
);
}
if (!projectInfo.tasks || projectInfo.tasks.length === 0) {
return error(
new BadRequestError(`Project does not have any tasks assigned yet`),
);
}
const completedTasks = projectInfo.tasks.filter(
(task) => task.submissions && task.submissions.length > 0
);
if (completedTasks.length < projectInfo.tasks.length) {
return error(
new BadRequestError(
`User ID ${userId} is not eligible: completed ${completedTasks.length} out of ${projectInfo.tasks.length} tasks`,
),
);
}
validatedUsers.push(userId);
}
const generatedCertificates = [];
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const date = String(now.getDate()).padStart(2, '0');
const dateString = `${year}${month}${date}`;
for (const userId of validatedUsers) {
const certificateNo = `CERT/${dateString}/PRJ${id_project}/USR${userId}`;
const certificateData = {
id_project: parseInt(id_project),
id_user: parseInt(userId),
certificate_no: certificateNo,
issued_at: now,
};
const certificate = await certificateRepository.create(certificateData);
generatedCertificates.push(this.mapCertificate(certificate));
}
return data({
message: `${generatedCertificates.length} certificates generated successfully.`,
certificates: generatedCertificates,
});
} catch (err) {
return error(err);
}
}
async getMyCertificates(actor) {
try {
if (!actor?.id) {