Merge branch 'dev'

This commit is contained in:
faris Aljohari
2024-09-03 14:44:23 +03:00
136 changed files with 4083 additions and 745 deletions

View File

@ -1,75 +1,92 @@
ACCESS_KEY=
AZURE_POSTGRESQL_DATABASE=
AZURE_POSTGRESQL_HOST=
AZURE_POSTGRESQL_PASSWORD=
AZURE_POSTGRESQL_PORT=
AZURE_POSTGRESQL_SSL=
AZURE_POSTGRESQL_SYNC=
AZURE_POSTGRESQL_USER=
AZURE_REDIS_CONNECTIONSTRING=
BASE_URL=
DB_SYNC=
DOCKER_REGISTRY_SERVER_PASSWORD=
DOCKER_REGISTRY_SERVER_URL=
DOCKER_REGISTRY_SERVER_USERNAME=
DOPPLER_CONFIG=
DOPPLER_ENVIRONMENT=
DOPPLER_PROJECT=
JWT_EXPIRE_TIME=
JWT_EXPIRE_TIME_REFRESH=
JWT_SECRET=
JWT_SECRET_REFRESH=
SECRET_KEY=
SMTP_HOST=
SMTP_PASSWORD=
SMTP_PORT=
SMTP_SECURE=
SMTP_USER=
WEBSITES_ENABLE_APP_SERVICE_STORAGE=
PORT=
SUPER_ADMIN_EMAIL=
SUPER_ADMIN_PASSWORD=
TUYA_ACCESS_ID=
TUYA_ACCESS_KEY=
ONESIGNAL_APP_ID=
ONESIGNAL_API_KEY=
TRUN_ON_TUYA_SOCKET=
TUYA_EU_URL=
MONGODB_URI=
FIREBASE_API_KEY=
FIREBASE_AUTH_DOMAIN=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_APP_ID=d
FIREBASE_MEASUREMENT_ID=
FIREBASE_DATABASE_URL=
OTP_LIMITER=

View File

