From 8cb131f395ce119211aa03fd6851683442cf3f1b Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:14:58 +0300 Subject: [PATCH 01/49] test --- libs/common/src/config/mongoose.config.ts | 8 + libs/common/src/database/database.module.ts | 10 + libs/common/src/helper/helper.module.ts | 7 +- .../services/tuya.web.socket.service.ts | 15 +- .../src/mongoose/database/mongoose.config.ts | 23 ++ .../controllers/devices-status.controller.ts | 27 ++ .../devices-status/devices-status.module.ts | 23 ++ .../dtos/add.devices-status.dto.ts | 38 ++ .../devices-status.repository.interface.ts | 6 + .../repository/devices-status.repository.ts | 49 +++ .../schema/devices-status.schema.ts | 20 + .../services/devices-status.service.ts | 140 +++++++ .../src/mongoose/mongoose.shared.module.ts | 9 + package-lock.json | 363 ++++++++++++++++++ package.json | 2 + src/automation/automation.module.ts | 3 +- src/device/device.module.ts | 8 +- src/device/services/device.service.ts | 20 +- src/scene/scene.module.ts | 3 +- 19 files changed, 760 insertions(+), 14 deletions(-) create mode 100644 libs/common/src/config/mongoose.config.ts create mode 100644 libs/common/src/mongoose/database/mongoose.config.ts create mode 100644 libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts create mode 100644 libs/common/src/mongoose/devices-status/devices-status.module.ts create mode 100644 libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts create mode 100644 libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts create mode 100644 libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts create mode 100644 libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts create mode 100644 libs/common/src/mongoose/devices-status/services/devices-status.service.ts create mode 100644 libs/common/src/mongoose/mongoose.shared.module.ts diff --git a/libs/common/src/config/mongoose.config.ts b/libs/common/src/config/mongoose.config.ts new file mode 100644 index 0000000..6292f3c --- /dev/null +++ b/libs/common/src/config/mongoose.config.ts @@ -0,0 +1,8 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs( + 'mongoose-config', + (): Record => ({ + MONGODB_URI: process.env.MONGODB_URI, + }), +); diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index e6ddb6c..a4fa323 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -18,6 +18,9 @@ import { UserNotificationEntity } from '../modules/user-notification/entities'; import { DeviceNotificationEntity } from '../modules/device-notification/entities'; import { RegionEntity } from '../modules/region/entities'; import { TimeZoneEntity } from '../modules/timezone/entities'; +import { MongooseModule } from '@nestjs/mongoose'; +import { mongooseConfig } from '../mongoose/database/mongoose.config'; +import { DeviceStatusMongoModule } from '../mongoose/devices-status/devices-status.module'; @Module({ imports: [ @@ -65,6 +68,13 @@ import { TimeZoneEntity } from '../modules/timezone/entities'; ssl: Boolean(JSON.parse(configService.get('DB_SSL'))), }), }), + MongooseModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (configService: ConfigService) => + mongooseConfig(configService), + }), + DeviceStatusMongoModule, ], }) export class DatabaseModule {} diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index 3378051..75ec360 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -9,6 +9,7 @@ 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 { DeviceStatusMongoModule } from '../mongoose/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, + DeviceNotificationRepositoryModule, + DeviceStatusMongoModule, + ], }) export class HelperModule {} diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index cca7fc6..779cc35 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -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 { DeviceStatusMongoService } from '@app/common/mongoose/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 deviceStatusMongoService: DeviceStatusMongoService, ) { // 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.deviceStatusMongoService.addDeviceStatusToMongo({ + 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); diff --git a/libs/common/src/mongoose/database/mongoose.config.ts b/libs/common/src/mongoose/database/mongoose.config.ts new file mode 100644 index 0000000..dfb3ed8 --- /dev/null +++ b/libs/common/src/mongoose/database/mongoose.config.ts @@ -0,0 +1,23 @@ +import { MongooseModuleOptions } from '@nestjs/mongoose'; +import { ConfigService } from '@nestjs/config'; + +export const mongooseConfig = async ( + configService: ConfigService, +): Promise => { + return { + uri: configService.get('MONGODB_URI'), + dbName: 'syncrow-dev', // Specify your database name here + connectionFactory: (connection) => { + connection.on('connected', () => { + console.log('Mongoose connected to MongoDB'); + }); + connection.on('error', (err) => { + console.error('Mongoose connection error:', err); + }); + connection.on('disconnected', () => { + console.log('Mongoose disconnected from MongoDB'); + }); + return connection; + }, + }; +}; diff --git a/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts b/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts new file mode 100644 index 0000000..6cd98f6 --- /dev/null +++ b/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts @@ -0,0 +1,27 @@ +import { Controller, Post, Param } from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; +import { DeviceStatusMongoService } from '../services/devices-status.service'; + +@ApiTags('Device Status Mongo Module') +@Controller({ + version: '1', + path: 'device-status-mongo', +}) +export class DeviceStatusMongoController { + constructor( + private readonly deviceStatusMongoService: DeviceStatusMongoService, + ) {} + + @ApiBearerAuth() + @Post(':deviceTuyaUuid') + async addDeviceStatus( + @Param('deviceTuyaUuid') deviceTuyaUuid: string, + ): Promise { + return this.deviceStatusMongoService.addDeviceStatusByDeviceUuid( + deviceTuyaUuid, + ); + } + + // Add other endpoints as needed +} diff --git a/libs/common/src/mongoose/devices-status/devices-status.module.ts b/libs/common/src/mongoose/devices-status/devices-status.module.ts new file mode 100644 index 0000000..3e9a0bf --- /dev/null +++ b/libs/common/src/mongoose/devices-status/devices-status.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { DeviceStatusRepository } from './repository/devices-status.repository'; +import { DeviceStatusSchema } from './schema/devices-status.schema'; +import { DeviceStatusMongoController } from './controllers/devices-status.controller'; +import { DeviceStatusMongoService } from './services/devices-status.service'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { name: 'DeviceStatus', schema: DeviceStatusSchema }, + ]), + ], + providers: [ + DeviceStatusMongoService, + DeviceStatusRepository, + DeviceRepository, + ], + controllers: [DeviceStatusMongoController], + exports: [DeviceStatusMongoService, DeviceStatusRepository], +}) +export class DeviceStatusMongoModule {} diff --git a/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts b/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts new file mode 100644 index 0000000..9ad7a11 --- /dev/null +++ b/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts @@ -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[]; +} diff --git a/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts b/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts new file mode 100644 index 0000000..0750c3f --- /dev/null +++ b/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts @@ -0,0 +1,6 @@ +import { Document } from 'mongoose'; + +export interface IRepository { + create(dto: Partial): Promise; + // Add more methods as needed (e.g., find, update, delete) +} diff --git a/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts b/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts new file mode 100644 index 0000000..a515842 --- /dev/null +++ b/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { AddDeviceStatusInterface } from '../schema/devices-status.schema'; +import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; + +@Injectable() +export class DeviceStatusRepository { + constructor( + @InjectModel('DeviceStatus') + private readonly deviceStatusModel: Model, + ) {} + + async addDeviceStatus( + addDeviceStatusDto: AddDeviceStatusDto, + ): Promise { + const { deviceTuyaUuid, status } = addDeviceStatusDto; + + // Update or insert the device document + await this.deviceStatusModel.findOneAndUpdate( + { deviceTuyaUuid }, + { + $set: { + deviceUuid: addDeviceStatusDto.deviceUuid, + productUuid: addDeviceStatusDto.productUuid, + productType: addDeviceStatusDto.productType, + }, + }, + { upsert: true, new: true, runValidators: true }, + ); + + // Update the status array + for (const statusItem of status) { + await this.deviceStatusModel.updateOne( + { deviceTuyaUuid, 'status.code': statusItem.code }, // Filter to find if the code already exists + { $set: { 'status.$.value': statusItem.value } }, // Update the value if code exists + ); + + // If the code doesn't exist, add it to the array + await this.deviceStatusModel.updateOne( + { deviceTuyaUuid, 'status.code': { $ne: statusItem.code } }, // Check if the code does not exist + { $push: { status: statusItem } }, // Add the new status item + ); + } + + // Return the updated document + return this.deviceStatusModel.findOne({ deviceTuyaUuid }); + } +} diff --git a/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts b/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts new file mode 100644 index 0000000..2ae0041 --- /dev/null +++ b/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts @@ -0,0 +1,20 @@ +import { Schema, Document } from 'mongoose'; + +export interface AddDeviceStatusInterface extends Document { + deviceUuid: string; + deviceTuyaUuid: string; + productUuid: string; + productType: string; + status: []; +} + +export const DeviceStatusSchema = new Schema( + { + deviceUuid: { type: String, required: true }, + deviceTuyaUuid: { type: String, required: true }, + productUuid: { type: String, required: true }, + productType: { type: String, required: true }, + status: [], + }, + { collection: 'devices-status' }, +); diff --git a/libs/common/src/mongoose/devices-status/services/devices-status.service.ts b/libs/common/src/mongoose/devices-status/services/devices-status.service.ts new file mode 100644 index 0000000..3715975 --- /dev/null +++ b/libs/common/src/mongoose/devices-status/services/devices-status.service.ts @@ -0,0 +1,140 @@ +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { DeviceStatusRepository } from '../repository/devices-status.repository'; +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'; +@Injectable() +export class DeviceStatusMongoService { + private tuya: TuyaContext; + constructor( + private readonly configService: ConfigService, + private readonly deviceStatusRepository: DeviceStatusRepository, + private readonly deviceRepository: DeviceRepository, + ) { + const accessKey = this.configService.get('auth-config.ACCESS_KEY'); + const secretKey = this.configService.get('auth-config.SECRET_KEY'); + const tuyaEuUrl = this.configService.get('tuya-config.TUYA_EU_URL'); + this.tuya = new TuyaContext({ + baseUrl: tuyaEuUrl, + accessKey, + secretKey, + }); + } + async addDeviceStatusByDeviceUuid( + deviceTuyaUuid: string, + ): Promise { + try { + const device = await this.getDeviceByDeviceTuyaUuid(deviceTuyaUuid); + if (device.uuid) { + const deviceStatus = await this.getDevicesInstructionStatus( + device.uuid, + ); + if (deviceStatus.productUuid) { + const deviceStatusSaved = await this.addDeviceStatusToMongo({ + 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 addDeviceStatusToMongo( + addDeviceStatusDto: AddDeviceStatusDto, + ): Promise { + try { + const device = await this.getDeviceByDeviceTuyaUuid( + addDeviceStatusDto.deviceTuyaUuid, + ); + if (device?.uuid) { + return await this.deviceStatusRepository.addDeviceStatus( + 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 { + 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'] }), + }); + } +} diff --git a/libs/common/src/mongoose/mongoose.shared.module.ts b/libs/common/src/mongoose/mongoose.shared.module.ts new file mode 100644 index 0000000..de79714 --- /dev/null +++ b/libs/common/src/mongoose/mongoose.shared.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { DeviceStatusMongoModule } from './devices-status/devices-status.module'; // Adjust path as needed + +@Module({ + imports: [DeviceStatusMongoModule], + providers: [], // Providers specific to MongooseSharedModule, if any + exports: [], // Export anything specific to MongooseSharedModule, if needed +}) +export class MongooseSharedModule {} diff --git a/package-lock.json b/package-lock.json index 7267182..b5ac870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", + "@nestjs/mongoose": "^10.0.10", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.0", @@ -28,6 +29,7 @@ "express-rate-limit": "^7.1.5", "helmet": "^7.1.0", "ioredis": "^5.3.2", + "mongoose": "^8.5.1", "morgan": "^1.10.0", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", @@ -1710,6 +1712,14 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", + "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@nestjs/cli": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.2.tgz", @@ -1938,6 +1948,17 @@ } } }, + "node_modules/@nestjs/mongoose": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.10.tgz", + "integrity": "sha512-3Ff60ock8nwlAJC823TG91Qy+Qc6av+ddIb6n6wlFsTK0akDF/aTcagX8cF8uI8mWxCWjEwEsgv99vo6p0yJ+w==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", + "rxjs": "^7.0.0" + } + }, "node_modules/@nestjs/passport": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", @@ -2506,6 +2527,22 @@ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", "integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3486,6 +3523,14 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5861,6 +5906,34 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "optional": true, + "peer": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "optional": true, + "peer": true + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "optional": true, + "peer": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6930,6 +7003,14 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7154,6 +7235,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -7287,6 +7373,225 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mongodb": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", + "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", + "optional": true, + "peer": true, + "dependencies": { + "bson": "^5.5.0", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=14.20.1" + }, + "optionalDependencies": { + "@mongodb-js/saslprep": "^1.1.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.0.0", + "kerberos": "^1.0.0 || ^2.0.0", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "optional": true, + "peer": true, + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "optional": true, + "peer": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb/node_modules/bson": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.20.1" + } + }, + "node_modules/mongoose": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.1.tgz", + "integrity": "sha512-OhVcwVl91A1G6+XpjDcpkGP7l7ikZkxa0DylX7NT/lcEqAjggzSdqDxb48A+xsDxqNAr0ntSJ1yiE3+KJTOd5Q==", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.7.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", + "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongoose/node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mongoose/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/mongoose/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -7326,6 +7631,25 @@ "node": ">= 0.8" } }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8936,6 +9260,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -8962,6 +9291,32 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "optional": true, + "peer": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -8990,6 +9345,14 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spawn-command": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", diff --git a/package.json b/package.json index 52c5cf7..0c8fb38 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", + "@nestjs/mongoose": "^10.0.10", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.0", @@ -39,6 +40,7 @@ "express-rate-limit": "^7.1.5", "helmet": "^7.1.0", "ioredis": "^5.3.2", + "mongoose": "^8.5.1", "morgan": "^1.10.0", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", diff --git a/src/automation/automation.module.ts b/src/automation/automation.module.ts index ebb8bf0..7139f6c 100644 --- a/src/automation/automation.module.ts +++ b/src/automation/automation.module.ts @@ -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 { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; @Module({ - imports: [ConfigModule, SpaceRepositoryModule], + imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusMongoModule], controllers: [AutomationController], providers: [ AutomationService, diff --git a/src/device/device.module.ts b/src/device/device.module.ts index 07d1ad0..ff9b7fb 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -10,8 +10,14 @@ import { PermissionTypeRepository } from '@app/common/modules/permission/reposit import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; @Module({ - imports: [ConfigModule, ProductRepositoryModule, DeviceRepositoryModule], + imports: [ + ConfigModule, + ProductRepositoryModule, + DeviceRepositoryModule, + DeviceStatusMongoModule, + ], controllers: [DeviceController], providers: [ DeviceService, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 0848bb3..fdf9aeb 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -25,6 +25,7 @@ import { PermissionType } from '@app/common/constants/permission-type.enum'; import { In } from 'typeorm'; import { ProductType } from '@app/common/constants/product-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { DeviceStatusMongoService } from '@app/common/mongoose/devices-status/services/devices-status.service'; @Injectable() export class DeviceService { @@ -33,6 +34,7 @@ export class DeviceService { private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, private readonly productRepository: ProductRepository, + private readonly deviceStatusMongoService: DeviceStatusMongoService, private readonly spaceRepository: SpaceRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -72,14 +74,28 @@ export class DeviceService { if (!device.productUuid) { throw new Error('Product UUID is missing for the device.'); } - - return await this.deviceRepository.save({ + const deviceSaved = await this.deviceRepository.save({ deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, productDevice: { uuid: device.productUuid }, user: { uuid: addDeviceDto.userUuid, }, }); + if (deviceSaved.uuid) { + const deviceStatus = await this.getDevicesInstructionStatus( + deviceSaved.uuid, + ); + if (deviceStatus.productUuid) { + await this.deviceStatusMongoService.addDeviceStatusToMongo({ + deviceUuid: deviceSaved.uuid, + deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, + status: deviceStatus.status, + productUuid: deviceStatus.productUuid, + productType: deviceStatus.productType, + }); + } + } + return deviceSaved; } catch (error) { if (error.code === '23505') { throw new HttpException( diff --git a/src/scene/scene.module.ts b/src/scene/scene.module.ts index 248f2e6..e378058 100644 --- a/src/scene/scene.module.ts +++ b/src/scene/scene.module.ts @@ -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 { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; @Module({ - imports: [ConfigModule, SpaceRepositoryModule], + imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusMongoModule], controllers: [SceneController], providers: [ SceneService, From 8f761168b4965c30ca63492f276aa3c042222d79 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Sat, 3 Aug 2024 23:50:49 +0300 Subject: [PATCH 02/49] changed entity and type structure --- libs/common/src/database/database.module.ts | 14 +- libs/common/src/helper/helper.module.ts | 6 +- .../services/device.messages.service.ts | 2 +- .../device.notification.module.ts | 10 -- .../dtos/device.notification.dto.ts | 15 --- .../modules/device-notification/dtos/index.ts | 1 - .../entities/device.notification.entity.ts | 33 ----- .../device-notification/entities/index.ts | 1 - .../device.notification.repository.ts | 10 -- .../device-notification/repositories/index.ts | 1 - ...evice.user.permission.repository.module.ts | 10 -- .../dtos/device.user.permission.dto.ts | 19 --- .../device-user-permission/dtos/index.ts | 1 - .../entities/device.user.permission.entity.ts | 43 ------- .../device-user-permission/entities/index.ts | 1 - .../device.user.permission.repository.ts | 10 -- .../repositories/index.ts | 1 - .../device/device.repository.module.ts | 14 +- .../src/modules/device/dtos/device.dto.ts | 32 +++++ .../modules/device/entities/device.entity.ts | 71 ++++++++++- .../device/repositories/device.repository.ts | 18 ++- .../permission/entities/permission.entity.ts | 2 +- .../role-type/entities/role.type.entity.ts | 2 +- .../src/modules/space-type/dtos/index.ts | 1 - .../modules/space-type/dtos/space.type.dto.ts | 11 -- .../src/modules/space-type/entities/index.ts | 1 - .../space-type/entities/space.type.entity.ts | 26 ---- .../modules/space-type/repositories/index.ts | 1 - .../repositories/space.type.repository.ts | 10 -- .../space.type.repository.module.ts | 11 -- .../src/modules/space/dtos/space.dto.ts | 10 ++ .../modules/space/entities/space.entity.ts | 29 ++++- .../space/repositories/space.repository.ts | 9 +- .../modules/space/space.repository.module.ts | 4 +- .../modules/user-notification/dtos/index.ts | 1 - .../dtos/user.notification.dto.ts | 19 --- .../user-notification/entities/index.ts | 1 - .../entities/user.notification.entity.ts | 27 ---- .../user-notification/repositories/index.ts | 1 - .../user.notification.repository.ts | 10 -- .../user.notification.repository.module.ts | 10 -- .../common/src/modules/user-otp/dtos/index.ts | 1 - .../src/modules/user-otp/dtos/user-otp.dto.ts | 19 --- .../src/modules/user-otp/entities/index.ts | 1 - .../user-otp/entities/user-otp.entity.ts | 34 ----- .../modules/user-otp/repositories/index.ts | 0 .../repositories/user-otp.repository.ts | 10 -- .../user-otp/user-otp.repository.module.ts | 11 -- .../src/modules/user-role/dtos/index.ts | 1 - .../modules/user-role/dtos/user.role.dto.ts | 15 --- .../src/modules/user-role/entities/index.ts | 1 - .../user-role/entities/user.role.entity.ts | 24 ---- .../modules/user-role/repositories/index.ts | 1 - .../repositories/user.role.repository.ts | 10 -- .../user-role/user.role.repository.module.ts | 10 -- .../src/modules/user-space/dtos/index.ts | 1 - .../modules/user-space/dtos/user.space.dto.ts | 15 --- .../src/modules/user-space/entities/index.ts | 1 - .../user-space/entities/user.space.entity.ts | 29 ----- .../modules/user-space/repositories/index.ts | 1 - .../repositories/user.space.repository.ts | 10 -- .../user.space.repository.module.ts | 11 -- libs/common/src/modules/user/dtos/user.dto.ts | 66 +++++++++- .../src/modules/user/entities/user.entity.ts | 120 ++++++++++++++++-- .../user/repositories/user.repository.ts | 36 +++++- .../modules/user/user.repository.module.ts | 18 ++- libs/common/src/seed/seeder.module.ts | 10 +- .../src/seed/services/space.type.seeder.ts | 2 +- .../src/seed/services/supper.admin.seeder.ts | 2 +- src/auth/auth.module.ts | 12 +- src/auth/services/user-auth.service.ts | 4 +- src/building/building.module.ts | 14 +- src/building/services/building.service.ts | 4 +- src/community/community.module.ts | 14 +- src/community/services/community.service.ts | 4 +- src/device-messages/device-messages.module.ts | 6 +- .../services/device-messages.service.ts | 2 +- src/device/device.module.ts | 2 +- src/floor/floor.module.ts | 14 +- src/floor/services/floor.service.ts | 4 +- src/role/role.module.ts | 4 +- src/role/services/role.service.ts | 2 +- src/room/room.module.ts | 14 +- src/room/services/room.service.ts | 4 +- src/unit/services/unit.service.ts | 4 +- src/unit/unit.module.ts | 16 +-- .../user-device-permission.service.ts | 2 +- .../user-device-permission.module.ts | 2 +- .../services/user-notification.service.ts | 2 +- .../user-notification.module.ts | 6 +- 90 files changed, 470 insertions(+), 625 deletions(-) delete mode 100644 libs/common/src/modules/device-notification/device.notification.module.ts delete mode 100644 libs/common/src/modules/device-notification/dtos/device.notification.dto.ts delete mode 100644 libs/common/src/modules/device-notification/dtos/index.ts delete mode 100644 libs/common/src/modules/device-notification/entities/device.notification.entity.ts delete mode 100644 libs/common/src/modules/device-notification/entities/index.ts delete mode 100644 libs/common/src/modules/device-notification/repositories/device.notification.repository.ts delete mode 100644 libs/common/src/modules/device-notification/repositories/index.ts delete mode 100644 libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts delete mode 100644 libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts delete mode 100644 libs/common/src/modules/device-user-permission/dtos/index.ts delete mode 100644 libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts delete mode 100644 libs/common/src/modules/device-user-permission/entities/index.ts delete mode 100644 libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts delete mode 100644 libs/common/src/modules/device-user-permission/repositories/index.ts delete mode 100644 libs/common/src/modules/space-type/dtos/index.ts delete mode 100644 libs/common/src/modules/space-type/dtos/space.type.dto.ts delete mode 100644 libs/common/src/modules/space-type/entities/index.ts delete mode 100644 libs/common/src/modules/space-type/entities/space.type.entity.ts delete mode 100644 libs/common/src/modules/space-type/repositories/index.ts delete mode 100644 libs/common/src/modules/space-type/repositories/space.type.repository.ts delete mode 100644 libs/common/src/modules/space-type/space.type.repository.module.ts delete mode 100644 libs/common/src/modules/user-notification/dtos/index.ts delete mode 100644 libs/common/src/modules/user-notification/dtos/user.notification.dto.ts delete mode 100644 libs/common/src/modules/user-notification/entities/index.ts delete mode 100644 libs/common/src/modules/user-notification/entities/user.notification.entity.ts delete mode 100644 libs/common/src/modules/user-notification/repositories/index.ts delete mode 100644 libs/common/src/modules/user-notification/repositories/user.notification.repository.ts delete mode 100644 libs/common/src/modules/user-notification/user.notification.repository.module.ts delete mode 100644 libs/common/src/modules/user-otp/dtos/index.ts delete mode 100644 libs/common/src/modules/user-otp/dtos/user-otp.dto.ts delete mode 100644 libs/common/src/modules/user-otp/entities/index.ts delete mode 100644 libs/common/src/modules/user-otp/entities/user-otp.entity.ts delete mode 100644 libs/common/src/modules/user-otp/repositories/index.ts delete mode 100644 libs/common/src/modules/user-otp/repositories/user-otp.repository.ts delete mode 100644 libs/common/src/modules/user-otp/user-otp.repository.module.ts delete mode 100644 libs/common/src/modules/user-role/dtos/index.ts delete mode 100644 libs/common/src/modules/user-role/dtos/user.role.dto.ts delete mode 100644 libs/common/src/modules/user-role/entities/index.ts delete mode 100644 libs/common/src/modules/user-role/entities/user.role.entity.ts delete mode 100644 libs/common/src/modules/user-role/repositories/index.ts delete mode 100644 libs/common/src/modules/user-role/repositories/user.role.repository.ts delete mode 100644 libs/common/src/modules/user-role/user.role.repository.module.ts delete mode 100644 libs/common/src/modules/user-space/dtos/index.ts delete mode 100644 libs/common/src/modules/user-space/dtos/user.space.dto.ts delete mode 100644 libs/common/src/modules/user-space/entities/index.ts delete mode 100644 libs/common/src/modules/user-space/entities/user.space.entity.ts delete mode 100644 libs/common/src/modules/user-space/repositories/index.ts delete mode 100644 libs/common/src/modules/user-space/repositories/user.space.repository.ts delete mode 100644 libs/common/src/modules/user-space/user.space.repository.module.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index e6ddb6c..1934a18 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -4,18 +4,18 @@ 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'; diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index 3378051..99ed24d 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -7,8 +7,8 @@ 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'; @Global() @Module({ @@ -23,6 +23,6 @@ import { DeviceNotificationRepository } from '../modules/device-notification/rep ], exports: [HelperHashService, SpacePermissionService], controllers: [], - imports: [SpaceRepositoryModule, DeviceNotificationRepositoryModule], + imports: [SpaceRepositoryModule, DeviceRepositoryModule], }) export class HelperModule {} diff --git a/libs/common/src/helper/services/device.messages.service.ts b/libs/common/src/helper/services/device.messages.service.ts index c51f6f4..c4e7024 100644 --- a/libs/common/src/helper/services/device.messages.service.ts +++ b/libs/common/src/helper/services/device.messages.service.ts @@ -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() diff --git a/libs/common/src/modules/device-notification/device.notification.module.ts b/libs/common/src/modules/device-notification/device.notification.module.ts deleted file mode 100644 index 73acce3..0000000 --- a/libs/common/src/modules/device-notification/device.notification.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/device-notification/dtos/device.notification.dto.ts b/libs/common/src/modules/device-notification/dtos/device.notification.dto.ts deleted file mode 100644 index 0746c14..0000000 --- a/libs/common/src/modules/device-notification/dtos/device.notification.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class DeviceNotificationDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsString() - @IsNotEmpty() - public userUuid: string; - - @IsString() - @IsNotEmpty() - public deviceUuid: string; -} diff --git a/libs/common/src/modules/device-notification/dtos/index.ts b/libs/common/src/modules/device-notification/dtos/index.ts deleted file mode 100644 index d205031..0000000 --- a/libs/common/src/modules/device-notification/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.notification.dto'; diff --git a/libs/common/src/modules/device-notification/entities/device.notification.entity.ts b/libs/common/src/modules/device-notification/entities/device.notification.entity.ts deleted file mode 100644 index 84e592a..0000000 --- a/libs/common/src/modules/device-notification/entities/device.notification.entity.ts +++ /dev/null @@ -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 { - @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) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/device-notification/entities/index.ts b/libs/common/src/modules/device-notification/entities/index.ts deleted file mode 100644 index fedfc80..0000000 --- a/libs/common/src/modules/device-notification/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.notification.entity'; diff --git a/libs/common/src/modules/device-notification/repositories/device.notification.repository.ts b/libs/common/src/modules/device-notification/repositories/device.notification.repository.ts deleted file mode 100644 index b226791..0000000 --- a/libs/common/src/modules/device-notification/repositories/device.notification.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { DeviceNotificationEntity } from '../entities'; - -@Injectable() -export class DeviceNotificationRepository extends Repository { - constructor(private dataSource: DataSource) { - super(DeviceNotificationEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/device-notification/repositories/index.ts b/libs/common/src/modules/device-notification/repositories/index.ts deleted file mode 100644 index 1f3da6f..0000000 --- a/libs/common/src/modules/device-notification/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.notification.repository'; diff --git a/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts b/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts deleted file mode 100644 index d950fd8..0000000 --- a/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts b/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts deleted file mode 100644 index e78ea3c..0000000 --- a/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts +++ /dev/null @@ -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; -} diff --git a/libs/common/src/modules/device-user-permission/dtos/index.ts b/libs/common/src/modules/device-user-permission/dtos/index.ts deleted file mode 100644 index b95b695..0000000 --- a/libs/common/src/modules/device-user-permission/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.user.permission.dto'; diff --git a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts deleted file mode 100644 index d136df7..0000000 --- a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts +++ /dev/null @@ -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 { - @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) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/device-user-permission/entities/index.ts b/libs/common/src/modules/device-user-permission/entities/index.ts deleted file mode 100644 index 9e95d82..0000000 --- a/libs/common/src/modules/device-user-permission/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.user.permission.entity'; diff --git a/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts b/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts deleted file mode 100644 index a1776b3..0000000 --- a/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { DeviceUserPermissionEntity } from '../entities'; - -@Injectable() -export class DeviceUserPermissionRepository extends Repository { - constructor(private dataSource: DataSource) { - super(DeviceUserPermissionEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/device-user-permission/repositories/index.ts b/libs/common/src/modules/device-user-permission/repositories/index.ts deleted file mode 100644 index 6957209..0000000 --- a/libs/common/src/modules/device-user-permission/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './device.user.permission.repository'; diff --git a/libs/common/src/modules/device/device.repository.module.ts b/libs/common/src/modules/device/device.repository.module.ts index 438e268..4b914fa 100644 --- a/libs/common/src/modules/device/device.repository.module.ts +++ b/libs/common/src/modules/device/device.repository.module.ts @@ -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 {} diff --git a/libs/common/src/modules/device/dtos/device.dto.ts b/libs/common/src/modules/device/dtos/device.dto.ts index 7a6ba0c..41a0476 100644 --- a/libs/common/src/modules/device/dtos/device.dto.ts +++ b/libs/common/src/modules/device/dtos/device.dto.ts @@ -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; +} diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index f782b4a..c02f8a1 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -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 { Object.assign(this, partial); } } + +@Entity({ name: 'device-notification' }) +@Unique(['userUuid', 'deviceUuid']) +export class DeviceNotificationEntity extends AbstractEntity { + @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) { + super(); + Object.assign(this, partial); + } +} + +@Entity({ name: 'device-user-permission' }) +@Unique(['userUuid', 'deviceUuid']) +export class DeviceUserPermissionEntity extends AbstractEntity { + @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) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/device/repositories/device.repository.ts b/libs/common/src/modules/device/repositories/device.repository.ts index 267c06c..dad195b 100644 --- a/libs/common/src/modules/device/repositories/device.repository.ts +++ b/libs/common/src/modules/device/repositories/device.repository.ts @@ -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 { @@ -8,3 +12,15 @@ export class DeviceRepository extends Repository { super(DeviceEntity, dataSource.createEntityManager()); } } +@Injectable() +export class DeviceNotificationRepository extends Repository { + constructor(private dataSource: DataSource) { + super(DeviceNotificationEntity, dataSource.createEntityManager()); + } +} +@Injectable() +export class DeviceUserPermissionRepository extends Repository { + constructor(private dataSource: DataSource) { + super(DeviceUserPermissionEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/permission/entities/permission.entity.ts b/libs/common/src/modules/permission/entities/permission.entity.ts index d15d936..ffb6c9f 100644 --- a/libs/common/src/modules/permission/entities/permission.entity.ts +++ b/libs/common/src/modules/permission/entities/permission.entity.ts @@ -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 { diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts index fd332fb..10f30bd 100644 --- a/libs/common/src/modules/role-type/entities/role.type.entity.ts +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -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']) diff --git a/libs/common/src/modules/space-type/dtos/index.ts b/libs/common/src/modules/space-type/dtos/index.ts deleted file mode 100644 index e9824e7..0000000 --- a/libs/common/src/modules/space-type/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './space.type.dto'; diff --git a/libs/common/src/modules/space-type/dtos/space.type.dto.ts b/libs/common/src/modules/space-type/dtos/space.type.dto.ts deleted file mode 100644 index 2f3d807..0000000 --- a/libs/common/src/modules/space-type/dtos/space.type.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class SpaceTypeDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsString() - @IsNotEmpty() - public type: string; -} diff --git a/libs/common/src/modules/space-type/entities/index.ts b/libs/common/src/modules/space-type/entities/index.ts deleted file mode 100644 index 71944ff..0000000 --- a/libs/common/src/modules/space-type/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './space.type.entity'; diff --git a/libs/common/src/modules/space-type/entities/space.type.entity.ts b/libs/common/src/modules/space-type/entities/space.type.entity.ts deleted file mode 100644 index f66ee86..0000000 --- a/libs/common/src/modules/space-type/entities/space.type.entity.ts +++ /dev/null @@ -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 { - @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) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/space-type/repositories/index.ts b/libs/common/src/modules/space-type/repositories/index.ts deleted file mode 100644 index 26f69ae..0000000 --- a/libs/common/src/modules/space-type/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './space.type.repository'; diff --git a/libs/common/src/modules/space-type/repositories/space.type.repository.ts b/libs/common/src/modules/space-type/repositories/space.type.repository.ts deleted file mode 100644 index 0ea5b13..0000000 --- a/libs/common/src/modules/space-type/repositories/space.type.repository.ts +++ /dev/null @@ -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 { - constructor(private dataSource: DataSource) { - super(SpaceTypeEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/space-type/space.type.repository.module.ts b/libs/common/src/modules/space-type/space.type.repository.module.ts deleted file mode 100644 index 6787b67..0000000 --- a/libs/common/src/modules/space-type/space.type.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/space/dtos/space.dto.ts b/libs/common/src/modules/space/dtos/space.dto.ts index 98706d0..04072fd 100644 --- a/libs/common/src/modules/space/dtos/space.dto.ts +++ b/libs/common/src/modules/space/dtos/space.dto.ts @@ -21,3 +21,13 @@ export class SpaceDto { @IsNotEmpty() public invitationCode: string; } + +export class SpaceTypeDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public type: string; +} diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 030db57..10e78a8 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -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 { + @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) { + super(); + Object.assign(this, partial); + } +} + @Entity({ name: 'space' }) @Unique(['invitationCode']) export class SpaceEntity extends AbstractEntity { @Column({ type: 'uuid', - default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value + default: () => 'gen_random_uuid()', nullable: false, }) public uuid: string; diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index c939761..ea11a6e 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -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 { @@ -8,3 +8,10 @@ export class SpaceRepository extends Repository { super(SpaceEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class SpaceTypeRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceTypeEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/space/space.repository.module.ts b/libs/common/src/modules/space/space.repository.module.ts index 58a8ad0..9906708 100644 --- a/libs/common/src/modules/space/space.repository.module.ts +++ b/libs/common/src/modules/space/space.repository.module.ts @@ -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 {} diff --git a/libs/common/src/modules/user-notification/dtos/index.ts b/libs/common/src/modules/user-notification/dtos/index.ts deleted file mode 100644 index 307e6f3..0000000 --- a/libs/common/src/modules/user-notification/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.notification.dto'; diff --git a/libs/common/src/modules/user-notification/dtos/user.notification.dto.ts b/libs/common/src/modules/user-notification/dtos/user.notification.dto.ts deleted file mode 100644 index 4e9f72f..0000000 --- a/libs/common/src/modules/user-notification/dtos/user.notification.dto.ts +++ /dev/null @@ -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; -} diff --git a/libs/common/src/modules/user-notification/entities/index.ts b/libs/common/src/modules/user-notification/entities/index.ts deleted file mode 100644 index 1acf5c0..0000000 --- a/libs/common/src/modules/user-notification/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.notification.entity'; diff --git a/libs/common/src/modules/user-notification/entities/user.notification.entity.ts b/libs/common/src/modules/user-notification/entities/user.notification.entity.ts deleted file mode 100644 index aa6ec6b..0000000 --- a/libs/common/src/modules/user-notification/entities/user.notification.entity.ts +++ /dev/null @@ -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 { - @ManyToOne(() => UserEntity, (user) => user.roles, { - nullable: false, - }) - user: UserEntity; - @Column({ - nullable: false, - }) - subscriptionUuid: string; - - @Column({ - nullable: false, - default: true, - }) - active: boolean; - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/user-notification/repositories/index.ts b/libs/common/src/modules/user-notification/repositories/index.ts deleted file mode 100644 index 76d3ac2..0000000 --- a/libs/common/src/modules/user-notification/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.notification.repository'; diff --git a/libs/common/src/modules/user-notification/repositories/user.notification.repository.ts b/libs/common/src/modules/user-notification/repositories/user.notification.repository.ts deleted file mode 100644 index 6862e88..0000000 --- a/libs/common/src/modules/user-notification/repositories/user.notification.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { UserNotificationEntity } from '../entities'; - -@Injectable() -export class UserNotificationRepository extends Repository { - constructor(private dataSource: DataSource) { - super(UserNotificationEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/user-notification/user.notification.repository.module.ts b/libs/common/src/modules/user-notification/user.notification.repository.module.ts deleted file mode 100644 index 73997be..0000000 --- a/libs/common/src/modules/user-notification/user.notification.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/user-otp/dtos/index.ts b/libs/common/src/modules/user-otp/dtos/index.ts deleted file mode 100644 index 114762e..0000000 --- a/libs/common/src/modules/user-otp/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user-otp.dto'; diff --git a/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts b/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts deleted file mode 100644 index febdacb..0000000 --- a/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts +++ /dev/null @@ -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; -} diff --git a/libs/common/src/modules/user-otp/entities/index.ts b/libs/common/src/modules/user-otp/entities/index.ts deleted file mode 100644 index d09957f..0000000 --- a/libs/common/src/modules/user-otp/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user-otp.entity'; diff --git a/libs/common/src/modules/user-otp/entities/user-otp.entity.ts b/libs/common/src/modules/user-otp/entities/user-otp.entity.ts deleted file mode 100644 index 454a3b1..0000000 --- a/libs/common/src/modules/user-otp/entities/user-otp.entity.ts +++ /dev/null @@ -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 { - @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) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/user-otp/repositories/index.ts b/libs/common/src/modules/user-otp/repositories/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts b/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts deleted file mode 100644 index 75cff43..0000000 --- a/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { UserOtpEntity } from '../entities'; - -@Injectable() -export class UserOtpRepository extends Repository { - constructor(private dataSource: DataSource) { - super(UserOtpEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/user-otp/user-otp.repository.module.ts b/libs/common/src/modules/user-otp/user-otp.repository.module.ts deleted file mode 100644 index 9286d8b..0000000 --- a/libs/common/src/modules/user-otp/user-otp.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/user-role/dtos/index.ts b/libs/common/src/modules/user-role/dtos/index.ts deleted file mode 100644 index 7879674..0000000 --- a/libs/common/src/modules/user-role/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.role.dto'; diff --git a/libs/common/src/modules/user-role/dtos/user.role.dto.ts b/libs/common/src/modules/user-role/dtos/user.role.dto.ts deleted file mode 100644 index 3ff4dab..0000000 --- a/libs/common/src/modules/user-role/dtos/user.role.dto.ts +++ /dev/null @@ -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; -} diff --git a/libs/common/src/modules/user-role/entities/index.ts b/libs/common/src/modules/user-role/entities/index.ts deleted file mode 100644 index b6f3bd9..0000000 --- a/libs/common/src/modules/user-role/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.role.entity'; diff --git a/libs/common/src/modules/user-role/entities/user.role.entity.ts b/libs/common/src/modules/user-role/entities/user.role.entity.ts deleted file mode 100644 index c733594..0000000 --- a/libs/common/src/modules/user-role/entities/user.role.entity.ts +++ /dev/null @@ -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 { - @ManyToOne(() => UserEntity, (user) => user.roles, { - nullable: false, - }) - user: UserEntity; - - @ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, { - nullable: false, - }) - roleType: RoleTypeEntity; - - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/user-role/repositories/index.ts b/libs/common/src/modules/user-role/repositories/index.ts deleted file mode 100644 index abf7247..0000000 --- a/libs/common/src/modules/user-role/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.role.repository'; diff --git a/libs/common/src/modules/user-role/repositories/user.role.repository.ts b/libs/common/src/modules/user-role/repositories/user.role.repository.ts deleted file mode 100644 index 9bc9a24..0000000 --- a/libs/common/src/modules/user-role/repositories/user.role.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { UserRoleEntity } from '../entities'; - -@Injectable() -export class UserRoleRepository extends Repository { - constructor(private dataSource: DataSource) { - super(UserRoleEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/user-role/user.role.repository.module.ts b/libs/common/src/modules/user-role/user.role.repository.module.ts deleted file mode 100644 index 540787e..0000000 --- a/libs/common/src/modules/user-role/user.role.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/user-space/dtos/index.ts b/libs/common/src/modules/user-space/dtos/index.ts deleted file mode 100644 index 41572f5..0000000 --- a/libs/common/src/modules/user-space/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.space.dto'; diff --git a/libs/common/src/modules/user-space/dtos/user.space.dto.ts b/libs/common/src/modules/user-space/dtos/user.space.dto.ts deleted file mode 100644 index b9ef4d0..0000000 --- a/libs/common/src/modules/user-space/dtos/user.space.dto.ts +++ /dev/null @@ -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; -} diff --git a/libs/common/src/modules/user-space/entities/index.ts b/libs/common/src/modules/user-space/entities/index.ts deleted file mode 100644 index ef6849a..0000000 --- a/libs/common/src/modules/user-space/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.space.entity'; diff --git a/libs/common/src/modules/user-space/entities/user.space.entity.ts b/libs/common/src/modules/user-space/entities/user.space.entity.ts deleted file mode 100644 index a6caaa5..0000000 --- a/libs/common/src/modules/user-space/entities/user.space.entity.ts +++ /dev/null @@ -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 { - @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) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/user-space/repositories/index.ts b/libs/common/src/modules/user-space/repositories/index.ts deleted file mode 100644 index 3ad6d48..0000000 --- a/libs/common/src/modules/user-space/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.space.repository'; diff --git a/libs/common/src/modules/user-space/repositories/user.space.repository.ts b/libs/common/src/modules/user-space/repositories/user.space.repository.ts deleted file mode 100644 index b4b7507..0000000 --- a/libs/common/src/modules/user-space/repositories/user.space.repository.ts +++ /dev/null @@ -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 { - constructor(private dataSource: DataSource) { - super(UserSpaceEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/user-space/user.space.repository.module.ts b/libs/common/src/modules/user-space/user.space.repository.module.ts deleted file mode 100644 index 9655252..0000000 --- a/libs/common/src/modules/user-space/user.space.repository.module.ts +++ /dev/null @@ -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 {} diff --git a/libs/common/src/modules/user/dtos/user.dto.ts b/libs/common/src/modules/user/dtos/user.dto.ts index 706ca31..0a4bda2 100644 --- a/libs/common/src/modules/user/dtos/user.dto.ts +++ b/libs/common/src/modules/user/dtos/user.dto.ts @@ -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; +} diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index 5b754e8..11dcd5c 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -1,15 +1,23 @@ -import { DeviceUserPermissionEntity } from '../../device-user-permission/entities/device.user.permission.entity'; -import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; -import { UserDto } from '../dtos'; +import { Column, 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'; @Entity({ name: 'user' }) export class UserEntity extends AbstractEntity { @@ -99,3 +107,99 @@ export class UserEntity extends AbstractEntity { Object.assign(this, partial); } } + +@Entity({ name: 'user-notification' }) +@Unique(['user', 'subscriptionUuid']) +export class UserNotificationEntity extends AbstractEntity { + @ManyToOne(() => UserEntity, (user) => user.roles, { + nullable: false, + }) + user: UserEntity; + @Column({ + nullable: false, + }) + subscriptionUuid: string; + + @Column({ + nullable: false, + default: true, + }) + active: boolean; + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} + +@Entity({ name: 'user-otp' }) +export class UserOtpEntity extends AbstractEntity { + @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) { + super(); + Object.assign(this, partial); + } +} + +@Entity({ name: 'user-role' }) +@Unique(['user', 'roleType']) +export class UserRoleEntity extends AbstractEntity { + @ManyToOne(() => UserEntity, (user) => user.roles, { + nullable: false, + }) + user: UserEntity; + + @ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, { + nullable: false, + }) + roleType: RoleTypeEntity; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} + +@Entity({ name: 'user-space' }) +@Unique(['user', 'space']) +export class UserSpaceEntity extends AbstractEntity { + @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) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/user/repositories/user.repository.ts b/libs/common/src/modules/user/repositories/user.repository.ts index f83f14f..ffc1aa1 100644 --- a/libs/common/src/modules/user/repositories/user.repository.ts +++ b/libs/common/src/modules/user/repositories/user.repository.ts @@ -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 { @@ -8,3 +14,31 @@ export class UserRepository extends Repository { super(UserEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class UserNotificationRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserNotificationEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class UserOtpRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserOtpEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class UserRoleRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserRoleEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class UserSpaceRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserSpaceEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/user/user.repository.module.ts b/libs/common/src/modules/user/user.repository.module.ts index 1b40c6a..11cefe0 100644 --- a/libs/common/src/modules/user/user.repository.module.ts +++ b/libs/common/src/modules/user/user.repository.module.ts @@ -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 {} diff --git a/libs/common/src/seed/seeder.module.ts b/libs/common/src/seed/seeder.module.ts index f148182..f63c05d 100644 --- a/libs/common/src/seed/seeder.module.ts +++ b/libs/common/src/seed/seeder.module.ts @@ -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 {} diff --git a/libs/common/src/seed/services/space.type.seeder.ts b/libs/common/src/seed/services/space.type.seeder.ts index 3cbcb87..56c2ee8 100644 --- a/libs/common/src/seed/services/space.type.seeder.ts +++ b/libs/common/src/seed/services/space.type.seeder.ts @@ -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 { diff --git a/libs/common/src/seed/services/supper.admin.seeder.ts b/libs/common/src/seed/services/supper.admin.seeder.ts index 6cb5f60..9cfdfee 100644 --- a/libs/common/src/seed/services/supper.admin.seeder.ts +++ b/libs/common/src/seed/services/supper.admin.seeder.ts @@ -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'; diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 10515e7..012312d 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -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({ diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index a514c1c..08d0c42 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -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,7 +12,7 @@ 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'; diff --git a/src/building/building.module.ts b/src/building/building.module.ts index 80391fe..a78c049 100644 --- a/src/building/building.module.ts +++ b/src/building/building.module.ts @@ -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, diff --git a/src/building/services/building.service.ts b/src/building/services/building.service.ts index dfc2089..c9f8273 100644 --- a/src/building/services/building.service.ts +++ b/src/building/services/building.service.ts @@ -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 { diff --git a/src/community/community.module.ts b/src/community/community.module.ts index e27627e..d5f7d93 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -4,22 +4,14 @@ import { CommunityController } from './controllers/community.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 { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepository } from '@app/common/modules/user/repositories'; import { SpacePermissionService } from '@app/common/helper/services'; @Module({ - imports: [ - ConfigModule, - SpaceRepositoryModule, - SpaceTypeRepositoryModule, - UserSpaceRepositoryModule, - UserRepositoryModule, - ], + imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], controllers: [CommunityController], providers: [ CommunityService, diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 66c7037..2476c25 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -1,5 +1,5 @@ import { GetCommunityChildDto } from './../dtos/get.community.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, @@ -16,7 +16,7 @@ import { } from '../interface/community.interface'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; -import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; +import { UserSpaceRepository } from '@app/common/modules/user/repositories'; @Injectable() export class CommunityService { diff --git a/src/device-messages/device-messages.module.ts b/src/device-messages/device-messages.module.ts index 451f013..185d38b 100644 --- a/src/device-messages/device-messages.module.ts +++ b/src/device-messages/device-messages.module.ts @@ -2,11 +2,11 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { DeviceMessagesSubscriptionController } from './controllers'; import { DeviceMessagesSubscriptionService } from './services'; -import { DeviceNotificationRepositoryModule } from '@app/common/modules/device-notification/device.notification.module'; -import { DeviceNotificationRepository } from '@app/common/modules/device-notification/repositories'; +import { DeviceRepositoryModule } from '@app/common/modules/device/device.repository.module'; +import { DeviceNotificationRepository } from '@app/common/modules/device/repositories'; @Module({ - imports: [ConfigModule, DeviceNotificationRepositoryModule], + imports: [ConfigModule, DeviceRepositoryModule], controllers: [DeviceMessagesSubscriptionController], providers: [DeviceNotificationRepository, DeviceMessagesSubscriptionService], exports: [DeviceMessagesSubscriptionService], diff --git a/src/device-messages/services/device-messages.service.ts b/src/device-messages/services/device-messages.service.ts index 73c8182..4722b79 100644 --- a/src/device-messages/services/device-messages.service.ts +++ b/src/device-messages/services/device-messages.service.ts @@ -1,6 +1,6 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { DeviceMessagesAddDto } from '../dtos/device-messages.dto'; -import { DeviceNotificationRepository } from '@app/common/modules/device-notification/repositories'; +import { DeviceNotificationRepository } from '@app/common/modules/device/repositories'; @Injectable() export class DeviceMessagesSubscriptionService { diff --git a/src/device/device.module.ts b/src/device/device.module.ts index 07d1ad0..03a65cd 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -8,7 +8,7 @@ import { DeviceRepositoryModule } from '@app/common/modules/device'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { UserRepository } from '@app/common/modules/user/repositories'; @Module({ imports: [ConfigModule, ProductRepositoryModule, DeviceRepositoryModule], diff --git a/src/floor/floor.module.ts b/src/floor/floor.module.ts index 71a6c67..9fdc1c7 100644 --- a/src/floor/floor.module.ts +++ b/src/floor/floor.module.ts @@ -4,21 +4,13 @@ import { FloorController } from './controllers/floor.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 { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepository } from '@app/common/modules/user/repositories'; @Module({ - imports: [ - ConfigModule, - SpaceRepositoryModule, - SpaceTypeRepositoryModule, - UserSpaceRepositoryModule, - UserRepositoryModule, - ], + imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], controllers: [FloorController], providers: [ FloorService, diff --git a/src/floor/services/floor.service.ts b/src/floor/services/floor.service.ts index af4b26f..5113206 100644 --- a/src/floor/services/floor.service.ts +++ b/src/floor/services/floor.service.ts @@ -1,5 +1,5 @@ import { GetFloorChildDto } from '../dtos/get.floor.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/floor.interface'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; -import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; +import { UserSpaceRepository } from '@app/common/modules/user/repositories'; @Injectable() export class FloorService { diff --git a/src/role/role.module.ts b/src/role/role.module.ts index a487979..4e51725 100644 --- a/src/role/role.module.ts +++ b/src/role/role.module.ts @@ -4,10 +4,10 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { RoleService } from './services/role.service'; import { RoleController } from './controllers/role.controller'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; -import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; +import { UserRoleRepository } from '@app/common/modules/user/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], diff --git a/src/role/services/role.service.ts b/src/role/services/role.service.ts index ece3780..42076d4 100644 --- a/src/role/services/role.service.ts +++ b/src/role/services/role.service.ts @@ -1,7 +1,7 @@ import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AddUserRoleDto } from '../dtos/role.add.dto'; -import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; +import { UserRoleRepository } from '@app/common/modules/user/repositories'; import { QueryFailedError } from 'typeorm'; @Injectable() diff --git a/src/room/room.module.ts b/src/room/room.module.ts index 2d6d98c..4a07d1a 100644 --- a/src/room/room.module.ts +++ b/src/room/room.module.ts @@ -4,21 +4,13 @@ import { RoomController } from './controllers/room.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 { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepository } from '@app/common/modules/user/repositories'; @Module({ - imports: [ - ConfigModule, - SpaceRepositoryModule, - SpaceTypeRepositoryModule, - UserSpaceRepositoryModule, - UserRepositoryModule, - ], + imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], controllers: [RoomController], providers: [ RoomService, diff --git a/src/room/services/room.service.ts b/src/room/services/room.service.ts index b9ea30e..7babd6b 100644 --- a/src/room/services/room.service.ts +++ b/src/room/services/room.service.ts @@ -1,4 +1,4 @@ -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, @@ -14,7 +14,7 @@ import { GetRoomByUserUuidInterface, } from '../interface/room.interface'; import { UpdateRoomNameDto } from '../dtos/update.room.dto'; -import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; +import { UserSpaceRepository } from '@app/common/modules/user/repositories'; @Injectable() export class RoomService { diff --git a/src/unit/services/unit.service.ts b/src/unit/services/unit.service.ts index 1106043..b5dfb8f 100644 --- a/src/unit/services/unit.service.ts +++ b/src/unit/services/unit.service.ts @@ -1,5 +1,5 @@ import { GetUnitChildDto } from '../dtos/get.unit.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, @@ -18,7 +18,7 @@ import { } from '../interface/unit.interface'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; -import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; +import { UserSpaceRepository } from '@app/common/modules/user/repositories'; import { generateRandomString } from '@app/common/helper/randomString'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; import { PermissionType } from '@app/common/constants/permission-type.enum'; diff --git a/src/unit/unit.module.ts b/src/unit/unit.module.ts index 7924e4a..7ecd965 100644 --- a/src/unit/unit.module.ts +++ b/src/unit/unit.module.ts @@ -4,24 +4,16 @@ import { UnitController } from './controllers/unit.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 { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepository } from '@app/common/modules/user/repositories'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Module({ - imports: [ - ConfigModule, - SpaceRepositoryModule, - SpaceTypeRepositoryModule, - UserSpaceRepositoryModule, - UserRepositoryModule, - ], + imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], controllers: [UnitController], providers: [ UnitService, diff --git a/src/user-device-permission/services/user-device-permission.service.ts b/src/user-device-permission/services/user-device-permission.service.ts index c1f3d07..9ec93e6 100644 --- a/src/user-device-permission/services/user-device-permission.service.ts +++ b/src/user-device-permission/services/user-device-permission.service.ts @@ -1,7 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Injectable() diff --git a/src/user-device-permission/user-device-permission.module.ts b/src/user-device-permission/user-device-permission.module.ts index e2a8b46..53b7377 100644 --- a/src/user-device-permission/user-device-permission.module.ts +++ b/src/user-device-permission/user-device-permission.module.ts @@ -4,7 +4,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { UserDevicePermissionService } from './services/user-device-permission.service'; import { UserDevicePermissionController } from './controllers/user-device-permission.controller'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Module({ diff --git a/src/user-notification/services/user-notification.service.ts b/src/user-notification/services/user-notification.service.ts index d8992c0..8c9616b 100644 --- a/src/user-notification/services/user-notification.service.ts +++ b/src/user-notification/services/user-notification.service.ts @@ -3,7 +3,7 @@ import { UserNotificationAddDto, UserNotificationUpdateDto, } from '../dtos/user-notification.dto'; -import { UserNotificationRepository } from '@app/common/modules/user-notification/repositories'; +import { UserNotificationRepository } from '@app/common/modules/user/repositories'; @Injectable() export class UserNotificationService { diff --git a/src/user-notification/user-notification.module.ts b/src/user-notification/user-notification.module.ts index 0b03df1..da564cf 100644 --- a/src/user-notification/user-notification.module.ts +++ b/src/user-notification/user-notification.module.ts @@ -1,12 +1,12 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { UserNotificationRepositoryModule } from '@app/common/modules/user-notification/user.notification.repository.module'; -import { UserNotificationRepository } from '@app/common/modules/user-notification/repositories'; +import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; +import { UserNotificationRepository } from '@app/common/modules/user/repositories'; import { UserNotificationService } from 'src/user-notification/services'; import { UserNotificationController } from 'src/user-notification/controllers'; @Module({ - imports: [ConfigModule, UserNotificationRepositoryModule], + imports: [ConfigModule, UserRepositoryModule], controllers: [UserNotificationController], providers: [UserNotificationRepository, UserNotificationService], exports: [UserNotificationService], From 4ba954d349de55866f2fe3d1dc8be6552573a174 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 18:24:35 +0300 Subject: [PATCH 03/49] removed token from get regions --- src/region/controllers/region.controller.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/region/controllers/region.controller.ts b/src/region/controllers/region.controller.ts index cea276a..d914642 100644 --- a/src/region/controllers/region.controller.ts +++ b/src/region/controllers/region.controller.ts @@ -1,13 +1,6 @@ -import { - Controller, - Get, - HttpException, - HttpStatus, - UseGuards, -} from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common'; import { RegionService } from '../services/region.service'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; +import { ApiTags } from '@nestjs/swagger'; @ApiTags('Region Module') @Controller({ @@ -17,8 +10,6 @@ import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; export class RegionController { constructor(private readonly regionService: RegionService) {} - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) @Get() async getAllRegions() { try { From 842e64a4d1428b3ade24f1be5c87892be08aa364 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 18:44:28 +0300 Subject: [PATCH 04/49] otp cooldown --- libs/common/src/helper/differenceInSeconds.ts | 4 +++ src/auth/services/user-auth.service.ts | 35 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 libs/common/src/helper/differenceInSeconds.ts diff --git a/libs/common/src/helper/differenceInSeconds.ts b/libs/common/src/helper/differenceInSeconds.ts new file mode 100644 index 0000000..d3ce7f5 --- /dev/null +++ b/libs/common/src/helper/differenceInSeconds.ts @@ -0,0 +1,4 @@ +export function differenceInSeconds(date1: Date, date2: Date): number { + const diffInMilliseconds = date1.getTime() - date2.getTime(); // Difference in milliseconds + return Math.floor(diffInMilliseconds / 1000); // Convert to seconds and round down +} diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index a514c1c..69541cc 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -18,6 +18,8 @@ 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'; @Injectable() export class UserAuthService { @@ -124,7 +126,38 @@ export class UserAuthService { } async generateOTP(data: UserOtpDto): Promise { - await this.otpRepository.delete({ email: data.email, type: data.type }); + const threeDaysAgo = new Date(); + threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); + await this.otpRepository.softDelete({ email: data.email, type: data.type }); + await this.otpRepository.delete({ + email: data.email, + type: data.type, + createdAt: LessThan(threeDaysAgo), + }); + const countOfOtp = await this.otpRepository.count({ + withDeleted: true, + where: { + email: data.email, + type: data.type, + createdAt: MoreThan(threeDaysAgo), + }, + }); + const lastOtp = await this.otpRepository.findOne({ + where: { email: data.email, type: data.type }, + order: { createdAt: 'DESC' }, + withDeleted: true, + }); + const cooldown = 30 * Math.pow(2, countOfOtp - 1); + if (lastOtp) { + const now = new Date(); + const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt); + + if (timeSinceLastOtp < cooldown) { + throw new Error( + `Please wait ${cooldown - timeSinceLastOtp} more seconds before requesting a new OTP.`, + ); + } + } const otpCode = Math.floor(100000 + Math.random() * 900000).toString(); const expiryTime = new Date(); expiryTime.setMinutes(expiryTime.getMinutes() + 1); From 966762556230e66e56c0729f930c564459bd04c5 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 18:54:53 +0300 Subject: [PATCH 05/49] otp check if user exists in region --- src/auth/dtos/user-otp.dto.ts | 5 +++++ src/auth/services/user-auth.service.ts | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/auth/dtos/user-otp.dto.ts b/src/auth/dtos/user-otp.dto.ts index bab47c8..b588cb2 100644 --- a/src/auth/dtos/user-otp.dto.ts +++ b/src/auth/dtos/user-otp.dto.ts @@ -12,6 +12,11 @@ export class UserOtpDto { @IsEnum(OtpType) @IsNotEmpty() type: OtpType; + + @ApiProperty() + @IsNotEmpty() + @IsString() + regionName: string; } export class VerifyOtpDto extends UserOtpDto { diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 69541cc..5bc8a6b 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -116,7 +116,7 @@ export class UserAuthService { 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 }); } @@ -128,6 +128,17 @@ export class UserAuthService { async generateOTP(data: UserOtpDto): Promise { const threeDaysAgo = new Date(); threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); + const userExists = await this.userRepository.exists({ + where: { + region: { + regionName: data.regionName, + }, + email: data.email, + }, + }); + 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, @@ -153,7 +164,7 @@ export class UserAuthService { const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt); if (timeSinceLastOtp < cooldown) { - throw new Error( + throw new BadRequestException( `Please wait ${cooldown - timeSinceLastOtp} more seconds before requesting a new OTP.`, ); } From be8152bad98fc28a12a643683574875e9d2d26b3 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 18:57:17 +0300 Subject: [PATCH 06/49] otp check if user is verified --- libs/common/src/helper/differenceInSeconds.ts | 4 ++-- src/auth/services/user-auth.service.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/common/src/helper/differenceInSeconds.ts b/libs/common/src/helper/differenceInSeconds.ts index d3ce7f5..9f05d67 100644 --- a/libs/common/src/helper/differenceInSeconds.ts +++ b/libs/common/src/helper/differenceInSeconds.ts @@ -1,4 +1,4 @@ export function differenceInSeconds(date1: Date, date2: Date): number { - const diffInMilliseconds = date1.getTime() - date2.getTime(); // Difference in milliseconds - return Math.floor(diffInMilliseconds / 1000); // Convert to seconds and round down + const diffInMilliseconds = date1.getTime() - date2.getTime(); + return Math.floor(diffInMilliseconds / 1000); } diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 5bc8a6b..fe459cb 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -134,6 +134,7 @@ export class UserAuthService { regionName: data.regionName, }, email: data.email, + isUserVerified: true, }, }); if (!userExists) { From 458fa1f66e366872ffdedb87cf9e8755cd191c3a Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 19:27:51 +0300 Subject: [PATCH 07/49] added region to login --- libs/common/src/auth/services/auth.service.ts | 13 ++++++++++++- src/auth/dtos/user-auth.dto.ts | 6 +++++- src/auth/dtos/user-login.dto.ts | 5 +++++ src/auth/services/user-auth.service.ts | 16 ++++++++++++++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index f1c1c0d..8f1080c 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -17,10 +17,21 @@ export class AuthService { private readonly configService: ConfigService, ) {} - async validateUser(email: string, pass: string): Promise { + async validateUser( + email: string, + pass: string, + regionUuid?: string, + ): Promise { const user = await this.userRepository.findOne({ where: { email, + region: regionUuid + ? { + uuid: regionUuid, + } + : { + regionName: 'United Arab Emirates', + }, }, relations: ['roles.roleType'], }); diff --git a/src/auth/dtos/user-auth.dto.ts b/src/auth/dtos/user-auth.dto.ts index 1d6e2a5..b934c4c 100644 --- a/src/auth/dtos/user-auth.dto.ts +++ b/src/auth/dtos/user-auth.dto.ts @@ -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; } diff --git a/src/auth/dtos/user-login.dto.ts b/src/auth/dtos/user-login.dto.ts index 6a14047..1f3662b 100644 --- a/src/auth/dtos/user-login.dto.ts +++ b/src/auth/dtos/user-login.dto.ts @@ -11,4 +11,9 @@ export class UserLoginDto { @IsString() @IsOptional() password: string; + + @ApiProperty() + @IsString() + @IsOptional() + regionUuid?: string; } diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index a514c1c..f464837 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -44,9 +44,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,7 +88,11 @@ export class UserAuthService { } async userLogin(data: UserLoginDto) { - const user = await this.authService.validateUser(data.email, data.password); + const user = await this.authService.validateUser( + data.email, + data.password, + data.regionUuid, + ); if (!user) { throw new UnauthorizedException('Invalid login credentials.'); From a899633d8c55abb807f66a29846ce85539e84377 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Fri, 9 Aug 2024 19:30:06 +0300 Subject: [PATCH 08/49] added default region in otp --- src/auth/dtos/user-otp.dto.ts | 12 +++++++++--- src/auth/services/user-auth.service.ts | 10 +++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/auth/dtos/user-otp.dto.ts b/src/auth/dtos/user-otp.dto.ts index b588cb2..043eebc 100644 --- a/src/auth/dtos/user-otp.dto.ts +++ b/src/auth/dtos/user-otp.dto.ts @@ -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() @@ -14,9 +20,9 @@ export class UserOtpDto { type: OtpType; @ApiProperty() - @IsNotEmpty() + @IsOptional() @IsString() - regionName: string; + regionUuid?: string; } export class VerifyOtpDto extends UserOtpDto { diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index fe459cb..085807a 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -130,9 +130,13 @@ export class UserAuthService { threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); const userExists = await this.userRepository.exists({ where: { - region: { - regionName: data.regionName, - }, + region: data.regionUuid + ? { + uuid: data.regionUuid, + } + : { + regionName: 'United Arab Emirates', + }, email: data.email, isUserVerified: true, }, From a4446b4620e0f059300ed4aac6f8ea809c41b7fd Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:00:01 +0300 Subject: [PATCH 09/49] Add Firebase Device Status Management Module --- .../controllers/devices-status.controller.ts | 25 +++ .../devices-status/devices-status.module.ts | 11 + .../dtos/add.devices-status.dto.ts | 38 ++++ .../services/devices-status.service.ts | 192 ++++++++++++++++++ libs/common/src/firebase/firebase.config.ts | 24 +++ .../src/firebase/firebase.shared.module.ts | 9 + 6 files changed, 299 insertions(+) create mode 100644 libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts create mode 100644 libs/common/src/firebase/devices-status/devices-status.module.ts create mode 100644 libs/common/src/firebase/devices-status/dtos/add.devices-status.dto.ts create mode 100644 libs/common/src/firebase/devices-status/services/devices-status.service.ts create mode 100644 libs/common/src/firebase/firebase.config.ts create mode 100644 libs/common/src/firebase/firebase.shared.module.ts diff --git a/libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts b/libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts new file mode 100644 index 0000000..70506c2 --- /dev/null +++ b/libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts @@ -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 { + return this.deviceStatusFirebaseService.addDeviceStatusByDeviceUuid( + deviceTuyaUuid, + ); + } +} diff --git a/libs/common/src/firebase/devices-status/devices-status.module.ts b/libs/common/src/firebase/devices-status/devices-status.module.ts new file mode 100644 index 0000000..ad13b29 --- /dev/null +++ b/libs/common/src/firebase/devices-status/devices-status.module.ts @@ -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 {} diff --git a/libs/common/src/firebase/devices-status/dtos/add.devices-status.dto.ts b/libs/common/src/firebase/devices-status/dtos/add.devices-status.dto.ts new file mode 100644 index 0000000..9ad7a11 --- /dev/null +++ b/libs/common/src/firebase/devices-status/dtos/add.devices-status.dto.ts @@ -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[]; +} diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts new file mode 100644 index 0000000..267d6b6 --- /dev/null +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -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('auth-config.ACCESS_KEY'); + const secretKey = this.configService.get('auth-config.SECRET_KEY'); + const tuyaEuUrl = this.configService.get('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 { + 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 { + 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 { + 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 { + 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; + } +} diff --git a/libs/common/src/firebase/firebase.config.ts b/libs/common/src/firebase/firebase.config.ts new file mode 100644 index 0000000..e34b0e3 --- /dev/null +++ b/libs/common/src/firebase/firebase.config.ts @@ -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('FIREBASE_API_KEY'), + authDomain: configService.get('FIREBASE_AUTH_DOMAIN'), + projectId: configService.get('FIREBASE_PROJECT_ID'), + storageBucket: configService.get('FIREBASE_STORAGE_BUCKET'), + messagingSenderId: configService.get( + 'FIREBASE_MESSAGING_SENDER_ID', + ), + appId: configService.get('FIREBASE_APP_ID'), + measurementId: configService.get('FIREBASE_MEASUREMENT_ID'), + databaseURL: configService.get('FIREBASE_DATABASE_URL'), + }; + + const app = initializeApp(firebaseConfig); + return getDatabase(app); +}; + +export const firebaseDataBase = (configService: ConfigService) => + initializeFirebaseApp(configService); diff --git a/libs/common/src/firebase/firebase.shared.module.ts b/libs/common/src/firebase/firebase.shared.module.ts new file mode 100644 index 0000000..df8f6bd --- /dev/null +++ b/libs/common/src/firebase/firebase.shared.module.ts @@ -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 {} From 4f688355723f12421cc27b3827cbdf51f9628775 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:00:30 +0300 Subject: [PATCH 10/49] Remove Mongoose Configuration and Dependencies from Common Library --- libs/common/src/config/mongoose.config.ts | 8 -------- libs/common/src/database/database.module.ts | 10 ---------- 2 files changed, 18 deletions(-) delete mode 100644 libs/common/src/config/mongoose.config.ts diff --git a/libs/common/src/config/mongoose.config.ts b/libs/common/src/config/mongoose.config.ts deleted file mode 100644 index 6292f3c..0000000 --- a/libs/common/src/config/mongoose.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerAs } from '@nestjs/config'; - -export default registerAs( - 'mongoose-config', - (): Record => ({ - MONGODB_URI: process.env.MONGODB_URI, - }), -); diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index a4fa323..e6ddb6c 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -18,9 +18,6 @@ import { UserNotificationEntity } from '../modules/user-notification/entities'; import { DeviceNotificationEntity } from '../modules/device-notification/entities'; import { RegionEntity } from '../modules/region/entities'; import { TimeZoneEntity } from '../modules/timezone/entities'; -import { MongooseModule } from '@nestjs/mongoose'; -import { mongooseConfig } from '../mongoose/database/mongoose.config'; -import { DeviceStatusMongoModule } from '../mongoose/devices-status/devices-status.module'; @Module({ imports: [ @@ -68,13 +65,6 @@ import { DeviceStatusMongoModule } from '../mongoose/devices-status/devices-stat ssl: Boolean(JSON.parse(configService.get('DB_SSL'))), }), }), - MongooseModule.forRootAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: async (configService: ConfigService) => - mongooseConfig(configService), - }), - DeviceStatusMongoModule, ], }) export class DatabaseModule {} From 415b1730ef53cb0950455aba122162cb8f192750 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:00:56 +0300 Subject: [PATCH 11/49] Remove unused Mongoose-related modules and configurations --- .../src/mongoose/database/mongoose.config.ts | 23 --- .../controllers/devices-status.controller.ts | 27 ---- .../devices-status/devices-status.module.ts | 23 --- .../dtos/add.devices-status.dto.ts | 38 ----- .../devices-status.repository.interface.ts | 6 - .../repository/devices-status.repository.ts | 49 ------ .../schema/devices-status.schema.ts | 20 --- .../services/devices-status.service.ts | 140 ------------------ .../src/mongoose/mongoose.shared.module.ts | 9 -- 9 files changed, 335 deletions(-) delete mode 100644 libs/common/src/mongoose/database/mongoose.config.ts delete mode 100644 libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts delete mode 100644 libs/common/src/mongoose/devices-status/devices-status.module.ts delete mode 100644 libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts delete mode 100644 libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts delete mode 100644 libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts delete mode 100644 libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts delete mode 100644 libs/common/src/mongoose/devices-status/services/devices-status.service.ts delete mode 100644 libs/common/src/mongoose/mongoose.shared.module.ts diff --git a/libs/common/src/mongoose/database/mongoose.config.ts b/libs/common/src/mongoose/database/mongoose.config.ts deleted file mode 100644 index dfb3ed8..0000000 --- a/libs/common/src/mongoose/database/mongoose.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MongooseModuleOptions } from '@nestjs/mongoose'; -import { ConfigService } from '@nestjs/config'; - -export const mongooseConfig = async ( - configService: ConfigService, -): Promise => { - return { - uri: configService.get('MONGODB_URI'), - dbName: 'syncrow-dev', // Specify your database name here - connectionFactory: (connection) => { - connection.on('connected', () => { - console.log('Mongoose connected to MongoDB'); - }); - connection.on('error', (err) => { - console.error('Mongoose connection error:', err); - }); - connection.on('disconnected', () => { - console.log('Mongoose disconnected from MongoDB'); - }); - return connection; - }, - }; -}; diff --git a/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts b/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts deleted file mode 100644 index 6cd98f6..0000000 --- a/libs/common/src/mongoose/devices-status/controllers/devices-status.controller.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Controller, Post, Param } from '@nestjs/common'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; -import { DeviceStatusMongoService } from '../services/devices-status.service'; - -@ApiTags('Device Status Mongo Module') -@Controller({ - version: '1', - path: 'device-status-mongo', -}) -export class DeviceStatusMongoController { - constructor( - private readonly deviceStatusMongoService: DeviceStatusMongoService, - ) {} - - @ApiBearerAuth() - @Post(':deviceTuyaUuid') - async addDeviceStatus( - @Param('deviceTuyaUuid') deviceTuyaUuid: string, - ): Promise { - return this.deviceStatusMongoService.addDeviceStatusByDeviceUuid( - deviceTuyaUuid, - ); - } - - // Add other endpoints as needed -} diff --git a/libs/common/src/mongoose/devices-status/devices-status.module.ts b/libs/common/src/mongoose/devices-status/devices-status.module.ts deleted file mode 100644 index 3e9a0bf..0000000 --- a/libs/common/src/mongoose/devices-status/devices-status.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { DeviceStatusRepository } from './repository/devices-status.repository'; -import { DeviceStatusSchema } from './schema/devices-status.schema'; -import { DeviceStatusMongoController } from './controllers/devices-status.controller'; -import { DeviceStatusMongoService } from './services/devices-status.service'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; - -@Module({ - imports: [ - MongooseModule.forFeature([ - { name: 'DeviceStatus', schema: DeviceStatusSchema }, - ]), - ], - providers: [ - DeviceStatusMongoService, - DeviceStatusRepository, - DeviceRepository, - ], - controllers: [DeviceStatusMongoController], - exports: [DeviceStatusMongoService, DeviceStatusRepository], -}) -export class DeviceStatusMongoModule {} diff --git a/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts b/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts deleted file mode 100644 index 9ad7a11..0000000 --- a/libs/common/src/mongoose/devices-status/dtos/add.devices-status.dto.ts +++ /dev/null @@ -1,38 +0,0 @@ -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[]; -} diff --git a/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts b/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts deleted file mode 100644 index 0750c3f..0000000 --- a/libs/common/src/mongoose/devices-status/interfaces/devices-status.repository.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Document } from 'mongoose'; - -export interface IRepository { - create(dto: Partial): Promise; - // Add more methods as needed (e.g., find, update, delete) -} diff --git a/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts b/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts deleted file mode 100644 index a515842..0000000 --- a/libs/common/src/mongoose/devices-status/repository/devices-status.repository.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; -import { AddDeviceStatusInterface } from '../schema/devices-status.schema'; -import { AddDeviceStatusDto } from '../dtos/add.devices-status.dto'; - -@Injectable() -export class DeviceStatusRepository { - constructor( - @InjectModel('DeviceStatus') - private readonly deviceStatusModel: Model, - ) {} - - async addDeviceStatus( - addDeviceStatusDto: AddDeviceStatusDto, - ): Promise { - const { deviceTuyaUuid, status } = addDeviceStatusDto; - - // Update or insert the device document - await this.deviceStatusModel.findOneAndUpdate( - { deviceTuyaUuid }, - { - $set: { - deviceUuid: addDeviceStatusDto.deviceUuid, - productUuid: addDeviceStatusDto.productUuid, - productType: addDeviceStatusDto.productType, - }, - }, - { upsert: true, new: true, runValidators: true }, - ); - - // Update the status array - for (const statusItem of status) { - await this.deviceStatusModel.updateOne( - { deviceTuyaUuid, 'status.code': statusItem.code }, // Filter to find if the code already exists - { $set: { 'status.$.value': statusItem.value } }, // Update the value if code exists - ); - - // If the code doesn't exist, add it to the array - await this.deviceStatusModel.updateOne( - { deviceTuyaUuid, 'status.code': { $ne: statusItem.code } }, // Check if the code does not exist - { $push: { status: statusItem } }, // Add the new status item - ); - } - - // Return the updated document - return this.deviceStatusModel.findOne({ deviceTuyaUuid }); - } -} diff --git a/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts b/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts deleted file mode 100644 index 2ae0041..0000000 --- a/libs/common/src/mongoose/devices-status/schema/devices-status.schema.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Schema, Document } from 'mongoose'; - -export interface AddDeviceStatusInterface extends Document { - deviceUuid: string; - deviceTuyaUuid: string; - productUuid: string; - productType: string; - status: []; -} - -export const DeviceStatusSchema = new Schema( - { - deviceUuid: { type: String, required: true }, - deviceTuyaUuid: { type: String, required: true }, - productUuid: { type: String, required: true }, - productType: { type: String, required: true }, - status: [], - }, - { collection: 'devices-status' }, -); diff --git a/libs/common/src/mongoose/devices-status/services/devices-status.service.ts b/libs/common/src/mongoose/devices-status/services/devices-status.service.ts deleted file mode 100644 index 3715975..0000000 --- a/libs/common/src/mongoose/devices-status/services/devices-status.service.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { - HttpException, - HttpStatus, - Injectable, - NotFoundException, -} from '@nestjs/common'; -import { DeviceStatusRepository } from '../repository/devices-status.repository'; -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'; -@Injectable() -export class DeviceStatusMongoService { - private tuya: TuyaContext; - constructor( - private readonly configService: ConfigService, - private readonly deviceStatusRepository: DeviceStatusRepository, - private readonly deviceRepository: DeviceRepository, - ) { - const accessKey = this.configService.get('auth-config.ACCESS_KEY'); - const secretKey = this.configService.get('auth-config.SECRET_KEY'); - const tuyaEuUrl = this.configService.get('tuya-config.TUYA_EU_URL'); - this.tuya = new TuyaContext({ - baseUrl: tuyaEuUrl, - accessKey, - secretKey, - }); - } - async addDeviceStatusByDeviceUuid( - deviceTuyaUuid: string, - ): Promise { - try { - const device = await this.getDeviceByDeviceTuyaUuid(deviceTuyaUuid); - if (device.uuid) { - const deviceStatus = await this.getDevicesInstructionStatus( - device.uuid, - ); - if (deviceStatus.productUuid) { - const deviceStatusSaved = await this.addDeviceStatusToMongo({ - 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 addDeviceStatusToMongo( - addDeviceStatusDto: AddDeviceStatusDto, - ): Promise { - try { - const device = await this.getDeviceByDeviceTuyaUuid( - addDeviceStatusDto.deviceTuyaUuid, - ); - if (device?.uuid) { - return await this.deviceStatusRepository.addDeviceStatus( - 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 { - 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'] }), - }); - } -} diff --git a/libs/common/src/mongoose/mongoose.shared.module.ts b/libs/common/src/mongoose/mongoose.shared.module.ts deleted file mode 100644 index de79714..0000000 --- a/libs/common/src/mongoose/mongoose.shared.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DeviceStatusMongoModule } from './devices-status/devices-status.module'; // Adjust path as needed - -@Module({ - imports: [DeviceStatusMongoModule], - providers: [], // Providers specific to MongooseSharedModule, if any - exports: [], // Export anything specific to MongooseSharedModule, if needed -}) -export class MongooseSharedModule {} From 16595432c3fa47db7831363f8025e9af135048d9 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:01:10 +0300 Subject: [PATCH 12/49] Switch device status storage to Firebase from MongoDB --- libs/common/src/helper/helper.module.ts | 4 ++-- libs/common/src/helper/services/tuya.web.socket.service.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index 75ec360..dfcc43b 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -9,7 +9,7 @@ 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 { DeviceStatusMongoModule } from '../mongoose/devices-status/devices-status.module'; +import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module'; @Global() @Module({ @@ -27,7 +27,7 @@ import { DeviceStatusMongoModule } from '../mongoose/devices-status/devices-stat imports: [ SpaceRepositoryModule, DeviceNotificationRepositoryModule, - DeviceStatusMongoModule, + DeviceStatusFirebaseModule, ], }) export class HelperModule {} diff --git a/libs/common/src/helper/services/tuya.web.socket.service.ts b/libs/common/src/helper/services/tuya.web.socket.service.ts index 779cc35..5b8d89a 100644 --- a/libs/common/src/helper/services/tuya.web.socket.service.ts +++ b/libs/common/src/helper/services/tuya.web.socket.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import TuyaWebsocket from '../../config/tuya-web-socket-config'; import { ConfigService } from '@nestjs/config'; -import { DeviceStatusMongoService } from '@app/common/mongoose/devices-status/services/devices-status.service'; +import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; @Injectable() export class TuyaWebSocketService { @@ -9,7 +9,7 @@ export class TuyaWebSocketService { constructor( private readonly configService: ConfigService, - private readonly deviceStatusMongoService: DeviceStatusMongoService, + private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, ) { // Initialize the TuyaWebsocket client this.client = new TuyaWebsocket({ @@ -37,7 +37,7 @@ export class TuyaWebSocketService { this.client.message(async (ws: WebSocket, message: any) => { try { - await this.deviceStatusMongoService.addDeviceStatusToMongo({ + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ deviceTuyaUuid: message.payload.data.bizData.devId, status: message.payload.data.bizData.properties, }); From 3d96cfcb2a672efb26029f5ba91f952caad4e42e Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:01:22 +0300 Subject: [PATCH 13/49] Switch Device Status Storage to Firebase --- src/automation/automation.module.ts | 4 ++-- src/device/device.module.ts | 4 ++-- src/device/services/device.service.ts | 6 +++--- src/scene/scene.module.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/automation/automation.module.ts b/src/automation/automation.module.ts index 7139f6c..49c3b02 100644 --- a/src/automation/automation.module.ts +++ b/src/automation/automation.module.ts @@ -7,10 +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 { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; +import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module'; @Module({ - imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusMongoModule], + imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], controllers: [AutomationController], providers: [ AutomationService, diff --git a/src/device/device.module.ts b/src/device/device.module.ts index ff9b7fb..4a82bd1 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -10,13 +10,13 @@ import { PermissionTypeRepository } from '@app/common/modules/permission/reposit import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; +import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module'; @Module({ imports: [ ConfigModule, ProductRepositoryModule, DeviceRepositoryModule, - DeviceStatusMongoModule, + DeviceStatusFirebaseModule, ], controllers: [DeviceController], providers: [ diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index fdf9aeb..2e109f7 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -25,7 +25,7 @@ import { PermissionType } from '@app/common/constants/permission-type.enum'; import { In } from 'typeorm'; import { ProductType } from '@app/common/constants/product-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { DeviceStatusMongoService } from '@app/common/mongoose/devices-status/services/devices-status.service'; +import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; @Injectable() export class DeviceService { @@ -34,7 +34,7 @@ export class DeviceService { private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, private readonly productRepository: ProductRepository, - private readonly deviceStatusMongoService: DeviceStatusMongoService, + private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, private readonly spaceRepository: SpaceRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -86,7 +86,7 @@ export class DeviceService { deviceSaved.uuid, ); if (deviceStatus.productUuid) { - await this.deviceStatusMongoService.addDeviceStatusToMongo({ + await this.deviceStatusFirebaseService.addDeviceStatusToFirebase({ deviceUuid: deviceSaved.uuid, deviceTuyaUuid: addDeviceDto.deviceTuyaUuid, status: deviceStatus.status, diff --git a/src/scene/scene.module.ts b/src/scene/scene.module.ts index e378058..c673748 100644 --- a/src/scene/scene.module.ts +++ b/src/scene/scene.module.ts @@ -7,10 +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 { DeviceStatusMongoModule } from '@app/common/mongoose/devices-status/devices-status.module'; +import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module'; @Module({ - imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusMongoModule], + imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], controllers: [SceneController], providers: [ SceneService, From 206d5e7f8fd828d131d57e18275ecfeb4aa10a75 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:01:43 +0300 Subject: [PATCH 14/49] install firebase --- package-lock.json | 718 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 717 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5ac870..c444e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", - "@nestjs/mongoose": "^10.0.10", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.0", @@ -27,9 +26,9 @@ "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", - "mongoose": "^8.5.1", "morgan": "^1.10.0", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", @@ -1052,6 +1051,547 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.7.tgz", + "integrity": "sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz", + "integrity": "sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==", + "dependencies": { + "@firebase/analytics": "0.10.7", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, + "node_modules/@firebase/app": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.8.tgz", + "integrity": "sha512-xSLmW0/RShcnUEXH7l+wC0AFWaUtty4tUFF2loIgbtXTRmra0UH/SqYDf/IcfreUninRrCsusNmvoTidGkXJPw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.7.tgz", + "integrity": "sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz", + "integrity": "sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==", + "dependencies": { + "@firebase/app-check": "0.8.7", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.38.tgz", + "integrity": "sha512-36ZrSvkYLW7QR01Sii2X+IY18ErMpRg6e2B2f/DVTtJBolthwXOnNBps+wvaVBvegdvdVPspgDXZUV0ppqh45w==", + "dependencies": { + "@firebase/app": "0.10.8", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.6.tgz", + "integrity": "sha512-T+lA5xoug9CByGYkD5WkfTh2ujEYq/frGZPbk0H+fNU6fNl7nqg88KcsmzsC6Fsqbjm3LLEb/i6wJvF6NSNEig==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.11.tgz", + "integrity": "sha512-7rE3MkQDoWwI2qd8qsra4/QZCO2GzQSbCL6AVQpult9+Nbimg+5A+YeHxpLTcYAxUV6HDg2CqTDQreFLhcm1CQ==", + "dependencies": { + "@firebase/auth": "1.7.6", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz", + "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==", + "dependencies": { + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.7.tgz", + "integrity": "sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.7.tgz", + "integrity": "sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/database": "1.0.7", + "@firebase/database-types": "1.0.4", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.4.tgz", + "integrity": "sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.9.7" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.6.5.tgz", + "integrity": "sha512-0+Ascaht4qUzj4pCopMPWmoAujk8HKjwCpaNYOOjbYMZ65RVfZPsfZwwbWi/zWMXj6xvPsai5oBiErUUkrLwNw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "@firebase/webchannel-wrapper": "1.0.1", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.34", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.34.tgz", + "integrity": "sha512-OBP2F/Ccydl2U2j8XIfpKBxf0EnQHEhbZ4LTwbSS2QlG9+8TwhvKFkKk/ZljWYqaype+qFKPuXZ5flCqYEETeA==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/firestore": "4.6.5", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.6.tgz", + "integrity": "sha512-GPfIBPtpwQvsC7SQbgaUjLTdja0CsNwMoKSgrzA1FGGRk4NX6qO7VQU6XCwBiAFWbpbQex6QWkSMsCzLx1uibQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.8", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.12.tgz", + "integrity": "sha512-r3XUb5VlITWpML46JymfJPkK6I9j4SNlO7qWIXUc0TUmkv0oAfVoiIt1F83/NuMZXaGr4YWA/794nVSy4GV8tw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/functions": "0.11.6", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz", + "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.8.tgz", + "integrity": "sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", + "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.10.tgz", + "integrity": "sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz", + "integrity": "sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/messaging": "0.12.10", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz", + "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.8.tgz", + "integrity": "sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/performance": "0.6.8", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.8.tgz", + "integrity": "sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz", + "integrity": "sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/remote-config": "0.4.8", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.6.tgz", + "integrity": "sha512-Zgb9WuehJxzhj7pGXUvkAEaH+3HvLjD9xSZ9nepuXf5f8378xME7oGJtREr/RnepdDA5YW0XIxe0QQBNHpe1nw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.9.tgz", + "integrity": "sha512-WWgAp5bTW961oIsCc9+98m4MIVKpEqztAlIngfHfwO/x3DYoBPRl/awMRG3CAXyVxG+7B7oHC5IsnqM+vTwx2A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/storage": "0.12.6", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz", + "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/vertexai-preview": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz", + "integrity": "sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz", + "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -2187,6 +2727,60 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -5068,6 +5662,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -5171,6 +5776,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "10.12.5", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.12.5.tgz", + "integrity": "sha512-J0yL3yh12CfFprTkSOQ9HqBugERyqvWwOuOoo1j1QHmYe9cYLKnBmtNCvGIYInDcsVUnJoRXCM+hxbGf48oVhg==", + "dependencies": { + "@firebase/analytics": "0.10.7", + "@firebase/analytics-compat": "0.2.13", + "@firebase/app": "0.10.8", + "@firebase/app-check": "0.8.7", + "@firebase/app-check-compat": "0.3.14", + "@firebase/app-compat": "0.2.38", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.7.6", + "@firebase/auth-compat": "0.5.11", + "@firebase/database": "1.0.7", + "@firebase/database-compat": "1.0.7", + "@firebase/firestore": "4.6.5", + "@firebase/firestore-compat": "0.3.34", + "@firebase/functions": "0.11.6", + "@firebase/functions-compat": "0.3.12", + "@firebase/installations": "0.6.8", + "@firebase/installations-compat": "0.2.8", + "@firebase/messaging": "0.12.10", + "@firebase/messaging-compat": "0.2.10", + "@firebase/performance": "0.6.8", + "@firebase/performance-compat": "0.2.8", + "@firebase/remote-config": "0.4.8", + "@firebase/remote-config-compat": "0.2.8", + "@firebase/storage": "0.12.6", + "@firebase/storage-compat": "0.3.9", + "@firebase/util": "1.9.7", + "@firebase/vertexai-preview": "0.0.3" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -5727,6 +6366,11 @@ "node": ">= 0.8" } }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -5761,6 +6405,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -7091,6 +7740,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -7164,6 +7818,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8450,6 +9109,29 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", + "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10300,6 +10982,17 @@ "node": ">=8" } }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -10565,6 +11258,27 @@ "node": ">=4.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 0c8fb38..87e2903 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", - "@nestjs/mongoose": "^10.0.10", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.0", @@ -38,9 +37,9 @@ "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", - "mongoose": "^8.5.1", "morgan": "^1.10.0", "nodemailer": "^6.9.10", "onesignal-node": "^3.4.0", From 2ce899089e59e48e84090d8d51a0df140087f837 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 10:48:40 +0300 Subject: [PATCH 15/49] moved region to login if is sent --- libs/common/src/auth/services/auth.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 8f1080c..af9d047 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -29,9 +29,7 @@ export class AuthService { ? { uuid: regionUuid, } - : { - regionName: 'United Arab Emirates', - }, + : undefined, }, relations: ['roles.roleType'], }); From 0d852cf0d1f11cdc41f0eb158c0ec9521a610960 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 10:56:47 +0300 Subject: [PATCH 16/49] added cooldown to data --- src/auth/services/user-auth.service.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 085807a..ef47a5a 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -169,9 +169,12 @@ export class UserAuthService { const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt); if (timeSinceLastOtp < cooldown) { - throw new BadRequestException( - `Please wait ${cooldown - timeSinceLastOtp} more seconds before requesting a new OTP.`, - ); + 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(); From 5825ce01d2d3c1aac4e9bddd36eb86b8c9d0962c Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 11:41:00 +0300 Subject: [PATCH 17/49] added deleted at --- libs/common/src/modules/user-otp/entities/user-otp.entity.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/user-otp/entities/user-otp.entity.ts b/libs/common/src/modules/user-otp/entities/user-otp.entity.ts index 454a3b1..527133a 100644 --- a/libs/common/src/modules/user-otp/entities/user-otp.entity.ts +++ b/libs/common/src/modules/user-otp/entities/user-otp.entity.ts @@ -1,4 +1,4 @@ -import { Column, Entity } from 'typeorm'; +import { Column, DeleteDateColumn, Entity } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserOtpDto } from '../dtos'; import { OtpType } from '../../../../src/constants/otp-type.enum'; @@ -27,6 +27,9 @@ export class UserOtpEntity extends AbstractEntity { }) type: OtpType; + @DeleteDateColumn({ nullable: true }) + deletedAt?: Date; + constructor(partial: Partial) { super(); Object.assign(this, partial); From 53799583bbc9ac536dd98f0a40a64bf0ca34d8e1 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 11:46:23 +0300 Subject: [PATCH 18/49] removed region from otp on no regionuuid --- src/auth/services/user-auth.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index ef47a5a..03a6b9f 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -134,9 +134,7 @@ export class UserAuthService { ? { uuid: data.regionUuid, } - : { - regionName: 'United Arab Emirates', - }, + : undefined, email: data.email, isUserVerified: true, }, From 9c58bb8b259d85ca5b583ae995c6650a088f06d3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 14:29:13 +0300 Subject: [PATCH 19/49] added check if OPT is verification --- src/auth/services/user-auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index e17f599..2fadf6f 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -136,7 +136,7 @@ export class UserAuthService { } : undefined, email: data.email, - isUserVerified: true, + isUserVerified: data.type === OtpType.PASSWORD ? true : undefined, }, }); if (!userExists) { From 6b425eba6f7f46b4d950d266642dfff22cce7129 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 15:32:20 +0300 Subject: [PATCH 20/49] error handling for login --- src/auth/services/user-auth.service.ts | 67 +++++++++++++------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 1e0aa41..7cf7435 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -40,7 +40,7 @@ export class UserAuthService { throw new BadRequestException('User already registered with given email'); } const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt - const hashedPassword = await this.helperHashService.bcrypt( + const hashedPassword = this.helperHashService.bcrypt( userSignUpDto.password, salt, ); @@ -90,39 +90,40 @@ export class UserAuthService { } async userLogin(data: UserLoginDto) { - 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({ + 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 UnauthorizedException('User unauthorized'); + } } async deleteUser(uuid: string) { From f27d84bd01da59f140ecfb496e0a82faa25562b0 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 15:39:58 +0300 Subject: [PATCH 21/49] error handling for login --- src/auth/services/user-auth.service.ts | 64 ++++++++++++++------------ 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index f464837..6bf90d4 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -88,39 +88,43 @@ export class UserAuthService { } async userLogin(data: UserLoginDto) { - const user = await this.authService.validateUser( - data.email, - data.password, - data.regionUuid, - ); + try { + const user = await this.authService.validateUser( + data.email, + data.password, + data.regionUuid, + ); - if (!user) { - throw new UnauthorizedException('Invalid login credentials.'); - } + 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({ + 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('Wrong email, password or region'); + } } async deleteUser(uuid: string) { From dc9e74bdaf7abcca1d3aca44793ba808274c4e74 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 11 Aug 2024 15:58:06 +0300 Subject: [PATCH 22/49] error handling for login --- src/auth/services/user-auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 9d0d804..1f0e7cd 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -125,7 +125,7 @@ export class UserAuthService { sessionId: session[1].uuid, }); } catch (error) { - throw new BadRequestException('Wrong email, password or region'); + throw new BadRequestException('Invalid credentials'); } } From 613f9de731e06aa095b423c0f462138a1766d3e8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 11 Aug 2024 19:45:11 +0300 Subject: [PATCH 23/49] Refactor email sending method name to improve clarity --- libs/common/src/util/email.service.ts | 2 +- src/auth/services/user-auth.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/src/util/email.service.ts b/libs/common/src/util/email.service.ts index 1b87334..f3a389d 100644 --- a/libs/common/src/util/email.service.ts +++ b/libs/common/src/util/email.service.ts @@ -19,7 +19,7 @@ export class EmailService { }; } - async sendOTPEmail( + async sendEmail( email: string, subject: string, message: string, diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index a514c1c..3c45794 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -136,7 +136,7 @@ export class UserAuthService { }); const subject = 'OTP send successfully'; const message = `Your OTP code is ${otpCode}`; - this.emailService.sendOTPEmail(data.email, subject, message); + this.emailService.sendEmail(data.email, subject, message); return otpCode; } From 39d3e18d9dd37b923ade063ab34062ae4c37e2a2 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 11 Aug 2024 19:45:33 +0300 Subject: [PATCH 24/49] Add Visitor Password Management Module --- src/vistor-password/controllers/index.ts | 1 + .../visitor-password.controller.ts | 125 +++ src/vistor-password/dtos/index.ts | 1 + src/vistor-password/dtos/temp-pass.dto.ts | 213 +++++ .../interfaces/visitor-password.interface.ts | 66 ++ src/vistor-password/services/index.ts | 1 + .../services/visitor-password.service.ts | 762 ++++++++++++++++++ .../visitor-password.module.ts | 20 + 8 files changed, 1189 insertions(+) create mode 100644 src/vistor-password/controllers/index.ts create mode 100644 src/vistor-password/controllers/visitor-password.controller.ts create mode 100644 src/vistor-password/dtos/index.ts create mode 100644 src/vistor-password/dtos/temp-pass.dto.ts create mode 100644 src/vistor-password/interfaces/visitor-password.interface.ts create mode 100644 src/vistor-password/services/index.ts create mode 100644 src/vistor-password/services/visitor-password.service.ts create mode 100644 src/vistor-password/visitor-password.module.ts diff --git a/src/vistor-password/controllers/index.ts b/src/vistor-password/controllers/index.ts new file mode 100644 index 0000000..ca4b986 --- /dev/null +++ b/src/vistor-password/controllers/index.ts @@ -0,0 +1 @@ +export * from './visitor-password.controller'; diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts new file mode 100644 index 0000000..5e1e39d --- /dev/null +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -0,0 +1,125 @@ +import { VisitorPasswordService } from '../services/visitor-password.service'; +import { + Body, + Controller, + Post, + HttpException, + HttpStatus, + UseGuards, +} from '@nestjs/common'; +import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; +import { + AddDoorLockOfflineMultipleDto, + AddDoorLockOfflineOneTimeDto, + AddDoorLockOnlineMultipleDto, + AddDoorLockOnlineOneTimeDto, +} from '../dtos/temp-pass.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; + +@ApiTags('Visitor Password Module') +@Controller({ + version: '1', + path: 'visitor-password', +}) +export class VisitorPasswordController { + constructor( + private readonly visitorPasswordService: VisitorPasswordService, + ) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('temporary-password/online/multiple-time') + async addOnlineTemporaryPasswordMultipleTime( + @Body() addDoorLockOnlineMultipleDto: AddDoorLockOnlineMultipleDto, + ) { + try { + const temporaryPasswords = + await this.visitorPasswordService.addOnlineTemporaryPasswordMultipleTime( + addDoorLockOnlineMultipleDto, + ); + + return { + statusCode: HttpStatus.CREATED, + data: temporaryPasswords, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('temporary-password/online/one-time') + async addOnlineTemporaryPassword( + @Body() addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, + ) { + try { + const temporaryPasswords = + await this.visitorPasswordService.addOnlineTemporaryPasswordOneTime( + addDoorLockOnlineOneTimeDto, + ); + + return { + statusCode: HttpStatus.CREATED, + data: temporaryPasswords, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('temporary-password/offline/one-time') + async addOfflineOneTimeTemporaryPassword( + @Body() addDoorLockOfflineOneTimeDto: AddDoorLockOfflineOneTimeDto, + ) { + try { + const temporaryPassword = + await this.visitorPasswordService.addOfflineOneTimeTemporaryPassword( + addDoorLockOfflineOneTimeDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'offline temporary password added successfully', + data: temporaryPassword, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('temporary-password/offline/multiple-time') + async addOfflineMultipleTimeTemporaryPassword( + @Body() + addDoorLockOfflineMultipleDto: AddDoorLockOfflineMultipleDto, + ) { + try { + const temporaryPassword = + await this.visitorPasswordService.addOfflineMultipleTimeTemporaryPassword( + addDoorLockOfflineMultipleDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'offline temporary password added successfully', + data: temporaryPassword, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/vistor-password/dtos/index.ts b/src/vistor-password/dtos/index.ts new file mode 100644 index 0000000..aa0f905 --- /dev/null +++ b/src/vistor-password/dtos/index.ts @@ -0,0 +1 @@ +export * from './temp-pass.dto'; diff --git a/src/vistor-password/dtos/temp-pass.dto.ts b/src/vistor-password/dtos/temp-pass.dto.ts new file mode 100644 index 0000000..0590935 --- /dev/null +++ b/src/vistor-password/dtos/temp-pass.dto.ts @@ -0,0 +1,213 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsNotEmpty, + IsString, + IsArray, + ValidateNested, + IsEnum, + Length, + IsOptional, + IsEmail, +} from 'class-validator'; +import { Type } from 'class-transformer'; +import { WorkingDays } from '@app/common/constants/working-days'; + +class ScheduleDto { + @ApiProperty({ + description: 'effectiveTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public effectiveTime: string; + + @ApiProperty({ + description: 'invalidTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public invalidTime: string; + + @ApiProperty({ + description: 'workingDay', + enum: WorkingDays, + isArray: true, + required: true, + }) + @IsArray() + @IsEnum(WorkingDays, { each: true }) + @IsNotEmpty() + public workingDay: WorkingDays[]; +} + +export class AddDoorLockOnlineMultipleDto { + @ApiProperty({ + description: 'email', + required: true, + }) + @IsEmail() + @IsNotEmpty() + public email: string; + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; + + @ApiProperty({ + description: 'password name', + required: true, + }) + @IsString() + @IsNotEmpty() + public passwordName: string; + @ApiProperty({ + description: 'password', + required: true, + }) + @IsString() + @IsNotEmpty() + @Length(7, 7) + public password: string; + + @ApiProperty({ + description: 'effectiveTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public effectiveTime: string; + + @ApiProperty({ + description: 'invalidTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public invalidTime: string; + + @ApiProperty({ + description: 'scheduleList', + type: [ScheduleDto], + required: false, + }) + @IsArray() + @ValidateNested({ each: true }) + @IsOptional() + @Type(() => ScheduleDto) + public scheduleList: ScheduleDto[]; +} +export class AddDoorLockOnlineOneTimeDto { + @ApiProperty({ + description: 'email', + required: true, + }) + @IsEmail() + @IsNotEmpty() + public email: string; + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; + + @ApiProperty({ + description: 'password name', + required: true, + }) + @IsString() + @IsNotEmpty() + public passwordName: string; + @ApiProperty({ + description: 'password', + required: true, + }) + @IsString() + @IsNotEmpty() + @Length(7, 7) + public password: string; + + @ApiProperty({ + description: 'effectiveTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public effectiveTime: string; + + @ApiProperty({ + description: 'invalidTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public invalidTime: string; +} +export class AddDoorLockOfflineOneTimeDto { + @ApiProperty({ + description: 'email', + required: true, + }) + @IsEmail() + @IsNotEmpty() + public email: string; + @ApiProperty({ + description: 'password name', + required: true, + }) + @IsString() + @IsNotEmpty() + public passwordName: string; + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; +} +export class AddDoorLockOfflineMultipleDto { + @ApiProperty({ + description: 'email', + required: true, + }) + @IsEmail() + @IsNotEmpty() + public email: string; + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; + + @ApiProperty({ + description: 'password name', + required: true, + }) + @IsString() + @IsNotEmpty() + public passwordName: string; + + @ApiProperty({ + description: 'effectiveTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public effectiveTime: string; + + @ApiProperty({ + description: 'invalidTime', + required: true, + }) + @IsString() + @IsNotEmpty() + public invalidTime: string; +} diff --git a/src/vistor-password/interfaces/visitor-password.interface.ts b/src/vistor-password/interfaces/visitor-password.interface.ts new file mode 100644 index 0000000..6dda2eb --- /dev/null +++ b/src/vistor-password/interfaces/visitor-password.interface.ts @@ -0,0 +1,66 @@ +import { WorkingDays } from '@app/common/constants/working-days'; + +export interface createTickInterface { + success: boolean; + result: { + expire_time: number; + ticket_id: string; + ticket_key: string; + id?: number; + offline_temp_password_id?: string; + offline_temp_password?: string; + }; + msg?: string; +} +export interface addDeviceObjectInterface { + passwordName: string; + encryptedPassword: string; + effectiveTime: string; + invalidTime: string; + ticketId: string; + scheduleList?: any[]; +} + +export interface ScheduleDto { + effectiveTime: string; + invalidTime: string; + workingDay: WorkingDays[]; +} + +export interface AddDoorLockOnlineInterface { + passwordName: string; + password: string; + effectiveTime: string; + invalidTime: string; + scheduleList?: ScheduleDto[]; +} +export interface getPasswordInterface { + success: boolean; + result: [ + { + effective_time: number; + id: number; + invalid_time: number; + passwordName: string; + phase: number; + phone: string; + schedule_list?: []; + sn: number; + time_zone: string; + type: number; + }, + ]; + msg?: string; +} +export interface deleteTemporaryPasswordInterface { + success: boolean; + result: boolean; + msg?: string; +} +export interface getPasswordOfflineInterface { + success: boolean; + result: { + records: []; + }; + msg?: string; +} diff --git a/src/vistor-password/services/index.ts b/src/vistor-password/services/index.ts new file mode 100644 index 0000000..14d8814 --- /dev/null +++ b/src/vistor-password/services/index.ts @@ -0,0 +1 @@ +export * from './visitor-password.service'; diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts new file mode 100644 index 0000000..66da260 --- /dev/null +++ b/src/vistor-password/services/visitor-password.service.ts @@ -0,0 +1,762 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; +import { ConfigService } from '@nestjs/config'; +import { + addDeviceObjectInterface, + createTickInterface, +} from '../interfaces/visitor-password.interface'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { ProductType } from '@app/common/constants/product-type.enum'; + +import { + AddDoorLockOfflineMultipleDto, + AddDoorLockOfflineOneTimeDto, + AddDoorLockOnlineMultipleDto, + AddDoorLockOnlineOneTimeDto, +} from '../dtos'; +import { EmailService } from '@app/common/util/email.service'; +import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; + +@Injectable() +export class VisitorPasswordService { + private tuya: TuyaContext; + constructor( + private readonly configService: ConfigService, + private readonly deviceRepository: DeviceRepository, + private readonly emailService: EmailService, + private readonly passwordEncryptionService: PasswordEncryptionService, + ) { + const accessKey = this.configService.get('auth-config.ACCESS_KEY'); + const secretKey = this.configService.get('auth-config.SECRET_KEY'); + const tuyaEuUrl = this.configService.get('tuya-config.TUYA_EU_URL'); + this.tuya = new TuyaContext({ + baseUrl: tuyaEuUrl, + accessKey, + secretKey, + }); + } + + async addOfflineMultipleTimeTemporaryPassword( + addDoorLockOfflineMultipleDto: AddDoorLockOfflineMultipleDto, + ) { + try { + const deviceResults = await Promise.allSettled( + addDoorLockOfflineMultipleDto.devicesUuid.map(async (deviceUuid) => { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + if (deviceDetails.productDevice.prodType !== ProductType.DL) { + throw new HttpException( + 'This is not a door lock device', + HttpStatus.BAD_REQUEST, + ); + } + + const createMultipleOfflinePass = + await this.addOfflineTemporaryPasswordTuya( + deviceDetails.deviceTuyaUuid, + 'multiple', + addDoorLockOfflineMultipleDto, + addDoorLockOfflineMultipleDto.passwordName, + ); + + if (!createMultipleOfflinePass.success) { + throw new HttpException( + createMultipleOfflinePass.msg, + HttpStatus.BAD_REQUEST, + ); + } + + // Send email if the password creation is successful + if (createMultipleOfflinePass.result.offline_temp_password_id) { + const emailSubject = 'One-Time Password Creation Success'; + const emailBody = `Your multiple time offline temporary password "${createMultipleOfflinePass.result.offline_temp_password}" has been successfully created for the device with UUID: ${deviceUuid}.\n\nThank you for using our service.\n\nBest Regards,\nSyncrow`; + + await this.emailService.sendEmail( + addDoorLockOfflineMultipleDto.email, + emailSubject, + emailBody, + ); + } + + return { + success: true, + result: createMultipleOfflinePass.result, + deviceUuid, + }; + } catch (error) { + return { + success: false, + deviceUuid, + error: error.message || 'Error processing device', + }; + } + }), + ); + + // Process results: separate successful and failed operations + const successfulResults = deviceResults + .filter( + (result) => + result.status === 'fulfilled' && + (result as PromiseFulfilledResult).value.success, + ) + .map((result) => (result as PromiseFulfilledResult).value); + + const failedResults = deviceResults + .filter( + (result) => + result.status === 'rejected' || + !(result as PromiseFulfilledResult).value.success, + ) + .map((result) => + result.status === 'rejected' + ? { + success: false, + error: + (result as PromiseRejectedResult).reason.message || + 'Error processing device', + } + : (result as PromiseFulfilledResult).value, + ); + + // Return results if there are successful operations + if (successfulResults.length > 0) { + return { + successOperations: successfulResults, + failedOperations: failedResults, + }; + } + + // If all failed, throw an error with all failure messages + throw new HttpException( + failedResults.map((res) => res.error).join('; ') || + 'All device operations failed', + HttpStatus.BAD_REQUEST, + ); + } catch (error) { + console.error(error); + + throw new HttpException( + error.message || + 'Error adding offline multiple-time temporary password from Tuya', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async addOfflineOneTimeTemporaryPassword( + addDoorLockOfflineOneTimeDto: AddDoorLockOfflineOneTimeDto, + ) { + try { + const deviceResults = await Promise.allSettled( + addDoorLockOfflineOneTimeDto.devicesUuid.map(async (deviceUuid) => { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + if (deviceDetails.productDevice.prodType !== ProductType.DL) { + throw new HttpException( + 'This is not a door lock device', + HttpStatus.BAD_REQUEST, + ); + } + + const createOnceOfflinePass = + await this.addOfflineTemporaryPasswordTuya( + deviceDetails.deviceTuyaUuid, + 'once', + null, + addDoorLockOfflineOneTimeDto.passwordName, + ); + + if (!createOnceOfflinePass.success) { + throw new HttpException( + createOnceOfflinePass.msg, + HttpStatus.BAD_REQUEST, + ); + } + + // Send email if the password creation is successful + if (createOnceOfflinePass.result.offline_temp_password_id) { + const emailSubject = 'One-Time Password Creation Success'; + const emailBody = `Your one-time offline temporary password "${createOnceOfflinePass.result.offline_temp_password}" has been successfully created for the device with UUID: ${deviceUuid}.\n\nThank you for using our service.\n\nBest Regards,\nSyncrow`; + + await this.emailService.sendEmail( + addDoorLockOfflineOneTimeDto.email, + emailSubject, + emailBody, + ); + } + + return { + success: true, + result: createOnceOfflinePass.result, + deviceUuid, + }; + } catch (error) { + return { + success: false, + deviceUuid, + error: error.message || 'Error processing device', + }; + } + }), + ); + + // Process results: separate successful and failed operations + const successfulResults = deviceResults + .filter( + (result) => + result.status === 'fulfilled' && + (result as PromiseFulfilledResult).value.success, + ) + .map((result) => (result as PromiseFulfilledResult).value); + + const failedResults = deviceResults + .filter( + (result) => + result.status === 'rejected' || + !(result as PromiseFulfilledResult).value.success, + ) + .map((result) => + result.status === 'rejected' + ? { + success: false, + error: + (result as PromiseRejectedResult).reason.message || + 'Error processing device', + } + : (result as PromiseFulfilledResult).value, + ); + + // Return results if there are successful operations + if (successfulResults.length > 0) { + return { + successOperations: successfulResults, + failedOperations: failedResults, + }; + } + + // If all failed, throw an error with all failure messages + throw new HttpException( + failedResults.map((res) => res.error).join('; ') || + 'All device operations failed', + HttpStatus.BAD_REQUEST, + ); + } catch (error) { + console.error(error); + + throw new HttpException( + error.message || + 'Error adding offline one-time temporary password from Tuya', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async addOfflineTemporaryPasswordTuya( + doorLockUuid: string, + type: string, + addDoorLockOfflineMultipleDto: AddDoorLockOfflineMultipleDto, + passwordName: string, + ): Promise { + try { + const path = `/v1.1/devices/${doorLockUuid}/door-lock/offline-temp-password`; + + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + ...(type === 'multiple' && { + effective_time: addDoorLockOfflineMultipleDto.effectiveTime, + invalid_time: addDoorLockOfflineMultipleDto.invalidTime, + }), + name: passwordName, + type, + }, + }); + + return response as createTickInterface; + } catch (error) { + throw new HttpException( + 'Error adding offline temporary password from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addOnlineTemporaryPasswordMultipleTime( + addDoorLockOnlineMultipleDto: AddDoorLockOnlineMultipleDto, + ) { + try { + const deviceResults = await Promise.allSettled( + addDoorLockOnlineMultipleDto.devicesUuid.map(async (deviceUuid) => { + try { + const passwordData = await this.getTicketAndEncryptedPassword( + deviceUuid, + addDoorLockOnlineMultipleDto.password, + ); + + if ( + !passwordData.ticketKey || + !passwordData.encryptedPassword || + !passwordData.deviceTuyaUuid + ) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + const addDeviceObj: addDeviceObjectInterface = { + ...addDoorLockOnlineMultipleDto, + ...passwordData, + }; + + const createPass = + await this.addOnlineTemporaryPasswordMultipleTuya( + addDeviceObj, + passwordData.deviceTuyaUuid, + ); + + if (!createPass.success) { + throw new HttpException(createPass.msg, HttpStatus.BAD_REQUEST); + } + + // Send email if the password creation is successful + if (createPass.result.id) { + const emailSubject = 'Password Creation Success'; + const emailBody = `Your temporary password "${addDoorLockOnlineMultipleDto.password}" has been successfully created for the device with UUID: ${deviceUuid}.\n\nThank you for using our service.\n\nBest Regards,\nSyncrow`; + + await this.emailService.sendEmail( + addDoorLockOnlineMultipleDto.email, + emailSubject, + emailBody, + ); + } + + return { + success: true, + id: createPass.result.id, + deviceUuid, + }; + } catch (error) { + return { + success: false, + deviceUuid, + error: error.message || 'Error processing device', + }; + } + }), + ); + + // Process results: separate successful and failed operations + const successfulResults = deviceResults + .filter( + (result) => + result.status === 'fulfilled' && + (result as PromiseFulfilledResult).value.success, + ) + .map((result) => (result as PromiseFulfilledResult).value); + + const failedResults = deviceResults + .filter( + (result) => + result.status === 'rejected' || + !(result as PromiseFulfilledResult).value.success, + ) + .map((result) => + result.status === 'rejected' + ? { + success: false, + error: + (result as PromiseRejectedResult).reason.message || + 'Error processing device', + } + : (result as PromiseFulfilledResult).value, + ); + + // Return results if there are successful operations + if (successfulResults.length > 0) { + return { + successOperations: successfulResults, + failedOperations: failedResults, + }; + } + + // Throw an error if all operations failed + throw new HttpException( + failedResults.map((res) => res.error).join('; ') || + 'All device operations failed', + HttpStatus.BAD_REQUEST, + ); + } catch (error) { + console.error(error); + + throw new HttpException( + error.message || 'Error adding online temporary password from Tuya', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addOnlineTemporaryPasswordOneTime( + addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, + ) { + try { + const deviceResults = await Promise.allSettled( + addDoorLockOnlineOneTimeDto.devicesUuid.map(async (deviceUuid) => { + try { + const passwordData = await this.getTicketAndEncryptedPassword( + deviceUuid, + addDoorLockOnlineOneTimeDto.password, + ); + + if ( + !passwordData.ticketKey || + !passwordData.encryptedPassword || + !passwordData.deviceTuyaUuid + ) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + const addDeviceObj: addDeviceObjectInterface = { + ...addDoorLockOnlineOneTimeDto, + ...passwordData, + }; + + const createPass = await this.addOnlineTemporaryPasswordOneTimeTuya( + addDeviceObj, + passwordData.deviceTuyaUuid, + ); + + if (!createPass.success) { + throw new HttpException(createPass.msg, HttpStatus.BAD_REQUEST); + } + + // Send email if the password creation is successful + if (createPass.result.id) { + const emailSubject = 'Password Creation Success'; + const emailBody = `Your temporary password "${addDoorLockOnlineOneTimeDto.password}" has been successfully created for the device with UUID: ${deviceUuid}.\n\nThank you for using our service.\n\nBest Regards,\nSyncrow`; + + await this.emailService.sendEmail( + addDoorLockOnlineOneTimeDto.email, + emailSubject, + emailBody, + ); + } + + return { + success: true, + id: createPass.result.id, + deviceUuid, + }; + } catch (error) { + return { + success: false, + deviceUuid, + error: error.message || 'Error processing device', + }; + } + }), + ); + + // Process results: separate successful and failed operations + const successfulResults = deviceResults + .filter( + (result) => + result.status === 'fulfilled' && + (result as PromiseFulfilledResult).value.success, + ) + .map((result) => (result as PromiseFulfilledResult).value); + + const failedResults = deviceResults + .filter( + (result) => + result.status === 'rejected' || + !(result as PromiseFulfilledResult).value.success, + ) + .map((result) => + result.status === 'rejected' + ? { + success: false, + error: + (result as PromiseRejectedResult).reason.message || + 'Error processing device', + } + : (result as PromiseFulfilledResult).value, + ); + + // Return results if there are successful operations + if (successfulResults.length > 0) { + return { + successOperations: successfulResults, + failedOperations: failedResults, + }; + } + + // Throw an error if all operations failed + throw new HttpException( + failedResults.map((res) => res.error).join('; ') || + 'All device operations failed', + HttpStatus.BAD_REQUEST, + ); + } catch (error) { + console.error(error); + + throw new HttpException( + error.message || 'Error adding online temporary password from Tuya', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getTicketAndEncryptedPassword( + doorLockUuid: string, + passwordPlan: string, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } else if (deviceDetails.productDevice.prodType !== ProductType.DL) { + throw new HttpException( + 'This is not a door lock device', + HttpStatus.BAD_REQUEST, + ); + } + const ticketDetails = await this.createDoorLockTicketTuya( + deviceDetails.deviceTuyaUuid, + ); + + if (!ticketDetails.result.ticket_id || !ticketDetails.result.ticket_key) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + const decrypted = + this.passwordEncryptionService.generateEncryptedPassword( + passwordPlan, + ticketDetails.result.ticket_key, + ); + return { + ticketId: ticketDetails.result.ticket_id, + ticketKey: ticketDetails.result.ticket_key, + encryptedPassword: decrypted, + deviceTuyaUuid: deviceDetails.deviceTuyaUuid, + }; + } catch (error) { + throw new HttpException( + error.message || 'Error processing the request', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async createDoorLockTicketTuya( + deviceUuid: string, + ): Promise { + try { + const path = `/v1.0/smart-lock/devices/${deviceUuid}/password-ticket`; + const response = await this.tuya.request({ + method: 'POST', + path, + }); + + return response as createTickInterface; + } catch (error) { + throw new HttpException( + 'Error creating door lock ticket from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async addOnlineTemporaryPasswordMultipleTuya( + addDeviceObj: addDeviceObjectInterface, + doorLockUuid: string, + ): Promise { + try { + const path = `/v1.0/devices/${doorLockUuid}/door-lock/temp-password`; + let scheduleList; + if (addDeviceObj.scheduleList.length > 0) { + scheduleList = addDeviceObj.scheduleList.map((schedule) => ({ + effective_time: this.timeToMinutes(schedule.effectiveTime), + invalid_time: this.timeToMinutes(schedule.invalidTime), + working_day: this.getWorkingDayValue(schedule.workingDay), + })); + } + + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + name: addDeviceObj.passwordName, + password: addDeviceObj.encryptedPassword, + effective_time: addDeviceObj.effectiveTime, + invalid_time: addDeviceObj.invalidTime, + password_type: 'ticket', + ticket_id: addDeviceObj.ticketId, + ...(addDeviceObj.scheduleList.length > 0 && { + schedule_list: scheduleList, + }), + + type: '0', + }, + }); + console.log('response', response); + + return response as createTickInterface; + } catch (error) { + console.log('error', error); + + throw new HttpException( + error.msg || 'Error adding online temporary password from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + getWorkingDayValue(days) { + // Array representing the days of the week + const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + + // Initialize a binary string with 7 bits + let binaryString = '0000000'; + + // Iterate through the input array and update the binary string + days.forEach((day) => { + const index = weekDays.indexOf(day); + if (index !== -1) { + // Set the corresponding bit to '1' + binaryString = + binaryString.substring(0, index) + + '1' + + binaryString.substring(index + 1); + } + }); + + // Convert the binary string to an integer + const workingDayValue = parseInt(binaryString, 2); + + return workingDayValue; + } + getDaysFromWorkingDayValue(workingDayValue) { + // Array representing the days of the week + const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + + // Convert the integer to a binary string and pad with leading zeros to ensure 7 bits + const binaryString = workingDayValue.toString(2).padStart(7, '0'); + + // Initialize an array to hold the days of the week + const days = []; + + // Iterate through the binary string and weekDays array + for (let i = 0; i < binaryString.length; i++) { + if (binaryString[i] === '1') { + days.push(weekDays[i]); + } + } + + return days; + } + timeToMinutes(timeStr) { + try { + // Special case for "24:00" + if (timeStr === '24:00') { + return 1440; + } + + // Regular expression to validate the 24-hour time format (HH:MM) + const timePattern = /^([01]\d|2[0-3]):([0-5]\d)$/; + const match = timeStr.match(timePattern); + + if (!match) { + throw new Error('Invalid time format'); + } + + // Extract hours and minutes from the matched groups + const hours = parseInt(match[1], 10); + const minutes = parseInt(match[2], 10); + + // Calculate the total minutes + const totalMinutes = hours * 60 + minutes; + + return totalMinutes; + } catch (error) { + throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + minutesToTime(totalMinutes) { + try { + if ( + typeof totalMinutes !== 'number' || + totalMinutes < 0 || + totalMinutes > 1440 + ) { + throw new Error('Invalid minutes value'); + } + + if (totalMinutes === 1440) { + return '24:00'; + } + + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + + const formattedHours = String(hours).padStart(2, '0'); + const formattedMinutes = String(minutes).padStart(2, '0'); + + return `${formattedHours}:${formattedMinutes}`; + } catch (error) { + throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + async getDeviceByDeviceUuid( + deviceUuid: string, + withProductDevice: boolean = true, + ) { + try { + return await this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + }, + ...(withProductDevice && { relations: ['productDevice'] }), + }); + } catch (error) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + } + async addOnlineTemporaryPasswordOneTimeTuya( + addDeviceObj: addDeviceObjectInterface, + doorLockUuid: string, + ): Promise { + try { + const path = `/v1.0/devices/${doorLockUuid}/door-lock/temp-password`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + name: addDeviceObj.passwordName, + password: addDeviceObj.encryptedPassword, + effective_time: addDeviceObj.effectiveTime, + invalid_time: addDeviceObj.invalidTime, + password_type: 'ticket', + ticket_id: addDeviceObj.ticketId, + type: '1', + }, + }); + console.log('response', response); + + return response as createTickInterface; + } catch (error) { + console.log('error', error); + + throw new HttpException( + error.msg || 'Error adding online temporary password from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts new file mode 100644 index 0000000..50ea9fb --- /dev/null +++ b/src/vistor-password/visitor-password.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { VisitorPasswordService } from './services/visitor-password.service'; +import { VisitorPasswordController } from './controllers/visitor-password.controller'; +import { ConfigModule } from '@nestjs/config'; +import { DeviceRepositoryModule } from '@app/common/modules/device'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { EmailService } from '@app/common/util/email.service'; +import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; +@Module({ + imports: [ConfigModule, DeviceRepositoryModule], + controllers: [VisitorPasswordController], + providers: [ + VisitorPasswordService, + EmailService, + PasswordEncryptionService, + DeviceRepository, + ], + exports: [VisitorPasswordService], +}) +export class VisitorPasswordModule {} From da1117f432694d2adee4831df2dcf960223b2e8d Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 11 Aug 2024 19:45:52 +0300 Subject: [PATCH 25/49] Add VisitorPasswordModule to AppModule --- src/app.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app.module.ts b/src/app.module.ts index 88c0550..2970185 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -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: [ From 0b7a87b6bd52f140b94efe2d59f39052b398b515 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Sun, 11 Aug 2024 23:02:37 +0300 Subject: [PATCH 26/49] added devices and passwords of devices --- src/door-lock/services/door.lock.service.ts | 52 ++++++++++++++++ .../visitor-password.controller.ts | 23 ++++++++ .../services/visitor-password.service.ts | 59 +++++++++++++++++++ .../visitor-password.module.ts | 3 +- 4 files changed, 136 insertions(+), 1 deletion(-) diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index a5892a6..0d90822 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -192,6 +192,58 @@ export class DoorLockService { ); } } + + async getOnlineTemporaryPasswordsOneTime(doorLockUuid: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } else if (deviceDetails.productDevice.prodType !== ProductType.DL) { + throw new HttpException( + 'This is not a door lock device', + HttpStatus.BAD_REQUEST, + ); + } + const passwords = await this.getOnlineTemporaryPasswordsTuya( + deviceDetails.deviceTuyaUuid, + ); + + if (passwords.result.length > 0) { + const passwordFiltered = passwords.result + .filter((item) => item.type === 1) + .map((password: any) => { + if (password.schedule_list?.length > 0) { + password.schedule_list = password.schedule_list.map( + (schedule) => { + schedule.working_day = this.getDaysFromWorkingDayValue( + schedule.working_day, + ); + schedule.effective_time = this.minutesToTime( + schedule.effective_time, + ); + schedule.invalid_time = this.minutesToTime( + schedule.invalid_time, + ); + return schedule; + }, + ); + } + return password; + }); + + return convertKeysToCamelCase(passwordFiltered); + } + + return passwords; + } catch (error) { + throw new HttpException( + error.message || 'Error getting online temporary passwords', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getOnlineTemporaryPasswordsTuya( doorLockUuid: string, ): Promise { diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index 5e1e39d..c0f09b6 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -6,6 +6,7 @@ import { HttpException, HttpStatus, UseGuards, + Get, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { @@ -122,4 +123,26 @@ export class VisitorPasswordController { ); } } + @Get('') + async GetVisitorPassword() { + try { + return await this.visitorPasswordService.getPasswords(); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @Get('/devices') + async GetVisitorDevices() { + try { + return await this.visitorPasswordService.getAllPassDevices(); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index 66da260..2e64faa 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -16,6 +16,7 @@ import { } from '../dtos'; import { EmailService } from '@app/common/util/email.service'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; +import { DoorLockService } from 'src/door-lock/services'; @Injectable() export class VisitorPasswordService { @@ -24,6 +25,7 @@ export class VisitorPasswordService { private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, private readonly emailService: EmailService, + private readonly doorLockService: DoorLockService, private readonly passwordEncryptionService: PasswordEncryptionService, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -403,6 +405,63 @@ export class VisitorPasswordService { ); } } + async getPasswords() { + const deviceIds = await this.deviceRepository.find({ + where: { + productDevice: { + prodType: ProductType.DL, + }, + }, + }); + const data = []; + deviceIds.forEach((deviceId) => { + data.push( + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid) + .catch(() => {}), + this.doorLockService + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid) + .catch(() => {}), + this.doorLockService + .getOnlineTemporaryPasswords(deviceId.uuid) + .catch(() => {}), + this.doorLockService + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid) + .catch(() => {}), + ); + }); + return (await Promise.all(data)).flat().map((item) => { + return { + id: item.pwdId || item.id || null, + name: item.pwdName || item.name || '', + type: item.pwdTypeCode || item.type || '', + startTime: item.gmtStart || item.effectiveTime || null, + endTime: item.gmtExpired || item.invalidTime || null, + status: item.status || item.success || null, + additionalInfo: { + optUid: item.optUid || null, + hasClearPwd: item.hasClearPwd || false, + phase: item.phase || null, + phone: item.phone || '', + timeZone: item.timeZone || '', + result: item.result || null, + tid: item.tid || null, + t: item.t || null, + }, + }; + }); + } + + async getAllPassDevices() { + return await this.deviceRepository.find({ + where: { + productDevice: { + prodType: ProductType.DL, + }, + }, + }); + } + async addOnlineTemporaryPasswordOneTime( addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, ) { diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index 50ea9fb..51239f4 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -6,8 +6,9 @@ import { DeviceRepositoryModule } from '@app/common/modules/device'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { EmailService } from '@app/common/util/email.service'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; +import { DoorLockModule } from 'src/door-lock/door.lock.module'; @Module({ - imports: [ConfigModule, DeviceRepositoryModule], + imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], providers: [ VisitorPasswordService, From d40af71e789f5fe4506e21b17519ba6ceff0e462 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:46:24 +0300 Subject: [PATCH 27/49] Add Default Door Lock Password and Open Door Lock Endpoint --- .../src/constants/default.door-lock-pass.ts | 1 + .../controllers/door.lock.controller.ts | 19 +++++++ src/door-lock/services/door.lock.service.ts | 53 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 libs/common/src/constants/default.door-lock-pass.ts diff --git a/libs/common/src/constants/default.door-lock-pass.ts b/libs/common/src/constants/default.door-lock-pass.ts new file mode 100644 index 0000000..3267ab8 --- /dev/null +++ b/libs/common/src/constants/default.door-lock-pass.ts @@ -0,0 +1 @@ +export const defaultDoorLockPass = '1233214'; diff --git a/src/door-lock/controllers/door.lock.controller.ts b/src/door-lock/controllers/door.lock.controller.ts index 44b9276..e57035c 100644 --- a/src/door-lock/controllers/door.lock.controller.ts +++ b/src/door-lock/controllers/door.lock.controller.ts @@ -211,4 +211,23 @@ export class DoorLockController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('open/:doorLockUuid') + async openDoorLock(@Param('doorLockUuid') doorLockUuid: string) { + try { + await this.doorLockService.openDoorLock(doorLockUuid); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'door lock opened successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index a5892a6..19b738c 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -16,6 +16,7 @@ import { PasswordEncryptionService } from './encryption.services'; import { AddDoorLockOfflineTempMultipleTimeDto } from '../dtos/add.offline-temp.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { UpdateDoorLockOfflineTempDto } from '../dtos/update.offline-temp.dto'; +import { defaultDoorLockPass } from '@app/common/constants/default.door-lock-pass'; @Injectable() export class DoorLockService { @@ -639,4 +640,56 @@ export class DoorLockService { ); } } + async openDoorLock( + doorLockUuid: string, + ): Promise { + try { + // Fetch ticket and encrypted password + const { ticketKey, encryptedPassword, deviceTuyaUuid, ticketId } = + await this.getTicketAndEncryptedPassword( + doorLockUuid, + defaultDoorLockPass, + ); + + // Validate ticket and device Tuya UUID + if (!ticketKey || !encryptedPassword || !deviceTuyaUuid) { + throw new HttpException( + 'Invalid Ticket or Device UUID', + HttpStatus.NOT_FOUND, + ); + } + + // Retrieve device details + const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); + + // Validate device details + if (!deviceDetails?.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + if (deviceDetails.productDevice.prodType !== ProductType.DL) { + throw new HttpException( + 'This is not a door lock device', + HttpStatus.BAD_REQUEST, + ); + } + + // Construct the path for the Tuya API request + const path = `/v1.0/devices/${deviceDetails.deviceTuyaUuid}/door-lock/password-free/open-door`; + + // Make the Tuya API request to open the door + const response = await this.tuya.request({ + method: 'POST', + path, + body: { ticket_id: ticketId }, + }); + + return response as deleteTemporaryPasswordInterface; + } catch (error) { + const status = error.status || HttpStatus.INTERNAL_SERVER_ERROR; + const message = error.message || 'Error opening door lock from Tuya'; + + throw new HttpException(message, status); + } + } } From c7e2d6a44ad7892da28272d512083911482906b7 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:03:40 +0300 Subject: [PATCH 28/49] Update country name --- libs/common/src/constants/regions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/constants/regions.ts b/libs/common/src/constants/regions.ts index 3d2eb1a..4ab2375 100644 --- a/libs/common/src/constants/regions.ts +++ b/libs/common/src/constants/regions.ts @@ -129,7 +129,7 @@ export const allCountries = [ 'Oman', 'Pakistan', 'Palau', - 'Palestine State', + 'Palestine', 'Panama', 'Papua New Guinea', 'Paraguay', From 4d7ea8c9bd402ae1c6dce340e9fe5078b7b960f3 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Aug 2024 15:37:13 +0300 Subject: [PATCH 29/49] added password return data --- src/door-lock/services/door.lock.service.ts | 114 ++++++++++++++---- .../visitor-password.controller.ts | 4 +- .../services/visitor-password.service.ts | 29 +---- 3 files changed, 101 insertions(+), 46 deletions(-) diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 0d90822..7a3cd72 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -1,4 +1,9 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { + Injectable, + HttpException, + HttpStatus, + BadRequestException, +} from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; import { @@ -83,7 +88,10 @@ export class DoorLockService { ); } } - async getOfflineMultipleTimeTemporaryPasswords(doorLockUuid: string) { + async getOfflineMultipleTimeTemporaryPasswords( + doorLockUuid: string, + fromVisitor?: boolean, + ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -99,11 +107,25 @@ export class DoorLockService { deviceDetails.deviceTuyaUuid, 'multiple', ); - - if (passwords.result.records.length > 0) { - return convertKeysToCamelCase(passwords.result.records); + if (!passwords.result.records.length && fromVisitor) { + throw new BadRequestException(); + } + if (passwords.result.records.length > 0) { + return fromVisitor + ? convertKeysToCamelCase(passwords.result.records).map((password) => { + return { + passwodId: `${password.pwdId}`, + passwodExpired: `${password.gmtExpired}`, + passwordStart: `${password.gmtStart}`, + passwordCreated: `${password.gmtCreate}`, + passwodName: password.pwdName, + passwordStatus: password.status, + passwordType: 'OFFLINE_MULTIPLE', + deviceUuid: doorLockUuid, + }; + }) + : convertKeysToCamelCase(passwords.result.records); } - return passwords; } catch (error) { throw new HttpException( @@ -113,10 +135,12 @@ export class DoorLockService { ); } } - async getOfflineOneTimeTemporaryPasswords(doorLockUuid: string) { + async getOfflineOneTimeTemporaryPasswords( + doorLockUuid: string, + fromVisitor?: boolean, + ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); - if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); } else if (deviceDetails.productDevice.prodType !== ProductType.DL) { @@ -129,9 +153,24 @@ export class DoorLockService { deviceDetails.deviceTuyaUuid, 'once', ); - + if (!passwords.result.records.length && fromVisitor) { + throw new BadRequestException(); + } if (passwords.result.records.length > 0) { - return convertKeysToCamelCase(passwords.result.records); + return fromVisitor + ? convertKeysToCamelCase(passwords.result.records).map((password) => { + return { + passwodId: `${password.pwdId}`, + passwodExpired: `${password.gmtExpired}`, + passwordStart: `${password.gmtStart}`, + passwordCreated: `${password.gmtCreate}`, + passwodName: password.pwdName, + passwordStatus: password.status, + passwordType: 'OFFLINE_ONETIME', + deviceUuid: doorLockUuid, + }; + }) + : convertKeysToCamelCase(passwords.result.records); } return passwords; @@ -142,7 +181,10 @@ export class DoorLockService { ); } } - async getOnlineTemporaryPasswords(doorLockUuid: string) { + async getOnlineTemporaryPasswords( + doorLockUuid: string, + fromVisitor?: boolean, + ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -157,8 +199,7 @@ export class DoorLockService { const passwords = await this.getOnlineTemporaryPasswordsTuya( deviceDetails.deviceTuyaUuid, ); - - if (passwords.result.length > 0) { + if (passwords.result?.length > 0) { const passwordFiltered = passwords.result .filter((item) => item.type === 0) .map((password: any) => { @@ -181,9 +222,24 @@ export class DoorLockService { return password; }); - return convertKeysToCamelCase(passwordFiltered); + return fromVisitor + ? convertKeysToCamelCase(passwordFiltered).map((password) => { + return { + passwodId: `${password.id}`, + passwodExpired: `${password.invalidTime}`, + passwordStart: `${password.effectiveTime}`, + passwordCreated: '', + passwodName: password.name, + passwordStatus: '', + passwordType: 'ONLINE_MULTIPLE', + deviceUuid: doorLockUuid, + }; + }) + : convertKeysToCamelCase(passwordFiltered); + } + if (fromVisitor) { + throw new BadRequestException(); } - return passwords; } catch (error) { throw new HttpException( @@ -192,8 +248,10 @@ export class DoorLockService { ); } } - - async getOnlineTemporaryPasswordsOneTime(doorLockUuid: string) { + async getOnlineTemporaryPasswordsOneTime( + doorLockUuid: string, + fromVisitor?: boolean, + ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -208,8 +266,7 @@ export class DoorLockService { const passwords = await this.getOnlineTemporaryPasswordsTuya( deviceDetails.deviceTuyaUuid, ); - - if (passwords.result.length > 0) { + if (passwords.result?.length > 0) { const passwordFiltered = passwords.result .filter((item) => item.type === 1) .map((password: any) => { @@ -232,9 +289,24 @@ export class DoorLockService { return password; }); - return convertKeysToCamelCase(passwordFiltered); + return fromVisitor + ? convertKeysToCamelCase(passwordFiltered).map((password) => { + return { + passwodId: `${password.id}`, + passwodExpired: `${password.invalidTime}`, + passwordStart: `${password.effectiveTime}`, + passwordCreated: '', + passwodName: password.name, + passwordStatus: '', + passwordType: 'ONLINE_ONETIME', + deviceUuid: doorLockUuid, + }; + }) + : convertKeysToCamelCase(passwordFiltered); + } + if (fromVisitor) { + throw new BadRequestException(); } - return passwords; } catch (error) { throw new HttpException( diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index c0f09b6..c3fdfc8 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -49,8 +49,8 @@ export class VisitorPasswordController { ); } } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + // @ApiBearerAuth() + // @UseGuards(JwtAuthGuard) @Post('temporary-password/online/one-time') async addOnlineTemporaryPassword( @Body() addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index 2e64faa..98b3a1e 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -417,38 +417,21 @@ export class VisitorPasswordService { deviceIds.forEach((deviceId) => { data.push( this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid) + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true) .catch(() => {}), this.doorLockService - .getOnlineTemporaryPasswordsOneTime(deviceId.uuid) + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true) .catch(() => {}), this.doorLockService - .getOnlineTemporaryPasswords(deviceId.uuid) + .getOnlineTemporaryPasswords(deviceId.uuid, true) .catch(() => {}), this.doorLockService - .getOfflineOneTimeTemporaryPasswords(deviceId.uuid) + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true) .catch(() => {}), ); }); - return (await Promise.all(data)).flat().map((item) => { - return { - id: item.pwdId || item.id || null, - name: item.pwdName || item.name || '', - type: item.pwdTypeCode || item.type || '', - startTime: item.gmtStart || item.effectiveTime || null, - endTime: item.gmtExpired || item.invalidTime || null, - status: item.status || item.success || null, - additionalInfo: { - optUid: item.optUid || null, - hasClearPwd: item.hasClearPwd || false, - phase: item.phase || null, - phone: item.phone || '', - timeZone: item.timeZone || '', - result: item.result || null, - tid: item.tid || null, - t: item.t || null, - }, - }; + return (await Promise.all(data)).flat().filter((datum) => { + return datum != null; }); } From 3aff8d59d20beaba1483a548a745dd7dbf6f83d8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 13 Aug 2024 01:41:01 +0300 Subject: [PATCH 30/49] Refactor DoorLockService to standardize password property names --- src/door-lock/services/door.lock.service.ts | 35 +++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 7a3cd72..3175bd7 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -114,12 +114,12 @@ export class DoorLockService { return fromVisitor ? convertKeysToCamelCase(passwords.result.records).map((password) => { return { - passwodId: `${password.pwdId}`, - passwodExpired: `${password.gmtExpired}`, - passwordStart: `${password.gmtStart}`, + passwordId: `${password.pwdId}`, + invalidTime: `${password.gmtExpired}`, + effectiveTime: `${password.gmtStart}`, passwordCreated: `${password.gmtCreate}`, - passwodName: password.pwdName, - passwordStatus: password.status, + createdTime: password.pwdName, + passwordStatus: `${password.status}`, passwordType: 'OFFLINE_MULTIPLE', deviceUuid: doorLockUuid, }; @@ -160,12 +160,12 @@ export class DoorLockService { return fromVisitor ? convertKeysToCamelCase(passwords.result.records).map((password) => { return { - passwodId: `${password.pwdId}`, - passwodExpired: `${password.gmtExpired}`, - passwordStart: `${password.gmtStart}`, - passwordCreated: `${password.gmtCreate}`, - passwodName: password.pwdName, - passwordStatus: password.status, + passwordId: `${password.pwdId}`, + invalidTime: `${password.gmtExpired}`, + effectiveTime: `${password.gmtStart}`, + createdTime: `${password.gmtCreate}`, + passwordName: password.pwdName, + passwordStatus: `${password.status}`, passwordType: 'OFFLINE_ONETIME', deviceUuid: doorLockUuid, }; @@ -226,9 +226,10 @@ export class DoorLockService { ? convertKeysToCamelCase(passwordFiltered).map((password) => { return { passwodId: `${password.id}`, - passwodExpired: `${password.invalidTime}`, - passwordStart: `${password.effectiveTime}`, - passwordCreated: '', + invalidTime: `${password.invalidTime}`, + effectiveTime: `${password.effectiveTime}`, + createdTime: '', + scheduleList: password?.scheduleList, passwodName: password.name, passwordStatus: '', passwordType: 'ONLINE_MULTIPLE', @@ -293,9 +294,9 @@ export class DoorLockService { ? convertKeysToCamelCase(passwordFiltered).map((password) => { return { passwodId: `${password.id}`, - passwodExpired: `${password.invalidTime}`, - passwordStart: `${password.effectiveTime}`, - passwordCreated: '', + invalidTime: `${password.invalidTime}`, + effectiveTime: `${password.effectiveTime}`, + createdTime: '', passwodName: password.name, passwordStatus: '', passwordType: 'ONLINE_ONETIME', From 764cfbe7e2e492ea9710577790f12685b45ee4c8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 13 Aug 2024 01:41:23 +0300 Subject: [PATCH 31/49] Refactor Visitor Password Controller and Service for Enhanced Security and Functionality --- .../visitor-password.controller.ts | 14 +++---- .../services/visitor-password.service.ts | 37 ++++++++++++------- .../visitor-password.module.ts | 8 ++++ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index c3fdfc8..f1a84bb 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -49,8 +49,8 @@ export class VisitorPasswordController { ); } } - // @ApiBearerAuth() - // @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Post('temporary-password/online/one-time') async addOnlineTemporaryPassword( @Body() addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, @@ -86,8 +86,6 @@ export class VisitorPasswordController { return { statusCode: HttpStatus.CREATED, - success: true, - message: 'offline temporary password added successfully', data: temporaryPassword, }; } catch (error) { @@ -112,8 +110,6 @@ export class VisitorPasswordController { return { statusCode: HttpStatus.CREATED, - success: true, - message: 'offline temporary password added successfully', data: temporaryPassword, }; } catch (error) { @@ -123,7 +119,9 @@ export class VisitorPasswordController { ); } } - @Get('') + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get() async GetVisitorPassword() { try { return await this.visitorPasswordService.getPasswords(); @@ -134,6 +132,8 @@ export class VisitorPasswordController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get('/devices') async GetVisitorDevices() { try { diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index 98b3a1e..e8b2df4 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -17,6 +17,8 @@ import { import { EmailService } from '@app/common/util/email.service'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; import { DoorLockService } from 'src/door-lock/services'; +import { GetDeviceDetailsInterface } from 'src/device/interfaces/get.device.interface'; +import { DeviceService } from 'src/device/services'; @Injectable() export class VisitorPasswordService { @@ -26,6 +28,7 @@ export class VisitorPasswordService { private readonly deviceRepository: DeviceRepository, private readonly emailService: EmailService, private readonly doorLockService: DoorLockService, + private readonly deviceService: DeviceService, private readonly passwordEncryptionService: PasswordEncryptionService, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -383,7 +386,7 @@ export class VisitorPasswordService { ); // Return results if there are successful operations - if (successfulResults.length > 0) { + if (successfulResults?.length > 0) { return { successOperations: successfulResults, failedOperations: failedResults, @@ -416,9 +419,6 @@ export class VisitorPasswordService { const data = []; deviceIds.forEach((deviceId) => { data.push( - this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true) - .catch(() => {}), this.doorLockService .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true) .catch(() => {}), @@ -428,6 +428,9 @@ export class VisitorPasswordService { this.doorLockService .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true) .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true) + .catch(() => {}), ); }); return (await Promise.all(data)).flat().filter((datum) => { @@ -436,13 +439,27 @@ export class VisitorPasswordService { } async getAllPassDevices() { - return await this.deviceRepository.find({ + const devices = await this.deviceRepository.find({ where: { productDevice: { prodType: ProductType.DL, }, }, + relations: ['productDevice'], }); + const devicesData = await Promise.all( + devices?.map(async (device) => { + return { + productUuid: device.productDevice.uuid, + productType: device.productDevice.prodType, + ...(await this.deviceService.getDeviceDetailsByDeviceIdTuya( + device.deviceTuyaUuid, + )), + uuid: device.uuid, + } as GetDeviceDetailsInterface; + }), + ); + return devicesData; } async addOnlineTemporaryPasswordOneTime( @@ -622,7 +639,7 @@ export class VisitorPasswordService { try { const path = `/v1.0/devices/${doorLockUuid}/door-lock/temp-password`; let scheduleList; - if (addDeviceObj.scheduleList.length > 0) { + if (addDeviceObj?.scheduleList?.length > 0) { scheduleList = addDeviceObj.scheduleList.map((schedule) => ({ effective_time: this.timeToMinutes(schedule.effectiveTime), invalid_time: this.timeToMinutes(schedule.invalidTime), @@ -640,19 +657,16 @@ export class VisitorPasswordService { invalid_time: addDeviceObj.invalidTime, password_type: 'ticket', ticket_id: addDeviceObj.ticketId, - ...(addDeviceObj.scheduleList.length > 0 && { + ...(addDeviceObj?.scheduleList?.length > 0 && { schedule_list: scheduleList, }), type: '0', }, }); - console.log('response', response); return response as createTickInterface; } catch (error) { - console.log('error', error); - throw new HttpException( error.msg || 'Error adding online temporary password from Tuya', HttpStatus.INTERNAL_SERVER_ERROR, @@ -789,12 +803,9 @@ export class VisitorPasswordService { type: '1', }, }); - console.log('response', response); return response as createTickInterface; } catch (error) { - console.log('error', error); - throw new HttpException( error.msg || 'Error adding online temporary password from Tuya', HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index 51239f4..33454a3 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -7,6 +7,10 @@ import { DeviceRepository } from '@app/common/modules/device/repositories'; import { EmailService } from '@app/common/util/email.service'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; import { DoorLockModule } from 'src/door-lock/door.lock.module'; +import { DeviceService } from 'src/device/services'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], @@ -14,6 +18,10 @@ import { DoorLockModule } from 'src/door-lock/door.lock.module'; VisitorPasswordService, EmailService, PasswordEncryptionService, + DeviceService, + ProductRepository, + DeviceStatusFirebaseService, + SpaceRepository, DeviceRepository, ], exports: [VisitorPasswordService], From c6b8945978bfea4a690cc695be7a0b435cb02dc1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 13 Aug 2024 01:44:02 +0300 Subject: [PATCH 32/49] Update .env.example with new environment variables for Firebase integration --- .env.example | 91 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/.env.example b/.env.example index 54819e3..944dba0 100644 --- a/.env.example +++ b/.env.example @@ -1,75 +1,90 @@ 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= \ No newline at end of file + +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= + From fb3753f2b253b6b452bd505d9819b1891807af76 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:53:23 +0300 Subject: [PATCH 33/49] Add get Communities endpoint --- .../controllers/community.controller.ts | 27 ++++++++++++++----- .../interface/community.interface.ts | 9 +++++++ src/community/services/community.service.ts | 18 +++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index c88acf1..af8d137 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -18,10 +18,10 @@ import { } from '../dtos/add.community.dto'; import { GetCommunityChildDto } from '../dtos/get.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; -import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; +// import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { CommunityPermissionGuard } from 'src/guards/community.permission.guard'; +// import { CommunityPermissionGuard } from 'src/guards/community.permission.guard'; @ApiTags('Community Module') @Controller({ @@ -53,7 +53,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CommunityPermissionGuard) + @UseGuards(JwtAuthGuard) @Get(':communityUuid') async getCommunityByUuid(@Param('communityUuid') communityUuid: string) { try { @@ -67,9 +67,22 @@ export class CommunityController { ); } } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CommunityPermissionGuard) + @UseGuards(JwtAuthGuard) + @Get() + async getCommunities() { + try { + const communities = await this.communityService.getCommunities(); + return communities; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get('child/:communityUuid') async getCommunityChildByUuid( @Param('communityUuid') communityUuid: string, @@ -103,7 +116,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(AdminRoleGuard, CheckUserCommunityGuard) + @UseGuards(AdminRoleGuard) @Post('user') async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) { try { @@ -121,7 +134,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CommunityPermissionGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:communityUuid') async renameCommunityByUuid( @Param('communityUuid') communityUuid: string, diff --git a/src/community/interface/community.interface.ts b/src/community/interface/community.interface.ts index 31c0579..e65a053 100644 --- a/src/community/interface/community.interface.ts +++ b/src/community/interface/community.interface.ts @@ -24,3 +24,12 @@ export interface GetCommunityByUserUuidInterface { name: string; type: string; } +export interface Community { + uuid: string; + createdAt: Date; + updatedAt: Date; + name: string; + type: string; +} + +export interface GetCommunitiesInterface extends Array {} diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 2476c25..27d7bcf 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -10,6 +10,7 @@ import { SpaceRepository } from '@app/common/modules/space/repositories'; import { AddCommunityDto, AddUserCommunityDto } from '../dtos'; import { CommunityChildInterface, + GetCommunitiesInterface, GetCommunityByUserUuidInterface, GetCommunityByUuidInterface, RenameCommunityByUuidInterface, @@ -79,6 +80,23 @@ export class CommunityService { } } } + async getCommunities(): Promise { + try { + const community = await this.spaceRepository.find({ + where: { spaceType: { type: 'community' } }, + relations: ['spaceType'], + }); + return community.map((community) => ({ + uuid: community.uuid, + createdAt: community.createdAt, + updatedAt: community.updatedAt, + name: community.spaceName, + type: community.spaceType.type, + })); + } catch (err) { + throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR); + } + } async getCommunityChildByUuid( communityUuid: string, getCommunityChildDto: GetCommunityChildDto, From 2581137f8b1c4d5155af62b83c4d36983cc8eeb7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 21 Aug 2024 11:19:19 +0300 Subject: [PATCH 34/49] added otp limiter --- .env.example | 2 ++ src/auth/services/user-auth.service.ts | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 944dba0..54773c4 100644 --- a/.env.example +++ b/.env.example @@ -88,3 +88,5 @@ FIREBASE_MEASUREMENT_ID= FIREBASE_DATABASE_URL= +OTP_LIMITER= + diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index ddcb8e9..4df01db 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -20,6 +20,7 @@ import { UserEntity } from '../../../libs/common/src/modules/user/entities/user. 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 { @@ -32,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 { @@ -139,8 +141,10 @@ export class UserAuthService { } async generateOTP(data: UserOtpDto): Promise { - const threeDaysAgo = new Date(); - threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); + const otpLimiter = new Date(); + otpLimiter.setDate( + otpLimiter.getDate() - this.configService.get('OTP_LIMITER'), + ); const userExists = await this.userRepository.exists({ where: { region: data.regionUuid @@ -159,14 +163,14 @@ export class UserAuthService { await this.otpRepository.delete({ email: data.email, type: data.type, - createdAt: LessThan(threeDaysAgo), + createdAt: LessThan(otpLimiter), }); const countOfOtp = await this.otpRepository.count({ withDeleted: true, where: { email: data.email, type: data.type, - createdAt: MoreThan(threeDaysAgo), + createdAt: MoreThan(otpLimiter), }, }); const lastOtp = await this.otpRepository.findOne({ From 95a315db8f6d79302fe7333df7393862d627bda6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 21 Aug 2024 11:22:08 +0300 Subject: [PATCH 35/49] added otp limiter --- src/auth/services/user-auth.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 4df01db..9a9c670 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -42,7 +42,7 @@ export class UserAuthService { throw new BadRequestException('User already registered with given email'); } const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt - const hashedPassword = this.helperHashService.bcrypt( + const hashedPassword = await this.helperHashService.bcrypt( userSignUpDto.password, salt, ); @@ -124,7 +124,7 @@ export class UserAuthService { sessionId: session[1].uuid, }); } catch (error) { - throw new UnauthorizedException('User unauthorized'); + throw new BadRequestException('Invalid credentials'); } } From eb7d294471d18c38b3ea59fe57f050cd0f6d49eb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 21 Aug 2024 14:25:06 +0300 Subject: [PATCH 36/49] added cooldown with OTP --- src/auth/controllers/user-auth.controller.ts | 2 +- src/auth/services/user-auth.service.ts | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index 86f9ce6..e650d39 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -71,7 +71,7 @@ export class UserAuthController { return { statusCode: HttpStatus.OK, data: { - otp: otpCode, + ...otpCode, }, message: 'Otp Send Successfully', }; diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 9a9c670..7b539e2 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -140,7 +140,10 @@ export class UserAuthService { return await this.userRepository.findOne({ where: { uuid: id } }); } - async generateOTP(data: UserOtpDto): Promise { + async generateOTP(data: UserOtpDto): Promise<{ + otpCode: string; + cooldown: number; + }> { const otpLimiter = new Date(); otpLimiter.setDate( otpLimiter.getDate() - this.configService.get('OTP_LIMITER'), @@ -178,7 +181,7 @@ export class UserAuthService { order: { createdAt: 'DESC' }, withDeleted: true, }); - const cooldown = 30 * Math.pow(2, countOfOtp - 1); + let cooldown = 30 * Math.pow(2, countOfOtp - 1); if (lastOtp) { const now = new Date(); const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt); @@ -201,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.sendEmail(data.email, subject, message); - return otpCode; + return { otpCode, cooldown }; } async verifyOTP(data: VerifyOtpDto): Promise { From adb9019749e9b286a311f115eccfff382b79fc3c Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:36:46 +0300 Subject: [PATCH 37/49] add expired status for vistor pass --- .../controllers/door.lock.controller.ts | 2 +- src/door-lock/services/door.lock.service.ts | 58 ++++++++++++++----- .../services/visitor-password.service.ts | 20 +++++-- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/door-lock/controllers/door.lock.controller.ts b/src/door-lock/controllers/door.lock.controller.ts index e57035c..56ba2b8 100644 --- a/src/door-lock/controllers/door.lock.controller.ts +++ b/src/door-lock/controllers/door.lock.controller.ts @@ -113,7 +113,7 @@ export class DoorLockController { @Param('doorLockUuid') doorLockUuid: string, ) { try { - return await this.doorLockService.getOnlineTemporaryPasswords( + return await this.doorLockService.getOnlineTemporaryPasswordsMultiple( doorLockUuid, ); } catch (error) { diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index a2f7a12..e875f67 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -92,6 +92,7 @@ export class DoorLockService { async getOfflineMultipleTimeTemporaryPasswords( doorLockUuid: string, fromVisitor?: boolean, + isExpired?: boolean, ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -107,6 +108,7 @@ export class DoorLockService { const passwords = await this.getTemporaryOfflinePasswordsTuya( deviceDetails.deviceTuyaUuid, 'multiple', + isExpired, ); if (!passwords.result.records.length && fromVisitor) { throw new BadRequestException(); @@ -114,15 +116,20 @@ export class DoorLockService { if (passwords.result.records.length > 0) { return fromVisitor ? convertKeysToCamelCase(passwords.result.records).map((password) => { + const timestampInSeconds = Math.floor(Date.now() / 1000); return { passwordId: `${password.pwdId}`, invalidTime: `${password.gmtExpired}`, effectiveTime: `${password.gmtStart}`, passwordCreated: `${password.gmtCreate}`, createdTime: password.pwdName, - passwordStatus: `${password.status}`, passwordType: 'OFFLINE_MULTIPLE', deviceUuid: doorLockUuid, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', }; }) : convertKeysToCamelCase(passwords.result.records); @@ -139,6 +146,7 @@ export class DoorLockService { async getOfflineOneTimeTemporaryPasswords( doorLockUuid: string, fromVisitor?: boolean, + isExpired?: boolean, ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -153,6 +161,7 @@ export class DoorLockService { const passwords = await this.getTemporaryOfflinePasswordsTuya( deviceDetails.deviceTuyaUuid, 'once', + isExpired, ); if (!passwords.result.records.length && fromVisitor) { throw new BadRequestException(); @@ -160,15 +169,20 @@ export class DoorLockService { if (passwords.result.records.length > 0) { return fromVisitor ? convertKeysToCamelCase(passwords.result.records).map((password) => { + const timestampInSeconds = Math.floor(Date.now() / 1000); return { passwordId: `${password.pwdId}`, invalidTime: `${password.gmtExpired}`, effectiveTime: `${password.gmtStart}`, createdTime: `${password.gmtCreate}`, passwordName: password.pwdName, - passwordStatus: `${password.status}`, passwordType: 'OFFLINE_ONETIME', deviceUuid: doorLockUuid, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.gmtStart + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', }; }) : convertKeysToCamelCase(passwords.result.records); @@ -182,9 +196,10 @@ export class DoorLockService { ); } } - async getOnlineTemporaryPasswords( + async getOnlineTemporaryPasswordsMultiple( doorLockUuid: string, fromVisitor?: boolean, + isExpired?: boolean, ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -197,8 +212,9 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } - const passwords = await this.getOnlineTemporaryPasswordsTuya( + const passwords = await this.getOnlineTemporaryPasswordsMultipleTuya( deviceDetails.deviceTuyaUuid, + isExpired, ); if (passwords.result?.length > 0) { const passwordFiltered = passwords.result @@ -225,16 +241,21 @@ export class DoorLockService { return fromVisitor ? convertKeysToCamelCase(passwordFiltered).map((password) => { + const timestampInSeconds = Math.floor(Date.now() / 1000); return { - passwodId: `${password.id}`, + passwordId: `${password.id}`, invalidTime: `${password.invalidTime}`, effectiveTime: `${password.effectiveTime}`, createdTime: '', scheduleList: password?.scheduleList, - passwodName: password.name, - passwordStatus: '', + passwordName: password.name, passwordType: 'ONLINE_MULTIPLE', deviceUuid: doorLockUuid, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', }; }) : convertKeysToCamelCase(passwordFiltered); @@ -253,6 +274,7 @@ export class DoorLockService { async getOnlineTemporaryPasswordsOneTime( doorLockUuid: string, fromVisitor?: boolean, + isExpired?: boolean, ) { try { const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); @@ -265,8 +287,9 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } - const passwords = await this.getOnlineTemporaryPasswordsTuya( + const passwords = await this.getOnlineTemporaryPasswordsMultipleTuya( deviceDetails.deviceTuyaUuid, + isExpired, ); if (passwords.result?.length > 0) { const passwordFiltered = passwords.result @@ -293,15 +316,20 @@ export class DoorLockService { return fromVisitor ? convertKeysToCamelCase(passwordFiltered).map((password) => { + const timestampInSeconds = Math.floor(Date.now() / 1000); return { - passwodId: `${password.id}`, + passwordId: `${password.id}`, invalidTime: `${password.invalidTime}`, effectiveTime: `${password.effectiveTime}`, createdTime: '', - passwodName: password.name, - passwordStatus: '', + passwordName: password.name, passwordType: 'ONLINE_ONETIME', deviceUuid: doorLockUuid, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', }; }) : convertKeysToCamelCase(passwordFiltered); @@ -318,11 +346,12 @@ export class DoorLockService { } } - async getOnlineTemporaryPasswordsTuya( + async getOnlineTemporaryPasswordsMultipleTuya( doorLockUuid: string, + isExpired?: boolean, ): Promise { try { - const path = `/v1.0/devices/${doorLockUuid}/door-lock/temp-passwords?valid=true`; + const path = `/v1.0/devices/${doorLockUuid}/door-lock/temp-passwords?valid=${!isExpired}`; const response = await this.tuya.request({ method: 'GET', @@ -340,9 +369,10 @@ export class DoorLockService { async getTemporaryOfflinePasswordsTuya( doorLockUuid: string, type: string, + isExpired?: boolean, ): Promise { try { - const path = `/v1.0/devices/${doorLockUuid}/door-lock/offline-temp-password?pwd_type_codes=${type}&target_status=EFFECTIVE&page_no=1&page_size=100`; + const path = `/v1.0/devices/${doorLockUuid}/door-lock/offline-temp-password?pwd_type_codes=${type}&target_status=${isExpired ? 'INEFFECTIVE' : 'EFFECTIVE'}&page_no=1&page_size=100`; const response = await this.tuya.request({ method: 'GET', diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index e8b2df4..c18ee84 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -420,16 +420,28 @@ export class VisitorPasswordService { deviceIds.forEach((deviceId) => { data.push( this.doorLockService - .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true) + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, false) .catch(() => {}), this.doorLockService - .getOnlineTemporaryPasswords(deviceId.uuid, true) + .getOnlineTemporaryPasswordsOneTime(deviceId.uuid, true, true) .catch(() => {}), this.doorLockService - .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true) + .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, false) .catch(() => {}), this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true) + .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, false) + .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, true) + .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, false) .catch(() => {}), ); }); From a9d0e06c021ce185baae823c661b262e7cd86b2a Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 21 Aug 2024 23:26:37 +0300 Subject: [PATCH 38/49] Add endpoint to fetch all devices with detailed information --- src/device/controllers/device.controller.ts | 13 +++++++ src/device/services/device.service.ts | 42 +++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index a54b5a9..6e834eb 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -223,4 +223,17 @@ export class DeviceController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get() + async getAllDevices() { + try { + return await this.deviceService.getAllDevices(); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 2e109f7..ff8bfed 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -620,4 +620,46 @@ export class DeviceService { ); } } + async getAllDevices(): Promise { + try { + const devices = await this.deviceRepository.find({ + relations: [ + 'spaceDevice.parent', + 'productDevice', + 'permission', + 'permission.permissionType', + ], + }); + const devicesData = await Promise.all( + devices.map(async (device) => { + const spaceDevice = device?.spaceDevice; + const parentDevice = spaceDevice?.parent; + return { + room: { + uuid: spaceDevice?.uuid, + name: spaceDevice?.spaceName, + }, + unit: { + uuid: parentDevice?.uuid, + name: parentDevice?.spaceName, + }, + productUuid: device.productDevice.uuid, + productType: device.productDevice.prodType, + permissionType: device.permission[0].permissionType.type, + ...(await this.getDeviceDetailsByDeviceIdTuya( + device.deviceTuyaUuid, + )), + uuid: device.uuid, + } as GetDeviceDetailsInterface; + }), + ); + + return devicesData; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } From 15987f76428c8ebaa9da1a200054c041568095fb Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 24 Aug 2024 16:45:00 +0300 Subject: [PATCH 39/49] Update Device Service to Handle Promise Settlements and Add Battery Status for Door Locks --- src/device/services/device.service.ts | 31 +++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index ff8bfed..db1b521 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -630,8 +630,26 @@ export class DeviceService { 'permission.permissionType', ], }); - const devicesData = await Promise.all( + const devicesData = await Promise.allSettled( devices.map(async (device) => { + let battery = null; + + // Check if the device is a door lock (DL) + if (device.productDevice.prodType === ProductType.DL) { + console.log('device', device.deviceTuyaUuid); + + const doorLockInstructionsStatus = + await this.getDevicesInstructionStatus(device.uuid); + + const batteryStatus: any = doorLockInstructionsStatus.status.find( + (status: any) => status.code === 'residual_electricity', + ); + + if (batteryStatus) { + battery = batteryStatus.value; + } + } + const spaceDevice = device?.spaceDevice; const parentDevice = spaceDevice?.parent; return { @@ -650,11 +668,20 @@ export class DeviceService { device.deviceTuyaUuid, )), uuid: device.uuid, + ...(battery && { battery }), } as GetDeviceDetailsInterface; }), ); - return devicesData; + // Filter out rejected promises and extract the fulfilled values + const fulfilledDevices = devicesData + .filter((result) => result.status === 'fulfilled') + .map( + (result) => + (result as PromiseFulfilledResult).value, + ); + + return fulfilledDevices; } catch (error) { throw new HttpException( error.message || 'Internal server error', From 807dc8450194cd0b956eb3cfe2f237511afe6817 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:41:02 +0300 Subject: [PATCH 40/49] Add Device Logs Retrieval Endpoint and Service --- src/device/controllers/device.controller.ts | 21 ++++++- src/device/dtos/get.device.dto.ts | 9 +++ src/device/interfaces/get.device.interface.ts | 6 ++ src/device/services/device.service.ts | 58 ++++++++++++++++++- 4 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 6e834eb..bc399b6 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -14,7 +14,10 @@ import { } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { AddDeviceDto, UpdateDeviceInRoomDto } from '../dtos/add.device.dto'; -import { GetDeviceByRoomUuidDto } from '../dtos/get.device.dto'; +import { + GetDeviceByRoomUuidDto, + GetDeviceLogsDto, +} from '../dtos/get.device.dto'; import { ControlDeviceDto } from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; @@ -236,4 +239,20 @@ export class DeviceController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('report-logs/:deviceUuid') + async getBuildingChildByUuid( + @Param('deviceUuid') deviceUuid: string, + @Query() query: GetDeviceLogsDto, + ) { + try { + return await this.deviceService.getDeviceLogs(deviceUuid, query); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index 26002bb..99c0dad 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -10,3 +10,12 @@ export class GetDeviceByRoomUuidDto { @IsNotEmpty() public roomUuid: string; } +export class GetDeviceLogsDto { + @ApiProperty({ + description: 'code', + required: true, + }) + @IsString() + @IsNotEmpty() + public code: string; +} diff --git a/src/device/interfaces/get.device.interface.ts b/src/device/interfaces/get.device.interface.ts index 526c199..7422ed2 100644 --- a/src/device/interfaces/get.device.interface.ts +++ b/src/device/interfaces/get.device.interface.ts @@ -64,3 +64,9 @@ export interface updateDeviceFirmwareInterface { result: boolean; msg: string; } +export interface getDeviceLogsInterface { + data: []; + startTime: string; + endTime: string; + deviceUuid?: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index db1b521..128ea73 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -15,9 +15,13 @@ import { GetDeviceDetailsFunctionsStatusInterface, GetDeviceDetailsInterface, controlDeviceInterface, + getDeviceLogsInterface, updateDeviceFirmwareInterface, } from '../interfaces/get.device.interface'; -import { GetDeviceByRoomUuidDto } from '../dtos/get.device.dto'; +import { + GetDeviceByRoomUuidDto, + GetDeviceLogsDto, +} from '../dtos/get.device.dto'; import { ControlDeviceDto } from '../dtos/control.device.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { DeviceRepository } from '@app/common/modules/device/repositories'; @@ -636,8 +640,6 @@ export class DeviceService { // Check if the device is a door lock (DL) if (device.productDevice.prodType === ProductType.DL) { - console.log('device', device.deviceTuyaUuid); - const doorLockInstructionsStatus = await this.getDevicesInstructionStatus(device.uuid); @@ -689,4 +691,54 @@ export class DeviceService { ); } } + async getDeviceLogs(deviceUuid: string, query: GetDeviceLogsDto) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails) { + throw new NotFoundException('Device Not Found'); + } + + const response = await this.getDeviceLogsTuya( + deviceDetails.deviceTuyaUuid, + query.code, + ); + + return { + deviceUuid, + ...response, + }; + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + HttpStatus.NOT_FOUND, + ); + } + } + async getDeviceLogsTuya( + deviceId: string, + code: string, + ): Promise { + try { + const now = Date.now(); + const twoWeeksAgo = now - 14 * 24 * 60 * 60 * 1000; + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${twoWeeksAgo}&end_time=${now}&codes=${code}&size=50`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + // Convert keys to camel case + const camelCaseResponse = convertKeysToCamelCase(response); + return { + startTime: twoWeeksAgo.toString(), + endTime: now.toString(), + data: camelCaseResponse.result.logs, + } as getDeviceLogsInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device logs from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } From df3a9ccc4f684f9497870389891dd09982159daf Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:05:27 +0300 Subject: [PATCH 41/49] Update Device Log Retrieval Logic and Interface --- src/device/interfaces/get.device.interface.ts | 4 ++-- src/device/services/device.service.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/device/interfaces/get.device.interface.ts b/src/device/interfaces/get.device.interface.ts index 7422ed2..880a976 100644 --- a/src/device/interfaces/get.device.interface.ts +++ b/src/device/interfaces/get.device.interface.ts @@ -66,7 +66,7 @@ export interface updateDeviceFirmwareInterface { } export interface getDeviceLogsInterface { data: []; - startTime: string; - endTime: string; + startTime: number; + endTime: number; deviceUuid?: string; } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 128ea73..0641d6f 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -721,18 +721,19 @@ export class DeviceService { ): Promise { try { const now = Date.now(); - const twoWeeksAgo = now - 14 * 24 * 60 * 60 * 1000; - const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${twoWeeksAgo}&end_time=${now}&codes=${code}&size=50`; + const oneHourAgo = now - 1 * 60 * 60 * 1000; + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${oneHourAgo}&end_time=${now}&codes=${code}&size=50`; const response = await this.tuya.request({ method: 'GET', path, }); // Convert keys to camel case const camelCaseResponse = convertKeysToCamelCase(response); + const logs = camelCaseResponse.result.logs ?? []; return { - startTime: twoWeeksAgo.toString(), - endTime: now.toString(), - data: camelCaseResponse.result.logs, + startTime: oneHourAgo, + endTime: now, + data: logs, } as getDeviceLogsInterface; } catch (error) { throw new HttpException( From d5e076c0561796213ae465b110d2cd8431a71076 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:21:01 +0300 Subject: [PATCH 42/49] Change log retrieval period to one day for device logs --- src/device/services/device.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 0641d6f..7f1bf44 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -721,8 +721,8 @@ export class DeviceService { ): Promise { try { const now = Date.now(); - const oneHourAgo = now - 1 * 60 * 60 * 1000; - const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${oneHourAgo}&end_time=${now}&codes=${code}&size=50`; + const oneDayAgo = now - 1 * 24 * 60 * 60 * 1000; + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${oneDayAgo}&end_time=${now}&codes=${code}&size=50`; const response = await this.tuya.request({ method: 'GET', path, @@ -731,7 +731,7 @@ export class DeviceService { const camelCaseResponse = convertKeysToCamelCase(response); const logs = camelCaseResponse.result.logs ?? []; return { - startTime: oneHourAgo, + startTime: oneDayAgo, endTime: now, data: logs, } as getDeviceLogsInterface; From 76f8f6ba70040518acb57b5a56e48fc99cc645c8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 27 Aug 2024 22:08:17 +0300 Subject: [PATCH 43/49] Add Visitor Password Management Module --- .../modules/visitor-password/dtos/index.ts | 1 + .../dtos/visitor-password.dto.ts | 15 +++++++++++ .../visitor-password/entities/index.ts | 1 + .../entities/visitor-password.entity.ts | 25 +++++++++++++++++++ .../visitor-password/repositories/index.ts | 1 + .../visitor-password.repository.ts | 10 ++++++++ .../visitor-password.repository.module.ts | 11 ++++++++ 7 files changed, 64 insertions(+) create mode 100644 libs/common/src/modules/visitor-password/dtos/index.ts create mode 100644 libs/common/src/modules/visitor-password/dtos/visitor-password.dto.ts create mode 100644 libs/common/src/modules/visitor-password/entities/index.ts create mode 100644 libs/common/src/modules/visitor-password/entities/visitor-password.entity.ts create mode 100644 libs/common/src/modules/visitor-password/repositories/index.ts create mode 100644 libs/common/src/modules/visitor-password/repositories/visitor-password.repository.ts create mode 100644 libs/common/src/modules/visitor-password/visitor-password.repository.module.ts diff --git a/libs/common/src/modules/visitor-password/dtos/index.ts b/libs/common/src/modules/visitor-password/dtos/index.ts new file mode 100644 index 0000000..daeb5d8 --- /dev/null +++ b/libs/common/src/modules/visitor-password/dtos/index.ts @@ -0,0 +1 @@ +export * from './visitor-password.dto'; diff --git a/libs/common/src/modules/visitor-password/dtos/visitor-password.dto.ts b/libs/common/src/modules/visitor-password/dtos/visitor-password.dto.ts new file mode 100644 index 0000000..10ed539 --- /dev/null +++ b/libs/common/src/modules/visitor-password/dtos/visitor-password.dto.ts @@ -0,0 +1,15 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class VisitorPasswordDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public authorizerUuid: string; + + @IsString() + @IsNotEmpty() + public passwordTuyaUuid: string; +} diff --git a/libs/common/src/modules/visitor-password/entities/index.ts b/libs/common/src/modules/visitor-password/entities/index.ts new file mode 100644 index 0000000..8b13691 --- /dev/null +++ b/libs/common/src/modules/visitor-password/entities/index.ts @@ -0,0 +1 @@ +export * from './visitor-password.entity'; diff --git a/libs/common/src/modules/visitor-password/entities/visitor-password.entity.ts b/libs/common/src/modules/visitor-password/entities/visitor-password.entity.ts new file mode 100644 index 0000000..f1c0ed6 --- /dev/null +++ b/libs/common/src/modules/visitor-password/entities/visitor-password.entity.ts @@ -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 { + @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) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/visitor-password/repositories/index.ts b/libs/common/src/modules/visitor-password/repositories/index.ts new file mode 100644 index 0000000..2b62574 --- /dev/null +++ b/libs/common/src/modules/visitor-password/repositories/index.ts @@ -0,0 +1 @@ +export * from './visitor-password.repository'; diff --git a/libs/common/src/modules/visitor-password/repositories/visitor-password.repository.ts b/libs/common/src/modules/visitor-password/repositories/visitor-password.repository.ts new file mode 100644 index 0000000..4414a5b --- /dev/null +++ b/libs/common/src/modules/visitor-password/repositories/visitor-password.repository.ts @@ -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 { + constructor(private dataSource: DataSource) { + super(VisitorPasswordEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/visitor-password/visitor-password.repository.module.ts b/libs/common/src/modules/visitor-password/visitor-password.repository.module.ts new file mode 100644 index 0000000..b6a2182 --- /dev/null +++ b/libs/common/src/modules/visitor-password/visitor-password.repository.module.ts @@ -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 {} From ef4541a371249617b42887f69acf0258b0c6151d Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 27 Aug 2024 22:08:31 +0300 Subject: [PATCH 44/49] Add Visitor Password Entity and Relationships --- libs/common/src/database/database.module.ts | 2 ++ libs/common/src/modules/user/entities/user.entity.ts | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 1934a18..ae01e55 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -18,6 +18,7 @@ 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'))), diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index acc9ad7..141f5de 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -25,6 +25,7 @@ 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 { @@ -109,6 +110,11 @@ export class UserEntity extends AbstractEntity { nullable: true, }) timezone: TimeZoneEntity; + @OneToMany( + () => VisitorPasswordEntity, + (visitorPassword) => visitorPassword.user, + ) + public visitorPasswords: VisitorPasswordEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); From 2a596d3b33b464e047f77cc6e2b58227baecee42 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 27 Aug 2024 22:09:33 +0300 Subject: [PATCH 45/49] Add Comprehensive Door Lock Module Enhancements --- src/door-lock/door.lock.module.ts | 16 +- src/door-lock/services/door.lock.service.ts | 236 ++++++++++++++------ 2 files changed, 182 insertions(+), 70 deletions(-) diff --git a/src/door-lock/door.lock.module.ts b/src/door-lock/door.lock.module.ts index 3b1920e..b72fad9 100644 --- a/src/door-lock/door.lock.module.ts +++ b/src/door-lock/door.lock.module.ts @@ -5,10 +5,24 @@ import { ConfigModule } from '@nestjs/config'; import { DeviceRepositoryModule } from '@app/common/modules/device'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { PasswordEncryptionService } from './services/encryption.services'; +import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; +import { DeviceService } from 'src/device/services'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [DoorLockController], - providers: [DoorLockService, PasswordEncryptionService, DeviceRepository], + providers: [ + DoorLockService, + PasswordEncryptionService, + DeviceRepository, + VisitorPasswordRepository, + DeviceService, + ProductRepository, + DeviceStatusFirebaseService, + SpaceRepository, + ], exports: [DoorLockService], }) export class DoorLockModule {} diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index e875f67..8f81c16 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -22,6 +22,8 @@ import { AddDoorLockOfflineTempMultipleTimeDto } from '../dtos/add.offline-temp. import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { UpdateDoorLockOfflineTempDto } from '../dtos/update.offline-temp.dto'; import { defaultDoorLockPass } from '@app/common/constants/default.door-lock-pass'; +import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; +import { DeviceService } from 'src/device/services'; @Injectable() export class DoorLockService { @@ -29,7 +31,9 @@ export class DoorLockService { constructor( private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, + private readonly deviceService: DeviceService, private readonly passwordEncryptionService: PasswordEncryptionService, + private readonly visitorPasswordRepository: VisitorPasswordRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -105,6 +109,10 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } + const deviceTuyaDetails = + await this.deviceService.getDeviceDetailsByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); const passwords = await this.getTemporaryOfflinePasswordsTuya( deviceDetails.deviceTuyaUuid, 'multiple', @@ -115,23 +123,37 @@ export class DoorLockService { } if (passwords.result.records.length > 0) { return fromVisitor - ? convertKeysToCamelCase(passwords.result.records).map((password) => { - const timestampInSeconds = Math.floor(Date.now() / 1000); - return { - passwordId: `${password.pwdId}`, - invalidTime: `${password.gmtExpired}`, - effectiveTime: `${password.gmtStart}`, - passwordCreated: `${password.gmtCreate}`, - createdTime: password.pwdName, - passwordType: 'OFFLINE_MULTIPLE', - deviceUuid: doorLockUuid, - passwordStatus: isExpired - ? 'EXPIRED' - : timestampInSeconds > password.effectiveTime - ? 'EFFECTIVE' - : 'TO_BE_EFFECTIVE', - }; - }) + ? Promise.all( + convertKeysToCamelCase(passwords.result.records).map( + async (password) => { + const passwordFromDB = await this.getVisitorPasswordFromDB( + password.pwdId, + ); + const timestampInSeconds = Math.floor(Date.now() / 1000); + return { + passwordId: `${password.pwdId}`, + invalidTime: `${password.gmtExpired}`, + effectiveTime: `${password.gmtStart}`, + passwordCreated: `${password.gmtCreate}`, + createdTime: password.pwdName, + passwordType: 'OFFLINE_MULTIPLE', + deviceUuid: doorLockUuid, + deviceName: deviceTuyaDetails.name, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', + authorizerEmail: passwordFromDB + ? passwordFromDB.user.email + : 'OTHER', + authorizerDate: passwordFromDB + ? `${Math.floor(new Date(passwordFromDB.createdAt).getTime() / 1000)}` + : '', + }; + }, + ), + ) : convertKeysToCamelCase(passwords.result.records); } return passwords; @@ -158,6 +180,10 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } + const deviceTuyaDetails = + await this.deviceService.getDeviceDetailsByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); const passwords = await this.getTemporaryOfflinePasswordsTuya( deviceDetails.deviceTuyaUuid, 'once', @@ -168,23 +194,38 @@ export class DoorLockService { } if (passwords.result.records.length > 0) { return fromVisitor - ? convertKeysToCamelCase(passwords.result.records).map((password) => { - const timestampInSeconds = Math.floor(Date.now() / 1000); - return { - passwordId: `${password.pwdId}`, - invalidTime: `${password.gmtExpired}`, - effectiveTime: `${password.gmtStart}`, - createdTime: `${password.gmtCreate}`, - passwordName: password.pwdName, - passwordType: 'OFFLINE_ONETIME', - deviceUuid: doorLockUuid, - passwordStatus: isExpired - ? 'EXPIRED' - : timestampInSeconds > password.gmtStart - ? 'EFFECTIVE' - : 'TO_BE_EFFECTIVE', - }; - }) + ? Promise.all( + convertKeysToCamelCase(passwords.result.records).map( + async (password) => { + const passwordFromDB = await this.getVisitorPasswordFromDB( + password.pwdId, + ); + + const timestampInSeconds = Math.floor(Date.now() / 1000); + return { + passwordId: `${password.pwdId}`, + invalidTime: `${password.gmtExpired}`, + effectiveTime: `${password.gmtStart}`, + createdTime: `${password.gmtCreate}`, + passwordName: password.pwdName, + passwordType: 'OFFLINE_ONETIME', + deviceUuid: doorLockUuid, + deviceName: deviceTuyaDetails.name, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.gmtStart + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', + authorizerEmail: passwordFromDB + ? passwordFromDB.user.email + : 'OTHER', + authorizerDate: passwordFromDB + ? `${Math.floor(new Date(passwordFromDB.createdAt).getTime() / 1000)}` + : '', + }; + }, + ), + ) : convertKeysToCamelCase(passwords.result.records); } @@ -212,6 +253,10 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } + const deviceTuyaDetails = + await this.deviceService.getDeviceDetailsByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); const passwords = await this.getOnlineTemporaryPasswordsMultipleTuya( deviceDetails.deviceTuyaUuid, isExpired, @@ -240,24 +285,36 @@ export class DoorLockService { }); return fromVisitor - ? convertKeysToCamelCase(passwordFiltered).map((password) => { - const timestampInSeconds = Math.floor(Date.now() / 1000); - return { - passwordId: `${password.id}`, - invalidTime: `${password.invalidTime}`, - effectiveTime: `${password.effectiveTime}`, - createdTime: '', - scheduleList: password?.scheduleList, - passwordName: password.name, - passwordType: 'ONLINE_MULTIPLE', - deviceUuid: doorLockUuid, - passwordStatus: isExpired - ? 'EXPIRED' - : timestampInSeconds > password.effectiveTime - ? 'EFFECTIVE' - : 'TO_BE_EFFECTIVE', - }; - }) + ? Promise.all( + convertKeysToCamelCase(passwordFiltered).map(async (password) => { + const passwordFromDB = await this.getVisitorPasswordFromDB( + password.id, + ); + const timestampInSeconds = Math.floor(Date.now() / 1000); + return { + passwordId: `${password.id}`, + invalidTime: `${password.invalidTime}`, + effectiveTime: `${password.effectiveTime}`, + createdTime: '', + scheduleList: password?.scheduleList, + passwordName: password.name, + passwordType: 'ONLINE_MULTIPLE', + deviceUuid: doorLockUuid, + deviceName: deviceTuyaDetails.name, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', + authorizerEmail: passwordFromDB + ? passwordFromDB.user.email + : 'OTHER', + authorizerDate: passwordFromDB + ? `${Math.floor(new Date(passwordFromDB.createdAt).getTime() / 1000)}` + : '', + }; + }), + ) : convertKeysToCamelCase(passwordFiltered); } if (fromVisitor) { @@ -271,12 +328,34 @@ export class DoorLockService { ); } } + async getVisitorPasswordFromDB(passwordId: string) { + try { + return await this.visitorPasswordRepository.findOne({ + where: { + passwordTuyaUuid: passwordId, + }, + relations: ['user'], + select: { + user: { + email: true, + }, + }, + }); + } catch (error) { + throw new HttpException( + error.message || 'Error retrieving visitor password from database', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getOnlineTemporaryPasswordsOneTime( doorLockUuid: string, fromVisitor?: boolean, isExpired?: boolean, ) { try { + // Retrieve device details const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { @@ -287,11 +366,17 @@ export class DoorLockService { HttpStatus.BAD_REQUEST, ); } + const deviceTuyaDetails = + await this.deviceService.getDeviceDetailsByDeviceIdTuya( + deviceDetails.deviceTuyaUuid, + ); + // Get online temporary passwords const passwords = await this.getOnlineTemporaryPasswordsMultipleTuya( deviceDetails.deviceTuyaUuid, isExpired, ); if (passwords.result?.length > 0) { + // Filter and map the passwords const passwordFiltered = passwords.result .filter((item) => item.type === 1) .map((password: any) => { @@ -314,24 +399,37 @@ export class DoorLockService { return password; }); + // Process passwords with optimized query handling return fromVisitor - ? convertKeysToCamelCase(passwordFiltered).map((password) => { - const timestampInSeconds = Math.floor(Date.now() / 1000); - return { - passwordId: `${password.id}`, - invalidTime: `${password.invalidTime}`, - effectiveTime: `${password.effectiveTime}`, - createdTime: '', - passwordName: password.name, - passwordType: 'ONLINE_ONETIME', - deviceUuid: doorLockUuid, - passwordStatus: isExpired - ? 'EXPIRED' - : timestampInSeconds > password.effectiveTime - ? 'EFFECTIVE' - : 'TO_BE_EFFECTIVE', - }; - }) + ? Promise.all( + convertKeysToCamelCase(passwordFiltered).map(async (password) => { + const passwordFromDB = await this.getVisitorPasswordFromDB( + password.id, + ); + const timestampInSeconds = Math.floor(Date.now() / 1000); + return { + passwordId: `${password.id}`, + invalidTime: `${password.invalidTime}`, + effectiveTime: `${password.effectiveTime}`, + createdTime: '', + passwordName: password.name, + passwordType: 'ONLINE_ONETIME', + deviceUuid: doorLockUuid, + deviceName: deviceTuyaDetails.name, + passwordStatus: isExpired + ? 'EXPIRED' + : timestampInSeconds > password.effectiveTime + ? 'EFFECTIVE' + : 'TO_BE_EFFECTIVE', + authorizerEmail: passwordFromDB + ? passwordFromDB.user.email + : 'OTHER', + authorizerDate: passwordFromDB + ? `${Math.floor(new Date(passwordFromDB.createdAt).getTime() / 1000)}` + : '', + }; + }), + ) : convertKeysToCamelCase(passwordFiltered); } if (fromVisitor) { From e8e68f7dfc56402028b8d6bef4043a39ae058661 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 27 Aug 2024 22:10:20 +0300 Subject: [PATCH 46/49] Add User UUID to Temporary Passwords and Save to Repository --- .../visitor-password.controller.ts | 13 +++++ .../services/visitor-password.service.ts | 50 ++++++++++++++++--- .../visitor-password.module.ts | 2 + 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index f1a84bb..bdd4324 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -7,6 +7,7 @@ import { HttpStatus, UseGuards, Get, + Req, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { @@ -31,11 +32,14 @@ export class VisitorPasswordController { @Post('temporary-password/online/multiple-time') async addOnlineTemporaryPasswordMultipleTime( @Body() addDoorLockOnlineMultipleDto: AddDoorLockOnlineMultipleDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; const temporaryPasswords = await this.visitorPasswordService.addOnlineTemporaryPasswordMultipleTime( addDoorLockOnlineMultipleDto, + userUuid, ); return { @@ -54,11 +58,14 @@ export class VisitorPasswordController { @Post('temporary-password/online/one-time') async addOnlineTemporaryPassword( @Body() addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; const temporaryPasswords = await this.visitorPasswordService.addOnlineTemporaryPasswordOneTime( addDoorLockOnlineOneTimeDto, + userUuid, ); return { @@ -77,11 +84,14 @@ export class VisitorPasswordController { @Post('temporary-password/offline/one-time') async addOfflineOneTimeTemporaryPassword( @Body() addDoorLockOfflineOneTimeDto: AddDoorLockOfflineOneTimeDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; const temporaryPassword = await this.visitorPasswordService.addOfflineOneTimeTemporaryPassword( addDoorLockOfflineOneTimeDto, + userUuid, ); return { @@ -101,11 +111,14 @@ export class VisitorPasswordController { async addOfflineMultipleTimeTemporaryPassword( @Body() addDoorLockOfflineMultipleDto: AddDoorLockOfflineMultipleDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; const temporaryPassword = await this.visitorPasswordService.addOfflineMultipleTimeTemporaryPassword( addDoorLockOfflineMultipleDto, + userUuid, ); return { diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index c18ee84..f15b8e4 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -1,3 +1,4 @@ +import { VisitorPasswordRepository } from './../../../libs/common/src/modules/visitor-password/repositories/visitor-password.repository'; import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; @@ -26,6 +27,7 @@ export class VisitorPasswordService { constructor( private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, + private readonly visitorPasswordRepository: VisitorPasswordRepository, private readonly emailService: EmailService, private readonly doorLockService: DoorLockService, private readonly deviceService: DeviceService, @@ -43,6 +45,7 @@ export class VisitorPasswordService { async addOfflineMultipleTimeTemporaryPassword( addDoorLockOfflineMultipleDto: AddDoorLockOfflineMultipleDto, + userUuid: string, ) { try { const deviceResults = await Promise.allSettled( @@ -87,7 +90,14 @@ export class VisitorPasswordService { emailBody, ); } - + if (createMultipleOfflinePass.result.offline_temp_password_id) { + await this.visitorPasswordRepository.save({ + passwordTuyaUuid: `${createMultipleOfflinePass.result.offline_temp_password_id}`, + user: { + uuid: userUuid, + }, + }); + } return { success: true, result: createMultipleOfflinePass.result, @@ -156,6 +166,7 @@ export class VisitorPasswordService { async addOfflineOneTimeTemporaryPassword( addDoorLockOfflineOneTimeDto: AddDoorLockOfflineOneTimeDto, + userUuid: string, ) { try { const deviceResults = await Promise.allSettled( @@ -200,7 +211,14 @@ export class VisitorPasswordService { emailBody, ); } - + if (createOnceOfflinePass.result.offline_temp_password_id) { + await this.visitorPasswordRepository.save({ + passwordTuyaUuid: `${createOnceOfflinePass.result.offline_temp_password_id}`, + user: { + uuid: userUuid, + }, + }); + } return { success: true, result: createOnceOfflinePass.result, @@ -299,6 +317,7 @@ export class VisitorPasswordService { } async addOnlineTemporaryPasswordMultipleTime( addDoorLockOnlineMultipleDto: AddDoorLockOnlineMultipleDto, + userUuid: string, ) { try { const deviceResults = await Promise.allSettled( @@ -343,7 +362,14 @@ export class VisitorPasswordService { emailBody, ); } - + if (createPass.result.id) { + await this.visitorPasswordRepository.save({ + passwordTuyaUuid: `${createPass.result.id}`, + user: { + uuid: userUuid, + }, + }); + } return { success: true, id: createPass.result.id, @@ -431,18 +457,18 @@ export class VisitorPasswordService { this.doorLockService .getOnlineTemporaryPasswordsMultiple(deviceId.uuid, true, true) .catch(() => {}), - this.doorLockService - .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, true) - .catch(() => {}), this.doorLockService .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, false) .catch(() => {}), this.doorLockService - .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, true) + .getOfflineOneTimeTemporaryPasswords(deviceId.uuid, true, true) .catch(() => {}), this.doorLockService .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, false) .catch(() => {}), + this.doorLockService + .getOfflineMultipleTimeTemporaryPasswords(deviceId.uuid, true, true) + .catch(() => {}), ); }); return (await Promise.all(data)).flat().filter((datum) => { @@ -476,6 +502,7 @@ export class VisitorPasswordService { async addOnlineTemporaryPasswordOneTime( addDoorLockOnlineOneTimeDto: AddDoorLockOnlineOneTimeDto, + userUuid: string, ) { try { const deviceResults = await Promise.allSettled( @@ -519,7 +546,14 @@ export class VisitorPasswordService { emailBody, ); } - + if (createPass.result.id) { + await this.visitorPasswordRepository.save({ + passwordTuyaUuid: `${createPass.result.id}`, + user: { + uuid: userUuid, + }, + }); + } return { success: true, id: createPass.result.id, diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index 33454a3..19e61ea 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -11,6 +11,7 @@ import { DeviceService } from 'src/device/services'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], @@ -23,6 +24,7 @@ import { SpaceRepository } from '@app/common/modules/space/repositories'; DeviceStatusFirebaseService, SpaceRepository, DeviceRepository, + VisitorPasswordRepository, ], exports: [VisitorPasswordService], }) From b9ab26441a3a602c850caae86a9f5ec887275573 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:35:56 +0300 Subject: [PATCH 47/49] Change log retrieval period to one hour in DeviceService --- src/device/services/device.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 7f1bf44..0641d6f 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -721,8 +721,8 @@ export class DeviceService { ): Promise { try { const now = Date.now(); - const oneDayAgo = now - 1 * 24 * 60 * 60 * 1000; - const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${oneDayAgo}&end_time=${now}&codes=${code}&size=50`; + const oneHourAgo = now - 1 * 60 * 60 * 1000; + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${oneHourAgo}&end_time=${now}&codes=${code}&size=50`; const response = await this.tuya.request({ method: 'GET', path, @@ -731,7 +731,7 @@ export class DeviceService { const camelCaseResponse = convertKeysToCamelCase(response); const logs = camelCaseResponse.result.logs ?? []; return { - startTime: oneDayAgo, + startTime: oneHourAgo, endTime: now, data: logs, } as getDeviceLogsInterface; From 77b374f0b2963d04811d209924082873cb15c844 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:02:53 +0300 Subject: [PATCH 48/49] Refactor DoorLockService to Correctly Map Password Creation Time and Name --- src/door-lock/services/door.lock.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 8f81c16..741663f 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -134,8 +134,8 @@ export class DoorLockService { passwordId: `${password.pwdId}`, invalidTime: `${password.gmtExpired}`, effectiveTime: `${password.gmtStart}`, - passwordCreated: `${password.gmtCreate}`, - createdTime: password.pwdName, + createdTime: `${password.gmtCreate}`, + passwordName: password.pwdName, passwordType: 'OFFLINE_MULTIPLE', deviceUuid: doorLockUuid, deviceName: deviceTuyaDetails.name, From c42d82648b2f748f66d06a3e3a2ecc9749d3e86d Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:20:32 +0300 Subject: [PATCH 49/49] Fix password status check in DoorLockService --- src/door-lock/services/door.lock.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 741663f..5864c5d 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -141,7 +141,7 @@ export class DoorLockService { deviceName: deviceTuyaDetails.name, passwordStatus: isExpired ? 'EXPIRED' - : timestampInSeconds > password.effectiveTime + : timestampInSeconds > password.gmtStart ? 'EFFECTIVE' : 'TO_BE_EFFECTIVE', authorizerEmail: passwordFromDB