From 1c5195d98690e9d676e6b7ce4457b35dab922b91 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Tue, 7 Jul 2026 09:03:33 +0700 Subject: [PATCH] update for revenue dashboard --- config/default.json | 8 +- src/controllers/auth.ts | 441 ++++++++----------- src/controllers/file/file.ts | 223 ---------- src/controllers/user.ts | 708 ++++++------------------------- src/controllers/user_activity.ts | 220 +++++----- src/controllers/user_roles.ts | 54 +-- src/helpers/express/return.ts | 2 +- src/helpers/orm.ts | 33 -- src/routes/private.ts | 24 +- src/routes/public.ts | 1 - src/swagger/builder.js | 10 +- 11 files changed, 478 insertions(+), 1246 deletions(-) delete mode 100644 src/controllers/file/file.ts diff --git a/config/default.json b/config/default.json index 766e0b2..adb832b 100644 --- a/config/default.json +++ b/config/default.json @@ -22,10 +22,10 @@ "database": { "engine": "postgres", "host": "127.0.0.1", - "port": "15432", - "username": "saude_stag", - "password": "gM*#o>3W4&5X", - "database": "saude_stag", + "port": "5432", + "username": "rivanwayan", + "password": "", + "database": "revenue", "synchronize": true, "logging": true }, diff --git a/src/controllers/auth.ts b/src/controllers/auth.ts index da4b16a..05cdc08 100644 --- a/src/controllers/auth.ts +++ b/src/controllers/auth.ts @@ -11,7 +11,7 @@ import dayjs from "dayjs" import jwt from "jsonwebtoken" import fs from "fs" import { v4 as uuidv4 } from "uuid" -import { Application, EmailBody, HospitalInformation, HrmsEmployee, Menu, User, UserRefreshToken, UserRole } from "entity" +import { User, UserRefreshToken, UserRole } from "entity" import axios from "axios" import CommonHelper from "../helpers/common" import { MoreThan } from "typeorm" @@ -38,7 +38,7 @@ export class AuthController { const schema = Joi.object().keys({ username: Joi.string().max(64).required().label("Username"), password: Joi.string().min(8).required().label("Password"), - application: Joi.string().required().label("Application"), + // application: Joi.string().required().label("Application"), }) const param: User & { application: string } = await schema.validateAsync(req.body) @@ -47,7 +47,7 @@ export class AuthController { const userRefreshTokenRepository = OrmHelper.DB.getRepository(UserRefreshToken) const user = await userRepository.findOne({ - relations: ["roles", "roles.application", "employee", "employee.department", "employee.position", "employee.shift"], + // relations: ["roles", "roles.application", "employee", "employee.department", "employee.position", "employee.shift"], select: ["id", "email", "name", "password", "username", "roles"], where: { username: param.username }, }) @@ -56,7 +56,9 @@ export class AuthController { return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "Username or password is incorrect") } - if (user != null && user.roles != null && user.roles.length > 0 && user.checkIfPasswordMatch(param.password)) { + if ( + // user != null && user.roles != null && user.roles.length > 0 && + user.checkIfPasswordMatch(param.password)) { const { browser, device, os } = UAParser(req.get("User-Agent")) const refresh_token = new UserRefreshToken() @@ -73,107 +75,109 @@ export class AuthController { // const userRoleRepository = OrmHelper.DB.getRepository(UserRole); - let role_id = "" - let roles: UserRole + // let role_id = "" + // let roles: UserRole - if (user.roles) { - for (let r of user.roles) { - if (r.application.id == param.application) { - role_id = r.id - roles = r - } - } + // if (user.roles) { + // for (let r of user.roles) { + // if (r.application.id == param.application) { + // role_id = r.id + // roles = r + // } + // } + // } + + // if (role_id == "") { + // return ReturnHelper.errorResponse(res, 403, 403, Language.lang.failed_access, "You don't have any roles in this application") + // } else { + // const userRole = await userRoleRepository.findOneBy({ id: role_id }) + // var hrms = { + // uuid: null, + // idx: null, + // employee_id: null, + // employeename: null, + // title: null, + // department_id: null, + // departement_uuid: null, + // department_name: null, + // level_id: null, + // level_name: null, + // } + + // If user has employee, populate hrms data + // if (user.employee && user.employee.id) { + // hrms.uuid = user.employee.id + // hrms.employee_id = user.employee.employee_id || null + // hrms.employeename = user.employee.name || null + // hrms.title = user.employee.position?.name || null + // hrms.department_id = user.employee.department?.id || null + // hrms.departement_uuid = user.employee.department?.id || null + // hrms.department_name = user.employee.department?.name || null + // hrms.level_id = user.employee.position?.id || null + // hrms.level_name = user.employee.position?.name || null + // } + + // const roomQuery = `SELECT r.id, r.room, r.code + // FROM rooms r + // INNER JOIN user_to_room utr ON utr.room_id = r.id + // WHERE utr.user_id = $1 AND r.deleted_at IS NULL AND utr.deleted_at IS NULL` + // const rooms = await OrmHelper.DB.query(roomQuery, [user.id]) + + // const facility = await OrmHelper.DB.getRepository(HospitalInformation).findOne({ + // select: { + // id: true, + // facility_id: true, + // name: true, + // address: true, + // phone: true, + // code: true, + // }, + // where: {}, + // }) + + var privateKey = fs.readFileSync("src/helpers/key/private.key") + + var token = jwt.sign( + { + exp: Math.floor(Date.now() / 1000) + Number(config.get("auth.access_token_lifetime")), + data: { + id: user.id, + username: user.username, + name: user.name, + // id_role: role_id, + // roles: roles.roles, + // // hrms: hrms, + }, + }, + privateKey, + { algorithm: "RS256" } + ) + + delete user.password + + const data = { + // user: { ...user, ...hrms, rooms }, + // role_name: roles.name, + // default_page: roles.default_page ?? null, + // facility: facility ?? null, + // roles_list: await AuthController.getListRole(roles.roles), + token: { + access_token: token, + refresh_token: refresh_token.refresh_token, + }, } - if (role_id == "") { - return ReturnHelper.errorResponse(res, 403, 403, Language.lang.failed_access, "You don't have any roles in this application") - } else { - // const userRole = await userRoleRepository.findOneBy({ id: role_id }) - var hrms = { - uuid: null, - idx: null, - employee_id: null, - employeename: null, - title: null, - department_id: null, - departement_uuid: null, - department_name: null, - level_id: null, - level_name: null, - } - - // If user has employee, populate hrms data - if (user.employee && user.employee.id) { - hrms.uuid = user.employee.id - hrms.employee_id = user.employee.employee_id || null - hrms.employeename = user.employee.name || null - hrms.title = user.employee.position?.name || null - hrms.department_id = user.employee.department?.id || null - hrms.departement_uuid = user.employee.department?.id || null - hrms.department_name = user.employee.department?.name || null - hrms.level_id = user.employee.position?.id || null - hrms.level_name = user.employee.position?.name || null - } - - const roomQuery = `SELECT r.id, r.room, r.code - FROM rooms r - INNER JOIN user_to_room utr ON utr.room_id = r.id - WHERE utr.user_id = $1 AND r.deleted_at IS NULL AND utr.deleted_at IS NULL` - const rooms = await OrmHelper.DB.query(roomQuery, [user.id]) - - const facility = await OrmHelper.DB.getRepository(HospitalInformation).findOne({ - select: { - id: true, - facility_id: true, - name: true, - address: true, - phone: true, - code: true, - }, - where: {}, - }) - - var privateKey = fs.readFileSync("src/helpers/key/private.key") - - var token = jwt.sign( - { - exp: Math.floor(Date.now() / 1000) + Number(config.get("auth.access_token_lifetime")), - data: { - id: user.id, - username: user.username, - name: user.name, - id_role: role_id, - roles: roles.roles, - hrms: hrms, - }, - }, - privateKey, - { algorithm: "RS256" } - ) - - delete user.password - - const data = { - user: { ...user, ...hrms, rooms }, - role_name: roles.name, - default_page: roles.default_page ?? null, - facility: facility ?? null, - roles_list: await AuthController.getListRole(roles.roles), - token: { - access_token: token, - refresh_token: refresh_token.refresh_token, - }, - } - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_login, data) - } + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_login, data) } + // } - if (user.roles == null || user.roles.length == 0) { - return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "You don't have any roles in this application") - } else { - return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "Username or password is incorrect") - } + // if (user.roles == null || user.roles.length == 0) { + // return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "You don't have any roles in this application") + // } else { + // return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "Username or password is incorrect") + // } + + return ReturnHelper.errorResponse(res, 401, 401, Language.lang.failed_access, "Username or password is incorrect") } catch (e: unknown) { log.error(e) const err = e as Error @@ -182,93 +186,93 @@ export class AuthController { } } - static async getListRole(roles: any): Promise { - const repoMenu = OrmHelper.DB.getRepository(Menu) + // static async getListRole(roles: any): Promise { + // // const repoMenu = OrmHelper.DB.getRepository(Menu) - const whereAttr = [] - const whereVal = {} + // const whereAttr = [] + // const whereVal = {} - for (let k in roles) { - let p = roles[k] + // for (let k in roles) { + // let p = roles[k] - whereAttr.push("id = :id" + k) - whereVal["id" + k] = p - } + // whereAttr.push("id = :id" + k) + // whereVal["id" + k] = p + // } - const res_list = repoMenu - .createQueryBuilder() - .where("(" + whereAttr.join(" or ") + ") and id_parent is null", whereVal) - .orderBy("order_number", "ASC") + // // const res_list = repoMenu + // // .createQueryBuilder() + // // .where("(" + whereAttr.join(" or ") + ") and id_parent is null", whereVal) + // // .orderBy("order_number", "ASC") - let list_data_final = await res_list.getMany() + // // let list_data_final = await res_list.getMany() - if (list_data_final.length > 0) { - const res_list_all = await repoMenu - .createQueryBuilder() - .where("(" + whereAttr.join(" or ") + ") and id_parent is not null", whereVal) - .orderBy("order_number", "ASC") - .getMany() + // // if (list_data_final.length > 0) { + // // const res_list_all = await repoMenu + // // .createQueryBuilder() + // // .where("(" + whereAttr.join(" or ") + ") and id_parent is not null", whereVal) + // // .orderBy("order_number", "ASC") + // // .getMany() - let list_child: any = {} - for (let p of res_list_all) { - if (!list_child[p.id_parent]) { - list_child[p.id_parent] = [] - } + // // let list_child: any = {} + // // for (let p of res_list_all) { + // // if (!list_child[p.id_parent]) { + // // list_child[p.id_parent] = [] + // // } - delete p.status - delete p.created_at - delete p.updated_at - delete p.deleted_at + // // delete p.status + // // delete p.created_at + // // delete p.updated_at + // // delete p.deleted_at - list_child[p.id_parent].push(p) - } + // // list_child[p.id_parent].push(p) + // // } - for (let f of list_data_final) { - delete f.status - delete f.created_at - delete f.updated_at - delete f.deleted_at + // // for (let f of list_data_final) { + // // delete f.status + // // delete f.created_at + // // delete f.updated_at + // // delete f.deleted_at - if (list_child[f.id]) { - f.children = list_child[f.id] + // // if (list_child[f.id]) { + // // f.children = list_child[f.id] - for (let g of f.children) { - if (list_child[g.id]) { - g.children = list_child[g.id] + // // for (let g of f.children) { + // // if (list_child[g.id]) { + // // g.children = list_child[g.id] - for (let h of g.children) { - if (list_child[h.id]) { - h.children = list_child[h.id] + // // for (let h of g.children) { + // // if (list_child[h.id]) { + // // h.children = list_child[h.id] - for (let i of h.children) { - if (list_child[i.id]) { - i.children = list_child[i.id] + // // for (let i of h.children) { + // // if (list_child[i.id]) { + // // i.children = list_child[i.id] - for (let j of i.children) { - if (list_child[j.id]) { - j.children = list_child[j.id] + // // for (let j of i.children) { + // // if (list_child[j.id]) { + // // j.children = list_child[j.id] - for (let k of j.children) { - if (list_child[k.id]) { - k.children = list_child[k.id] - } - } - } - } - } - } - } - } - } - } - } - } + // // for (let k of j.children) { + // // if (list_child[k.id]) { + // // k.children = list_child[k.id] + // // } + // // } + // // } + // // } + // // } + // // } + // // } + // // } + // // } + // // } + // // } + // // } - return list_data_final - } + // // return list_data_final + // // } - return {} - } + // return {} + // } static async logout(req: Request, res: Response, next: NextFunction): Promise { /* @@ -305,79 +309,6 @@ export class AuthController { } } - static async resetPassword(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['Auth'] - #swagger.parameters['email'] = { - in: 'path', - description: 'Email', - required: true, - type: 'string' - } - #swagger.parameters['application'] = { - in: 'path', - description: 'Application', - required: true, - type: 'string' - } - */ - - try { - const schema = Joi.object().keys({ - email: Joi.string().email().required().label("Email"), - application: Joi.string().required().label("Application"), - }) - - const param: { email: string; application: string } = await schema.validateAsync(req.params) - - const userRepository = OrmHelper.DB.getRepository(User) - - const user = await userRepository.findOne({ - where: { email: param.email }, - }) - - user.reset_token = uuidv4() - - await userRepository.save(user) - - //send email reset password - const url = config.get("service.notification") + "api/email/send" - - // console.log(url, 'url') - - const template_raw = fs.readFileSync("assets/reset_password.html").toString() - - const appRepository = OrmHelper.DB.getRepository(Application) - const app = await appRepository.findOneBy({ id: param.application }) - - const pairMap = { - // 'LINK': config.get('auth.reset_password_url') + user.reset_token, - LINK: app.reset_password_url + user.reset_token, - NAME: user.name, - } - - const template = CommonHelper.generateTemplate(template_raw, pairMap) - - const email: EmailBody = { - subject: "Reset Password", - to: [user.email], - cc: [], - bcc: [], - html: template, - } - - const { data } = await axios.post(url, email) - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success, data) - - // return ReturnHelper.errorResponse(res, 403, 401, Language.lang.failed_access); - } catch (e: unknown) { - log.error(e) - const err = e as Error - - return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed, err.message) - } - } static async renewToken(req: Request, res: Response, next: NextFunction): Promise { /* @@ -415,17 +346,17 @@ export class AuthController { let role_id = "" let roles: UserRole - if (user.roles != null && user.roles.length > 0) { - for (let r of user.roles) { - if (r.application.id == param.application) { - role_id = r.id + // if (user.roles != null && user.roles.length > 0) { + // for (let r of user.roles) { + // if (r.application.id == param.application) { + // role_id = r.id - roles = r - } - } - } else { - return ReturnHelper.errorResponse(res, 403, 401, Language.lang.failed_access + ", you don't have any roles in this application") - } + // roles = r + // } + // } + // } else { + // return ReturnHelper.errorResponse(res, 403, 401, Language.lang.failed_access + ", you don't have any roles in this application") + // } // const userRoleRepository = OrmHelper.DB.getRepository(UserRole); // const userRole = await userRoleRepository.findOneBy({ id: role_id }); @@ -451,7 +382,7 @@ export class AuthController { return ReturnHelper.successResponseAny(res, 200, Language.lang.success, { token, role_name: roles.name, - roles_list: await AuthController.getListRole(roles.roles), + // roles_list: await AuthController.getListRole(roles.roles), }) } else { return ReturnHelper.errorResponse(res, 403, 402, Language.lang.failed_access + ", you don't have any roles in this application") @@ -470,14 +401,14 @@ export class AuthController { static async updatePassword(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Auth'] - + #swagger.parameters['token'] = { in: 'path', description: 'Token', required: true, type: 'string' } - + #swagger.requestBody = { required: true, description: "This action will effect to user related token", diff --git a/src/controllers/file/file.ts b/src/controllers/file/file.ts deleted file mode 100644 index 083c32d..0000000 --- a/src/controllers/file/file.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Response, NextFunction } from "express"; -import { Request } from "express-jwt"; -import { ReturnHelper } from "../../helpers/express/return"; -import Joi from "joi"; -import { ILogObj, Logger } from "tslog"; -import { Language } from "../../langs/lang"; -import fileUpload from "express-fileupload"; -import { v4 as uuidv4 } from 'uuid'; -import fs from "fs"; -import path from "path"; - -const log: Logger = new Logger({ name: '[FileController]', type: 'pretty' }); -const STORAGE_DIR = 'assets/saude/'; - -export class FileController { - static async upload(req: Request, res: Response, next: NextFunction): Promise { - /* #swagger.tags = ['Handle File'] - #swagger.security = [{ "bearerAuth": [] }] - #swagger.consumes = ['multipart/form-data'] - #swagger.requestBody = { - required: true, - content: { - "multipart/form-data": { - schema: { - type: "object", - properties: { - file: { - type: "string", - format: "binary", - description: "Image file (max 1MB, jpg/png/webp)" - }, - }, - required: ["file"] - } - } - } - } - */ - try { - // Check if file exists - if (!req.files || !req.files.file) { - return ReturnHelper.errorResponse(res, 400, 404, Language.lang.failed_insert, "File not found"); - } - - const file = req.files.file as fileUpload.UploadedFile; - - // Validate file size (max 1MB) - const maxSize = 10 * 1024 * 1024; // 10MB - if (file.size > maxSize) { - return ReturnHelper.errorResponse( - res, - 400, - 400, - Language.lang.failed_insert, - "File size exceeds 10MB limit" - ); - } - - // Validate file type - const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/svg+xml', 'image/svg']; - if (!allowedMimes.includes(file.mimetype)) { - return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_insert, "Invalid file type. Only JPG, PNG, WEBP, and SVG are allowed"); - } - const ext = file.name.split('.'); - const name = uuidv4() + '.' + ext[ext.length - 1]; - - const result: { file: string } = { - file: "", - }; - - const folder_path = STORAGE_DIR; - const file_path = path.join(folder_path, name); - - // Create directory if it doesn't exist - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, { recursive: true }); - } - - // Move file to destination - await file.mv(file_path); - - result.file = name; - - log.info(`File uploaded successfully: ${file_path}`); - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_insert, result); - - } catch (e: unknown) { - // Proper error logging to avoid tslog serialization issues - if (e instanceof Error) { - log.error("File upload failed:", { - message: e.message, - stack: e.stack, - name: e.name - }); - return ReturnHelper.errorResponse(res, 500, 500, Language.lang.failed_insert, e.message); - } else { - log.error("File upload failed with unknown error:", String(e)); - return ReturnHelper.errorResponse(res, 500, 500, Language.lang.failed_insert, "Unknown error occurred"); - } - } - } - - static async delete(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['Handle File'] - #swagger.security = [{ "bearerAuth": [] }] - #swagger.requestBody = { - required: true, - content: { - "application/json": { - schema: { - type: "object", - properties: { - file_name: { type: "string", description: "File name to delete" } - }, - required: ["file_name"] - } - } - } - } - */ - - try { - const schema = Joi.object().keys({ - file_name: Joi.string().required().label('File Name'), - }); - - const param: { file_name: string } = await schema.validateAsync(req.body); - - if (param.file_name.includes('..') || param.file_name.includes('/') || param.file_name.includes('\\')) { - return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_delete, "Invalid file name"); - } - - const file_path = path.join(STORAGE_DIR, param.file_name); - - // Check if file exists - if (!fs.existsSync(file_path)) { - return ReturnHelper.errorResponse(res, 404, 404, Language.lang.failed_not_found, "File not found"); - } - - // Delete file - fs.unlinkSync(file_path); - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {}); - } catch (e: unknown) { - log.error(e); - const err = e as Error; - - return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_delete, err.message); - } - } - - static async download(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['Handle File'] - #swagger.security = [{ "bearerAuth": [] }] - #swagger.parameters['name'] = { - in: 'query', - required: true, - type: 'string', - description: 'File name' - } - #swagger.parameters['token'] = { - in: 'query', - required: true, - type: 'string', - description: 'Token' - } - */ - try { - const schema = Joi.object({ - name: Joi.string().required().label("File Name"), - token: Joi.string().required().label("Token"), - }); - - const param: { name: string } = await schema.validateAsync(req.query); - - if (param.name.includes('..') || param.name.includes('/') || param.name.includes('\\')) { - return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_not_found, "Invalid file name"); - } - - const file_path = path.join(STORAGE_DIR, param.name); - - if (!fs.existsSync(file_path)) { - return ReturnHelper.errorResponse(res, 404, 404, Language.lang.failed_not_found, "File not found"); - } - - const buffer = fs.readFileSync(file_path); - const fileName = param.name; - const ext = fileName.split(".").pop()?.toLowerCase(); - - const mimeMap: Record = { - jpg: "image/jpeg", - jpeg: "image/jpeg", - png: "image/png", - webp: "image/webp", - gif: "image/gif", - pdf: "application/pdf", - svg: "image/svg+xml", - }; - - const contentType = mimeMap[ext ?? ""] ?? "application/octet-stream"; - - res.setHeader("Content-Type", contentType); - res.setHeader("Content-Length", buffer.length); - - const isInline = contentType.startsWith("image/") || contentType === "application/pdf"; - - if (isInline) { - res.setHeader("Content-Disposition", `inline; filename="${encodeURIComponent(fileName)}"`); - } else { - res.setHeader("Content-Disposition", `attachment; filename="${encodeURIComponent(fileName)}"`); - } - - return res.end(buffer); - } catch (e: unknown) { - log.error(e); - const err = e as Error; - return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_not_found, err.message); - } - } -} \ No newline at end of file diff --git a/src/controllers/user.ts b/src/controllers/user.ts index 78249c0..d07059b 100644 --- a/src/controllers/user.ts +++ b/src/controllers/user.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { HospitalInformation, HrmsEmployee, Paging, User, UserRefreshToken, UserRole, UserToRoom } from "entity"; +import { Paging, User, UserRefreshToken } from "entity"; import exceljs from "exceljs"; import { NextFunction, Response } from "express"; import { Request } from "express-jwt"; @@ -9,23 +9,13 @@ import CommonHelper from "../helpers/common"; import { ReturnHelper } from "../helpers/express/return"; import { OrmHelper } from "../helpers/orm"; 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 { IsNull, Not } from "typeorm"; +import { IsNull } from "typeorm"; const log: Logger = new Logger({ name: "[UserController]", type: "pretty", }); -const STORAGE_DIR = "assets/saude/"; -const buildFileUrl = (fileName: string): string => { - const base = config.server.host_swagger.endsWith("/") ? config.server.host_swagger : `${config.server.host_swagger}/`; - return `${base}${STORAGE_DIR}${fileName}`; -}; export class UserController { static async list(req: Request, res: Response, next: NextFunction): Promise { @@ -85,7 +75,7 @@ export class UserController { const { whereAttr, whereVal } = CommonHelper.handleFilter({ filter: param.filter, col_any_eq: ["email"], - col_any_like: ["User.name", "User.username", "roles.name"], // ← tambah roles.name + col_any_like: ["User.name", "User.username"], }); const res_count = userRepository @@ -96,7 +86,7 @@ export class UserController { const subquery = userRepository .createQueryBuilder("User") .select("User.id", "id") - .leftJoin("User.roles", "roles") + // .leftJoin("User.roles", "roles") .where(whereAttr, whereVal) .orderBy("User." + param.order_field, param.order_direction) .offset(offset) @@ -106,8 +96,8 @@ export class UserController { .createQueryBuilder("User") .innerJoin("(" + subquery.getQuery() + ")", "sub", "User.id = sub.id") .setParameters(subquery.getParameters()) - .leftJoinAndSelect("User.roles", "roles") - .leftJoinAndSelect("roles.application", "application") + // .leftJoinAndSelect("User.roles", "roles") + // .leftJoinAndSelect("roles.application", "application") .orderBy("User." + param.order_field, param.order_direction); // const subquery = userRepository @@ -146,228 +136,6 @@ export class UserController { // ← Tambahkan mapping ini const mapped_data = list_data.map((user) => ({ ...user, - profile_picture: user.profile_picture ?? null, - file_profile_picture: user.profile_picture ? buildFileUrl(user.profile_picture) : null, - })); - - 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; - - return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message); - } - } - - static async listDoctor(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ "bearerAuth": [] }] - #swagger.parameters['filter'] = { - description: 'Filter with 2 format :
  • Simple format use text plaint will filter eq:email or like %name% or like %username%
  • Advance format using field existing {status:\'Y\', any:\'SAME AS PLAINT LOGIC\', etc...}
', - in: 'query', - type: 'string' - } - #swagger.parameters['limit'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['page'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['with_deleted'] = { - in: 'query', - required: true, - type: 'boolean' - } - #swagger.parameters['order_field'] = { - in: 'query', - required: true, - type: 'string' - } - #swagger.parameters['order_direction'] = { - in: 'query', - required: true, - schema: { - '@enum': ['ASC', 'DESC'] - } - } - */ - try { - const schema = Joi.object().keys({ - filter: Joi.any().allow("").optional().label("Filter"), - page: Joi.number().required().min(1).label("Page"), - limit: Joi.number().required().min(1).label("Limit"), - with_deleted: Joi.bool().required().label("With Deleted"), - order_field: Joi.string().required().label("Order Field"), - order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"), - }); - - const param: Paging = await schema.validateAsync(req.query); - const userRepository = OrmHelper.DB.getRepository(User); - const offset = (param.page - 1) * param.limit; - - const { whereAttr, whereVal } = CommonHelper.handleFilter({ - filter: param.filter, - col_any_eq: ["email"], - col_any_like: ["User.name", "User.username"], - additional_where: "roles.is_doctor = true", - }); - - const res_count = userRepository - .createQueryBuilder("User") - .leftJoin("User.roles", "roles") - .where(whereAttr, whereVal); - - 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") - .innerJoin("(" + subquery.getQuery() + ")", "sub", "User.id = sub.id") - .setParameters(subquery.getParameters()) - .leftJoinAndSelect("User.roles", "roles") - .leftJoinAndSelect("roles.application", "application") - .orderBy("User." + param.order_field, param.order_direction) - .distinct(true); - - if (param.with_deleted) { - res_count.withDeleted(); - res_list.withDeleted(); - } - - const current_page = param.page; - const total_count_data = await res_count.getCount(); - const list_data = await res_list.getMany(); - const count_data = CommonHelper.countObject(list_data); - - const mapped_data = list_data.map((user) => ({ - ...user, - profile_picture: user.profile_picture ?? null, - file_profile_picture: user.profile_picture ? buildFileUrl(user.profile_picture) : null, - })); - - 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; - - return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message); - } - } - - static async listDoctorAndNurse(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ "bearerAuth": [] }] - #swagger.parameters['filter'] = { - description: 'Filter with 2 format :
  • Simple format use text plaint will filter eq:email or like %name% or like %username%
  • Advance format using field existing {status:\'Y\', any:\'SAME AS PLAINT LOGIC\', etc...}
', - in: 'query', - type: 'string' - } - #swagger.parameters['limit'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['page'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['with_deleted'] = { - in: 'query', - required: true, - type: 'boolean' - } - #swagger.parameters['order_field'] = { - in: 'query', - required: true, - type: 'string' - } - #swagger.parameters['order_direction'] = { - in: 'query', - required: true, - schema: { - '@enum': ['ASC', 'DESC'] - } - } - */ - try { - const schema = Joi.object().keys({ - filter: Joi.any().allow("").optional().label("Filter"), - page: Joi.number().required().min(1).label("Page"), - limit: Joi.number().required().min(1).label("Limit"), - with_deleted: Joi.bool().required().label("With Deleted"), - order_field: Joi.string().required().label("Order Field"), - order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"), - }); - - const param: Paging = await schema.validateAsync(req.query); - const userRepository = OrmHelper.DB.getRepository(User); - const offset = (param.page - 1) * param.limit; - - const { whereAttr, whereVal } = CommonHelper.handleFilter({ - filter: param.filter, - col_any_eq: ["email"], - col_any_like: ["User.name", "User.username"], - additional_where: "roles.is_doctor = true OR roles.is_nurse = true", - }); - - const res_count = userRepository - .createQueryBuilder("User") - .leftJoin("User.roles", "roles") - .where(whereAttr, whereVal); - - 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") - .innerJoin("(" + subquery.getQuery() + ")", "sub", "User.id = sub.id") - .setParameters(subquery.getParameters()) - .leftJoinAndSelect("User.roles", "roles") - .leftJoinAndSelect("roles.application", "application") - .orderBy("User." + param.order_field, param.order_direction) - .distinct(true); - - if (param.with_deleted) { - res_count.withDeleted(); - res_list.withDeleted(); - } - - const current_page = param.page; - const total_count_data = await res_count.getCount(); - const list_data = await res_list.getMany(); - const count_data = CommonHelper.countObject(list_data); - - const mapped_data = list_data.map((user) => ({ - ...user, - profile_picture: user.profile_picture ?? null, - file_profile_picture: user.profile_picture ? buildFileUrl(user.profile_picture) : null, - signature: user.signature ?? null, - file_signature: user.signature ? buildFileUrl(user.signature) : null, })); return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, mapped_data); @@ -491,11 +259,9 @@ export class UserController { const schema = Joi.object().keys({ email: Joi.string().email().max(64).allow("", null).optional().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().allow("").optional().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"), status: Joi.string().required().label("Status"), }); @@ -528,13 +294,6 @@ export class UserController { const data = new User(); - if (param.employee_id && param.employee_id != "") { - const employee = await OrmHelper.DB.manager.getRepository(HrmsEmployee).findOneByOrFail({ id: param.employee_id }); - data.employee = employee; - } else { - data.employee = null; - } - data.name = param.name; data.username = param.username; data.password = param.password; @@ -556,156 +315,156 @@ export class UserController { } } - static async addRole(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ - "bearerAuth": [] - }] + // static async addRole(req: Request, res: Response, next: NextFunction): Promise { + // /* + // #swagger.tags = ['User'] + // #swagger.security = [{ + // "bearerAuth": [] + // }] - #swagger.parameters['id'] = { - in: 'path', - description: 'User ID.', - required: true, - type: 'string' - } + // #swagger.parameters['id'] = { + // in: 'path', + // description: 'User ID.', + // required: true, + // type: 'string' + // } - #swagger.parameters['id_role'] = { - in: 'path', - description: 'User Role ID.', - required: true, - type: 'string' - } - */ + // #swagger.parameters['id_role'] = { + // in: 'path', + // description: 'User Role ID.', + // required: true, + // type: 'string' + // } + // */ - try { - const schema = Joi.object().keys({ - id: Joi.string().uuid().required().label("ID"), - id_role: Joi.string().uuid().required().label("ID"), - }); + // try { + // const schema = Joi.object().keys({ + // id: Joi.string().uuid().required().label("ID"), + // id_role: Joi.string().uuid().required().label("ID"), + // }); - const param: { id: string; id_role: string } = await schema.validateAsync(req.params); + // const param: { id: string; id_role: string } = await schema.validateAsync(req.params); - const userRepository = OrmHelper.DB.getRepository(User); - const repo_role = OrmHelper.DB.getRepository(UserRole); + // const userRepository = OrmHelper.DB.getRepository(User); + // const repo_role = OrmHelper.DB.getRepository(UserRole); - const data = await userRepository.findOne({ - relations: ["roles", "roles.application"], - where: { id: param.id }, - }); + // const data = await userRepository.findOne({ + // relations: ["roles", "roles.application"], + // where: { id: param.id }, + // }); - if (data != null) { - // const new_role = await repo_role.findOneBy({ id: param.id_role }); - const new_role = await repo_role.findOne({ - relations: { - application: true, - }, - where: { id: param.id_role }, - }); + // if (data != null) { + // // const new_role = await repo_role.findOneBy({ id: param.id_role }); + // const new_role = await repo_role.findOne({ + // relations: { + // application: true, + // }, + // where: { id: param.id_role }, + // }); - if (data.roles) { - 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 ", ""); - } + // if (data.roles) { + // 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 ", ""); + // } - if (r.application.id == new_role?.application.id) { - return ReturnHelper.errorResponse(res, 409, 402, Language.lang.failed_insert + ", roles in this application 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 ", ""); + // } + // } + // } - if (!data.roles) { - data.roles = []; - } + // if (!data.roles) { + // data.roles = []; + // } - data.roles.push(new_role); + // data.roles.push(new_role); - await userRepository.save(data); + // await userRepository.save(data); - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, data); - } else { - return ReturnHelper.errorResponse(res, 404, 403, Language.lang.failed_not_found, ""); - } - } catch (e: unknown) { - log.error(e); - const err = e as Error; + // return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, data); + // } else { + // return ReturnHelper.errorResponse(res, 404, 403, Language.lang.failed_not_found, ""); + // } + // } catch (e: unknown) { + // log.error(e); + // const err = e as Error; - return ReturnHelper.errorResponse(res, 500, 404, Language.lang.failed_update, err.message); - } - } + // return ReturnHelper.errorResponse(res, 500, 404, Language.lang.failed_update, err.message); + // } + // } - static async deleteRole(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ - "bearerAuth": [] - }] + // static async deleteRole(req: Request, res: Response, next: NextFunction): Promise { + // /* + // #swagger.tags = ['User'] + // #swagger.security = [{ + // "bearerAuth": [] + // }] - #swagger.parameters['id'] = { - in: 'path', - description: 'User ID.', - required: true, - type: 'string' - } + // #swagger.parameters['id'] = { + // in: 'path', + // description: 'User ID.', + // required: true, + // type: 'string' + // } - #swagger.parameters['id_role'] = { - in: 'path', - description: 'User Role ID.', - required: true, - type: 'string' - } - */ + // #swagger.parameters['id_role'] = { + // in: 'path', + // description: 'User Role ID.', + // required: true, + // type: 'string' + // } + // */ - try { - const schema = Joi.object().keys({ - id: Joi.string().uuid().required().label("ID"), - id_role: Joi.string().uuid().required().label("ID"), - }); + // try { + // const schema = Joi.object().keys({ + // id: Joi.string().uuid().required().label("ID"), + // id_role: Joi.string().uuid().required().label("ID"), + // }); - const param: { id: string; id_role: string } = await schema.validateAsync(req.params); + // const param: { id: string; id_role: string } = await schema.validateAsync(req.params); - const userRepository = OrmHelper.DB.getRepository(User); + // const userRepository = OrmHelper.DB.getRepository(User); - const data = await userRepository.findOne({ - relations: { - roles: true, - }, - where: { id: param.id }, - }); + // const data = await userRepository.findOne({ + // relations: { + // roles: true, + // }, + // where: { id: param.id }, + // }); - if (data != null && data.roles) { - let found = false; - let new_roles: UserRole[] = []; + // if (data != null && data.roles) { + // let found = false; + // let new_roles: UserRole[] = []; - for (let r of data.roles) { - if (r.id != param.id_role) { - new_roles.push(r); - } else if (r.id == param.id_role) { - found = true; - } - } + // for (let r of data.roles) { + // if (r.id != param.id_role) { + // new_roles.push(r); + // } else if (r.id == param.id_role) { + // found = true; + // } + // } - data.roles = new_roles; + // data.roles = new_roles; - if (found) { - await userRepository.save(data); + // if (found) { + // await userRepository.save(data); - 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", ""); - } - } else { - return ReturnHelper.errorResponse(res, 404, 402, Language.lang.failed_not_found, ""); - } - } catch (e: unknown) { - log.error(e); - const err = e as Error; + // 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", ""); + // } + // } else { + // return ReturnHelper.errorResponse(res, 404, 402, Language.lang.failed_not_found, ""); + // } + // } catch (e: unknown) { + // log.error(e); + // const err = e as Error; - return ReturnHelper.errorResponse(res, 500, 403, Language.lang.failed_delete, err.message); - } - } + // return ReturnHelper.errorResponse(res, 500, 403, Language.lang.failed_delete, err.message); + // } + // } static async updateProfile(req: Request, res: Response, next: NextFunction): Promise { /* @@ -818,47 +577,6 @@ export class UserController { } } - static async uploadFile(files: fileUpload.FileArray | null | undefined, fieldName: string): Promise { - if (!files || !files[fieldName]) return null; - - const file = files[fieldName] as fileUpload.UploadedFile; - - const maxSize = 1 * 1024 * 1024; // 1MB - if (file.size > maxSize) { - throw { message: `${fieldName}: File size exceeds 1MB limit` }; - } - - const allowedMimes = ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/svg+xml", "image/svg"]; - if (!allowedMimes.includes(file.mimetype)) { - throw { - message: `${fieldName}: Invalid file type. Only JPG, PNG, WEBP, and SVG are allowed`, - }; - } - - const ext = file.name.split("."); - const name = uuidv4() + "." + ext[ext.length - 1]; - const file_path = path.join(STORAGE_DIR, name); - - if (!fs.existsSync(STORAGE_DIR)) { - fs.mkdirSync(STORAGE_DIR, { recursive: true }); - } - - await file.mv(file_path); - log.info(`File uploaded successfully: ${file_path}`); - - return name; - } - - static async deleteFile(filePath: string): Promise { - try { - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - log.info(`File deleted successfully: ${filePath}`); - } - } catch (e) { - log.error(`Failed to delete file: ${filePath}`, e); - } - } static async update(req: Request, res: Response, next: NextFunction): Promise { /* @@ -877,19 +595,10 @@ export class UserController { #swagger.requestBody = { required: true, content: { - "multipart/form-data": { + "application/json": { schema: { - type: "object", - properties: { - email: { type: "string", description: "Email" }, - username: { type: "string", description: "Username" }, - password: { type: "string", description: "Password" }, - retype_password: { type: "string", description: "Retype Password" }, - name: { type: "string", description: "name" }, - status: { type: "string", description: "Status" }, - profile_picture: { type: "string", description: "String" } - } - } + $ref: "#/components/schemas/user" + } } } } @@ -903,9 +612,7 @@ export class UserController { password: Joi.string().allow("").optional().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"), status: Joi.string().required().label("Status"), - profile_picture: Joi.string().allow("", null).optional().label("Profile Picture"), }); req.body.id = req.params["id"]; @@ -925,7 +632,6 @@ export class UserController { // } // } - data.profile_picture = param.profile_picture || data.profile_picture; data.name = param.name; data.username = param.username; if (param.email !== undefined) { @@ -968,97 +674,6 @@ export class UserController { } } - static async updateUserSignature(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ - "bearerAuth": [] - }] - #swagger.consumes = ['multipart/form-data'] - #swagger.parameters['id'] = { - in: 'path', - description: 'User ID.', - required: true, - type: 'string' - } - - #swagger.requestBody = { - required: true, - content: { - "multipart/form-data": { - schema: { - type: "object", - required: ["user_signature"], - properties: { - user_signature: { type: "string", format: "binary", description: "Image file (max 1MB, jpg/png/webp)" } - } - } - } - } - } - */ - - try { - const schema = Joi.object().keys({ - id: Joi.string().uuid().required().label("ID"), - user_signature: Joi.string().allow("", null).optional().label("User Signature"), - }); - - req.body.id = req.params["id"]; - - const param: any = await schema.validateAsync(req.body); - - const userRepository = OrmHelper.DB.getRepository(User); - - const data = await userRepository.findOneBy({ id: param.id }); - - if (data != null) { - const usersignature = await UserController.uploadFile(req.files, "user_signature"); - - if (usersignature) { - // ← Hapus signature lama jika ada - if (data.signature) { - const oldFilePath = path.join(STORAGE_DIR, data.signature); - await UserController.deleteFile(oldFilePath); - } - - data.signature = usersignature; - } - - // data.name = param.name; - // data.username = param.username; - // data.email = param.email; - // data.status = param.status; - // data.updated_by = req.auth?.data.name; - // data.updated_at = new Date(); - - // if (param.employee_id && param.employee_id != "") { - // const employee = await OrmHelper.DB.manager - // .getRepository(HrmsEmployee) - // .findOneByOrFail({id: param.employee_id}); - // data.employee = employee; - // } else { - // data.employee = null; - // } - - // if (param.password) { - // data.password = param.password; - // data.hashPassword(); - // } - - await userRepository.save(data); - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, data); - } else { - return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); - } - } catch (e: unknown) { - log.error(e); - const err = e as Error; - - return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_update, err.message); - } - } static async delete(req: Request, res: Response, next: NextFunction): Promise { /* @@ -1106,65 +721,6 @@ export class UserController { } } - static async detail(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User'] - #swagger.security = [{ - "bearerAuth": [] - }] - #swagger.parameters['id'] = { - in: 'path', - description: 'User ID.', - required: true, - type: 'string' - } - */ - try { - const schema = Joi.object().keys({ - id: Joi.string().uuid().required().label("ID"), - }); - - const param: User = await schema.validateAsync(req.params); - - 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"], - where: { id: param.id }, - }); - - const repoHospital = OrmHelper.DB.getRepository(HospitalInformation); - const hospital = await repoHospital.findOne({ where: { id: Not(IsNull()) } }); - - if (detail != null) { - const query = `SELECT r.id, r.room, r.code - FROM rooms r - INNER JOIN user_to_room utr ON utr.room_id = r.id - WHERE utr.user_id = $1 AND r.deleted_at IS NULL AND utr.deleted_at IS NULL` - const rooms = await OrmHelper.DB.query(query, [detail.id]) - - const data = { - ...detail, - rooms: rooms, - signature: detail.signature ?? null, - file_signature: detail.signature ? buildFileUrl(detail.signature) : null, - profile: detail.profile_picture ?? null, - profile_picture: detail.profile_picture ? buildFileUrl(detail.profile_picture) : null, - hospital: hospital, - }; - - return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, data); - } else { - return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); - } - } catch (e: unknown) { - log.error(e); - const err = e as Error; - - return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_view, err.message); - } - } - static async restore(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['User'] diff --git a/src/controllers/user_activity.ts b/src/controllers/user_activity.ts index dbcffb9..059fa68 100644 --- a/src/controllers/user_activity.ts +++ b/src/controllers/user_activity.ts @@ -5,7 +5,7 @@ import { OrmHelper } from "../helpers/orm" import Joi from "joi" import { ILogObj, Logger } from "tslog" import { Language } from "../langs/lang" -import { ActivityTypeReverse, Application, Paging, User } from "entity" +import { ActivityTypeReverse, Paging, User } from "entity" import CommonHelper from "../helpers/common" import { ActivityType } from "entity" import * as fastcsv from "fast-csv" @@ -22,50 +22,50 @@ interface PagingActivity extends Paging { export class UserActivityController { static async list(req: Request, res: Response, next: NextFunction): Promise { /* - #swagger.tags = ['User Activity'] - #swagger.parameters['filter'] = { - description: 'Filter with 2 format :
  • Simple format use text plaint will filter like %module% or like %description%
  • Advance format using field existing {action:\'C\', any:\'SAME AS PLAINT LOGIC\', etc...}
', - in: 'query', - type: 'string' - } - #swagger.parameters['limit'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['page'] = { - in: 'query', - required: true, - type: 'number' - } - #swagger.parameters['with_deleted'] = { - in: 'query', - required: true, - type: 'boolean' - } - #swagger.parameters['all_user'] = { - in: 'query', - required: true, - type: 'boolean' - } - #swagger.parameters['order_field'] = { - in: 'query', - required: true, - type: 'string' - } - #swagger.parameters['order_direction'] = { - in: 'query', - required: true, - schema: { - '@enum': ['ASC', 'DESC'] - } - } - #swagger.parameters['token'] = { - in: 'query', - required: true, - type: 'string' - } - */ + #swagger.tags = ['User Activity'] + #swagger.parameters['filter'] = { + description: 'Filter with 2 format :
  • Simple format use text plaint will filter like %module% or like %description%
  • Advance format using field existing {action:\'C\', any:\'SAME AS PLAINT LOGIC\', etc...}
', + in: 'query', + type: 'string' + } + #swagger.parameters['limit'] = { + in: 'query', + required: true, + type: 'number' + } + #swagger.parameters['page'] = { + in: 'query', + required: true, + type: 'number' + } + #swagger.parameters['with_deleted'] = { + in: 'query', + required: true, + type: 'boolean' + } + #swagger.parameters['all_user'] = { + in: 'query', + required: true, + type: 'boolean' + } + #swagger.parameters['order_field'] = { + in: 'query', + required: true, + type: 'string' + } + #swagger.parameters['order_direction'] = { + in: 'query', + required: true, + schema: { + '@enum': ['ASC', 'DESC'] + } + } + #swagger.parameters['token'] = { + in: 'query', + required: true, + type: 'string' + } + */ try { const schema = Joi.object().keys({ @@ -96,8 +96,28 @@ export class UserActivityController { whereVal["id_user"] = req.auth.data.id } - const res_count = userRepository.createQueryBuilder("user_activity").leftJoinAndSelect("user_activity.application", "application").leftJoinAndSelect("user_activity.user", "user").where(whereAttr, whereVal) - const res_list = userRepository.createQueryBuilder("user_activity").leftJoinAndSelect("user_activity.application", "application").leftJoinAndSelect("user_activity.user", "user").where(whereAttr, whereVal).orderBy(param.order_field, param.order_direction).offset(offset).limit(param.limit) + // Whitelist kolom yang boleh dipakai untuk order, sekaligus hindari ambiguous column + const allowedOrderFields: Record = { + created_at: "user_activity.created_at", + updated_at: "user_activity.updated_at", + module: "user_activity.module", + description: "user_activity.description", + action: "user_activity.action", + // tambahkan kolom lain sesuai kebutuhan + } + + const order_field = allowedOrderFields[param.order_field] ?? "user_activity.created_at" + + const res_count = userRepository.createQueryBuilder("user_activity") + // .leftJoinAndSelect("user_activity.application", "application") + .leftJoinAndSelect("user_activity.user", "user").where(whereAttr, whereVal) + + const res_list = userRepository.createQueryBuilder("user_activity") + // .leftJoinAndSelect("user_activity.application", "application") + .leftJoinAndSelect("user_activity.user", "user").where(whereAttr, whereVal) + .orderBy(order_field, param.order_direction) + .offset(offset) + .limit(param.limit) if (param.with_deleted) { res_count.withDeleted() @@ -120,39 +140,39 @@ export class UserActivityController { static async export(req: Request, res: Response, next: NextFunction): Promise { /* - #swagger.tags = ['User Activity'] - #swagger.parameters['filter'] = { - description: 'Filter with 2 format :
  • Simple format use text plaint will filter like %module% or like %description%
  • Advance format using field existing {action:\'C\', any:\'SAME AS PLAINT LOGIC\', etc...}
', - in: 'query', - type: 'string' - } - #swagger.parameters['filter'] = { - in: 'query', - type: 'string' - } - #swagger.parameters['all_user'] = { - in: 'query', - required: true, - type: 'boolean' - } - #swagger.parameters['order_field'] = { - in: 'query', - required: true, - type: 'string' - } - #swagger.parameters['order_direction'] = { - in: 'query', - required: true, - schema: { - '@enum': ['ASC', 'DESC'] - } - } - #swagger.parameters['token'] = { - in: 'query', - required: true, - type: 'string' - } - */ + #swagger.tags = ['User Activity'] + #swagger.parameters['filter'] = { + description: 'Filter with 2 format :
  • Simple format use text plaint will filter like %module% or like %description%
  • Advance format using field existing {action:\'C\', any:\'SAME AS PLAINT LOGIC\', etc...}
', + in: 'query', + type: 'string' + } + #swagger.parameters['filter'] = { + in: 'query', + type: 'string' + } + #swagger.parameters['all_user'] = { + in: 'query', + required: true, + type: 'boolean' + } + #swagger.parameters['order_field'] = { + in: 'query', + required: true, + type: 'string' + } + #swagger.parameters['order_direction'] = { + in: 'query', + required: true, + schema: { + '@enum': ['ASC', 'DESC'] + } + } + #swagger.parameters['token'] = { + in: 'query', + required: true, + type: 'string' + } + */ try { const schema = Joi.object().keys({ @@ -260,23 +280,23 @@ export class UserActivityController { static async create(req: Request, res: Response, next: NextFunction): Promise { /* - #swagger.tags = ['User Activity'] - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.requestBody = { - required: true, - description: "Use this to fill action Create = C, View = V, Update = U, Delete = D, Restore = T", - content: { - "application/json": { - schema: { - $ref: "#/components/schemas/user_activity" - } - } - } - } - */ + #swagger.tags = ['User Activity'] + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.requestBody = { + required: true, + description: "Use this to fill action Create = C, View = V, Update = U, Delete = D, Restore = T", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/user_activity" + } + } + } + } + */ try { const schema = Joi.object().keys({ @@ -290,12 +310,12 @@ export class UserActivityController { .label("Action"), url: Joi.string().optional().allow("").label("URL"), data_body: Joi.string().optional().allow("").label("data_body"), - application: Joi.string().required().label("Application"), + // application: Joi.string().required().label("Application"), }) const param: UserActivity & { application: string; id_user: string } = await schema.validateAsync(req.body) - const repo_app = OrmHelper.DB.getRepository(Application) + // const repo_app = OrmHelper.DB.getRepository(Application) const repo_user = OrmHelper.DB.getRepository(User) const data = new UserActivity() @@ -306,7 +326,7 @@ export class UserActivityController { data.action = param.action data.url = param.url data.data_body = param.data_body - data.application = await repo_app.findOneBy({ id: param.application }) + // data.application = await repo_app.findOneBy({ id: param.application }) await OrmHelper.DB.manager.save(data) diff --git a/src/controllers/user_roles.ts b/src/controllers/user_roles.ts index b3078a3..a202d64 100644 --- a/src/controllers/user_roles.ts +++ b/src/controllers/user_roles.ts @@ -4,10 +4,10 @@ import { OrmHelper } from "../helpers/orm" import Joi from "joi" import { ILogObj, Logger } from "tslog" import { Language } from "../langs/lang" -import { Application, Paging, User, UserRole } from "entity" +import { Paging, User, UserRole } from "entity" import CommonHelper from "../helpers/common" import { Status } from "entity" -import { RoleList } from "entity" +// import { RoleList } from "entity" import * as fastcsv from "fast-csv" import dayjs from "dayjs" import { Request } from "express-jwt" @@ -195,30 +195,30 @@ export class UserRoleController { } } - static async role(req: Request, res: Response, next: NextFunction): Promise { - /* - #swagger.tags = ['User Roles'] - #swagger.parameters['token'] = { - in: 'query', - required: true, - type: 'string' - } - */ + // static async role(req: Request, res: Response, next: NextFunction): Promise { + // /* + // #swagger.tags = ['User Roles'] + // #swagger.parameters['token'] = { + // in: 'query', + // required: true, + // type: 'string' + // } + // */ - try { - const current_page = 1 - const total_count_data = CommonHelper.countObject(RoleList) - const list_data = CommonHelper.objectFlip(RoleList) - const count_data = CommonHelper.countObject(RoleList) + // try { + // const current_page = 1 + // const total_count_data = CommonHelper.countObject(RoleList) + // const list_data = CommonHelper.objectFlip(RoleList) + // const count_data = CommonHelper.countObject(RoleList) - return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, list_data) - } catch (e: unknown) { - log.error(e) - const err = e as Error + // return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, list_data) + // } catch (e: unknown) { + // log.error(e) + // const err = e as Error - return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message) - } - } + // return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message) + // } + // } static async create(req: Request, res: Response, next: NextFunction): Promise { /* @@ -255,13 +255,13 @@ export class UserRoleController { const param: UserRole & { application: string } = await schema.validateAsync(req.body) - const repo_app = OrmHelper.DB.getRepository(Application) + // const repo_app = OrmHelper.DB.getRepository(Application) const data = new UserRole() data.name = param.name data.roles = param.roles data.status = param.status - data.application = await repo_app.findOneBy({ id: param.application }) + // data.application = await repo_app.findOneBy({ id: param.application }) data.is_doctor = param.is_doctor data.is_nurse = param.is_nurse data.default_page = param.default_page?.trim() || null @@ -329,12 +329,12 @@ export class UserRoleController { const data = await repo.findOneBy({ id: param.id }) if (data != null) { - const repo_app = OrmHelper.DB.getRepository(Application) + // const repo_app = OrmHelper.DB.getRepository(Application) data.name = param.name data.roles = param.roles data.status = param.status - data.application = await repo_app.findOneBy({ id: param.application }) + // data.application = await repo_app.findOneBy({ id: param.application }) data.is_doctor = param.is_doctor data.is_nurse = param.is_nurse data.default_page = param.default_page?.trim() || null diff --git a/src/helpers/express/return.ts b/src/helpers/express/return.ts index 2132b3f..317531e 100644 --- a/src/helpers/express/return.ts +++ b/src/helpers/express/return.ts @@ -1,5 +1,5 @@ import { Response } from 'express'; -import { ErrorType, ErrorValidation } from 'entity'; +// import { ErrorType, ErrorValidation } from 'entity'; export class ReturnHelper { static successResponseAny( diff --git a/src/helpers/orm.ts b/src/helpers/orm.ts index 0459c77..1becbcd 100644 --- a/src/helpers/orm.ts +++ b/src/helpers/orm.ts @@ -1,26 +1,10 @@ import config from "config" import { DataSource } from "typeorm" import { - Aldeia, - Suco, - Munisipo, - Administrativu, - Nationality, - Application, - Menu, User, UserActivity, UserRefreshToken, UserRole, - HrmsPosition, - HrmsEmployee, - HrmsDepartment, - HrmsShift, - HospitalInformation, - Province, - City, - Subdistrict, - Ward, } from "entity" import { ILogObj, Logger } from "tslog" @@ -42,27 +26,10 @@ export class OrmHelper { synchronize: true, logging: config.get("database.logging"), entities: [ - Aldeia, - Suco, - Munisipo, - Administrativu, - Nationality, User, // UserRole, UserActivity, UserRefreshToken, - Menu, - Application, - Nationality, - HrmsDepartment, - HrmsPosition, - HrmsShift, - HrmsEmployee, - HospitalInformation, - Province, - City, - Subdistrict, - Ward, ], subscribers: [], migrations: [], diff --git a/src/routes/private.ts b/src/routes/private.ts index 91082f5..409b069 100644 --- a/src/routes/private.ts +++ b/src/routes/private.ts @@ -5,7 +5,6 @@ import { UserActivityController } from '../controllers/user_activity'; import { UserRefreshTokenController } from '../controllers/user_refresh_token'; import JwtHelper from '../helpers/jwt'; import { UserRoleController } from '../controllers/user_roles'; -import { FileController } from '../controllers/file/file'; export class RoutePrivate { static setup(app: express.Application) { @@ -15,27 +14,21 @@ export class RoutePrivate { JwtHelper.secure(app); app.get('/api/user/list', UserController.list) - app.get('/api/user/list_doctor', UserController.listDoctor) - app.get('/api/user/list_doctor_nurse', UserController.listDoctorAndNurse) app.get('/api/user/export', UserController.export) app.post('/api/user/create', UserController.create) app.put('/api/user/update/:id', UserController.update) - app.put('/api/user/add_role/:id/:id_role', UserController.addRole) - app.put('/api/user/delete_role/:id/:id_role', UserController.deleteRole) app.put('/api/user/update_profile', UserController.updateProfile) app.put('/api/user/update_password', UserController.updatePassword) - app.get('/api/user/detail/:id', UserController.detail) app.delete('/api/user/delete/:id/:hard', UserController.delete) app.put('/api/user/restore/:id', UserController.restore) - app.put('/api/user/update-signature/:id', UserController.updateUserSignature) - app.get('/api/user_role/list', UserRoleController.list) - app.get('/api/user_role/export', UserRoleController.export) - // app.get('/api/user_role/role', UserRoleController.role) - app.post('/api/user_role/create', UserRoleController.create) - app.put('/api/user_role/update/:id', UserRoleController.update) - app.delete('/api/user_role/delete/:id/:hard', UserRoleController.delete) - app.put('/api/user_role/restore/:id', UserRoleController.restore) + // app.get('/api/user_role/list', UserRoleController.list) + // app.get('/api/user_role/export', UserRoleController.export) + // // app.get('/api/user_role/role', UserRoleController.role) + // app.post('/api/user_role/create', UserRoleController.create) + // app.put('/api/user_role/update/:id', UserRoleController.update) + // app.delete('/api/user_role/delete/:id/:hard', UserRoleController.delete) + // app.put('/api/user_role/restore/:id', UserRoleController.restore) app.get('/api/user_activity/list', UserActivityController.list) app.get('/api/user_activity/export', UserActivityController.export) @@ -44,8 +37,5 @@ export class RoutePrivate { app.get('/api/refresh_token/list', UserRefreshTokenController.list) app.put('/api/refresh_token/force_logout/:refresh_token', UserRefreshTokenController.forceLogout) - app.post('/api/upload', FileController.upload) - app.delete('/api/delete', FileController.delete) - app.get('/api/download', FileController.download) } } \ No newline at end of file diff --git a/src/routes/public.ts b/src/routes/public.ts index 8f95235..2f07fa9 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -11,7 +11,6 @@ export class RoutePublic { app.use(Language.apply); app.post('/api/login', AuthController.login) - app.put('/api/reset_password/:email/:application', AuthController.resetPassword) app.put('/api/update_password/:token', AuthController.updatePassword) app.put('/api/logout/:refresh_token', AuthController.logout) app.put('/api/renew_token/:refresh_token/:application', AuthController.renewToken) diff --git a/src/swagger/builder.js b/src/swagger/builder.js index c4747ae..ad9dac2 100644 --- a/src/swagger/builder.js +++ b/src/swagger/builder.js @@ -18,20 +18,12 @@ const doc = { login: { $username: "admin", $password: "12345aA!", - $application: "saude", }, user: { - $employee_id: "EMP001", email: "admin@gmail.com", $username: "admin", $password: "12345aA!", - $retype_password: "12345aA!", $name: "Administrator", - // $phone: "08123456789", - // $jabatan: "4a6991b6-78a0-4b3b-bf5c-fa0af5f69be8", - // $organisasi: "4a6991b6-78a0-4b3b-bf5c-fa0af5f69be8", - // $divisi: "4a6991b6-78a0-4b3b-bf5c-fa0af5f69be8", - // $unit: "4a6991b6-78a0-4b3b-bf5c-fa0af5f69be8", $status: "Y" }, user_profile: { @@ -59,7 +51,7 @@ const doc = { $action: "V", $url: "https://bri-dev.shiblysolution.id/", $data_body: "{\"from\":\"2024-11-01\",\"to\":\"2024-11-26\"}", - $application: "ukln" + // $application: "ukln" } }, parameters: {