@ -17,10 +17,19 @@ export class AuthService {
private readonly configService: ConfigService,
) {}
async validateUser(email: string, pass: string): Promise<any> {
async validateUser(
email: string,
pass: string,
regionUuid?: string,
): Promise<any> {
const user = await this.userRepository.findOne({
where: {
email,
region: regionUuid
? {
uuid: regionUuid,
}
: undefined,
},
relations: ['roles.roleType'],
});

View File

@ -0,0 +1 @@
export const defaultDoorLockPass = '1233214';

View File

@ -129,7 +129,7 @@ export const allCountries = [
'Oman',
'Pakistan',
'Palau',
'Palestine State',
'Palestine',
'Panama',
'Papua New Guinea',
'Paraguay',

View File

@ -4,20 +4,21 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { SnakeNamingStrategy } from './strategies';
import { UserEntity } from '../modules/user/entities/user.entity';
import { UserSessionEntity } from '../modules/session/entities/session.entity';
import { UserOtpEntity } from '../modules/user-otp/entities';
import { UserOtpEntity } from '../modules/user/entities';
import { ProductEntity } from '../modules/product/entities';
import { DeviceEntity } from '../modules/device/entities';
import { PermissionTypeEntity } from '../modules/permission/entities';
import { SpaceEntity } from '../modules/space/entities';
import { SpaceTypeEntity } from '../modules/space-type/entities';
import { UserSpaceEntity } from '../modules/user-space/entities';
import { DeviceUserPermissionEntity } from '../modules/device-user-permission/entities';
import { UserRoleEntity } from '../modules/user-role/entities';
import { SpaceTypeEntity } from '../modules/space/entities';
import { UserSpaceEntity } from '../modules/user/entities';
import { DeviceUserPermissionEntity } from '../modules/device/entities';
import { UserRoleEntity } from '../modules/user/entities';
import { RoleTypeEntity } from '../modules/role-type/entities';
import { UserNotificationEntity } from '../modules/user-notification/entities';
import { DeviceNotificationEntity } from '../modules/device-notification/entities';
import { UserNotificationEntity } from '../modules/user/entities';
import { DeviceNotificationEntity } from '../modules/device/entities';
import { RegionEntity } from '../modules/region/entities';
import { TimeZoneEntity } from '../modules/timezone/entities';
import { VisitorPasswordEntity } from '../modules/visitor-password/entities';
@Module({
imports: [
@ -50,6 +51,7 @@ import { TimeZoneEntity } from '../modules/timezone/entities';
DeviceNotificationEntity,
RegionEntity,
TimeZoneEntity,
VisitorPasswordEntity,
],
namingStrategy: new SnakeNamingStrategy(),
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),

View File

@ -0,0 +1,25 @@
import { Controller, Post, Param } from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto';
import { DeviceStatusFirebaseService } from '../services/devices-status.service';
@ApiTags('Device Status Firebase Module')
@Controller({
version: '1',
path: 'device-status-firebase',
})
export class DeviceStatusFirebaseController {
constructor(
private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService,
) {}
@ApiBearerAuth()
@Post(':deviceTuyaUuid')
async addDeviceStatus(
@Param('deviceTuyaUuid') deviceTuyaUuid: string,
): Promise<AddDeviceStatusDto> {
return this.deviceStatusFirebaseService.addDeviceStatusByDeviceUuid(
deviceTuyaUuid,
);
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { DeviceStatusFirebaseController } from './controllers/devices-status.controller';
import { DeviceStatusFirebaseService } from './services/devices-status.service';
import { DeviceRepository } from '@app/common/modules/device/repositories';
@Module({
providers: [DeviceStatusFirebaseService, DeviceRepository],
controllers: [DeviceStatusFirebaseController],
exports: [DeviceStatusFirebaseService],
})
export class DeviceStatusFirebaseModule {}

View File

@ -0,0 +1,38 @@
import {
IsString,
IsArray,
ValidateNested,
IsNotEmpty,
IsOptional,
} from 'class-validator';
import { Type } from 'class-transformer';
class StatusDto {
@IsString()
code: string;
@IsNotEmpty()
value: any;
}
export class AddDeviceStatusDto {
@IsString()
@IsOptional()
deviceUuid?: string;
@IsString()
deviceTuyaUuid: string;
@IsString()
@IsOptional()
productUuid?: string;
@IsString()
@IsOptional()
productType?: string;
@IsArray()
@ValidateNested({ each: true })
@Type(() => StatusDto)
status: StatusDto[];
}

View File

@ -0,0 +1,192 @@
import {
HttpException,
HttpStatus,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto';
import { DeviceRepository } from '@app/common/modules/device/repositories';
import { GetDeviceDetailsFunctionsStatusInterface } from 'src/device/interfaces/get.device.interface';
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
import { ConfigService } from '@nestjs/config';
import { firebaseDataBase } from '../../firebase.config';
import { Database, DataSnapshot, get, ref, set } from 'firebase/database';
@Injectable()
export class DeviceStatusFirebaseService {
private tuya: TuyaContext;
private firebaseDb: Database;
constructor(
private readonly configService: ConfigService,
private readonly deviceRepository: DeviceRepository,
) {
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
const tuyaEuUrl = this.configService.get<string>('tuya-config.TUYA_EU_URL');
this.tuya = new TuyaContext({
baseUrl: tuyaEuUrl,
accessKey,
secretKey,
});
// Initialize firebaseDb using firebaseDataBase function
this.firebaseDb = firebaseDataBase(this.configService);
}
async addDeviceStatusByDeviceUuid(
deviceTuyaUuid: string,
): Promise<AddDeviceStatusDto> {
try {
const device = await this.getDeviceByDeviceTuyaUuid(deviceTuyaUuid);
if (device.uuid) {
const deviceStatus = await this.getDevicesInstructionStatus(
device.uuid,
);
if (deviceStatus.productUuid) {
const deviceStatusSaved = await this.createDeviceStatusFirebase({
deviceUuid: device.uuid,
deviceTuyaUuid: deviceTuyaUuid,
status: deviceStatus.status,
productUuid: deviceStatus.productUuid,
productType: deviceStatus.productType,
});
return deviceStatusSaved;
}
}
} catch (error) {
throw new HttpException(
'Device Tuya UUID not found',
error.status || HttpStatus.BAD_REQUEST,
);
}
}
async addDeviceStatusToFirebase(
addDeviceStatusDto: AddDeviceStatusDto,
): Promise<AddDeviceStatusDto | null> {
try {
const device = await this.getDeviceByDeviceTuyaUuid(
addDeviceStatusDto.deviceTuyaUuid,
);
if (device?.uuid) {
return await this.createDeviceStatusFirebase({
deviceUuid: device.uuid,
...addDeviceStatusDto,
});
}
// Return null if device not found or no UUID
return null;
} catch (error) {
// Handle the error silently, perhaps log it internally or ignore it
return null;
}
}
async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) {
return await this.deviceRepository.findOne({
where: {
deviceTuyaUuid,
},
relations: ['productDevice'],
});
}
async getDevicesInstructionStatus(deviceUuid: string) {
try {
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
if (!deviceDetails) {
throw new NotFoundException('Device Not Found');
}
const deviceStatus = await this.getDevicesInstructionStatusTuya(
deviceDetails.deviceTuyaUuid,
);
return {
productUuid: deviceDetails.productDevice.uuid,
productType: deviceDetails.productDevice.prodType,
status: deviceStatus.result[0].status,
};
} catch (error) {
throw new HttpException(
'Error fetching device functions status',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getDevicesInstructionStatusTuya(
deviceUuid: string,
): Promise<GetDeviceDetailsFunctionsStatusInterface> {
try {
const path = `/v1.0/iot-03/devices/status`;
const response = await this.tuya.request({
method: 'GET',
path,
query: {
device_ids: deviceUuid,
},
});
return response as GetDeviceDetailsFunctionsStatusInterface;
} catch (error) {
throw new HttpException(
'Error fetching device functions status from Tuya',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getDeviceByDeviceUuid(
deviceUuid: string,
withProductDevice: boolean = true,
) {
return await this.deviceRepository.findOne({
where: {
uuid: deviceUuid,
},
...(withProductDevice && { relations: ['productDevice'] }),
});
}
async createDeviceStatusFirebase(
addDeviceStatusDto: AddDeviceStatusDto,
): Promise<any> {
const dataRef = ref(
this.firebaseDb,
`device-status/${addDeviceStatusDto.deviceUuid}`,
);
const snapshot: DataSnapshot = await get(dataRef);
const existingData = snapshot.val() || {};
// Assign default values if fields are not present
if (!existingData.deviceTuyaUuid) {
existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid;
}
if (!existingData.productUuid) {
existingData.productUuid = addDeviceStatusDto.productUuid;
}
if (!existingData.productType) {
existingData.productType = addDeviceStatusDto.productType;
}
if (!existingData.status) {
existingData.status = [];
}
// Create a map to track existing status codes
const statusMap = new Map(
existingData.status.map((item) => [item.code, item.value]),
);
// Update or add status codes
for (const statusItem of addDeviceStatusDto.status) {
statusMap.set(statusItem.code, statusItem.value);
}
// Convert the map back to an array format
existingData.status = Array.from(statusMap, ([code, value]) => ({
code,
value,
}));
// Save the updated data to Firebase
await set(dataRef, existingData);
// Return the updated data
return existingData;
}
}

View File

@ -0,0 +1,24 @@
import { initializeApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
import { ConfigService } from '@nestjs/config';
export const initializeFirebaseApp = (configService: ConfigService) => {
const firebaseConfig = {
apiKey: configService.get<string>('FIREBASE_API_KEY'),
authDomain: configService.get<string>('FIREBASE_AUTH_DOMAIN'),
projectId: configService.get<string>('FIREBASE_PROJECT_ID'),
storageBucket: configService.get<string>('FIREBASE_STORAGE_BUCKET'),
messagingSenderId: configService.get<string>(
'FIREBASE_MESSAGING_SENDER_ID',
),
appId: configService.get<string>('FIREBASE_APP_ID'),
measurementId: configService.get<string>('FIREBASE_MEASUREMENT_ID'),
databaseURL: configService.get<string>('FIREBASE_DATABASE_URL'),
};
const app = initializeApp(firebaseConfig);
return getDatabase(app);
};
export const firebaseDataBase = (configService: ConfigService) =>
initializeFirebaseApp(configService);

View File

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { DeviceStatusFirebaseModule } from './devices-status/devices-status.module';
@Module({
imports: [DeviceStatusFirebaseModule],
providers: [],
exports: [],
})
export class FirebaseSharedModule {}

View File

@ -0,0 +1,4 @@
export function differenceInSeconds(date1: Date, date2: Date): number {
const diffInMilliseconds = date1.getTime() - date2.getTime();
return Math.floor(diffInMilliseconds / 1000);
}

View File

@ -7,8 +7,9 @@ import { TuyaWebSocketService } from './services/tuya.web.socket.service';
import { OneSignalService } from './services/onesignal.service';
import { DeviceMessagesService } from './services/device.messages.service';
import { DeviceNotificationRepositoryModule } from '../modules/device-notification/device.notification.module';
import { DeviceNotificationRepository } from '../modules/device-notification/repositories';
import { DeviceRepositoryModule } from '../modules/device/device.repository.module';
import { DeviceNotificationRepository } from '../modules/device/repositories';
import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module';
@Global()
@Module({
@ -23,6 +24,10 @@ import { DeviceNotificationRepository } from '../modules/device-notification/rep
],
exports: [HelperHashService, SpacePermissionService],
controllers: [],
imports: [SpaceRepositoryModule, DeviceNotificationRepositoryModule],
imports: [
SpaceRepositoryModule,
DeviceRepositoryModule,
DeviceStatusFirebaseModule,
],
})
export class HelperModule {}

View File

@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { DeviceNotificationRepository } from '@app/common/modules/device-notification/repositories';
import { DeviceNotificationRepository } from '@app/common/modules/device/repositories';
import { OneSignalService } from './onesignal.service';
@Injectable()

View File

@ -1,8 +1,7 @@
import { Injectable } from '@nestjs/common';
import TuyaWebsocket from '../../config/tuya-web-socket-config';
import { ConfigService } from '@nestjs/config';
import { OneSignalService } from './onesignal.service';
import { DeviceMessagesService } from './device.messages.service';
import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service';
@Injectable()
export class TuyaWebSocketService {
@ -10,8 +9,7 @@ export class TuyaWebSocketService {
constructor(
private readonly configService: ConfigService,
private readonly oneSignalService: OneSignalService,
private readonly deviceMessagesService: DeviceMessagesService,
private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService,
) {
// Initialize the TuyaWebsocket client
this.client = new TuyaWebsocket({
@ -39,10 +37,11 @@ export class TuyaWebSocketService {
this.client.message(async (ws: WebSocket, message: any) => {
try {
await this.deviceMessagesService.getDevicesUserNotifications(
message.payload.data.bizData.devId,
message.payload.data.bizData,
);
await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({
deviceTuyaUuid: message.payload.data.bizData.devId,
status: message.payload.data.bizData.properties,
});
this.client.ackMessage(message.messageId);
} catch (error) {
console.error('Error processing message:', error);

View File

@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DeviceNotificationEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([DeviceNotificationEntity])],
})
export class DeviceNotificationRepositoryModule {}

View File

@ -1 +0,0 @@
export * from './device.notification.dto';

View File

@ -1,33 +0,0 @@
import { Column, Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { DeviceNotificationDto } from '../dtos';
import { DeviceEntity } from '../../device/entities';
import { UserEntity } from '../../user/entities';
@Entity({ name: 'device-notification' })
@Unique(['userUuid', 'deviceUuid'])
export class DeviceNotificationEntity extends AbstractEntity<DeviceNotificationDto> {
@Column({
nullable: false,
})
public userUuid: string;
@Column({
nullable: false,
})
deviceUuid: string;
@ManyToOne(() => DeviceEntity, (device) => device.permission, {
nullable: false,
})
device: DeviceEntity;
@ManyToOne(() => UserEntity, (user) => user.userPermission, {
nullable: false,
})
user: UserEntity;
constructor(partial: Partial<DeviceNotificationEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './device.notification.entity';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { DeviceNotificationEntity } from '../entities';
@Injectable()
export class DeviceNotificationRepository extends Repository<DeviceNotificationEntity> {
constructor(private dataSource: DataSource) {
super(DeviceNotificationEntity, dataSource.createEntityManager());
}
}

View File

@ -1 +0,0 @@
export * from './device.notification.repository';

View File

@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DeviceUserPermissionEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([DeviceUserPermissionEntity])],
})
export class DeviceUserPermissionRepositoryModule {}

View File

@ -1,19 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class DeviceUserPermissionDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public deviceUuid: string;
@IsString()
@IsNotEmpty()
public permissionTypeUuid: string;
}

View File

@ -1 +0,0 @@
export * from './device.user.permission.dto';

View File

@ -1,43 +0,0 @@
import { Column, Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { DeviceUserPermissionDto } from '../dtos';
import { PermissionTypeEntity } from '../../permission/entities';
import { DeviceEntity } from '../../device/entities';
import { UserEntity } from '../../user/entities';
@Entity({ name: 'device-user-permission' })
@Unique(['userUuid', 'deviceUuid'])
export class DeviceUserPermissionEntity extends AbstractEntity<DeviceUserPermissionDto> {
@Column({
nullable: false,
})
public userUuid: string;
@Column({
nullable: false,
})
deviceUuid: string;
@ManyToOne(() => DeviceEntity, (device) => device.permission, {
nullable: false,
})
device: DeviceEntity;
@ManyToOne(
() => PermissionTypeEntity,
(permissionType) => permissionType.permission,
{
nullable: false,
},
)
permissionType: PermissionTypeEntity;
@ManyToOne(() => UserEntity, (user) => user.userPermission, {
nullable: false,
})
user: UserEntity;
constructor(partial: Partial<DeviceUserPermissionEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './device.user.permission.entity';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { DeviceUserPermissionEntity } from '../entities';
@Injectable()
export class DeviceUserPermissionRepository extends Repository<DeviceUserPermissionEntity> {
constructor(private dataSource: DataSource) {
super(DeviceUserPermissionEntity, dataSource.createEntityManager());
}
}

View File

@ -1 +0,0 @@
export * from './device.user.permission.repository';

View File

@ -1,11 +1,21 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DeviceEntity } from './entities';
import {
DeviceEntity,
DeviceNotificationEntity,
DeviceUserPermissionEntity,
} from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([DeviceEntity])],
imports: [
TypeOrmModule.forFeature([
DeviceEntity,
DeviceNotificationEntity,
DeviceUserPermissionEntity,
]),
],
})
export class DeviceRepositoryModule {}

View File

@ -21,3 +21,35 @@ export class DeviceDto {
@IsNotEmpty()
productUuid: string;
}
export class DeviceUserPermissionDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public deviceUuid: string;
@IsString()
@IsNotEmpty()
public permissionTypeUuid: string;
}
export class DeviceNotificationDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public deviceUuid: string;
}

View File

@ -1,11 +1,11 @@
import { Column, Entity, ManyToOne, OneToMany, Unique, Index } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { DeviceDto } from '../dtos/device.dto';
import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto';
import { SpaceEntity } from '../../space/entities';
import { ProductEntity } from '../../product/entities';
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
import { DeviceNotificationEntity } from '../../device-notification/entities';
import { UserEntity } from '../../user/entities';
import { DeviceNotificationDto } from '../dtos';
import { PermissionTypeEntity } from '../../permission/entities';
@Entity({ name: 'device' })
@Unique(['deviceTuyaUuid'])
@ -60,3 +60,68 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
Object.assign(this, partial);
}
}
@Entity({ name: 'device-notification' })
@Unique(['userUuid', 'deviceUuid'])
export class DeviceNotificationEntity extends AbstractEntity<DeviceNotificationDto> {
@Column({
nullable: false,
})
public userUuid: string;
@Column({
nullable: false,
})
deviceUuid: string;
@ManyToOne(() => DeviceEntity, (device) => device.permission, {
nullable: false,
})
device: DeviceEntity;
@ManyToOne(() => UserEntity, (user) => user.userPermission, {
nullable: false,
})
user: UserEntity;
constructor(partial: Partial<DeviceNotificationEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'device-user-permission' })
@Unique(['userUuid', 'deviceUuid'])
export class DeviceUserPermissionEntity extends AbstractEntity<DeviceUserPermissionDto> {
@Column({
nullable: false,
})
public userUuid: string;
@Column({
nullable: false,
})
deviceUuid: string;
@ManyToOne(() => DeviceEntity, (device) => device.permission, {
nullable: false,
})
device: DeviceEntity;
@ManyToOne(
() => PermissionTypeEntity,
(permissionType) => permissionType.permission,
{
nullable: false,
},
)
permissionType: PermissionTypeEntity;
@ManyToOne(() => UserEntity, (user) => user.userPermission, {
nullable: false,
})
user: UserEntity;
constructor(partial: Partial<DeviceUserPermissionEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1,6 +1,10 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { DeviceEntity } from '../entities';
import {
DeviceEntity,
DeviceNotificationEntity,
DeviceUserPermissionEntity,
} from '../entities';
@Injectable()
export class DeviceRepository extends Repository<DeviceEntity> {
@ -8,3 +12,15 @@ export class DeviceRepository extends Repository<DeviceEntity> {
super(DeviceEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class DeviceNotificationRepository extends Repository<DeviceNotificationEntity> {
constructor(private dataSource: DataSource) {
super(DeviceNotificationEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class DeviceUserPermissionRepository extends Repository<DeviceUserPermissionEntity> {
constructor(private dataSource: DataSource) {
super(DeviceUserPermissionEntity, dataSource.createEntityManager());
}
}

View File

@ -2,7 +2,7 @@ import { Column, Entity, OneToMany } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { PermissionType } from '@app/common/constants/permission-type.enum';
import { PermissionTypeDto } from '../dtos/permission.dto';
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
import { DeviceUserPermissionEntity } from '../../device/entities';
@Entity({ name: 'permission-type' })
export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {

View File

@ -2,7 +2,7 @@ import { Column, Entity, OneToMany, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { RoleTypeDto } from '../dtos/role.type.dto';
import { RoleType } from '@app/common/constants/role.type.enum';
import { UserRoleEntity } from '../../user-role/entities';
import { UserRoleEntity } from '../../user/entities';
@Entity({ name: 'role-type' })
@Unique(['type'])

View File

@ -1 +0,0 @@
export * from './space.type.dto';

View File

@ -1,11 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class SpaceTypeDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public type: string;
}

View File

@ -1 +0,0 @@
export * from './space.type.entity';

View File

@ -1,26 +0,0 @@
import { Column, Entity, OneToMany } from 'typeorm';
import { SpaceTypeDto } from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { SpaceEntity } from '../../space/entities';
@Entity({ name: 'space-type' })
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
nullable: false,
})
public uuid: string;
@Column({
nullable: false,
})
type: string;
@OneToMany(() => SpaceEntity, (space) => space.spaceType)
spaces: SpaceEntity[];
constructor(partial: Partial<SpaceTypeEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './space.type.repository';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { SpaceTypeEntity } from '../entities/space.type.entity';
@Injectable()
export class SpaceTypeRepository extends Repository<SpaceTypeEntity> {
constructor(private dataSource: DataSource) {
super(SpaceTypeEntity, dataSource.createEntityManager());
}
}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SpaceTypeEntity } from './entities/space.type.entity';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([SpaceTypeEntity])],
})
export class SpaceTypeRepositoryModule {}

View File

@ -21,3 +21,13 @@ export class SpaceDto {
@IsNotEmpty()
public invitationCode: string;
}
export class SpaceTypeDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public type: string;
}

View File

@ -1,16 +1,37 @@
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
import { SpaceDto } from '../dtos';
import { SpaceDto, SpaceTypeDto } from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { SpaceTypeEntity } from '../../space-type/entities';
import { UserSpaceEntity } from '../../user-space/entities';
import { UserSpaceEntity } from '../../user/entities';
import { DeviceEntity } from '../../device/entities';
@Entity({ name: 'space-type' })
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()',
nullable: false,
})
public uuid: string;
@Column({
nullable: false,
})
type: string;
@OneToMany(() => SpaceEntity, (space) => space.spaceType)
spaces: SpaceEntity[];
constructor(partial: Partial<SpaceTypeEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'space' })
@Unique(['invitationCode'])
export class SpaceEntity extends AbstractEntity<SpaceDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
default: () => 'gen_random_uuid()',
nullable: false,
})
public uuid: string;

View File

@ -1,6 +1,6 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { SpaceEntity } from '../entities/space.entity';
import { SpaceEntity, SpaceTypeEntity } from '../entities';
@Injectable()
export class SpaceRepository extends Repository<SpaceEntity> {
@ -8,3 +8,10 @@ export class SpaceRepository extends Repository<SpaceEntity> {
super(SpaceEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class SpaceTypeRepository extends Repository<SpaceTypeEntity> {
constructor(private dataSource: DataSource) {
super(SpaceTypeEntity, dataSource.createEntityManager());
}
}

View File

@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SpaceEntity } from './entities/space.entity';
import { SpaceEntity, SpaceTypeEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([SpaceEntity])],
imports: [TypeOrmModule.forFeature([SpaceEntity, SpaceTypeEntity])],
})
export class SpaceRepositoryModule {}

View File

@ -1 +0,0 @@
export * from './user.notification.dto';

View File

@ -1,19 +0,0 @@
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
export class UserNotificationDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public subscriptionUuid: string;
@IsBoolean()
@IsNotEmpty()
public active: boolean;
}

View File

@ -1 +0,0 @@
export * from './user.notification.entity';

View File

@ -1,27 +0,0 @@
import { Column, Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserNotificationDto } from '../dtos';
import { UserEntity } from '../../user/entities';
@Entity({ name: 'user-notification' })
@Unique(['user', 'subscriptionUuid'])
export class UserNotificationEntity extends AbstractEntity<UserNotificationDto> {
@ManyToOne(() => UserEntity, (user) => user.roles, {
nullable: false,
})
user: UserEntity;
@Column({
nullable: false,
})
subscriptionUuid: string;
@Column({
nullable: false,
default: true,
})
active: boolean;
constructor(partial: Partial<UserNotificationEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './user.notification.repository';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserNotificationEntity } from '../entities';
@Injectable()
export class UserNotificationRepository extends Repository<UserNotificationEntity> {
constructor(private dataSource: DataSource) {
super(UserNotificationEntity, dataSource.createEntityManager());
}
}

View File

@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserNotificationEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserNotificationEntity])],
})
export class UserNotificationRepositoryModule {}

View File

@ -1 +0,0 @@
export * from './user-otp.dto';

View File

@ -1,19 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class UserOtpDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public email: string;
@IsString()
@IsNotEmpty()
public otpCode: string;
@IsString()
@IsNotEmpty()
public expiryTime: string;
}

View File

@ -1 +0,0 @@
export * from './user-otp.entity';

View File

@ -1,34 +0,0 @@
import { Column, Entity } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserOtpDto } from '../dtos';
import { OtpType } from '../../../../src/constants/otp-type.enum';
@Entity({ name: 'user-otp' })
export class UserOtpEntity extends AbstractEntity<UserOtpDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()',
nullable: false,
})
public uuid: string;
@Column({ nullable: false })
email: string;
@Column({ nullable: false })
otpCode: string;
@Column({ nullable: false })
expiryTime: Date;
@Column({
type: 'enum',
enum: Object.values(OtpType),
})
type: OtpType;
constructor(partial: Partial<UserOtpEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserOtpEntity } from '../entities';
@Injectable()
export class UserOtpRepository extends Repository<UserOtpEntity> {
constructor(private dataSource: DataSource) {
super(UserOtpEntity, dataSource.createEntityManager());
}
}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserOtpEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserOtpEntity])],
})
export class UserOtpRepositoryModule {}

