forked from UKSOURCE/cms.hailearning.edu.vn
163 lines
6.0 KiB
JavaScript
163 lines
6.0 KiB
JavaScript
const path = require('path');
|
|
const Certificate = require('../models/certificate');
|
|
const Department = require('../models/department');
|
|
const Level = require('../models/level');
|
|
const writeAuditLog = require('../audit/writeAuditLog');
|
|
const AUDIT_ACTIONS = require('../constants/auditAction');
|
|
const { generateSignedUrl } = require('../utils/signedUrl');
|
|
|
|
function normalizePath(filePath) {
|
|
if (!filePath) return undefined;
|
|
return path.basename(filePath.replace(/\\/g, '/'));
|
|
}
|
|
|
|
// GET /admin/certificate
|
|
exports.index = async (req, res) => {
|
|
try {
|
|
const { search, status } = req.query;
|
|
const filter = {};
|
|
if (search) {
|
|
filter.$or = [
|
|
{ certification_number: { $regex: search, $options: 'i' } },
|
|
{ student_name: { $regex: search, $options: 'i' } }
|
|
];
|
|
}
|
|
if (status) filter.status = status;
|
|
|
|
const [certificates, departments, levels] = await Promise.all([
|
|
Certificate.find(filter).populate('department level').sort({ createdAt: -1 }),
|
|
Department.find(), Level.find()
|
|
]);
|
|
|
|
res.render('admin/certificate/index', {
|
|
certificates, departments, levels, query: req.query,
|
|
user: req.session.user, layout: 'layouts/admin', title: 'Certificates'
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash('error', 'Error loading certificates');
|
|
res.redirect('/admin/dashboard');
|
|
}
|
|
};
|
|
|
|
// GET /admin/certificate/create
|
|
exports.createForm = async (req, res) => {
|
|
try {
|
|
const [departments, levels] = await Promise.all([Department.find(), Level.find()]);
|
|
res.render('admin/certificate/create', {
|
|
departments, levels, user: req.session.user,
|
|
layout: 'layouts/admin', title: 'Create Certificate'
|
|
});
|
|
} catch (err) {
|
|
req.flash('error', 'Error'); res.redirect('/admin/certificate');
|
|
}
|
|
};
|
|
|
|
// POST /admin/certificate/create
|
|
exports.create = async (req, res) => {
|
|
try {
|
|
const data = { ...req.body };
|
|
const imgPath = req.files?.certificate_image?.[0]?.path;
|
|
if (imgPath) data.certificate_image = normalizePath(imgPath);
|
|
|
|
const cert = new Certificate(data);
|
|
await cert.save();
|
|
await writeAuditLog({ model: 'Certificate', documentId: cert._id, action: AUDIT_ACTIONS.CREATE_CERTIFICATE, before: null, after: cert.toObject(), req });
|
|
|
|
req.flash('success', 'Certificate created');
|
|
res.redirect('/admin/certificate');
|
|
} catch (err) {
|
|
console.error(err);
|
|
try {
|
|
const [departments, levels] = await Promise.all([Department.find(), Level.find()]);
|
|
res.render('admin/certificate/create', {
|
|
error: err.message, formData: req.body, departments, levels,
|
|
user: req.session.user, layout: 'layouts/admin', title: 'Create Certificate'
|
|
});
|
|
} catch { req.flash('error', err.message); res.redirect('/admin/certificate'); }
|
|
}
|
|
};
|
|
|
|
// GET /admin/certificate/:id/edit
|
|
exports.editForm = async (req, res) => {
|
|
try {
|
|
const cert = await Certificate.findById(req.params.id).populate('department level');
|
|
if (!cert) { req.flash('error', 'Not found'); return res.redirect('/admin/certificate'); }
|
|
const [departments, levels] = await Promise.all([Department.find(), Level.find()]);
|
|
res.render('admin/certificate/edit', {
|
|
cert, departments, levels, user: req.session.user,
|
|
layout: 'layouts/admin', title: 'Edit Certificate'
|
|
});
|
|
} catch (err) {
|
|
req.flash('error', 'Error'); res.redirect('/admin/certificate');
|
|
}
|
|
};
|
|
|
|
// POST /admin/certificate/:id/edit
|
|
exports.update = async (req, res) => {
|
|
try {
|
|
const cert = await Certificate.findById(req.params.id);
|
|
if (!cert) { req.flash('error', 'Not found'); return res.redirect('/admin/certificate'); }
|
|
const before = cert.toObject();
|
|
|
|
const fields = ['certification_number','student_name','program_name','department','level',
|
|
'issued_date','status','passport_number','address'];
|
|
fields.forEach(f => { if (req.body[f] !== undefined) cert[f] = req.body[f]; });
|
|
|
|
const imgPath = req.files?.certificate_image?.[0]?.path;
|
|
if (imgPath) cert.certificate_image = normalizePath(imgPath);
|
|
|
|
await cert.save();
|
|
await writeAuditLog({ model: 'Certificate', documentId: cert._id, action: AUDIT_ACTIONS.UPDATE_CERTIFICATE, before, after: cert.toObject(), req });
|
|
|
|
req.flash('success', 'Certificate updated');
|
|
res.redirect('/admin/certificate');
|
|
} catch (err) {
|
|
req.flash('error', err.message); res.redirect('back');
|
|
}
|
|
};
|
|
|
|
// POST /admin/certificate/:id/delete
|
|
exports.destroy = async (req, res) => {
|
|
try {
|
|
const cert = await Certificate.findById(req.params.id);
|
|
if (!cert) { req.flash('error', 'Not found'); return res.redirect('/admin/certificate'); }
|
|
await writeAuditLog({ model: 'Certificate', documentId: cert._id, action: AUDIT_ACTIONS.DELETE_CERTIFICATE, before: cert.toObject(), after: null, req });
|
|
await cert.deleteOne();
|
|
req.flash('success', 'Certificate deleted');
|
|
res.redirect('/admin/certificate');
|
|
} catch (err) {
|
|
req.flash('error', 'Error deleting'); res.redirect('/admin/certificate');
|
|
}
|
|
};
|
|
|
|
// GET /api/verify-certificate/:cert_id?api_key=xxx
|
|
exports.apiVerify = async (req, res) => {
|
|
try {
|
|
const cert = await Certificate.findOne({
|
|
certification_number: { $regex: new RegExp('^' + req.params.cert_id + '$', 'i') }
|
|
}).populate('department level');
|
|
|
|
if (!cert) return res.status(404).json({ error: 'Certificate not found' });
|
|
if (cert.status === 'revoked') return res.status(404).json({ error: 'Certificate has been revoked' });
|
|
|
|
const baseUrl = `${req.protocol}://${req.get('host')}`;
|
|
const buildUrl = (f) => f ? [generateSignedUrl(baseUrl, path.basename(f))] : undefined;
|
|
|
|
const response = {
|
|
full_name: cert.student_name,
|
|
certification_title: cert.program_name,
|
|
certificate_id: cert.certification_number,
|
|
};
|
|
if (cert.passport_number) response.passport_number = cert.passport_number;
|
|
if (cert.address) response.address = cert.address;
|
|
const imgs = buildUrl(cert.certificate_image);
|
|
if (imgs) response.certificate_image = imgs;
|
|
|
|
return res.json(response);
|
|
} catch (err) {
|
|
console.error(err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|