import { Response, NextFunction } from "express" import { Request } from "express-jwt" import { ReturnHelper } from "../helpers/express/return" import { OrmHelper } from "../helpers/orm" import Joi from "joi" import config from "config" import { ILogObj, Logger } from "tslog" import { Language } from "../langs/lang" import { UAParser } from "ua-parser-js" import dayjs from "dayjs" import jwt from "jsonwebtoken" import fs from "fs" import { v4 as uuidv4 } from "uuid" import { User, UserRefreshToken, UserRole } from "entity" import axios from "axios" import CommonHelper from "../helpers/common" import { MoreThan } from "typeorm" const log: Logger = new Logger({ name: "[UserController]", type: "pretty" }) export class AuthController { static async login(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Auth'] #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/login" } } } } */ try { 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"), }) const param: User & { application: string } = await schema.validateAsync(req.body) const userRepository = OrmHelper.DB.getRepository(User) const userRefreshTokenRepository = OrmHelper.DB.getRepository(UserRefreshToken) const user = await userRepository.findOne({ // relations: ["roles", "roles.application", "employee", "employee.department", "employee.position", "employee.shift"], select: ["id", "email", "name", "password", "username", "roles"], where: { username: param.username }, }) if (!user) { 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)) { const { browser, device, os } = UAParser(req.get("User-Agent")) const refresh_token = new UserRefreshToken() refresh_token.id_user = user.id refresh_token.user_agent = req.get("User-Agent") refresh_token.remote_addr = req.ip.split(":").pop() refresh_token.browser = browser.name + " " + browser.version refresh_token.os = os.name + " " + os.version refresh_token.device = (device.model ?? "") + " " + (device.vendor ?? "") refresh_token.platform = device.type ?? "desktop" refresh_token.expired_at = dayjs().add(config.get("auth.refresh_token_lifetime_day"), "day").toDate() await userRefreshTokenRepository.save(refresh_token) // const userRoleRepository = OrmHelper.DB.getRepository(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 (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, }, user: user, } 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") // } 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 return ReturnHelper.errorResponse(res, 500, 403, Language.lang.failed_access, err.message) } } // static async getListRole(roles: any): Promise { // // const repoMenu = OrmHelper.DB.getRepository(Menu) // const whereAttr = [] // const whereVal = {} // for (let k in roles) { // let p = roles[k] // 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") // // 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() // // 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 // // 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 // // 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 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 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] // // } // // } // // } // // } // // } // // } // // } // // } // // } // // } // // } // // } // // return list_data_final // // } // return {} // } static async logout(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Auth'] #swagger.parameters['refresh_token'] = { in: 'path', description: 'Refresh Token', required: true, type: 'string' } */ try { const schema = Joi.object().keys({ refresh_token: Joi.string().uuid().required().label("Refresh Token"), }) const param: UserRefreshToken = await schema.validateAsync(req.params) const repo = OrmHelper.DB.getRepository(UserRefreshToken) const affected = (await repo.delete({ refresh_token: param.refresh_token })).affected ?? 0 if (affected > 0) { return ReturnHelper.successResponseAny(res, 200, Language.lang.success_logout) } else { return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_logout, "") } } catch (e: unknown) { log.error(e) const err = e as Error return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_logout, err.message) } } static async renewToken(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Auth'] #swagger.parameters['refresh_token'] = { in: 'path', description: 'Refresh Token', required: true, type: 'string' } #swagger.parameters['application'] = { in: 'path', description: 'Application', required: true, type: 'string' } */ try { const schema = Joi.object().keys({ refresh_token: Joi.string().uuid().required().label("Refresh Token"), application: Joi.string().required().label("Application"), }) const param: UserRefreshToken & { application: string } = await schema.validateAsync(req.params) const userRefreshTokenRepo = OrmHelper.DB.getRepository(UserRefreshToken) const refresh_token = await userRefreshTokenRepo.findOneBy({ refresh_token: param.refresh_token, expired_at: MoreThan(new Date()) }) if (refresh_token != null) { const userRepo = OrmHelper.DB.getRepository(User) const user = await userRepo.findOne({ relations: ["roles", "roles.application"], where: { id: refresh_token.id_user } }) 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 // 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 }); if (roles) { 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, }, }, privateKey, { algorithm: "RS256" } ) return ReturnHelper.successResponseAny(res, 200, Language.lang.success, { token, role_name: roles.name, // 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") } } else { return ReturnHelper.errorResponse(res, 404, 403, Language.lang.failed, "") } } catch (e: unknown) { log.error(e) const err = e as Error return ReturnHelper.errorResponse(res, 500, 404, Language.lang.failed, err.message) } } 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", content: { "application/json": { schema: { $ref: "#/components/schemas/user_password" } } } } */ try { const schema = Joi.object().keys({ reset_token: Joi.string().uuid().required().label("Token"), password: Joi.string().required().label("Password"), retype_password: Joi.ref("password"), }) req.body.reset_token = req.params.token ?? "" const param: User = await schema.validateAsync(req.body) const userRepository = OrmHelper.DB.getRepository(User) const data = await userRepository.findOneBy({ reset_token: param.reset_token }) if (data != null) { data.password = param.password data.reset_token = null data.hashPassword() await userRepository.save(data) return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update) } 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) } } }