View File

@ -1 +0,0 @@
export * from './user.role.dto';

View File

@ -1,15 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class UserRoleDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public roleTypeUuid: string;
}

View File

@ -1 +0,0 @@
export * from './user.role.entity';

View File

@ -1,24 +0,0 @@
import { Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserRoleDto } from '../dtos';
import { UserEntity } from '../../user/entities';
import { RoleTypeEntity } from '../../role-type/entities';
@Entity({ name: 'user-role' })
@Unique(['user', 'roleType'])
export class UserRoleEntity extends AbstractEntity<UserRoleDto> {
@ManyToOne(() => UserEntity, (user) => user.roles, {
nullable: false,
})
user: UserEntity;
@ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, {
nullable: false,
})
roleType: RoleTypeEntity;
constructor(partial: Partial<UserRoleEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './user.role.repository';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserRoleEntity } from '../entities';
@Injectable()
export class UserRoleRepository extends Repository<UserRoleEntity> {
constructor(private dataSource: DataSource) {
super(UserRoleEntity, dataSource.createEntityManager());
}
}

View File

@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRoleEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserRoleEntity])],
})
export class UserRoleRepositoryModule {}

View File

@ -1 +0,0 @@
export * from './user.space.dto';

View File

@ -1,15 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class UserSpaceDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public spaceUuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
}

