diff --git a/src/app.module.ts b/src/app.module.ts index ca71ede..fabac75 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -77,6 +77,8 @@ import { ExportModule } from './modules/configuration/export/export.module'; import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model'; import { SupersetModule } from './modules/configuration/superset/superset.module'; import { GateScanModule } from './modules/gates/gate.module'; +import { UserLoginModel } from './modules/user-related/user/data/models/user-login.model'; +import { LogUserLoginModel } from './modules/configuration/log/data/models/log-user-login.model'; @Module({ imports: [ @@ -102,6 +104,7 @@ import { GateScanModule } from './modules/gates/gate.module'; ItemCategoryModel, ItemRateModel, LogModel, + LogUserLoginModel, NewsModel, PaymentMethodModel, PosLogModel, @@ -118,6 +121,8 @@ import { GateScanModule } from './modules/gates/gate.module'; TransactionDemographyModel, TransactionItemBreakdownModel, UserModel, + UserLoginModel, + VipCategoryModel, VipCodeModel, diff --git a/src/core/helpers/constant/index.ts b/src/core/helpers/constant/index.ts new file mode 100644 index 0000000..fdb844c --- /dev/null +++ b/src/core/helpers/constant/index.ts @@ -0,0 +1,4 @@ +export enum LogUserType { + login = 'login', + logout = 'logout', +} diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 9fa6af6..f6d4b21 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -24,6 +24,8 @@ export enum TABLE_NAME { TRANSACTION_TAX = 'transaction_taxes', TRANSACTION_DEMOGRAPHY = 'transaction_demographies', USER = 'users', + USER_LOGIN = 'users_login', + LOG_USER_LOGIN = 'log_users_login', USER_PRIVILEGE = 'user_privileges', USER_PRIVILEGE_CONFIGURATION = 'user_privilege_configurations', VIP_CATEGORY = 'vip_categories', diff --git a/src/database/migrations/1726115025759-add-table-user-login.ts b/src/database/migrations/1726115025759-add-table-user-login.ts new file mode 100644 index 0000000..5099208 --- /dev/null +++ b/src/database/migrations/1726115025759-add-table-user-login.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTableUserLogin1726115025759 implements MigrationInterface { + name = 'AddTableUserLogin1726115025759'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "login_date" bigint NOT NULL, "login_token" character varying, "user_id" uuid, CONSTRAINT "REL_2a80a213b51423ce5b8211f058" UNIQUE ("user_id"), CONSTRAINT "PK_e564194a9a22f8c623354284f75" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query(`DROP TABLE "users_login"`); + } +} diff --git a/src/database/migrations/1726122619596-update-table-user-login.ts b/src/database/migrations/1726122619596-update-table-user-login.ts new file mode 100644 index 0000000..4855807 --- /dev/null +++ b/src/database/migrations/1726122619596-update-table-user-login.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateTableUserLogin1726122619596 implements MigrationInterface { + name = 'UpdateTableUserLogin1726122619596'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } +} diff --git a/src/database/migrations/1726123955427-add-table-log-user-login.ts b/src/database/migrations/1726123955427-add-table-log-user-login.ts new file mode 100644 index 0000000..91a5788 --- /dev/null +++ b/src/database/migrations/1726123955427-add-table-log-user-login.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTableLogUserLogin1726123955427 implements MigrationInterface { + name = 'AddTableLogUserLogin1726123955427'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."log_users_login_type_enum" AS ENUM('login', 'logout')`, + ); + await queryRunner.query( + `CREATE TYPE "public"."log_users_login_role_enum" AS ENUM('superadmin', 'staff', 'tenant', 'queue_admin')`, + ); + await queryRunner.query( + `CREATE TABLE "log_users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" "public"."log_users_login_type_enum", "role" "public"."log_users_login_role_enum", "user_id" uuid, "username" character varying, "created_at" bigint, CONSTRAINT "PK_75141588aa6ee560504f7d3adce" PRIMARY KEY ("id"))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "log_users_login"`); + await queryRunner.query(`DROP TYPE "public"."log_users_login_role_enum"`); + await queryRunner.query(`DROP TYPE "public"."log_users_login_type_enum"`); + } +} diff --git a/src/modules/configuration/auth/auth.module.ts b/src/modules/configuration/auth/auth.module.ts index 84b5ed8..c16d27f 100644 --- a/src/modules/configuration/auth/auth.module.ts +++ b/src/modules/configuration/auth/auth.module.ts @@ -9,6 +9,10 @@ import { CqrsModule } from '@nestjs/cqrs'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { UserDataService } from 'src/modules/user-related/user/data/services/user-data.service'; +import { AuthAdminQueueController } from './infrastructure/auth-admin-queue.controller'; +import { AuthAdminQueueOrchestrator } from './domain/auth-admin-queue.orchestrator'; +import { LoginAdminQueueManager } from './domain/managers/admin-queue/login-admin-queue.manager'; +import { LogoutAdminQueueManager } from './domain/managers/admin-queue/logout-admin-queue.manager'; @Module({ imports: [ @@ -16,7 +20,17 @@ import { UserDataService } from 'src/modules/user-related/user/data/services/use TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), CqrsModule, ], - controllers: [AuthController], - providers: [LoginManager, LogoutManager, UserDataService, AuthOrchestrator], + controllers: [AuthController, AuthAdminQueueController], + providers: [ + LoginManager, + LogoutManager, + UserDataService, + AuthOrchestrator, + + // ADMIN QUEUE + AuthAdminQueueOrchestrator, + LoginAdminQueueManager, + LogoutAdminQueueManager, + ], }) export class AuthModule {} diff --git a/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts b/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts new file mode 100644 index 0000000..0a755bf --- /dev/null +++ b/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; +import { LoginAdminQueueManager } from './managers/admin-queue/login-admin-queue.manager'; +import { LogoutAdminQueueManager } from './managers/admin-queue/logout-admin-queue.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserDataService } from 'src/modules/user-related/user/data/services/user-data.service'; + +@Injectable() +export class AuthAdminQueueOrchestrator { + constructor( + private loginManager: LoginAdminQueueManager, + private logoutManager: LogoutAdminQueueManager, + private serviceData: UserDataService, + ) {} + + async login(data): Promise { + this.loginManager.setData(data); + this.loginManager.setService(this.serviceData, TABLE_NAME.USER); + await this.loginManager.execute(); + return this.loginManager.getResult(); + } + + async logout(id?: string): Promise { + if (id) this.logoutManager.setData({ id }); + this.logoutManager.setService(this.serviceData, TABLE_NAME.USER); + await this.logoutManager.execute(); + return this.logoutManager.getResult(); + } +} diff --git a/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts new file mode 100644 index 0000000..465e4eb --- /dev/null +++ b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts @@ -0,0 +1,146 @@ +import { + HttpStatus, + Inject, + Injectable, + Logger, + UnauthorizedException, +} from '@nestjs/common'; +import { validatePassword } from 'src/core/helpers/password/bcrypt.helpers'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { SessionService } from 'src/core/sessions'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { In } from 'typeorm'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; + +@Injectable() +export class LoginAdminQueueManager extends BaseCustomManager { + @Inject() + protected session: SessionService; + protected token; + protected userLogin; + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async process(): Promise { + // get user active by username + this.userLogin = await this.dataService.getOneByOptions({ + where: { + username: this.data.username, + status: STATUS.ACTIVE, + role: In([UserRole.QUEUE_ADMIN, UserRole.SUPERADMIN]), + }, + relations: ['user_login'], + }); + if (!this.userLogin) this.throwError(); + + // validasi password + const valid = await validatePassword( + this.data.password, + this.userLogin?.password, + ); + if (!valid) this.throwError(); + + if ( + this.userLogin.user_login && + this.userLogin.role === UserRole.QUEUE_ADMIN + ) { + throw new UnauthorizedException({ + statusCode: HttpStatus.UNAUTHORIZED, + message: `Gagal! akun anda sudah login di perangkat lain.`, + error: 'Unauthorized', + }); + } + + // * Disini untuk isi token + const tokenData = { + id: this.userLogin.id, + name: this.userLogin.name, + username: this.userLogin.username, + role: this.userLogin.role, + user_privilege_id: this.userLogin.user_privilege_id, + }; + + Logger.debug('Sign Token Admin Queue', 'LoginAdminQueueManager'); + this.token = this.session.createAccessToken(tokenData); + + Logger.debug('refreshToken Admin Queue', 'LoginAdminQueueManager'); + const refreshToken = this.session.createAccessToken(tokenData); + + Logger.debug('Update Refresh Token Admin Queue', 'LoginAdminQueueManager'); + + const newDataUser = { refresh_token: refreshToken }; + if (this.userLogin.role === UserRole.QUEUE_ADMIN) { + Object.assign(newDataUser, { + user_login: { + user_id: this.userLogin.id, + login_token: this.token, + login_date: new Date().getTime(), + }, + }); + } + // Update refresh token + await this.dataService.update( + this.queryRunner, + this.entityTarget, + { id: this.userLogin.id }, + newDataUser, + ); + await this.publishEvents(); + + Logger.debug('Process Login Admin Queue Done', 'LoginAdminQueueManager'); + return; + } + + async afterProcess(): Promise { + return; + } + + getResult() { + return { + id: this.userLogin.id, + name: this.userLogin.name, + username: this.userLogin.username, + role: this.userLogin.role, + token: this.token, + }; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: LogUserLoginEvent, + data: { + type: LogUserType.login, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.username, + created_at: new Date().getTime(), + }, + }, + ]; + } + + // !throw errornya akan sama, untuk security + throwError() { + throw new UnauthorizedException({ + statusCode: HttpStatus.UNAUTHORIZED, + message: `Gagal! username atau password tidak sesuai`, + error: 'Unauthorized', + }); + } +} diff --git a/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts b/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts new file mode 100644 index 0000000..b40f7ad --- /dev/null +++ b/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts @@ -0,0 +1,65 @@ +import { LogUserType } from 'src/core/helpers/constant'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +export class LogoutAdminQueueManager extends BaseCustomManager { + protected userLogin; + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async process(): Promise { + const id = this.data?.id ?? this.user.id; + + this.userLogin = await this.dataService.getOneByOptions({ + where: { id }, + }); + + await this.dataService.update( + this.queryRunner, + this.entityTarget, + { id: this.userLogin.id }, + { + refresh_token: null, + user_login: null, + }, + ); + await this.publishEvents(); + return; + } + + async afterProcess(): Promise { + return; + } + + getResult() { + return `Success Logout User`; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: LogUserLoginEvent, + data: { + type: LogUserType.logout, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.name, + created_at: new Date().getTime(), + }, + }, + ]; + } +} diff --git a/src/modules/configuration/auth/domain/managers/login.manager.ts b/src/modules/configuration/auth/domain/managers/login.manager.ts index bc1b26f..0ef6596 100644 --- a/src/modules/configuration/auth/domain/managers/login.manager.ts +++ b/src/modules/configuration/auth/domain/managers/login.manager.ts @@ -12,9 +12,10 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; import { EventTopics } from 'src/core/strings/constants/interface.constants'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; -import { UserLoginEvent } from '../entities/login.event'; import { Not } from 'typeorm'; import { UserRole } from 'src/modules/user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; @Injectable() export class LoginManager extends BaseCustomManager { @@ -80,6 +81,7 @@ export class LoginManager extends BaseCustomManager { }, ); + await this.publishEvents(); Logger.debug('Process Login Done', 'LoginManager'); return; } @@ -122,11 +124,13 @@ export class LoginManager extends BaseCustomManager { get eventTopics(): EventTopics[] { return [ { - topic: UserLoginEvent, + topic: LogUserLoginEvent, data: { - id: this.userLogin.id, - type: 'login', - timestamp: new Date().getTime(), + type: LogUserType.login, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.username, + created_at: new Date().getTime(), }, }, ]; diff --git a/src/modules/configuration/auth/domain/managers/logout.manager.ts b/src/modules/configuration/auth/domain/managers/logout.manager.ts index 482a84c..cb1b18b 100644 --- a/src/modules/configuration/auth/domain/managers/logout.manager.ts +++ b/src/modules/configuration/auth/domain/managers/logout.manager.ts @@ -2,7 +2,8 @@ import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base import { EventTopics } from 'src/core/strings/constants/interface.constants'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; -import { UserLogoutEvent } from '../entities/logout.event'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; export class LogoutManager extends BaseCustomManager { async validateProcess(): Promise { @@ -23,6 +24,7 @@ export class LogoutManager extends BaseCustomManager { }, ); + await this.publishEvents(); return; } @@ -41,11 +43,13 @@ export class LogoutManager extends BaseCustomManager { get eventTopics(): EventTopics[] { return [ { - topic: UserLogoutEvent, + topic: LogUserLoginEvent, data: { - id: this.user.id, - type: 'logout', - timestamp: new Date().getTime(), + type: LogUserType.logout, + role: this.user.role, + user_id: this.user.id, + username: this.user.name, + created_at: new Date().getTime(), }, }, ]; diff --git a/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts new file mode 100644 index 0000000..1503130 --- /dev/null +++ b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; +import { ExcludePrivilege, Public } from 'src/core/guards'; +import { ApiBearerAuth } from '@nestjs/swagger'; +import { LoginDto } from './dto/login.dto'; +import { AuthAdminQueueOrchestrator } from '../domain/auth-admin-queue.orchestrator'; + +@Controller('v1/auth/queue') +export class AuthAdminQueueController { + constructor(private orchestrator: AuthAdminQueueOrchestrator) {} + + @Post() + @Public(true) + async login(@Body() body: LoginDto) { + return await this.orchestrator.login(body); + } + + @ApiBearerAuth('JWT') + @Public(false) + @ExcludePrivilege() + @Delete('logout') + async logoout() { + return await this.orchestrator.logout(); + } + + @Put(':id/logout') + async logoutQueueAdmin(@Param('id') dataId: string) { + return await this.orchestrator.logout(dataId); + } +} diff --git a/src/modules/configuration/log/data/models/log-user-login.model.ts b/src/modules/configuration/log/data/models/log-user-login.model.ts new file mode 100644 index 0000000..cda2429 --- /dev/null +++ b/src/modules/configuration/log/data/models/log-user-login.model.ts @@ -0,0 +1,28 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserEntity } from '../../../../user-related/user/domain/entities/user.entity'; +import { Column, Entity } from 'typeorm'; +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; +import { LogUserLoginEntity } from '../../domain/entities/log-user-login.entity'; +import { UserRole } from '../../../../user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; + +@Entity(TABLE_NAME.LOG_USER_LOGIN) +export class LogUserLoginModel + extends BaseCoreModel + implements LogUserLoginEntity +{ + @Column({ type: 'enum', enum: LogUserType, nullable: true }) + type: LogUserType; + + @Column({ type: 'enum', enum: UserRole, nullable: true }) + role: UserRole; + + @Column({ type: 'uuid', nullable: true }) + user_id: string; + + @Column({ type: 'varchar', nullable: true }) + username: string; + + @Column({ type: 'bigint', nullable: true }) + created_at: number; +} diff --git a/src/modules/configuration/log/data/services/log-user-login.service.ts b/src/modules/configuration/log/data/services/log-user-login.service.ts new file mode 100644 index 0000000..5d1865c --- /dev/null +++ b/src/modules/configuration/log/data/services/log-user-login.service.ts @@ -0,0 +1,21 @@ +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { LogUserLoginEntity } from '../../domain/entities/log-user-login.entity'; +import { LogUserLoginModel } from '../models/log-user-login.model'; + +@Injectable() +export class LogUserLoginService extends BaseDataService { + constructor( + @InjectRepository(LogUserLoginModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } + + async saveData(data) { + this.repo.save(data); + } +} diff --git a/src/modules/configuration/log/domain/entities/log-user-login.entity.ts b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts new file mode 100644 index 0000000..308c43c --- /dev/null +++ b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts @@ -0,0 +1,11 @@ +import { LogUserType } from 'src/core/helpers/constant'; +import { UserRole } from '../../../../user-related/user/constants'; +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; + +export interface LogUserLoginEntity extends BaseCoreEntity { + type: LogUserType; + role: UserRole; + user_id: string; + username: string; + created_at: number; +} diff --git a/src/modules/configuration/log/domain/entities/log-user-login.event.ts b/src/modules/configuration/log/domain/entities/log-user-login.event.ts new file mode 100644 index 0000000..813681f --- /dev/null +++ b/src/modules/configuration/log/domain/entities/log-user-login.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class LogUserLoginEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts b/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts new file mode 100644 index 0000000..c22ec5d --- /dev/null +++ b/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts @@ -0,0 +1,14 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; + +import { LogUserLoginEvent } from '../entities/log-user-login.event'; +import { LogUserLoginService } from '../../data/services/log-user-login.service'; + +@EventsHandler(LogUserLoginEvent) +export class LogUserLoginHandler implements IEventHandler { + constructor(private service: LogUserLoginService) {} + + async handle(event: LogUserLoginEvent) { + const data = event.data.data; + await this.service.saveData(data); + } +} diff --git a/src/modules/configuration/log/log.module.ts b/src/modules/configuration/log/log.module.ts index 06002f9..7eaa8cc 100644 --- a/src/modules/configuration/log/log.module.ts +++ b/src/modules/configuration/log/log.module.ts @@ -12,12 +12,15 @@ import { LogService } from './data/services/log.service'; import { PosLogModel } from './data/models/pos-log.model'; import { PosLogService } from './data/services/pos-log.service'; import { RecordPosLogHandler } from './domain/handlers/pos-log.handler'; +import { LogUserLoginModel } from './data/models/log-user-login.model'; +import { LogUserLoginService } from './data/services/log-user-login.service'; +import { LogUserLoginHandler } from './domain/handlers/log-user-login.handler'; @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forFeature( - [LogModel, ErrorLogModel, PosLogModel], + [LogModel, ErrorLogModel, PosLogModel, LogUserLoginModel], CONNECTION_NAME.DEFAULT, ), CqrsModule, @@ -27,10 +30,12 @@ import { RecordPosLogHandler } from './domain/handlers/pos-log.handler'; RecordLogHandler, RecordPosLogHandler, RecordErrorLogHandler, + LogUserLoginHandler, LogService, PosLogService, ErrorLogService, + LogUserLoginService, ], }) export class LogModule {} diff --git a/src/modules/user-related/user/data/models/user-login.model.ts b/src/modules/user-related/user/data/models/user-login.model.ts new file mode 100644 index 0000000..9f39afe --- /dev/null +++ b/src/modules/user-related/user/data/models/user-login.model.ts @@ -0,0 +1,30 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserEntity } from '../../domain/entities/user.entity'; +import { Column, Entity, JoinColumn, OneToOne } from 'typeorm'; +import { UserLoginEntity } from '../../domain/entities/user-login.entity'; +import { UserModel } from './user.model'; +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; + +@Entity(TABLE_NAME.USER_LOGIN) +export class UserLoginModel + extends BaseCoreModel + implements UserLoginEntity +{ + @Column({ type: 'bigint', nullable: false }) + login_date: number; + + @Column({ type: 'varchar', name: 'login_token', nullable: true }) + login_token: string; + + @Column('varchar', { name: 'user_id', nullable: true }) + user_id: string; + + @OneToOne(() => UserModel, (model) => model.user_login, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + nullable: false, + orphanedRowAction: 'delete', + }) + @JoinColumn({ name: 'user_id' }) + user: UserModel; +} diff --git a/src/modules/user-related/user/data/models/user.model.ts b/src/modules/user-related/user/data/models/user.model.ts index 55eb6a9..1d8c33c 100644 --- a/src/modules/user-related/user/data/models/user.model.ts +++ b/src/modules/user-related/user/data/models/user.model.ts @@ -1,10 +1,18 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { UserEntity } from '../../domain/entities/user.entity'; -import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; +import { + Column, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + OneToOne, +} from 'typeorm'; import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; import { UserPrivilegeModel } from 'src/modules/user-related/user-privilege/data/models/user-privilege.model'; import { UserRole } from '../../constants'; import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; +import { UserLoginModel } from './user-login.model'; @Entity(TABLE_NAME.USER) export class UserModel @@ -48,4 +56,10 @@ export class UserModel onUpdate: 'CASCADE', }) items: ItemModel[]; + + // relasi ke user login for admin queue + @OneToOne(() => UserLoginModel, (model) => model.user, { + cascade: true, + }) + user_login: UserLoginModel; } diff --git a/src/modules/user-related/user/domain/entities/user-login.entity.ts b/src/modules/user-related/user/domain/entities/user-login.entity.ts new file mode 100644 index 0000000..975223c --- /dev/null +++ b/src/modules/user-related/user/domain/entities/user-login.entity.ts @@ -0,0 +1,6 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; + +export interface UserLoginEntity extends BaseCoreEntity { + login_date: number; + login_token: string; +} diff --git a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts index dbabb32..92eaa14 100644 --- a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts @@ -28,7 +28,7 @@ export class IndexUserManager extends BaseIndexManager { joinRelations: [], // relation join and select (relasi yang ingin ditampilkan), - selectRelations: ['user_privilege'], + selectRelations: ['user_privilege', 'user_login'], // relation yang hanya ingin dihitung (akan return number) countRelations: [], @@ -49,6 +49,9 @@ export class IndexUserManager extends BaseIndexManager { 'user_privilege.id', 'user_privilege.name', + + 'user_login.id', + 'user_login.login_date', ]; } diff --git a/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts b/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts index b77e469..ea2b9ec 100644 --- a/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts +++ b/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts @@ -31,6 +31,7 @@ export class UserDataOrchestrator extends BaseDataTransactionOrchestrator