update entity type and reset password

This commit is contained in:
2024-11-30 12:56:44 +07:00
parent 37aa7842fd
commit 388fff9175
21 changed files with 137 additions and 315 deletions

View File

@ -0,0 +1,73 @@
<!doctype html>
<html lang="en-US">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Reset Password BRI TL</title>
<meta name="description" content="Reset Password BRI TL">
<style type="text/css">
a:hover {
text-decoration: underline !important;
}
</style>
</head>
<body marginheight="0" topmargin="0" marginwidth="0" style="margin: 0px; background-color: #f2f3f8;" leftmargin="0">
<!--100% body table-->
<table cellspacing="0" border="0" cellpadding="0" width="100%" bgcolor="#f2f3f8"
style="@import url(https://fonts.googleapis.com/css?family=Rubik:300,400,500,700|Open+Sans:300,400,600,700); font-family: 'Open Sans', sans-serif;">
<tr>
<td>
<table style="background-color: #f2f3f8; max-width:670px; margin:0 auto;" width="100%" border="0"
align="center" cellpadding="0" cellspacing="0">
<tr>
<td style="height:80px;">&nbsp;</td>
</tr>
<tr>
<td style="height:20px;">&nbsp;</td>
</tr>
<tr>
<td>
<table width="95%" border="0" align="center" cellpadding="0" cellspacing="0"
style="max-width:670px;background:#fff; border-radius:3px; text-align:center;-webkit-box-shadow:0 6px 18px 0 rgba(0,0,0,.06);-moz-box-shadow:0 6px 18px 0 rgba(0,0,0,.06);box-shadow:0 6px 18px 0 rgba(0,0,0,.06);">
<tr>
<td style="height:40px;">&nbsp;</td>
</tr>
<tr>
<td style="padding:0 35px;">
<h1
style="color:#1e1e2d; font-weight:500; margin:0;font-size:32px;font-family:'Rubik',sans-serif;">
Hi {NAME}, You have requested to reset your password</h1>
<span
style="display:inline-block; vertical-align:middle; margin:29px 0 26px; border-bottom:1px solid #cecece; width:100px;"></span>
<p style="color:#455056; font-size:15px;line-height:24px; margin:0;">
We cannot simply send you your old password. A unique link to reset your
password has been generated for you. To reset your password, click the
following link and follow the instructions.
</p>
<a href="{LINK}"
style="background:#20e277;text-decoration:none !important; font-weight:500; margin-top:35px; color:#fff;text-transform:uppercase; font-size:14px;padding:10px 24px;display:inline-block;border-radius:50px;">Reset
Password</a>
</td>
</tr>
<tr>
<td style="height:40px;">&nbsp;</td>
</tr>
</table>
</td>
<tr>
<td style="height:20px;">&nbsp;</td>
</tr>
<tr>
<td style="height:80px;">&nbsp;</td>
</tr>
</table>
</td>
</tr>
</table>
<!--/100% body table-->
</body>
</html>

View File

@ -29,6 +29,10 @@
},
"auth": {
"refresh_token_lifetime_day": 30,
"access_token_lifetime": 300
"access_token_lifetime": 300,
"reset_password_url": "https://bri-dev.shiblysolution.id/reset-password/"
},
"service": {
"notification": "http://127.0.0.1:4008/"
}
}

View File

@ -29,6 +29,10 @@
},
"auth": {
"refresh_token_lifetime_day": 30,
"access_token_lifetime": 300
"access_token_lifetime": 300,
"reset_password_url": "https://bri-dev.shiblysolution.id/reset-password/"
},
"service": {
"notification": "http://127.0.0.1:4008/"
}
}

View File