View File

@ -1 +0,0 @@
export * from './user.space.entity';

View File

@ -1,29 +0,0 @@
import { Column, Entity, ManyToOne, Unique } from 'typeorm';
import { UserSpaceDto } from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { SpaceEntity } from '../../space/entities';
import { UserEntity } from '../../user/entities';
@Entity({ name: 'user-space' })
@Unique(['user', 'space'])
export class UserSpaceEntity extends AbstractEntity<UserSpaceDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
nullable: false,
})
public uuid: string;
@ManyToOne(() => UserEntity, (user) => user.userSpaces, { nullable: false })
user: UserEntity;
@ManyToOne(() => SpaceEntity, (space) => space.userSpaces, {
nullable: false,
})
space: SpaceEntity;
constructor(partial: Partial<UserSpaceEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1 +0,0 @@
export * from './user.space.repository';

View File

@ -1,10 +0,0 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserSpaceEntity } from '../entities/user.space.entity';
@Injectable()
export class UserSpaceRepository extends Repository<UserSpaceEntity> {
constructor(private dataSource: DataSource) {
super(UserSpaceEntity, dataSource.createEntityManager());
}
}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserSpaceEntity } from './entities/user.space.entity';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserSpaceEntity])],
})
export class UserSpaceRepositoryModule {}

