refactor: rewrite ke codebase lama

This commit is contained in:
Muhammad Zhafran Ilham
2026-06-07 20:17:49 +07:00
parent ce5487dcbf
commit fe74b2213d
146 changed files with 14608 additions and 6686 deletions
+138
View File
@@ -0,0 +1,138 @@
const feedbackService = require('../services/feedback.service');
const { response, error } = require('../../../helpers/utils/wrapper');
const { BadRequestError, InternalServerError } = require('../../../helpers/error');
const { SUCCESS, ERROR } = require('../../../helpers/http-status/status_code');
const { isValidPayload } = require('../../../helpers/utils/validator');
const { createFeedbackModel, updateFeedbackModel, getAllFeedbackModel } = require('../models/feedback.model');
const { deleteFileIfExists } = require('../../../helpers/utils/fileHelper');
class FeedbackController {
async addFeedback(req, res) {
try {
const image_path = req.file;
const validatePayload = isValidPayload(req.body, createFeedbackModel);
if (validatePayload.err) {
if (image_path?.filename) {
await deleteFileIfExists(image_path.filename);
}
return response(
res,
'fail',
{ err: validatePayload.err, data: null },
'Data tidak valid',
ERROR.EXPECTATION_FAILED
);
}
const result = await feedbackService.createFeedback(validatePayload.data, image_path);
if (result.err) {
return response(res, 'fail', result);
}
return response(res, 'success', result, 'Feedback berhasil dibuat', SUCCESS.CREATED);
} catch (err) {
if (req.file?.filename) {
await deleteFileIfExists(req.file.filename);
}
return response(res, 'fail', error(new InternalServerError(err.message)), 'Terjadi kesalahan yang tidak terduga', ERROR.INTERNAL_ERROR);
}
}
async getAllFeedback(req, res) {
try {
const validatePayload = isValidPayload(req.query, getAllFeedbackModel);
if (validatePayload.err) {
return response(
res,
'fail',
{ err: validatePayload.err, data: null },
'Query parameter tidak valid',
ERROR.EXPECTATION_FAILED
);
}
const result = await feedbackService.getAllFeedback(validatePayload.data);
if (result.err) {
return response(res, 'fail', result);
}
return response(res, 'success', result, 'Berhasil mengambil semua feedback', SUCCESS.OK);
} catch (err) {
return response(res, 'fail', error(new InternalServerError(err.message)), 'Terjadi kesalahan yang tidak terduga', ERROR.INTERNAL_ERROR);
}
}
async getFeedbackById(req, res) {
try {
const { id } = req.params;
const result = await feedbackService.getFeedbackById(id);
if (result.err) {
return response(res, 'fail', result);
}
return response(res, 'success', result, 'Berhasil mengambil feedback berdasarkan ID', SUCCESS.OK);
} catch (err) {
return response(res, 'fail', error(new InternalServerError(err.message)), 'Terjadi kesalahan yang tidak terduga', ERROR.INTERNAL_ERROR);
}
}
async updateFeedback(req, res) {
try {
const { id } = req.params;
const image_path = req.file;
const validatePayload = isValidPayload(req.body, updateFeedbackModel);
if (validatePayload.err) {
if (image_path?.filename) {
await deleteFileIfExists(image_path.filename);
}
return response(
res,
'fail',
{ err: validatePayload.err, data: null },
'Data tidak valid',
ERROR.EXPECTATION_FAILED
);
}
const result = await feedbackService.updateFeedback(id, validatePayload.data, image_path);
if (result.err) {
return response(res, 'fail', result);
}
return response(res, 'success', result, 'Feedback berhasil diperbarui', SUCCESS.OK);
} catch (err) {
if (req.file?.filename) {
await deleteFileIfExists(req.file.filename);
}
return response(res, 'fail', error(new InternalServerError(err.message)), 'Terjadi kesalahan yang tidak terduga', ERROR.INTERNAL_ERROR);
}
}
async deleteFeedback(req, res) {
try {
const { id } = req.params;
const result = await feedbackService.deleteFeedback(id);
if (result.err) {
return response(res, 'fail', result);
}
return response(res, 'success', result, 'Feedback berhasil dihapus', SUCCESS.OK);
} catch (err) {
return response(res, 'fail', error(new InternalServerError(err.message)), 'Terjadi kesalahan yang tidak terduga', ERROR.INTERNAL_ERROR);
}
}
}
module.exports = new FeedbackController();
+5
View File
@@ -0,0 +1,5 @@
const feedbackController = require('./controllers/feedback.controller');
module.exports = {
feedbackController,
};
+108
View File
@@ -0,0 +1,108 @@
const joi = require('joi');
const createFeedbackModel = joi.object().keys({
nama: joi.string().max(255).required().messages({
'string.base': 'Nama harus berupa teks.',
'string.empty': 'Nama tidak boleh kosong.',
'string.max': 'Nama maksimal {#limit} karakter.',
'any.required': 'Nama wajib diisi.',
}),
universitas: joi.string().max(255).required().messages({
'string.base': 'Universitas harus berupa teks.',
'string.empty': 'Universitas tidak boleh kosong.',
'string.max': 'Universitas maksimal {#limit} karakter.',
'any.required': 'Universitas wajib diisi.',
}),
pesan: joi.string().required().messages({
'string.base': 'Pesan harus berupa teks.',
'string.empty': 'Pesan tidak boleh kosong.',
'any.required': 'Pesan wajib diisi.',
}),
batch: joi.number().integer().min(1).required().messages({
'number.base': 'Batch harus berupa angka.',
'number.integer': 'Batch harus berupa bilangan bulat.',
'number.min': 'Batch minimal {#limit}.',
'any.required': 'Batch wajib diisi.',
}),
posisi: joi.string().max(255).required().messages({
'string.base': 'Posisi harus berupa teks.',
'string.empty': 'Posisi tidak boleh kosong.',
'string.max': 'Posisi maksimal {#limit} karakter.',
'any.required': 'Posisi wajib diisi.',
}),
tahun: joi.number().integer().min(2000).max(2100).required().messages({
'number.base': 'Tahun harus berupa angka.',
'number.integer': 'Tahun harus berupa bilangan bulat.',
'number.min': 'Tahun minimal {#limit}.',
'number.max': 'Tahun maksimal {#limit}.',
'any.required': 'Tahun wajib diisi.',
}),
image_path: joi.string().optional().allow('', null).messages({
'string.base': 'Path gambar harus berupa teks.',
}),
});
const updateFeedbackModel = joi.object().keys({
nama: joi.string().max(255).optional().messages({
'string.base': 'Nama harus berupa teks.',
'string.max': 'Nama maksimal {#limit} karakter.',
}),
universitas: joi.string().max(255).optional().messages({
'string.base': 'Universitas harus berupa teks.',
'string.max': 'Universitas maksimal {#limit} karakter.',
}),
pesan: joi.string().optional().messages({
'string.base': 'Pesan harus berupa teks.',
}),
batch: joi.number().integer().min(1).optional().messages({
'number.base': 'Batch harus berupa angka.',
'number.integer': 'Batch harus berupa bilangan bulat.',
'number.min': 'Batch minimal {#limit}.',
}),
posisi: joi.string().max(255).optional().messages({
'string.base': 'Posisi harus berupa teks.',
'string.max': 'Posisi maksimal {#limit} karakter.',
}),
tahun: joi.number().integer().min(2000).max(2100).optional().messages({
'number.base': 'Tahun harus berupa angka.',
'number.integer': 'Tahun harus berupa bilangan bulat.',
'number.min': 'Tahun minimal {#limit}.',
'number.max': 'Tahun maksimal {#limit}.',
}),
image_path: joi.string().optional().allow('', null).messages({
'string.base': 'Path gambar harus berupa teks.',
}),
});
const getAllFeedbackModel = joi.object().keys({
batch: joi.number().integer().min(1).optional().messages({
'number.base': 'Batch harus berupa angka.',
'number.integer': 'Batch harus berupa bilangan bulat.',
'number.min': 'Batch minimal {#limit}.',
}),
tahun: joi.number().integer().min(2000).max(2100).optional().messages({
'number.base': 'Tahun harus berupa angka.',
'number.integer': 'Tahun harus berupa bilangan bulat.',
'number.min': 'Tahun minimal {#limit}.',
'number.max': 'Tahun maksimal {#limit}.',
}),
});
module.exports = {
createFeedbackModel,
updateFeedbackModel,
getAllFeedbackModel,
};
+60
View File
@@ -0,0 +1,60 @@
const prisma = require('../../../helpers/db/db_connection');
class FeedbackRepository {
async create(feedbackData) {
return prisma.feedback.create({
data: {
...feedbackData,
created_at: new Date(),
},
});
}
async findAll() {
return prisma.feedback.findMany({
orderBy: {
created_at: 'desc',
},
});
}
async findAllWithFilters({ batch, tahun }) {
const where = {};
if (batch) {
where.batch = parseInt(batch);
}
if (tahun) {
where.tahun = parseInt(tahun);
}
return prisma.feedback.findMany({
where,
orderBy: {
created_at: 'desc',
},
});
}
async findById(id) {
return prisma.feedback.findUnique({
where: { id: parseInt(id) },
});
}
async updateById(id, feedbackData) {
return prisma.feedback.update({
where: { id: parseInt(id) },
data: feedbackData,
});
}
async deleteById(id) {
return prisma.feedback.delete({
where: { id: parseInt(id) },
});
}
}
module.exports = new FeedbackRepository();
+120
View File
@@ -0,0 +1,120 @@
const path = require('path');
const feedbackRepository = require('../repositories/feedback.repository');
const imageUploadHelper = require('../../../helpers/utils/imageUpload.helper');
const { deleteFileIfExists } = require('../../../helpers/utils/fileHelper');
const { data, error } = require('../../../helpers/utils/wrapper');
const { NotFoundError } = require('../../../helpers/error');
class FeedbackService {
async createFeedback(feedbackData, imageFile) {
try {
const imagePath = imageUploadHelper.uploadImage(imageFile);
const newFeedbackData = {
...feedbackData,
image_path: imagePath,
};
const result = await feedbackRepository.create(newFeedbackData);
return data(result);
} catch (err) {
if (imageFile?.filename) {
try {
await deleteFileIfExists(imageFile.filename);
} catch (cleanupErr) {
console.error('Failed to cleanup file:', cleanupErr);
}
}
return error(err);
}
}
async getAllFeedback(query = {}) {
try {
const feedbackList = await feedbackRepository.findAllWithFilters(query);
if (feedbackList.length === 0) {
return data([]);
}
return data(feedbackList);
} catch (err) {
return error(err);
}
}
async getFeedbackById(id) {
try {
const feedback = await feedbackRepository.findById(id);
if (!feedback) {
return error(new NotFoundError('Feedback tidak ditemukan'));
}
return data(feedback);
} catch (err) {
return error(err);
}
}
async updateFeedback(id, feedbackData, imageFile) {
let oldImagePath = null;
let newImageUploaded = false;
try {
const checkResult = await this.getFeedbackById(id);
if (checkResult.err) {
return checkResult;
}
const existingFeedback = checkResult.data;
if (imageFile) {
oldImagePath = existingFeedback.image_path;
feedbackData.image_path = imageUploadHelper.uploadImage(imageFile);
newImageUploaded = true;
if (oldImagePath) {
const oldFileName = path.basename(oldImagePath);
await deleteFileIfExists(oldFileName);
}
}
const result = await feedbackRepository.updateById(id, feedbackData);
return data(result);
} catch (err) {
if (newImageUploaded && imageFile?.filename) {
try {
await deleteFileIfExists(imageFile.filename);
} catch (cleanupErr) {
console.error('Failed to cleanup new file:', cleanupErr);
}
}
return error(err);
}
}
async deleteFeedback(id) {
try {
const checkResult = await this.getFeedbackById(id);
if (checkResult.err) {
return checkResult;
}
const feedback = checkResult.data;
if (feedback.image_path) {
const fileName = path.basename(feedback.image_path);
await deleteFileIfExists(fileName);
}
const result = await feedbackRepository.deleteById(id);
return data(result);
} catch (err) {
return error(err);
}
}
}
module.exports = new FeedbackService();