@ -1,11 +1,11 @@
{
"name": "boilerplate-bri",
"name": "Service user",
"version": "1.0.0",
"description": "This kind of project was built to present base application regarding BRI ERP Project",
"description": "Purpose of this service is to provide authorization, roles, user activity and etc",
"main": "src/index.ts",
"scripts": {
"dev": "nodemon",
"build": "rimraf dist && tsc",
"build": "tsc",
"swagger": "node src/swagger/builder.js"
},
"keywords": [
@ -14,11 +14,13 @@
"author": "STS",
"license": "ISC",
"dependencies": {
"axios": "^1.7.8",
"bcryptjs": "^2.4.3",
"compression": "^1.7.5",
"config": "^3.3.12",
"cors": "^2.8.5",
"dayjs": "^1.11.13",
"Entity": "file:../entity",
"express": "^4.21.1",
"express-jwt": "^8.4.1",
"fast-csv": "^5.0.2",

View File

@ -2,18 +2,18 @@ import { Response, NextFunction, Application } from "express";
import { Request } from 'express-jwt'
import { ReturnHelper } from "../helpers/express/return";
import { OrmHelper } from "../helpers/orm";
import { User } from "../entity/users";
import Joi from "joi";
import config from "config";
import { ILogObj, Logger } from "tslog";
import { Language } from "../langs/lang";
import { UserRefreshToken } from "../entity/user_refresh_token";
import { UAParser } from 'ua-parser-js';
import dayjs from 'dayjs'
import jwt from 'jsonwebtoken';
import fs from 'fs';
import { UserRole } from "../entity/users_roles";
import { v4 as uuidv4 } from 'uuid';
import { EmailBody, User, UserRefreshToken, UserRole } from "Entity";
import axios from 'axios';
import CommonHelper from "../helpers/common";
const log: Logger<ILogObj> = new Logger({ name: '[UserController]', type: 'pretty' });
@ -170,8 +170,30 @@ export class AuthController {
await userRepository.save(user);
//send email reset password
const url = config.get("service.notification") + "api/email/send";
return ReturnHelper.successResponseAny(res, 200, Language.lang.success, {});
console.log(url, 'url')
const template_raw = fs.readFileSync('assets/reset_password.html').toString();
const pairMap = {
'LINK': config.get('auth.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);

View File

@ -2,11 +2,11 @@ import { Response, NextFunction, Application } from "express";
import { Request } from "express-jwt";
import { ReturnHelper } from "../helpers/express/return";
import { OrmHelper } from "../helpers/orm";
import { User } from "../entity/users";
import { User } from "Entity";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Language } from "../langs/lang";
import { Paging } from "../types/paging";
import { Paging } from "Entity";
import CommonHelper from "../helpers/common";
import * as fastcsv from 'fast-csv';
import dayjs from "dayjs";

View File

@ -5,12 +5,12 @@ import { OrmHelper } from "../helpers/orm";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Language } from "../langs/lang";
import { Paging } from "../types/paging";
import { Paging } from "Entity";
import CommonHelper from "../helpers/common";
import { ActivityType } from "../types/action";
import { UserActivity } from "../entity/user_activity";
import { ActivityType } from "Entity";
import * as fastcsv from 'fast-csv';
import dayjs from "dayjs";
import { UserActivity } from "Entity";
const log: Logger<ILogObj> = new Logger({ name: '[UserActivityController]', type: 'pretty' });

View File

@ -5,9 +5,9 @@ import { OrmHelper } from "../helpers/orm";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Language } from "../langs/lang";
import { Paging } from "../types/paging";
import { Paging } from "Entity";
import CommonHelper from "../helpers/common";
import { UserRefreshToken } from "../entity/user_refresh_token";
import { UserRefreshToken } from "Entity";
const log: Logger<ILogObj> = new Logger({ name: '[UserRefreshTokenController]', type: 'pretty' });

View File

@ -4,13 +4,13 @@ import { OrmHelper } from "../helpers/orm";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Language } from "../langs/lang";
import { Paging } from "../types/paging";
import { Paging } from "Entity";
import CommonHelper from "../helpers/common";
import { Status } from "../types/status";
import { UserRole } from "../entity/users_roles";
import { RoleList } from "../types/role";
import { Status } from "Entity";
import { RoleList } from "Entity";
import * as fastcsv from 'fast-csv';
import dayjs from "dayjs";
import { UserRole } from "Entity";
const log: Logger<ILogObj> = new Logger({ name: '[UserRoleController]', type: 'pretty' });

View File

@ -1,64 +0,0 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Index } from 'typeorm';
import { ActivityType } from '../types/action';
@Entity('user_activities')
export class UserActivity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Index()
@Column({
type: 'uuid'
})
id_user: string;
@Index()
@Column({
type: 'uuid'
})
refresh_token: string;
@Index()
@Column({
nullable: false,
length: 512
})
module: string;
@Column({
nullable: false,
length: 2048
})
description: string;
@Column({
nullable: true,
length: 1
})
action: ActivityType;
@Column({
nullable: false,
length: 2048,
})
url: String;
@Column({
type: 'text',
nullable: true,
})
data_body: String;
@Column()
@CreateDateColumn()
created_at: Date;
@Column({ nullable: true })
@UpdateDateColumn()
updated_at: Date;
@Column({ nullable: true })
@DeleteDateColumn()
deleted_at: Date;
}

View File

@ -1,64 +0,0 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Index } from 'typeorm';
@Entity('user_refresh_tokens')
export class UserRefreshToken {
@PrimaryGeneratedColumn('uuid')
refresh_token: string;
@Index()
@Column({
type: 'uuid'
})
id_user: string;
@Column({
nullable: false,
length: 4096,
})
user_agent: string;
@Column({
nullable: true,
length: 128
})
os: string;
@Column({
nullable: true,
length: 256
})
device: string;
@Column({
nullable: true,
length: 64
})
platform: string;
@Column({
nullable: true,
length: 64
})
browser: string;
@Column({
nullable: true,
length: 32
})
remote_addr: string;
@Column('timestamp')
expired_at: Date;
@Column()
@CreateDateColumn()
created_at: Date;
@Column({ nullable: true })
@UpdateDateColumn()
updated_at: Date;
@Column({ nullable: true })
@DeleteDateColumn()
deleted_at: Date;
}