View File

@ -1,4 +1,4 @@
import { IsNotEmpty, IsString } from 'class-validator';
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
export class UserDto {
@IsString()
@ -21,3 +21,67 @@ export class UserDto {
@IsNotEmpty()
public lastName: string;
}
export class UserNotificationDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public subscriptionUuid: string;
@IsBoolean()
@IsNotEmpty()
public active: boolean;
}
export class UserOtpDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public email: string;
@IsString()
@IsNotEmpty()
public otpCode: string;
@IsString()
@IsNotEmpty()
public expiryTime: string;
}
export class UserRoleDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public roleTypeUuid: string;
}
export class UserSpaceDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public spaceUuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
}

View File

@ -1,15 +1,31 @@
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities/device.user.permission.entity';
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
import { UserDto } from '../dtos';
import {
Column,
DeleteDateColumn,
Entity,
ManyToOne,
OneToMany,
Unique,
} from 'typeorm';
import {
UserDto,
UserNotificationDto,
UserOtpDto,
UserRoleDto,
UserSpaceDto,
} from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserSpaceEntity } from '../../user-space/entities';
import { UserRoleEntity } from '../../user-role/entities';
import { DeviceNotificationEntity } from '../../device-notification/entities';
import { UserNotificationEntity } from '../../user-notification/entities';
import { DeviceEntity } from '../../device/entities';
import {
DeviceEntity,
DeviceNotificationEntity,
DeviceUserPermissionEntity,
} from '../../device/entities';
import { defaultProfilePicture } from '@app/common/constants/default.profile.picture';
import { RegionEntity } from '../../region/entities';
import { TimeZoneEntity } from '../../timezone/entities';
import { OtpType } from '../../../../src/constants/otp-type.enum';
import { RoleTypeEntity } from '../../role-type/entities';
import { SpaceEntity } from '../../space/entities';
import { VisitorPasswordEntity } from '../../visitor-password/entities';
@Entity({ name: 'user' })
export class UserEntity extends AbstractEntity<UserDto> {
@ -94,8 +110,112 @@ export class UserEntity extends AbstractEntity<UserDto> {
nullable: true,
})
timezone: TimeZoneEntity;
@OneToMany(
() => VisitorPasswordEntity,
(visitorPassword) => visitorPassword.user,
)
public visitorPasswords: VisitorPasswordEntity[];
constructor(partial: Partial<UserEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'user-notification' })
@Unique(['user', 'subscriptionUuid'])
export class UserNotificationEntity extends AbstractEntity<UserNotificationDto> {
@ManyToOne(() => UserEntity, (user) => user.roles, {
nullable: false,
})
user: UserEntity;
@Column({
nullable: false,
})
subscriptionUuid: string;
@Column({
nullable: false,
default: true,
})
active: boolean;
constructor(partial: Partial<UserNotificationEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'user-otp' })
export class UserOtpEntity extends AbstractEntity<UserOtpDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()',
nullable: false,
})
public uuid: string;
@Column({ nullable: false })
email: string;
@Column({ nullable: false })
otpCode: string;
@Column({ nullable: false })
expiryTime: Date;
@Column({
type: 'enum',
enum: Object.values(OtpType),
})
type: OtpType;
@DeleteDateColumn({ nullable: true })
deletedAt?: Date;
constructor(partial: Partial<UserOtpEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'user-role' })
@Unique(['user', 'roleType'])
export class UserRoleEntity extends AbstractEntity<UserRoleDto> {
@ManyToOne(() => UserEntity, (user) => user.roles, {
nullable: false,
})
user: UserEntity;
@ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, {
nullable: false,
})
roleType: RoleTypeEntity;
constructor(partial: Partial<UserRoleEntity>) {
super();
Object.assign(this, partial);
}
}
@Entity({ name: 'user-space' })
@Unique(['user', 'space'])
export class UserSpaceEntity extends AbstractEntity<UserSpaceDto> {
@Column({
type: 'uuid',
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
nullable: false,
})
public uuid: string;
@ManyToOne(() => UserEntity, (user) => user.userSpaces, { nullable: false })
user: UserEntity;
@ManyToOne(() => SpaceEntity, (space) => space.userSpaces, {
nullable: false,
})
space: SpaceEntity;
constructor(partial: Partial<UserSpaceEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -1,6 +1,12 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserEntity } from '../entities/user.entity';
import {
UserEntity,
UserNotificationEntity,
UserOtpEntity,
UserRoleEntity,
UserSpaceEntity,
} from '../entities/';
@Injectable()
export class UserRepository extends Repository<UserEntity> {
@ -8,3 +14,31 @@ export class UserRepository extends Repository<UserEntity> {
super(UserEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class UserNotificationRepository extends Repository<UserNotificationEntity> {
constructor(private dataSource: DataSource) {
super(UserNotificationEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class UserOtpRepository extends Repository<UserOtpEntity> {
constructor(private dataSource: DataSource) {
super(UserOtpEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class UserRoleRepository extends Repository<UserRoleEntity> {
constructor(private dataSource: DataSource) {
super(UserRoleEntity, dataSource.createEntityManager());
}
}
@Injectable()
export class UserSpaceRepository extends Repository<UserSpaceEntity> {
constructor(private dataSource: DataSource) {
super(UserSpaceEntity, dataSource.createEntityManager());
}
}

View File

@ -1,11 +1,25 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from './entities/user.entity';
import {
UserEntity,
UserNotificationEntity,
UserOtpEntity,
UserRoleEntity,
UserSpaceEntity,
} from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserEntity])],
imports: [
TypeOrmModule.forFeature([
UserEntity,
UserNotificationEntity,
UserOtpEntity,
UserRoleEntity,
UserSpaceEntity,
]),
],
})
export class UserRepositoryModule {}

View File

@ -0,0 +1 @@
export * from './visitor-password.dto';

View File

@ -1,15 +1,15 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class DeviceNotificationDto {
export class VisitorPasswordDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
public authorizerUuid: string;
@IsString()
@IsNotEmpty()
public deviceUuid: string;
public passwordTuyaUuid: string;
}

View File

@ -0,0 +1 @@
export * from './visitor-password.entity';

View File

@ -0,0 +1,25 @@
import { Column, Entity, ManyToOne, JoinColumn, Index } from 'typeorm';
import { VisitorPasswordDto } from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserEntity } from '../../user/entities/user.entity';
@Entity({ name: 'visitor-password' })
@Index('IDX_PASSWORD_TUYA_UUID', ['passwordTuyaUuid'])
export class VisitorPasswordEntity extends AbstractEntity<VisitorPasswordDto> {
@Column({
nullable: false,
unique: true,
})
public passwordTuyaUuid: string;
@ManyToOne(() => UserEntity, (user) => user.visitorPasswords, {
nullable: false,
})
@JoinColumn({ name: 'authorizer_uuid' })
public user: UserEntity;
constructor(partial: Partial<VisitorPasswordEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -0,0 +1 @@
export * from './visitor-password.repository';

View File

@ -0,0 +1,10 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { VisitorPasswordEntity } from '../entities/visitor-password.entity';
@Injectable()
export class VisitorPasswordRepository extends Repository<VisitorPasswordEntity> {
constructor(private dataSource: DataSource) {
super(VisitorPasswordEntity, dataSource.createEntityManager());
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { VisitorPasswordEntity } from './entities/visitor-password.entity';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([VisitorPasswordEntity])],
})
export class VisitorPasswordRepositoryModule {}

View File

@ -7,13 +7,12 @@ import { ConfigModule } from '@nestjs/config';
import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module';
import { RoleTypeRepository } from '../modules/role-type/repositories';
import { RoleTypeSeeder } from './services/role.type.seeder';
import { SpaceTypeRepository } from '../modules/space-type/repositories';
import { SpaceTypeRepository } from '../modules/space/repositories';
import { SpaceTypeSeeder } from './services/space.type.seeder';
import { SpaceTypeRepositoryModule } from '../modules/space-type/space.type.repository.module';
import { SpaceRepositoryModule } from '../modules/space/space.repository.module';
import { SuperAdminSeeder } from './services/supper.admin.seeder';
import { UserRepository } from '../modules/user/repositories';
import { UserRoleRepository } from '../modules/user-role/repositories';
import { UserRoleRepositoryModule } from '../modules/user-role/user.role.repository.module';
import { UserRoleRepository } from '../modules/user/repositories';
import { UserRepositoryModule } from '../modules/user/user.repository.module';
import { RegionSeeder } from './services/regions.seeder';
import { RegionRepository } from '../modules/region/repositories';
@ -44,8 +43,7 @@ import { TimeZoneRepository } from '../modules/timezone/repositories';
PermissionTypeRepositoryModule,
RoleTypeRepositoryModule,
UserRepositoryModule,
UserRoleRepositoryModule,
SpaceTypeRepositoryModule,
SpaceRepositoryModule,
],
})
export class SeederModule {}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { SpaceType } from '../../constants/space-type.enum';
import { SpaceTypeRepository } from '../../modules/space-type/repositories';
import { SpaceTypeRepository } from '../../modules/space/repositories';
@Injectable()
export class SpaceTypeSeeder {

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { UserRepository } from '@app/common/modules/user/repositories';
import { RoleType } from '@app/common/constants/role.type.enum';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { UserRoleRepository } from '@app/common/modules/user/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
import { ConfigService } from '@nestjs/config';
import { HelperHashService } from '../../helper/services';

View File

@ -19,7 +19,7 @@ export class EmailService {
};
}
async sendOTPEmail(
async sendEmail(
email: string,
subject: string,
message: string,

1077
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@
"class-validator": "^0.14.1",
"crypto-js": "^4.2.0",
"express-rate-limit": "^7.1.5",
"firebase": "^10.12.5",
"helmet": "^7.1.0",
"ioredis": "^5.3.2",
"morgan": "^1.10.0",

View File

@ -23,6 +23,7 @@ import { LoggingInterceptor } from './interceptors/logging.interceptor';
import { AutomationModule } from './automation/automation.module';
import { RegionModule } from './region/region.module';
import { TimeZoneModule } from './timezone/timezone.module';
import { VisitorPasswordModule } from './vistor-password/visitor-password.module';
@Module({
imports: [
ConfigModule.forRoot({
@ -48,6 +49,7 @@ import { TimeZoneModule } from './timezone/timezone.module';
DoorLockModule,
RegionModule,
TimeZoneModule,
VisitorPasswordModule,
],
controllers: [AuthenticationController],
providers: [

View File

@ -2,14 +2,16 @@ import { Module } from '@nestjs/common';
import { AuthenticationController } from './controllers/authentication.controller';
import { AuthenticationService } from './services/authentication.service';
import { ConfigModule } from '@nestjs/config';
import { UserRepositoryModule } from '../../libs/common/src/modules/user/user.repository.module';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
import { CommonModule } from '../../libs/common/src';
import { UserAuthController } from './controllers';
import { UserAuthService } from './services';
import { UserRepository } from '../../libs/common/src/modules/user/repositories';
import { UserSessionRepository } from '../../libs/common/src/modules/session/repositories/session.repository';
import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/repositories/user-otp.repository';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { UserRepository } from '@app/common/modules/user/repositories';
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
import {
UserRoleRepository,
UserOtpRepository,
} from '@app/common/modules/user/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
@Module({

View File

@ -71,7 +71,7 @@ export class UserAuthController {
return {
statusCode: HttpStatus.OK,
data: {
otp: otpCode,
...otpCode,
},
message: 'Otp Send Successfully',
};

View File

@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsPasswordStrong } from 'src/validators/password.validator';
export class UserSignUpDto {
@ -38,4 +38,8 @@ export class UserSignUpDto {
@IsString()
@IsNotEmpty()
public lastName: string;
@IsString()
@IsOptional()
public regionUuid: string;
}

View File

@ -11,4 +11,9 @@ export class UserLoginDto {
@IsString()
@IsOptional()
password: string;
@ApiProperty()
@IsString()
@IsOptional()
regionUuid?: string;
}

View File

@ -1,6 +1,12 @@
import { OtpType } from '../../../libs/common/src/constants/otp-type.enum';
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsEnum, IsNotEmpty, IsString } from 'class-validator';
import {
IsEmail,
IsEnum,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class UserOtpDto {
@ApiProperty()
@ -12,6 +18,11 @@ export class UserOtpDto {
@IsEnum(OtpType)
@IsNotEmpty()
type: OtpType;
@ApiProperty()
@IsOptional()
@IsString()
regionUuid?: string;
}
export class VerifyOtpDto extends UserOtpDto {

View File

@ -1,5 +1,5 @@
import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository';
import { UserRoleRepository } from './../../../libs/common/src/modules/user-role/repositories/user.role.repository';
import { UserRoleRepository } from './../../../libs/common/src/modules/user/repositories/user.repository';
import { UserRepository } from '../../../libs/common/src/modules/user/repositories';
import {
BadRequestException,
@ -12,12 +12,15 @@ import { HelperHashService } from '../../../libs/common/src/helper/services';
import { UserLoginDto } from '../dtos/user-login.dto';
import { AuthService } from '../../../libs/common/src/auth/services/auth.service';
import { UserSessionRepository } from '../../../libs/common/src/modules/session/repositories/session.repository';
import { UserOtpRepository } from '../../../libs/common/src/modules/user-otp/repositories/user-otp.repository';
import { UserOtpRepository } from '../../../libs/common/src/modules/user/repositories/user.repository';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
import { EmailService } from '../../../libs/common/src/util/email.service';
import { OtpType } from '../../../libs/common/src/constants/otp-type.enum';
import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity';
import * as argon2 from 'argon2';
import { differenceInSeconds } from '@app/common/helper/differenceInSeconds';
import { LessThan, MoreThan } from 'typeorm';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class UserAuthService {
@ -30,6 +33,7 @@ export class UserAuthService {
private readonly emailService: EmailService,
private readonly userRoleRepository: UserRoleRepository,
private readonly roleTypeRepository: RoleTypeRepository,
private readonly configService: ConfigService,
) {}
async signUp(userSignUpDto: UserSignUpDto): Promise<UserEntity> {
@ -44,9 +48,17 @@ export class UserAuthService {
);
try {
const { regionUuid, ...rest } = userSignUpDto;
const user = await this.userRepository.save({
...userSignUpDto,
...rest,
password: hashedPassword,
region: regionUuid
? {
uuid: regionUuid,
}
: {
regionName: 'United Arab Emirates',
},
});
return user;
@ -80,41 +92,46 @@ export class UserAuthService {
}
async userLogin(data: UserLoginDto) {
const user = await this.authService.validateUser(data.email, data.password);
if (!user) {
throw new UnauthorizedException('Invalid login credentials.');
}
const session = await Promise.all([
await this.sessionRepository.update(
{ userId: user.id },
{
isLoggedOut: true,
},
),
await this.authService.createSession({
try {
const user = await this.authService.validateUser(
data.email,
data.password,
data.regionUuid,
);
if (!user) {
throw new UnauthorizedException('Invalid login credentials.');
}
const session = await Promise.all([
await this.sessionRepository.update(
{ userId: user.id },
{
isLoggedOut: true,
},
),
await this.authService.createSession({
userId: user.uuid,
loginTime: new Date(),
isLoggedOut: false,
}),
]);
return await this.authService.login({
email: user.email,
userId: user.uuid,
loginTime: new Date(),
isLoggedOut: false,
}),
]);
return await this.authService.login({
email: user.email,
userId: user.uuid,
uuid: user.uuid,
roles: user?.roles?.map((role) => {
return { uuid: role.uuid, type: role.roleType.type };
}),
sessionId: session[1].uuid,
});
uuid: user.uuid,
roles: user?.roles?.map((role) => {
return { uuid: role.uuid, type: role.roleType.type };
}),
sessionId: session[1].uuid,
});
} catch (error) {
throw new BadRequestException('Invalid credentials');
}
}
async deleteUser(uuid: string) {
const user = await this.findOneById(uuid);
if (!user) {
throw new BadRequestException('User does not found');
throw new BadRequestException('User not found');
}
return await this.userRepository.update({ uuid }, { isActive: false });
}
@ -123,8 +140,61 @@ export class UserAuthService {
return await this.userRepository.findOne({ where: { uuid: id } });
}
async generateOTP(data: UserOtpDto): Promise<string> {
await this.otpRepository.delete({ email: data.email, type: data.type });
async generateOTP(data: UserOtpDto): Promise<{
otpCode: string;
cooldown: number;
}> {
const otpLimiter = new Date();
otpLimiter.setDate(
otpLimiter.getDate() - this.configService.get<number>('OTP_LIMITER'),
);
const userExists = await this.userRepository.exists({
where: {
region: data.regionUuid
? {
uuid: data.regionUuid,
}
: undefined,
email: data.email,
isUserVerified: data.type === OtpType.PASSWORD ? true : undefined,
},
});
if (!userExists) {
throw new BadRequestException('User not found');
}
await this.otpRepository.softDelete({ email: data.email, type: data.type });
await this.otpRepository.delete({
email: data.email,
type: data.type,
createdAt: LessThan(otpLimiter),
});
const countOfOtp = await this.otpRepository.count({
withDeleted: true,
where: {
email: data.email,
type: data.type,
createdAt: MoreThan(otpLimiter),
},
});
const lastOtp = await this.otpRepository.findOne({
where: { email: data.email, type: data.type },
order: { createdAt: 'DESC' },
withDeleted: true,
});
let cooldown = 30 * Math.pow(2, countOfOtp - 1);
if (lastOtp) {
const now = new Date();
const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt);
if (timeSinceLastOtp < cooldown) {
throw new BadRequestException({
message: `Please wait ${cooldown - timeSinceLastOtp} more seconds before requesting a new OTP.`,
data: {
cooldown: cooldown - timeSinceLastOtp,
},
});
}
}
const otpCode = Math.floor(100000 + Math.random() * 900000).toString();
const expiryTime = new Date();
expiryTime.setMinutes(expiryTime.getMinutes() + 1);
@ -134,10 +204,19 @@ export class UserAuthService {
expiryTime,
type: data.type,
});
const countOfOtpToReturn = await this.otpRepository.count({
withDeleted: true,
where: {
email: data.email,
type: data.type,
createdAt: MoreThan(otpLimiter),
},
});
cooldown = 30 * Math.pow(2, countOfOtpToReturn - 1);
const subject = 'OTP send successfully';
const message = `Your OTP code is ${otpCode}`;
this.emailService.sendOTPEmail(data.email, subject, message);
return otpCode;
this.emailService.sendEmail(data.email, subject, message);
return { otpCode, cooldown };
}
async verifyOTP(data: VerifyOtpDto): Promise<boolean> {

View File

@ -7,9 +7,10 @@ import { SpaceRepository } from '@app/common/modules/space/repositories';
import { DeviceService } from 'src/device/services';
import { DeviceRepository } from '@app/common/modules/device/repositories';
import { ProductRepository } from '@app/common/modules/product/repositories';
import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module';
@Module({
imports: [ConfigModule, SpaceRepositoryModule],
imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule],
controllers: [AutomationController],
providers: [
AutomationService,

View File

@ -4,21 +4,13 @@ import { BuildingController } from './controllers/building.controller';
import { ConfigModule } from '@nestjs/config';
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
import { SpaceRepository } from '@app/common/modules/space/repositories';
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
import { UserSpaceRepositoryModule } from '@app/common/modules/user-space/user.space.repository.module';
import { UserSpaceRepository } from '@app/common/modules/user-space/repositories';
import { SpaceTypeRepository } from '@app/common/modules/space/repositories';
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
import { UserRepository } from '@app/common/modules/user/repositories';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
@Module({
imports: [
ConfigModule,
SpaceRepositoryModule,
SpaceTypeRepositoryModule,
UserSpaceRepositoryModule,
UserRepositoryModule,
],
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
controllers: [BuildingController],
providers: [
BuildingService,

View File

@ -1,5 +1,5 @@
import { GetBuildingChildDto } from '../dtos/get.building.dto';
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space-type/repositories/space.type.repository';
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository';
import {
Injectable,
HttpException,
@ -17,7 +17,7 @@ import {
} from '../interface/building.interface';
import { SpaceEntity } from '@app/common/modules/space/entities';
import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
import { UserSpaceRepository } from '@app/common/modules/user-space/repositories';
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
@Injectable()
export class BuildingService {

Some files were not shown because too many files have changed in this diff Show More