This commit is contained in:
2026-05-04 18:30:49 +07:00
parent c848db9e65
commit ed5a1288c3
3 changed files with 48 additions and 147 deletions

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"printWidth": 5000,
"semi": true,
"singleQuote": false,
"tabWidth": 4,
"useTabs": true
}

View File

@ -22,10 +22,10 @@
"database": {
"engine": "postgres",
"host": "127.0.0.1",
"port": "15432",
"username": "saude_tl",
"password": "saude_tl_local",
"database": "saude_tl",
"port": "1520",
"username": "saude_stag",
"password": "gM*#o>3W4&5X",
"database": "saude_stag",
"synchronize": true,
"logging": true
},

View File

@ -12,8 +12,8 @@ import { Language } from "../langs/lang";
import fs from "fs";
import path from "path";
import fileUpload from "express-fileupload";
import { v4 as uuidv4 } from 'uuid';
import config from '../../config/stag.json';
import { v4 as uuidv4 } from "uuid";
import config from "../../config/stag.json";
import { profile } from "console";
const log: Logger<ILogObj> = new Logger({
@ -137,20 +137,10 @@ export class UserController {
const mapped_data = list_data.map((user) => ({
...user,
profile_picture: user.profile_picture ?? null,
file_profile_picture: user.profile_picture
? `${config.server.host_swagger}uploads/doctor-profile/${user.profile_picture}`
: null,
file_profile_picture: user.profile_picture ? `${config.server.host_swagger}uploads/doctor-profile/${user.profile_picture}` : null,
}));
return ReturnHelper.successResponselist(
res,
200,
Language.lang.success_view,
count_data,
current_page,
total_count_data,
mapped_data,
);
return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, mapped_data);
} catch (e: unknown) {
log.error(e);
const err = e as Error;
@ -214,24 +204,14 @@ export class UserController {
filter: param.filter,
col_any_eq: ["email"],
col_any_like: ["User.name", "User.username"],
additional_where: "roles.is_doctor = :is_doctor",
additional_where: "roles.name = :doctor_role_name",
});
whereVal.is_doctor = true;
whereVal.doctor_role_name = "Doctor";
const res_count = userRepository
.createQueryBuilder("User")
.leftJoin("User.roles", "roles")
.where(whereAttr, whereVal);
const res_count = userRepository.createQueryBuilder("User").leftJoin("User.roles", "roles").where(whereAttr, whereVal);
const subquery = userRepository
.createQueryBuilder("User")
.select("User.id", "id")
.leftJoin("User.roles", "roles")
.where(whereAttr, whereVal)
.orderBy("User." + param.order_field, param.order_direction)
.offset(offset)
.limit(param.limit)
.distinct(true);
const orderExpr = "User." + param.order_field;
const subquery = userRepository.createQueryBuilder("User").select("User.id", "id").addSelect(orderExpr, "_list_doctor_order").leftJoin("User.roles", "roles").where(whereAttr, whereVal).orderBy(orderExpr, param.order_direction).offset(offset).limit(param.limit).distinct(true);
const res_list = userRepository
.createQueryBuilder("User")
@ -255,20 +235,10 @@ export class UserController {
const mapped_data = list_data.map((user) => ({
...user,
profile_picture: user.profile_picture ?? null,
file_profile_picture: user.profile_picture
? `${config.server.host_swagger}uploads/doctor-profile/${user.profile_picture}`
: null,
file_profile_picture: user.profile_picture ? `${config.server.host_swagger}uploads/doctor-profile/${user.profile_picture}` : null,
}));
return ReturnHelper.successResponselist(
res,
200,
Language.lang.success_view,
count_data,
current_page,
total_count_data,
mapped_data,
);
return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, mapped_data);
} catch (e: unknown) {
log.error(e);
const err = e as Error;
@ -323,10 +293,7 @@ export class UserController {
const filename = "user.xlsx";
res.setHeader(
"Content-Type",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
);
res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
res.setHeader("Content-Disposition", "attachment; filename=" + filename);
const workbook = new exceljs.stream.xlsx.WorkbookWriter({ stream: res });
@ -348,25 +315,7 @@ export class UserController {
const fetchAndWrite = async (page: any) => {
const offset = (page - 1) * limit;
const data = await userRepository
.createQueryBuilder()
.where(whereAttr, whereVal)
.select([
"User.id as id",
"User.name as name",
"User.email as email",
"User.username as username",
"User.status as status",
"User.created_at as created_at",
"roles.name as role_name",
"application.id as application",
])
.leftJoin("User.roles", "roles")
.leftJoin("roles.application", "application")
.orderBy(param.order_field, param.order_direction)
.offset(offset)
.limit(limit)
.getRawMany();
const data = await userRepository.createQueryBuilder().where(whereAttr, whereVal).select(["User.id as id", "User.name as name", "User.email as email", "User.username as username", "User.status as status", "User.created_at as created_at", "roles.name as role_name", "application.id as application"]).leftJoin("User.roles", "roles").leftJoin("roles.application", "application").orderBy(param.order_field, param.order_direction).offset(offset).limit(limit).getRawMany();
data.forEach((item: any) => sheet.addRow(item).commit());
@ -410,11 +359,7 @@ export class UserController {
const schema = Joi.object().keys({
email: Joi.string().email().max(64).required().label("Email"),
username: Joi.string().max(64).required().label("Username"),
password: Joi.string()
.pattern(new RegExp("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"))
.min(8)
.required()
.label("Password"),
password: Joi.string().pattern(new RegExp("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$")).min(8).required().label("Password"),
retype_password: Joi.ref("password"),
name: Joi.string().max(64).required().label("Name"),
employee_id: Joi.string().uuid().optional().allow("").label("Employee ID"),
@ -431,9 +376,7 @@ export class UserController {
data.status = param.status;
if (param.employee_id && param.employee_id != "") {
const employee = await OrmHelper.DB.manager
.getRepository(HrmsEmployee)
.findOneByOrFail({ id: param.employee_id });
const employee = await OrmHelper.DB.manager.getRepository(HrmsEmployee).findOneByOrFail({ id: param.employee_id });
data.employee = employee;
} else {
data.employee = null;
@ -505,23 +448,11 @@ export class UserController {
for (let r of data.roles) {
if (r.id == param.id_role) {
//already added
return ReturnHelper.errorResponse(
res,
409,
401,
Language.lang.failed_insert + ", roles already exists ",
"",
);
return ReturnHelper.errorResponse(res, 409, 401, Language.lang.failed_insert + ", roles already exists ", "");
}
if (r.application.id == new_role?.application.id) {
return ReturnHelper.errorResponse(
res,
409,
402,
Language.lang.failed_insert + ", roles in this application already exists ",
"",
);
return ReturnHelper.errorResponse(res, 409, 402, Language.lang.failed_insert + ", roles in this application already exists ", "");
}
}
}
@ -604,13 +535,7 @@ export class UserController {
return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, data);
} else {
return ReturnHelper.errorResponse(
res,
404,
401,
Language.lang.failed_not_found + ", role not found",
"",
);
return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found + ", role not found", "");
}
} else {
return ReturnHelper.errorResponse(res, 404, 402, Language.lang.failed_not_found, "");
@ -702,11 +627,7 @@ export class UserController {
try {
const schema = Joi.object().keys({
id: Joi.string().uuid().required().label("ID"),
password: Joi.string()
.pattern(new RegExp("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"))
.min(8)
.required()
.label("Password"),
password: Joi.string().pattern(new RegExp("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$")).min(8).required().label("Password"),
retype_password: Joi.ref("password"),
});
@ -738,11 +659,7 @@ export class UserController {
}
}
static async uploadFile(
files: fileUpload.FileArray | null | undefined,
fieldName: string,
folderPath: string
): Promise<string | null> {
static async uploadFile(files: fileUpload.FileArray | null | undefined, fieldName: string, folderPath: string): Promise<string | null> {
if (!files || !files[fieldName]) return null;
const file = files[fieldName] as fileUpload.UploadedFile;
@ -752,13 +669,15 @@ export class UserController {
throw { message: `${fieldName}: File size exceeds 1MB limit` };
}
const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
const allowedMimes = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
if (!allowedMimes.includes(file.mimetype)) {
throw { message: `${fieldName}: Invalid file type. Only JPG, PNG, and WEBP are allowed` };
throw {
message: `${fieldName}: Invalid file type. Only JPG, PNG, and WEBP are allowed`,
};
}
const ext = file.name.split('.');
const name = uuidv4() + '.' + ext[ext.length - 1];
const ext = file.name.split(".");
const name = uuidv4() + "." + ext[ext.length - 1];
const file_path = path.join(folderPath, name);
if (!fs.existsSync(folderPath)) {
@ -825,7 +744,7 @@ export class UserController {
name: Joi.string().max(64).required().label("Name"),
employee_id: Joi.string().uuid().optional().allow("").label("Employee ID"),
status: Joi.string().required().label("Status"),
profile_picture: Joi.string().allow('', null).optional().label("Profile Picture"),
profile_picture: Joi.string().allow("", null).optional().label("Profile Picture"),
});
req.body.id = req.params["id"];
@ -837,10 +756,10 @@ export class UserController {
const data = await userRepository.findOneBy({ id: param.id });
if (data != null) {
const doctorprofile = await UserController.uploadFile(req.files, 'profile_picture', 'uploads/doctor-profile');
const doctorprofile = await UserController.uploadFile(req.files, "profile_picture", "uploads/doctor-profile");
if (doctorprofile) {
if (data.profile_picture) {
const oldFilePath = path.join('uploads/doctor-profile', data.profile_picture);
const oldFilePath = path.join("uploads/doctor-profile", data.profile_picture);
await UserController.deleteFile(oldFilePath);
}
data.profile_picture = doctorprofile;
@ -884,7 +803,6 @@ export class UserController {
return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_update, err.message);
}
}
static async updateUserSignature(req: Request, res: Response, next: NextFunction): Promise<Response> {
@ -920,7 +838,7 @@ export class UserController {
try {
const schema = Joi.object().keys({
id: Joi.string().uuid().required().label("ID"),
user_signature: Joi.string().allow('', null).optional().label("User Signature"),
user_signature: Joi.string().allow("", null).optional().label("User Signature"),
});
req.body.id = req.params["id"];
@ -932,12 +850,12 @@ export class UserController {
const data = await userRepository.findOneBy({ id: param.id });
if (data != null) {
const usersignature = await UserController.uploadFile(req.files, 'user_signature', 'uploads/user-signature');
const usersignature = await UserController.uploadFile(req.files, "user_signature", "uploads/user-signature");
if (usersignature) {
// ← Hapus signature lama jika ada
if (data.signature) {
const oldFilePath = path.join('uploads/user-signature', data.signature);
const oldFilePath = path.join("uploads/user-signature", data.signature);
await UserController.deleteFile(oldFilePath);
}
@ -1009,16 +927,8 @@ export class UserController {
const userRepository = OrmHelper.DB.getRepository(User);
const userRTRepository = OrmHelper.DB.getRepository(UserRefreshToken);
const affected = (
!param.hard
? await userRepository.softDelete({ id: param.id })
: await userRepository.delete({ id: param.id })
).affected ?? 0;
const affected_rt = (
!param.hard
? await userRTRepository.softDelete({ id_user: param.id })
: await userRTRepository.delete({ id_user: param.id })
).affected;
const affected = (!param.hard ? await userRepository.softDelete({ id: param.id }) : await userRepository.delete({ id: param.id })).affected ?? 0;
const affected_rt = (!param.hard ? await userRTRepository.softDelete({ id_user: param.id }) : await userRTRepository.delete({ id_user: param.id })).affected;
if (affected > 0) {
return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {});
@ -1056,19 +966,7 @@ export class UserController {
const userRepository = OrmHelper.DB.getRepository(User);
const detail = await userRepository.findOne({
relations: [
"roles",
"roles.application",
"employee",
"employee.department",
"employee.position",
"employee.shift",
"employee.nationality",
"employee.aldeia",
"employee.suco",
"employee.administrativu",
"employee.munisipo",
],
relations: ["roles", "roles.application", "employee", "employee.department", "employee.position", "employee.shift", "employee.nationality", "employee.aldeia", "employee.suco", "employee.administrativu", "employee.munisipo"],
where: { id: param.id },
});
@ -1076,13 +974,9 @@ export class UserController {
const data = {
...detail,
signature: detail.signature ?? null,
file_signature: detail.signature
? `${config.server.host_swagger}uploads/user-signature/${detail.signature}`
: null,
file_signature: detail.signature ? `${config.server.host_swagger}uploads/user-signature/${detail.signature}` : null,
profile: detail.profile_picture ?? null,
profile_picture: detail.profile_picture
? `${config.server.host_swagger}uploads/doctor-profile/${detail.profile_picture}`
: null,
profile_picture: detail.profile_picture ? `${config.server.host_swagger}uploads/doctor-profile/${detail.profile_picture}` : null,
};
return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, data);