View File

@ -1,76 +0,0 @@
import bcrypt from 'bcryptjs';
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Index } from 'typeorm';
import { Status } from '../types/status';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({
nullable: false,
unique: true,
length: 64
})
email: string;
@Column({
nullable: false,
length: 64,
select: false
})
password: string;
@Column({
nullable: false,
unique: true,
length: 64
})
username: string;
@Column({
nullable: true,
length: 64
})
name: string;
@Column({
nullable: true,
type: 'uuid'
})
id_role: string;
@Index()
@Column({
nullable: true,
type: 'uuid'
})
reset_token: string;
@Column({
nullable: false,
length: 64
})
status: Status;
@Column()
@CreateDateColumn()
created_at: Date;
@Column({ nullable: true })
@UpdateDateColumn()
updated_at: Date;
@Column({ nullable: true })
@DeleteDateColumn()
deleted_at: Date;
hashPassword() {
this.password = bcrypt.hashSync(this.password, 8);
}
checkIfPasswordMatch(unencryptedPassword: string) {
return bcrypt.compareSync(unencryptedPassword, this.password);
}
}

View File

@ -1,41 +0,0 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
import { Status } from '../types/status';
import { RoleList } from '../types/role';
@Entity('user_roles')
export class UserRole {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({
nullable: false,
unique: true,
length: 128
})
name: string;
@Column({
nullable: false,
type: 'jsonb'
})
roles: RoleList[];
@Column({
nullable: false,
length: 64
})
status: Status;
@Column()
@CreateDateColumn()
created_at: Date;
@Column({ nullable: true })
@UpdateDateColumn()
updated_at: Date;
@Column({ nullable: true })
@DeleteDateColumn()
deleted_at: Date;
}

View File

@ -81,4 +81,14 @@ export default class CommonHelper {
return '';
}
}
static generateTemplate(template: string, pairMap: any): string {
for (let p in pairMap) {
let v = pairMap[p];
template = template.split('{' + p + '}').join(v);
}
return template
}
}

View File

@ -1,5 +1,5 @@
import { Response } from 'express';
import { ErrorType, ErrorValidation } from '../../types/error';
import { ErrorType, ErrorValidation } from 'Entity';
export class ReturnHelper {
static successResponseAny(

View File

@ -1,10 +1,7 @@
import config from 'config';
import { DataSource } from "typeorm";
import { User } from "../entity/users";
import { User, UserActivity, UserRefreshToken, UserRole } from "Entity";
import { ILogObj, Logger } from 'tslog';
import { UserActivity } from '../entity/user_activity';
import { UserRefreshToken } from '../entity/user_refresh_token';
import { UserRole } from '../entity/users_roles';
export class OrmHelper {
static DB: DataSource = null

View File

@ -1,7 +0,0 @@
export enum ActivityType {
"Create" = "C",
"View" = "V",
"Update" = "U",
"Delete" = "D",
"Restore" = "T",
};

View File

@ -1,3 +0,0 @@
export type ErrorType = 'General' | 'Raw' | 'Validation' | 'Unauthorized';
export type ErrorValidation = { [key: string]: string };

View File

@ -1,8 +0,0 @@
export interface Paging {
page: number,
limit: number,
filter: any,
with_deleted: boolean,
order_field: string,
order_direction: 'ASC' | 'DESC'
}

View File

@ -1,23 +0,0 @@
export enum RoleList {
"Dashboard" = "dashboard",
"Kinerja UKLN" = "ukln",
"Dana Pihak Ketiga" = "dpk",
"Posisi Outstanding Pinjaman" = "outstanding_loan",
"Presentasi NPL" = "npl",
"PPAP Amount NPL" = "ppap_amount",
"Report Regulator Bulanan" = "regulator_bulanan",
"Report Regulator Triwulan" = "regulator_triwulan",
"Report Regulator Publikasi" = "regulator_publikasi",
"Perhitungan Simulasi Perpajakan Per Bulan" = "pajak_per_bulan",
"Perhitungan Simulasi Perpajakan Per Tahun" = "pajak_per_tahun",
"Data Nasabah Potensial" = "nasabah_potensial",
"Master Data User" = "master_user",
"Master Data Product" = "master_product",
"Master Data Tier" = "master_tier",
"Master Klasifikasi Ekonomi" = "master_klasifikasi_ekonomi",
"Master District" = "master_district",
"Master Munisipu" = "master_munisipu",
"Master Administrativu" = "master_administrativu",
"Master Suco" = "master_suco",
"Master Aldeia" = "master_aldeia",
}

View File

@ -1,4 +0,0 @@
export enum Status {
"Active" = "Y",
"Not Active" = "N"
};