feat: create feature log user login and create auth admin queue
continuous-integration/drone/push Build is passing Details

pull/89/head
Firman Ramdhani 2024-09-12 17:16:56 +07:00
parent 88b4c66139
commit 3e920755bd
25 changed files with 526 additions and 16 deletions

View File

@ -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,

View File

@ -0,0 +1,4 @@
export enum LogUserType {
login = 'login',
logout = 'logout',
}

View File

@ -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',

View File

@ -0,0 +1,21 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTableUserLogin1726115025759 implements MigrationInterface {
name = 'AddTableUserLogin1726115025759';
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(`DROP TABLE "users_login"`);
}
}

View File

@ -0,0 +1,23 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateTableUserLogin1726122619596 implements MigrationInterface {
name = 'UpdateTableUserLogin1726122619596';
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
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`,
);
}
}

View File

@ -0,0 +1,23 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTableLogUserLogin1726123955427 implements MigrationInterface {
name = 'AddTableLogUserLogin1726123955427';
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
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"`);
}
}

View File

@ -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 {}

View File

@ -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<any> {
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<any> {
if (id) this.logoutManager.setData({ id });
this.logoutManager.setService(this.serviceData, TABLE_NAME.USER);
await this.logoutManager.execute();
return this.logoutManager.getResult();
}
}

View File

@ -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<UserEntity> {
@Inject()
protected session: SessionService;
protected token;
protected userLogin;
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async process(): Promise<void> {
// 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<void> {
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',
});
}
}

View File

@ -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<UserEntity> {
protected userLogin;
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async process(): Promise<void> {
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<void> {
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(),
},
},
];
}
}

View File

@ -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<UserEntity> {
@ -80,6 +81,7 @@ export class LoginManager extends BaseCustomManager<UserEntity> {
},
);
await this.publishEvents();
Logger.debug('Process Login Done', 'LoginManager');
return;
}
@ -122,11 +124,13 @@ export class LoginManager extends BaseCustomManager<UserEntity> {
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(),
},
},
];

View File

@ -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<UserEntity> {
async validateProcess(): Promise<void> {
@ -23,6 +24,7 @@ export class LogoutManager extends BaseCustomManager<UserEntity> {
},
);
await this.publishEvents();
return;
}
@ -41,11 +43,13 @@ export class LogoutManager extends BaseCustomManager<UserEntity> {
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(),
},
},
];

View File

@ -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);
}
}

View File

@ -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<UserEntity>
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;
}

View File

@ -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<LogUserLoginEntity> {
constructor(
@InjectRepository(LogUserLoginModel, CONNECTION_NAME.DEFAULT)
private repo: Repository<LogUserLoginModel>,
) {
super(repo);
}
async saveData(data) {
this.repo.save(data);
}
}

View File

@ -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;
}

View File

@ -0,0 +1,5 @@
import { IEvent } from 'src/core/strings/constants/interface.constants';
export class LogUserLoginEvent {
constructor(public readonly data: IEvent) {}
}

View File

@ -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<LogUserLoginEvent> {
constructor(private service: LogUserLoginService) {}
async handle(event: LogUserLoginEvent) {
const data = event.data.data;
await this.service.saveData(data);
}
}

View File

@ -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 {}

View File

@ -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<UserEntity>
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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -28,7 +28,7 @@ export class IndexUserManager extends BaseIndexManager<UserEntity> {
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<UserEntity> {
'user_privilege.id',
'user_privilege.name',
'user_login.id',
'user_login.login_date',
];
}

View File

@ -31,6 +31,7 @@ export class UserDataOrchestrator extends BaseDataTransactionOrchestrator<UserEn
private batchConfirmManager: BatchConfirmUserManager,
private batchInactiveManager: BatchInactiveUserManager,
private updatePasswordManager: UpdatePasswordUserManager,
private serviceData: UserDataService,
) {
super();

View File

@ -23,11 +23,15 @@ import { BatchConfirmUserManager } from './domain/usecases/managers/batch-confir
import { BatchInactiveUserManager } from './domain/usecases/managers/batch-inactive-user.manager';
import { UserModel } from './data/models/user.model';
import { UpdatePasswordUserManager } from './domain/usecases/managers/update-password-user.manager';
import { UserLoginModel } from './data/models/user-login.model';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT),
TypeOrmModule.forFeature(
[UserModel, UserLoginModel],
CONNECTION_NAME.DEFAULT,
),
CqrsModule,
],
controllers: [UserDataController, UserReadController],