From 70aae9899d3618af576ed7e443ec254574e58e65 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 22 Aug 2024 09:12:19 +0400 Subject: [PATCH 01/34] chore: setup initial Swagger documentation configuration --- libs/common/src/constants/controller-route.ts | 11 ++ package-lock.json | 182 +----------------- src/region/controllers/region.controller.ts | 9 +- 3 files changed, 28 insertions(+), 174 deletions(-) create mode 100644 libs/common/src/constants/controller-route.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts new file mode 100644 index 0000000..a1e43ee --- /dev/null +++ b/libs/common/src/constants/controller-route.ts @@ -0,0 +1,11 @@ +export class ControllerRoute { + static REGION = class { + public static readonly ROUTE = 'region'; + static ACTIONS = class { + public static readonly GET_REGIONS_SUMMARY = 'Get list of all regions'; + + public static readonly GET_REGIONS_DESCRIPTION = + 'Retrieve the list of all regions registered in Syncrow.'; + }; + }; +} diff --git a/package-lock.json b/package-lock.json index db7b28d..64662e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2258,6 +2258,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "optional": true, + "peer": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2490,17 +2492,6 @@ } } }, - "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", @@ -3138,7 +3129,9 @@ "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==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "optional": true, + "peer": true }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -4131,14 +4124,6 @@ "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", @@ -7666,14 +7651,6 @@ "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", @@ -7911,7 +7888,9 @@ "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==" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true, + "peer": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -8146,125 +8125,6 @@ "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", @@ -8304,25 +8164,6 @@ "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", @@ -9956,11 +9797,6 @@ "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", @@ -10045,6 +9881,8 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "peer": true, "dependencies": { "memory-pager": "^1.0.2" } diff --git a/src/region/controllers/region.controller.ts b/src/region/controllers/region.controller.ts index d914642..8df4a11 100644 --- a/src/region/controllers/region.controller.ts +++ b/src/region/controllers/region.controller.ts @@ -1,16 +1,21 @@ import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common'; import { RegionService } from '../services/region.service'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; +import { ControllerRoute } from '@app/common/constants/controller-route'; @ApiTags('Region Module') @Controller({ version: '1', - path: 'region', + path: ControllerRoute.REGION.ROUTE, }) export class RegionController { constructor(private readonly regionService: RegionService) {} @Get() + @ApiOperation({ + summary: ControllerRoute.REGION.ACTIONS.GET_REGIONS_SUMMARY, + description: ControllerRoute.REGION.ACTIONS.GET_REGIONS_DESCRIPTION, + }) async getAllRegions() { try { return await this.regionService.getAllRegions(); From bc0a8627c4c6fb75c9f625215f015790eb6dbdf8 Mon Sep 17 00:00:00 2001 From: yousef-alkhrissat Date: Sat, 24 Aug 2024 13:09:21 +0300 Subject: [PATCH 02/34] added google login --- libs/common/src/auth/services/auth.service.ts | 31 ++++++++++++++++++- src/auth/dtos/user-login.dto.ts | 4 +++ src/auth/services/user-auth.service.ts | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index af9d047..5808ce7 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as argon2 from 'argon2'; import { HelperHashService } from '../../helper/services'; @@ -6,6 +6,7 @@ import { UserRepository } from '../../../../common/src/modules/user/repositories import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository'; import { UserSessionEntity } from '../../../../common/src/modules/session/entities'; import { ConfigService } from '@nestjs/config'; +import axios from 'axios'; @Injectable() export class AuthService { @@ -80,8 +81,18 @@ export class AuthService { type: user.type, sessionId: user.sessionId, roles: user?.roles, + googleCode: user.googleCode, }; + if (payload.googleCode) { + const profile = await this.getProfile(payload.googleCode); + user = await this.userRepository.findOne({ + where: { email: profile.data.email }, + }); + if (!user) { + throw new UnauthorizedException('wrong credentials'); + } + } const tokens = await this.getTokens(payload); await this.updateRefreshToken(user.uuid, tokens.refreshToken); return tokens; @@ -100,4 +111,22 @@ export class AuthService { hashData(data: string) { return argon2.hash(data); } + + async getProfile(googleCode: string) { + try { + const response = await axios.post('https://oauth2.googleapis.com/token', { + client_id: process.env.GOOGLE_CLIENT_ID, + client_secret: process.env.GOOGLE_CLIENT_SECRET, + code: googleCode, + grant_type: 'authorization_code', + redirect_uri: 'http://localhost:3000/auth/google/callback', + }); + return axios.get( + `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${response.data.access_token}`, + ); + } catch (error) { + console.error('Failed to get profile:', error); + throw new UnauthorizedException('google login failed'); + } + } } diff --git a/src/auth/dtos/user-login.dto.ts b/src/auth/dtos/user-login.dto.ts index 1f3662b..dedb08f 100644 --- a/src/auth/dtos/user-login.dto.ts +++ b/src/auth/dtos/user-login.dto.ts @@ -16,4 +16,8 @@ export class UserLoginDto { @IsString() @IsOptional() regionUuid?: string; + + @IsOptional() + @IsString() + googleCode?: string; } diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 7b539e2..88e989e 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -122,6 +122,7 @@ export class UserAuthService { return { uuid: role.uuid, type: role.roleType.type }; }), sessionId: session[1].uuid, + googleCode: data.googleCode, }); } catch (error) { throw new BadRequestException('Invalid credentials'); From 20156568e23737eb9936c46dac79340c08f954d6 Mon Sep 17 00:00:00 2001 From: yousef-khriasat-uba Date: Sun, 25 Aug 2024 10:53:27 +0300 Subject: [PATCH 03/34] added env example --- .env.example | 3 +++ libs/common/src/auth/services/auth.service.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 54773c4..8fb1460 100644 --- a/.env.example +++ b/.env.example @@ -90,3 +90,6 @@ FIREBASE_DATABASE_URL= OTP_LIMITER= +GOOGLE_CLIENT_ID= + +GOOGLE_CLIENT_SECRET= \ No newline at end of file diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 5808ce7..3174b11 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -115,8 +115,8 @@ export class AuthService { async getProfile(googleCode: string) { try { const response = await axios.post('https://oauth2.googleapis.com/token', { - client_id: process.env.GOOGLE_CLIENT_ID, - client_secret: process.env.GOOGLE_CLIENT_SECRET, + client_id: this.configService('GOOGLE_CLIENT_ID'), + client_secret: this.configService('GOOGLE_CLIENT_SECRET'), code: googleCode, grant_type: 'authorization_code', redirect_uri: 'http://localhost:3000/auth/google/callback', From 1fc04b5c789e0c1f65a249808415f387e8cc9d49 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:32:51 +0300 Subject: [PATCH 04/34] finished devices batch control endpoint --- src/device/controllers/device.controller.ts | 22 +++++- src/device/dtos/control.device.dto.ts | 24 +++++- src/device/services/device.service.ts | 83 ++++++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index bc399b6..8e7e6d6 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -18,7 +18,10 @@ import { GetDeviceByRoomUuidDto, GetDeviceLogsDto, } from '../dtos/get.device.dto'; -import { ControlDeviceDto } from '../dtos/control.device.dto'; +import { + ControlDeviceDto, + BatchControlDevicesDto, +} from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; import { CheckUserHaveControllablePermission } from 'src/guards/user.device.controllable.permission.guard'; @@ -255,4 +258,21 @@ export class DeviceController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('control/batch') + async batchControlDevices( + @Body() batchControlDevicesDto: BatchControlDevicesDto, + ) { + try { + return await this.deviceService.batchControlDevices( + batchControlDevicesDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index ab2125d..9bb1d40 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsArray, IsNotEmpty, IsString } from 'class-validator'; export class ControlDeviceDto { @ApiProperty({ @@ -16,3 +16,25 @@ export class ControlDeviceDto { @IsNotEmpty() public value: any; } +export class BatchControlDevicesDto { + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; + @ApiProperty({ + description: 'code', + required: true, + }) + @IsString() + @IsNotEmpty() + public code: string; + @ApiProperty({ + description: 'value', + required: true, + }) + @IsNotEmpty() + public value: any; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 0641d6f..93433a3 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -22,7 +22,10 @@ import { GetDeviceByRoomUuidDto, GetDeviceLogsDto, } from '../dtos/get.device.dto'; -import { ControlDeviceDto } from '../dtos/control.device.dto'; +import { + BatchControlDevicesDto, + ControlDeviceDto, +} from '../dtos/control.device.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { PermissionType } from '@app/common/constants/permission-type.enum'; @@ -308,7 +311,85 @@ export class DeviceService { ); } } + async batchControlDevices(batchControlDevicesDto: BatchControlDevicesDto) { + const { devicesUuid } = batchControlDevicesDto; + try { + // Check if all devices have the same product UUID + await this.checkAllDevicesHaveSameProductUuid(devicesUuid); + + // Perform all operations concurrently + const results = await Promise.allSettled( + devicesUuid.map(async (deviceUuid) => { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + const result = await this.controlDeviceTuya( + deviceDetails.deviceTuyaUuid, + batchControlDevicesDto, + ); + return { deviceUuid, result }; + }), + ); + + // Separate successful and failed operations + const successResults = []; + const failedResults = []; + + for (const result of results) { + if (result.status === 'fulfilled') { + const { deviceUuid, result: operationResult } = result.value; + + if (operationResult.success) { + // Add to success results if operationResult.success is true + successResults.push({ deviceUuid, result: operationResult }); + } else { + // Add to failed results if operationResult.success is false + failedResults.push({ deviceUuid, error: operationResult.msg }); + } + } else { + // Add to failed results if promise is rejected + failedResults.push({ + deviceUuid: devicesUuid[results.indexOf(result)], + error: result.reason.message, + }); + } + } + + return { successResults, failedResults }; + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } + + async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { + const firstDevice = await this.deviceRepository.findOne({ + where: { uuid: deviceUuids[0] }, + relations: ['productDevice'], + }); + + if (!firstDevice) { + throw new BadRequestException('First device not found'); + } + + const firstProductType = firstDevice.productDevice.prodType; + + for (let i = 1; i < deviceUuids.length; i++) { + const device = await this.deviceRepository.findOne({ + where: { uuid: deviceUuids[i] }, + relations: ['productDevice'], + }); + + if (!device) { + throw new BadRequestException(`Device ${deviceUuids[i]} not found`); + } + + if (device.productDevice.prodType !== firstProductType) { + throw new BadRequestException(`Devices have different product types`); + } + } + } async getDeviceDetailsByDeviceId(deviceUuid: string, userUuid: string) { try { const userDevicePermission = await this.getUserDevicePermission( From 21d31538be56e9ee565c577ecf31b313ec435340 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 Sep 2024 16:31:15 +0300 Subject: [PATCH 05/34] added otp handling user --- src/auth/services/user-auth.service.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 7b539e2..976731b 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -225,7 +225,15 @@ export class UserAuthService { }); if (!otp) { - throw new BadRequestException('this email is not registered'); + const user = await this.userRepository.findOne({ + where: { + email: data.email, + }, + }); + if (user) { + throw new BadRequestException('this email is not registered'); + } + throw new BadRequestException('You entered wrong otp'); } if (otp.otpCode !== data.otpCode) { From eebb676dc8fb3e4ea6ddc55a0e76cc40f02b482f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 Sep 2024 16:32:26 +0300 Subject: [PATCH 06/34] added otp handling user --- 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 976731b..e709a51 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -230,7 +230,7 @@ export class UserAuthService { email: data.email, }, }); - if (user) { + if (!user) { throw new BadRequestException('this email is not registered'); } throw new BadRequestException('You entered wrong otp'); From 0df4af175f7f16d002771ec64fdcbd402fb14ddb Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:02:37 +0300 Subject: [PATCH 07/34] add gangs types --- .../common/src/constants/product-type.enum.ts | 4 +- package-lock.json | 182 +----------------- 2 files changed, 13 insertions(+), 173 deletions(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index f0d24ad..bff76e3 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -4,5 +4,7 @@ export enum ProductType { CPS = 'CPS', DL = 'DL', WPS = 'WPS', - TH_G = '3G', + THREE_G = '3G', + TWO_G = '2G', + ONE_G = '1G', } diff --git a/package-lock.json b/package-lock.json index db7b28d..64662e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2258,6 +2258,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "optional": true, + "peer": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2490,17 +2492,6 @@ } } }, - "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", @@ -3138,7 +3129,9 @@ "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==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "optional": true, + "peer": true }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -4131,14 +4124,6 @@ "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", @@ -7666,14 +7651,6 @@ "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", @@ -7911,7 +7888,9 @@ "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==" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true, + "peer": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -8146,125 +8125,6 @@ "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", @@ -8304,25 +8164,6 @@ "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", @@ -9956,11 +9797,6 @@ "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", @@ -10045,6 +9881,8 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "peer": true, "dependencies": { "memory-pager": "^1.0.2" } From d962d4a7b3fd0676e2e3bf45df4b960f8c16d710 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:03:30 +0300 Subject: [PATCH 08/34] finished schedule module --- .../src/helper/convertTimestampToDubaiTime.ts | 23 ++ libs/common/src/helper/getScheduleStatus.ts | 27 ++ src/app.module.ts | 2 + src/schedule/controllers/index.ts | 1 + .../controllers/schedule.controller.ts | 116 ++++++++ src/schedule/dtos/add.schedule.dto.ts | 84 ++++++ src/schedule/dtos/index.ts | 1 + .../interfaces/get.schedule.interface.ts | 10 + src/schedule/schedule.module.ts | 13 + src/schedule/services/index.ts | 1 + src/schedule/services/schedule.service.ts | 281 ++++++++++++++++++ 11 files changed, 559 insertions(+) create mode 100644 libs/common/src/helper/convertTimestampToDubaiTime.ts create mode 100644 libs/common/src/helper/getScheduleStatus.ts create mode 100644 src/schedule/controllers/index.ts create mode 100644 src/schedule/controllers/schedule.controller.ts create mode 100644 src/schedule/dtos/add.schedule.dto.ts create mode 100644 src/schedule/dtos/index.ts create mode 100644 src/schedule/interfaces/get.schedule.interface.ts create mode 100644 src/schedule/schedule.module.ts create mode 100644 src/schedule/services/index.ts create mode 100644 src/schedule/services/schedule.service.ts diff --git a/libs/common/src/helper/convertTimestampToDubaiTime.ts b/libs/common/src/helper/convertTimestampToDubaiTime.ts new file mode 100644 index 0000000..ade7c29 --- /dev/null +++ b/libs/common/src/helper/convertTimestampToDubaiTime.ts @@ -0,0 +1,23 @@ +export function convertTimestampToDubaiTime(timestamp) { + // Convert timestamp to milliseconds + const date = new Date(timestamp * 1000); + + // Convert to Dubai time (UTC+4) + const dubaiTimeOffset = 4 * 60; // 4 hours in minutes + const dubaiTime = new Date(date.getTime() + dubaiTimeOffset * 60 * 1000); + + // Format the date as YYYYMMDD + const year = dubaiTime.getUTCFullYear(); + const month = String(dubaiTime.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-based + const day = String(dubaiTime.getUTCDate()).padStart(2, '0'); + + // Format the time as HH:MM (24-hour format) + const hours = String(dubaiTime.getUTCHours()).padStart(2, '0'); + const minutes = String(dubaiTime.getUTCMinutes()).padStart(2, '0'); + + // Return formatted date and time + return { + date: `${year}${month}${day}`, + time: `${hours}:${minutes}`, + }; +} diff --git a/libs/common/src/helper/getScheduleStatus.ts b/libs/common/src/helper/getScheduleStatus.ts new file mode 100644 index 0000000..e77496c --- /dev/null +++ b/libs/common/src/helper/getScheduleStatus.ts @@ -0,0 +1,27 @@ +export function getScheduleStatus(daysEnabled: string[]): string { + const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + const schedule: string[] = Array(7).fill('0'); + + daysEnabled.forEach((day) => { + const index: number = daysMap.indexOf(day); + if (index !== -1) { + schedule[index] = '1'; + } + }); + + return schedule.join(''); +} +export function getEnabledDays(schedule: string): string[] { + const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + const enabledDays: string[] = []; + + // Iterate through the schedule string + for (let i = 0; i < schedule.length; i++) { + if (schedule[i] === '1') { + enabledDays.push(daysMap[i]); + } + } + + return enabledDays; +} diff --git a/src/app.module.ts b/src/app.module.ts index 2970185..29d07ac 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,6 +24,7 @@ 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'; +import { ScheduleModule } from './schedule/schedule.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -50,6 +51,7 @@ import { VisitorPasswordModule } from './vistor-password/visitor-password.module RegionModule, TimeZoneModule, VisitorPasswordModule, + ScheduleModule, ], controllers: [AuthenticationController], providers: [ diff --git a/src/schedule/controllers/index.ts b/src/schedule/controllers/index.ts new file mode 100644 index 0000000..edd3a1d --- /dev/null +++ b/src/schedule/controllers/index.ts @@ -0,0 +1 @@ +export * from './schedule.controller'; diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts new file mode 100644 index 0000000..94ad414 --- /dev/null +++ b/src/schedule/controllers/schedule.controller.ts @@ -0,0 +1,116 @@ +import { ScheduleService } from '../services/schedule.service'; +import { + Body, + Controller, + Get, + Post, + Param, + HttpException, + HttpStatus, + UseGuards, + Put, + Delete, +} from '@nestjs/common'; +import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; + +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; + +@ApiTags('Schedule Module') +@Controller({ + version: '1', + path: 'schedule', +}) +export class ScheduleController { + constructor(private readonly scheduleService: ScheduleService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post(':deviceUuid') + async addDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Body() addScheduleDto: AddScheduleDto, + ) { + try { + const device = await this.scheduleService.addDeviceSchedule( + deviceUuid, + addScheduleDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule added successfully', + data: device, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':deviceUuid/category/:category') + async getDeviceScheduleByCategory( + @Param('deviceUuid') deviceUuid: string, + @Param('category') category: string, + ) { + try { + return await this.scheduleService.getDeviceScheduleByCategory( + deviceUuid, + category, + ); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete(':deviceUuid/:scheduleId') + async deleteDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Param('scheduleId') scheduleId: string, + ) { + try { + await this.scheduleService.deleteDeviceSchedule(deviceUuid, scheduleId); + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule deleted successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put('enable/:deviceUuid') + async enableDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Body() enableScheduleDto: EnableScheduleDto, + ) { + try { + await this.scheduleService.enableDeviceSchedule( + deviceUuid, + enableScheduleDto, + ); + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule updated successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/schedule/dtos/add.schedule.dto.ts b/src/schedule/dtos/add.schedule.dto.ts new file mode 100644 index 0000000..c51a027 --- /dev/null +++ b/src/schedule/dtos/add.schedule.dto.ts @@ -0,0 +1,84 @@ +import { WorkingDays } from '@app/common/constants/working-days'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsArray, + IsBoolean, + IsEnum, + IsNotEmpty, + IsString, + ValidateNested, +} from 'class-validator'; +export class FunctionDto { + @ApiProperty({ + description: 'code', + required: true, + }) + @IsString() + @IsNotEmpty() + public code: string; + + @ApiProperty({ + description: 'value', + required: true, + }) + @IsNotEmpty() + public value: any; +} + +// Update the main DTO class +export class AddScheduleDto { + @ApiProperty({ + description: 'category', + required: true, + }) + @IsString() + @IsNotEmpty() + public category: string; + + @ApiProperty({ + description: 'time', + required: true, + }) + @IsString() + @IsNotEmpty() + public time: string; + + @ApiProperty({ + description: 'function', + required: true, + type: FunctionDto, + }) + @ValidateNested() + @Type(() => FunctionDto) + public function: FunctionDto; + + @ApiProperty({ + description: 'days', + enum: WorkingDays, + isArray: true, + required: true, + }) + @IsArray() + @IsEnum(WorkingDays, { each: true }) + @IsNotEmpty() + public days: WorkingDays[]; +} + +export class EnableScheduleDto { + @ApiProperty({ + description: 'scheduleId', + required: true, + }) + @IsString() + @IsNotEmpty() + public scheduleId: string; + + @ApiProperty({ + description: 'enable', + required: true, + }) + @IsBoolean() + @IsNotEmpty() + public enable: boolean; +} diff --git a/src/schedule/dtos/index.ts b/src/schedule/dtos/index.ts new file mode 100644 index 0000000..e71549f --- /dev/null +++ b/src/schedule/dtos/index.ts @@ -0,0 +1 @@ +export * from './add.schedule.dto'; diff --git a/src/schedule/interfaces/get.schedule.interface.ts b/src/schedule/interfaces/get.schedule.interface.ts new file mode 100644 index 0000000..40c2011 --- /dev/null +++ b/src/schedule/interfaces/get.schedule.interface.ts @@ -0,0 +1,10 @@ +export interface getDeviceScheduleInterface { + success: boolean; + result: []; + msg: string; +} +export interface addScheduleDeviceInterface { + success: boolean; + result: boolean; + msg: string; +} diff --git a/src/schedule/schedule.module.ts b/src/schedule/schedule.module.ts new file mode 100644 index 0000000..f0f0a64 --- /dev/null +++ b/src/schedule/schedule.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ScheduleService } from './services/schedule.service'; +import { ScheduleController } from './controllers/schedule.controller'; +import { ConfigModule } from '@nestjs/config'; +import { DeviceRepositoryModule } from '@app/common/modules/device'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +@Module({ + imports: [ConfigModule, DeviceRepositoryModule], + controllers: [ScheduleController], + providers: [ScheduleService, DeviceRepository], + exports: [ScheduleService], +}) +export class ScheduleModule {} diff --git a/src/schedule/services/index.ts b/src/schedule/services/index.ts new file mode 100644 index 0000000..ad5aed0 --- /dev/null +++ b/src/schedule/services/index.ts @@ -0,0 +1 @@ +export * from './schedule.service'; diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts new file mode 100644 index 0000000..f5a6417 --- /dev/null +++ b/src/schedule/services/schedule.service.ts @@ -0,0 +1,281 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; +import { ConfigService } from '@nestjs/config'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { + addScheduleDeviceInterface, + getDeviceScheduleInterface, +} from '../interfaces/get.schedule.interface'; + +import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { ProductType } from '@app/common/constants/product-type.enum'; +import { convertTimestampToDubaiTime } from '@app/common/helper/convertTimestampToDubaiTime'; +import { + getEnabledDays, + getScheduleStatus, +} from '@app/common/helper/getScheduleStatus'; + +@Injectable() +export class ScheduleService { + private tuya: TuyaContext; + 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, + }); + } + + async enableDeviceSchedule( + deviceUuid: string, + enableScheduleDto: EnableScheduleDto, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + return await this.enableScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + enableScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Updating Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async enableScheduleDeviceInTuya( + deviceId: string, + enableScheduleDto: EnableScheduleDto, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/state`; + const response = await this.tuya.request({ + method: 'PUT', + path, + body: { + enable: enableScheduleDto.enable, + timer_id: enableScheduleDto.scheduleId, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while updating schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async deleteDeviceSchedule(deviceUuid: string, scheduleId: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + return await this.deleteScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + scheduleId, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Deleting Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async deleteScheduleDeviceInTuya( + deviceId: string, + scheduleId: string, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/batch?timer_ids=${scheduleId}`; + const response = await this.tuya.request({ + method: 'DELETE', + path, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while deleting schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addDeviceSchedule(deviceUuid: string, addScheduleDto: AddScheduleDto) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + await this.addScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + addScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Adding Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addScheduleDeviceInTuya( + deviceId: string, + addScheduleDto: AddScheduleDto, + ): Promise { + try { + const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); + const loops = getScheduleStatus(addScheduleDto.days); + + const path = `/v2.0/cloud/timer/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + time: convertedTime.time, + timezone_id: 'Asia/Dubai', + loops: `${loops}`, + functions: [ + { + code: addScheduleDto.function.code, + value: addScheduleDto.function.value, + }, + ], + category: `category_${addScheduleDto.category}`, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error adding schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceScheduleByCategory(deviceUuid: string, category: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + const schedules = await this.getScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + category, + ); + const result = schedules.result.map((schedule: any) => { + return { + category: schedule.category.replace('category_', ''), + enable: schedule.enable, + function: { + code: schedule.functions[0].code, + value: schedule.functions[0].value, + }, + time: schedule.time, + schedule_id: schedule.timer_id, + timezone_id: schedule.timezone_id, + days: getEnabledDays(schedule.loops), + }; + }); + return convertKeysToCamelCase(result); + } catch (error) { + throw new HttpException( + error.message || 'Error While Adding Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getScheduleDeviceInTuya( + deviceId: string, + category: string, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}?category=category_${category}`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + + return response as getDeviceScheduleInterface; + } catch (error) { + console.error('Error fetching device schedule from Tuya:', error); + + throw new HttpException( + 'Error fetching device schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceByDeviceUuid( + deviceUuid: string, + withProductDevice: boolean = true, + ) { + return await this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + }, + ...(withProductDevice && { relations: ['productDevice'] }), + }); + } +} From 5b6dcc1440b6feac371fc6ca49853cc2b3bd77ae Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:09:34 +0300 Subject: [PATCH 09/34] edit category from param to query --- src/schedule/controllers/schedule.controller.ts | 13 +++++++++---- src/schedule/dtos/index.ts | 2 +- .../dtos/{add.schedule.dto.ts => schedule.dto.ts} | 9 +++++++++ src/schedule/services/schedule.service.ts | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) rename src/schedule/dtos/{add.schedule.dto.ts => schedule.dto.ts} (90%) diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts index 94ad414..c14c6fe 100644 --- a/src/schedule/controllers/schedule.controller.ts +++ b/src/schedule/controllers/schedule.controller.ts @@ -10,9 +10,14 @@ import { UseGuards, Put, Delete, + Query, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { + AddScheduleDto, + EnableScheduleDto, + GetScheduleDeviceDto, +} from '../dtos/schedule.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @@ -51,15 +56,15 @@ export class ScheduleController { } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Get(':deviceUuid/category/:category') + @Get(':deviceUuid') async getDeviceScheduleByCategory( @Param('deviceUuid') deviceUuid: string, - @Param('category') category: string, + @Query() query: GetScheduleDeviceDto, ) { try { return await this.scheduleService.getDeviceScheduleByCategory( deviceUuid, - category, + query.category, ); } catch (error) { throw new HttpException( diff --git a/src/schedule/dtos/index.ts b/src/schedule/dtos/index.ts index e71549f..8398723 100644 --- a/src/schedule/dtos/index.ts +++ b/src/schedule/dtos/index.ts @@ -1 +1 @@ -export * from './add.schedule.dto'; +export * from './schedule.dto'; diff --git a/src/schedule/dtos/add.schedule.dto.ts b/src/schedule/dtos/schedule.dto.ts similarity index 90% rename from src/schedule/dtos/add.schedule.dto.ts rename to src/schedule/dtos/schedule.dto.ts index c51a027..550519c 100644 --- a/src/schedule/dtos/add.schedule.dto.ts +++ b/src/schedule/dtos/schedule.dto.ts @@ -82,3 +82,12 @@ export class EnableScheduleDto { @IsNotEmpty() public enable: boolean; } +export class GetScheduleDeviceDto { + @ApiProperty({ + description: 'category', + required: true, + }) + @IsString() + @IsNotEmpty() + public category: string; +} diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index f5a6417..1f9e224 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/schedule.dto'; import { addScheduleDeviceInterface, getDeviceScheduleInterface, From d1cdc1375a0e99f9f86c8c9c95218112f4b168f2 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:18:40 +0300 Subject: [PATCH 10/34] finished get status for batch devices endpoint --- src/device/controllers/device.controller.ts | 16 ++++++++++++++ src/device/dtos/control.device.dto.ts | 7 +++++++ src/device/services/device.service.ts | 23 +++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 8e7e6d6..6ded947 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -21,6 +21,7 @@ import { import { ControlDeviceDto, BatchControlDevicesDto, + BatchStatusDevicesDto, } from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; @@ -275,4 +276,19 @@ export class DeviceController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('status/batch') + async batchStatusDevices( + @Query() batchStatusDevicesDto: BatchStatusDevicesDto, + ) { + try { + return await this.deviceService.batchStatusDevices(batchStatusDevicesDto); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index 9bb1d40..7634fd0 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -38,3 +38,10 @@ export class BatchControlDevicesDto { @IsNotEmpty() public value: any; } +export class BatchStatusDevicesDto { + @ApiProperty({ + example: 'uuid1,uuid2,uuid3', + description: 'Comma-separated list of device UUIDs', + }) + devicesUuid: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 93433a3..c856143 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -24,6 +24,7 @@ import { } from '../dtos/get.device.dto'; import { BatchControlDevicesDto, + BatchStatusDevicesDto, ControlDeviceDto, } from '../dtos/control.device.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; @@ -362,7 +363,29 @@ export class DeviceService { ); } } + async batchStatusDevices(batchStatusDevicesDto: BatchStatusDevicesDto) { + const { devicesUuid } = batchStatusDevicesDto; + const devicesUuidArray = devicesUuid.split(','); + try { + await this.checkAllDevicesHaveSameProductUuid(devicesUuidArray); + const statuses = await Promise.all( + devicesUuidArray.map(async (deviceUuid) => { + const result = await this.getDevicesInstructionStatus(deviceUuid); + return { deviceUuid, result }; + }), + ); + return { + status: statuses[0].result, + devices: statuses, + }; + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { const firstDevice = await this.deviceRepository.findOne({ where: { uuid: deviceUuids[0] }, From 5681a13c596400fed7f32b47b65b7fe26a077ce4 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:38:14 +0300 Subject: [PATCH 11/34] Add water heater type for the schedule --- libs/common/src/constants/product-type.enum.ts | 1 + src/schedule/services/schedule.service.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index bff76e3..834dac4 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -7,4 +7,5 @@ export enum ProductType { THREE_G = '3G', TWO_G = '2G', ONE_G = '1G', + WH = 'WH', } diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index 1f9e224..866e279 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -150,7 +150,8 @@ export class ScheduleService { if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G + deviceDetails.productDevice.prodType !== ProductType.TWO_G && + deviceDetails.productDevice.prodType !== ProductType.WH ) { throw new HttpException( 'This device is not supported for schedule', From 20dc93c3543fec203a0c4059b02ecbf1f3ec3fe9 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:13:45 +0300 Subject: [PATCH 12/34] add start time and end time for the device logs --- src/device/dtos/get.device.dto.ts | 16 +++++++++++++++- src/device/interfaces/get.device.interface.ts | 4 ++-- src/device/services/device.service.ts | 14 +++++++++----- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index 99c0dad..13204de 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class GetDeviceByRoomUuidDto { @ApiProperty({ @@ -18,4 +18,18 @@ export class GetDeviceLogsDto { @IsString() @IsNotEmpty() public code: string; + @ApiProperty({ + description: 'startTime', + required: false, + }) + @IsString() + @IsOptional() + public startTime: string; + @ApiProperty({ + description: 'endTime', + required: false, + }) + @IsString() + @IsOptional() + public endTime: string; } diff --git a/src/device/interfaces/get.device.interface.ts b/src/device/interfaces/get.device.interface.ts index 880a976..7422ed2 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: number; - endTime: number; + startTime: string; + endTime: string; deviceUuid?: string; } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index c856143..0ad4167 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -806,6 +806,8 @@ export class DeviceService { const response = await this.getDeviceLogsTuya( deviceDetails.deviceTuyaUuid, query.code, + query.startTime, + query.endTime, ); return { @@ -822,11 +824,13 @@ export class DeviceService { async getDeviceLogsTuya( deviceId: string, code: string, + startTime: string = (Date.now() - 1 * 60 * 60 * 1000).toString(), + endTime: string = Date.now().toString(), ): 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`; + console.log(startTime, endTime); + + const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${startTime}&end_time=${endTime}&codes=${code}&size=50`; const response = await this.tuya.request({ method: 'GET', path, @@ -835,8 +839,8 @@ export class DeviceService { const camelCaseResponse = convertKeysToCamelCase(response); const logs = camelCaseResponse.result.logs ?? []; return { - startTime: oneHourAgo, - endTime: now, + startTime, + endTime, data: logs, } as getDeviceLogsInterface; } catch (error) { From f814405825651e9aeaf90ca240911b1181450352 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:19:00 +0300 Subject: [PATCH 13/34] remove console log --- src/device/services/device.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 0ad4167..8e4377e 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -828,8 +828,6 @@ export class DeviceService { endTime: string = Date.now().toString(), ): Promise { try { - console.log(startTime, endTime); - const path = `/v2.0/cloud/thing/${deviceId}/report-logs?start_time=${startTime}&end_time=${endTime}&codes=${code}&size=50`; const response = await this.tuya.request({ method: 'GET', From 90127bd510b0a787657422e7915c07ab28c32b73 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:02:00 +0300 Subject: [PATCH 14/34] change time from UTC+4 to UTC+3 --- libs/common/src/helper/convertTimestampToDubaiTime.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/src/helper/convertTimestampToDubaiTime.ts b/libs/common/src/helper/convertTimestampToDubaiTime.ts index ade7c29..225e781 100644 --- a/libs/common/src/helper/convertTimestampToDubaiTime.ts +++ b/libs/common/src/helper/convertTimestampToDubaiTime.ts @@ -2,8 +2,8 @@ export function convertTimestampToDubaiTime(timestamp) { // Convert timestamp to milliseconds const date = new Date(timestamp * 1000); - // Convert to Dubai time (UTC+4) - const dubaiTimeOffset = 4 * 60; // 4 hours in minutes + // Convert to Dubai time (UTC+3) + const dubaiTimeOffset = 3 * 60; // 3 hours in minutes const dubaiTime = new Date(date.getTime() + dubaiTimeOffset * 60 * 1000); // Format the date as YYYYMMDD From c49aad3e4355872c7ad232ce68c8c462ca9dd1db Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:58:46 +0300 Subject: [PATCH 15/34] finished factory reset endpoint --- src/device/controllers/device.controller.ts | 18 ++++++ src/device/dtos/control.device.dto.ts | 9 +++ src/device/services/device.service.ts | 71 +++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 6ded947..6a844d9 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -22,6 +22,7 @@ import { ControlDeviceDto, BatchControlDevicesDto, BatchStatusDevicesDto, + BatchFactoryResetDevicesDto, } from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; @@ -291,4 +292,21 @@ export class DeviceController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('factory/reset/:deviceUuid') + async batchFactoryResetDevices( + @Body() batchFactoryResetDevicesDto: BatchFactoryResetDevicesDto, + ) { + try { + return await this.deviceService.batchFactoryResetDevices( + batchFactoryResetDevicesDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index 7634fd0..d917a96 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -45,3 +45,12 @@ export class BatchStatusDevicesDto { }) devicesUuid: string; } +export class BatchFactoryResetDevicesDto { + @ApiProperty({ + description: 'devicesUuid', + required: true, + }) + @IsArray() + @IsNotEmpty() + public devicesUuid: [string]; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 8e4377e..82c813a 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -24,6 +24,7 @@ import { } from '../dtos/get.device.dto'; import { BatchControlDevicesDto, + BatchFactoryResetDevicesDto, BatchStatusDevicesDto, ControlDeviceDto, } from '../dtos/control.device.dto'; @@ -288,6 +289,24 @@ export class DeviceService { ); } } + async factoryResetDeviceTuya( + deviceUuid: string, + ): Promise { + try { + const path = `/v2.0/cloud/thing/${deviceUuid}/reset`; + const response = await this.tuya.request({ + method: 'POST', + path, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error factory resetting device from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } async controlDeviceTuya( deviceUuid: string, controlDeviceDto: ControlDeviceDto, @@ -413,6 +432,58 @@ export class DeviceService { } } } + async batchFactoryResetDevices( + batchFactoryResetDevicesDto: BatchFactoryResetDevicesDto, + ) { + const { devicesUuid } = batchFactoryResetDevicesDto; + + try { + // Check if all devices have the same product UUID + await this.checkAllDevicesHaveSameProductUuid(devicesUuid); + + // Perform all operations concurrently + const results = await Promise.allSettled( + devicesUuid.map(async (deviceUuid) => { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + const result = await this.factoryResetDeviceTuya( + deviceDetails.deviceTuyaUuid, + ); + return { deviceUuid, result }; + }), + ); + + // Separate successful and failed operations + const successResults = []; + const failedResults = []; + + for (const result of results) { + if (result.status === 'fulfilled') { + const { deviceUuid, result: operationResult } = result.value; + + if (operationResult.success) { + // Add to success results if operationResult.success is true + successResults.push({ deviceUuid, result: operationResult }); + } else { + // Add to failed results if operationResult.success is false + failedResults.push({ deviceUuid, error: operationResult.msg }); + } + } else { + // Add to failed results if promise is rejected + failedResults.push({ + deviceUuid: devicesUuid[results.indexOf(result)], + error: result.reason.message, + }); + } + } + + return { successResults, failedResults }; + } catch (error) { + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); + } + } async getDeviceDetailsByDeviceId(deviceUuid: string, userUuid: string) { try { const userDevicePermission = await this.getUserDevicePermission( From 9145935c5cd502c83828ffccda9bcd16466b2c97 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 23 Sep 2024 03:29:03 -0500 Subject: [PATCH 16/34] add water heater type for the schedule --- src/schedule/services/schedule.service.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index 866e279..c4717e6 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -48,7 +48,8 @@ export class ScheduleService { if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G + deviceDetails.productDevice.prodType !== ProductType.TWO_G && + deviceDetails.productDevice.prodType !== ProductType.WH ) { throw new HttpException( 'This device is not supported for schedule', @@ -101,7 +102,8 @@ export class ScheduleService { if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G + deviceDetails.productDevice.prodType !== ProductType.TWO_G && + deviceDetails.productDevice.prodType !== ProductType.WH ) { throw new HttpException( 'This device is not supported for schedule', @@ -214,7 +216,8 @@ export class ScheduleService { if ( deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && - deviceDetails.productDevice.prodType !== ProductType.TWO_G + deviceDetails.productDevice.prodType !== ProductType.TWO_G && + deviceDetails.productDevice.prodType !== ProductType.WH ) { throw new HttpException( 'This device is not supported for schedule', From 2b9788eff72483c6830adf42350a8dfd01786840 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Sep 2024 02:10:41 -0500 Subject: [PATCH 17/34] refactor delete user endpoint --- src/auth/controllers/user-auth.controller.ts | 16 ---------------- src/auth/services/user-auth.service.ts | 12 ------------ src/users/controllers/user.controller.ts | 15 +++++++++++++++ src/users/services/user.service.ts | 11 +++++++++++ 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index e650d39..1eee19e 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -1,10 +1,8 @@ import { Body, Controller, - Delete, Get, HttpStatus, - Param, Post, Req, UseGuards, @@ -51,20 +49,6 @@ export class UserAuthController { }; } - @ApiBearerAuth() - @UseGuards(SuperAdminRoleGuard) - @Delete('user/delete/:id') - async userDelete(@Param('id') id: string) { - await this.userAuthService.deleteUser(id); - return { - statusCode: HttpStatus.OK, - data: { - id, - }, - message: 'User Deleted Successfully', - }; - } - @Post('user/send-otp') async sendOtp(@Body() otpDto: UserOtpDto) { const otpCode = await this.userAuthService.generateOTP(otpDto); diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index e709a51..f31c214 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -1,5 +1,3 @@ -import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository'; -import { UserRoleRepository } from './../../../libs/common/src/modules/user/repositories/user.repository'; import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; import { BadRequestException, @@ -31,8 +29,6 @@ export class UserAuthService { private readonly helperHashService: HelperHashService, private readonly authService: AuthService, private readonly emailService: EmailService, - private readonly userRoleRepository: UserRoleRepository, - private readonly roleTypeRepository: RoleTypeRepository, private readonly configService: ConfigService, ) {} @@ -128,14 +124,6 @@ export class UserAuthService { } } - async deleteUser(uuid: string) { - const user = await this.findOneById(uuid); - if (!user) { - throw new BadRequestException('User not found'); - } - return await this.userRepository.update({ uuid }, { isActive: false }); - } - async findOneById(id: string): Promise { return await this.userRepository.findOne({ where: { uuid: id } }); } diff --git a/src/users/controllers/user.controller.ts b/src/users/controllers/user.controller.ts index b088b93..c669f08 100644 --- a/src/users/controllers/user.controller.ts +++ b/src/users/controllers/user.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, + Delete, Get, HttpException, HttpStatus, @@ -18,6 +19,7 @@ import { UpdateTimezoneDataDto, } from '../dtos'; import { CheckProfilePictureGuard } from 'src/guards/profile.picture.guard'; +import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; @ApiTags('User Module') @Controller({ @@ -140,4 +142,17 @@ export class UserController { ); } } + @ApiBearerAuth() + @UseGuards(SuperAdminRoleGuard) + @Delete('/:userUuid') + async userDelete(@Param('userUuid') userUuid: string) { + await this.userService.deleteUser(userUuid); + return { + statusCode: HttpStatus.OK, + data: { + userUuid, + }, + message: 'User Deleted Successfully', + }; + } } diff --git a/src/users/services/user.service.ts b/src/users/services/user.service.ts index b035c05..80dad24 100644 --- a/src/users/services/user.service.ts +++ b/src/users/services/user.service.ts @@ -14,6 +14,7 @@ import { UserRepository } from '@app/common/modules/user/repositories'; import { RegionRepository } from '@app/common/modules/region/repositories'; import { TimeZoneRepository } from '@app/common/modules/timezone/repositories'; import { removeBase64Prefix } from '@app/common/helper/removeBase64Prefix'; +import { UserEntity } from '@app/common/modules/user/entities'; @Injectable() export class UserService { @@ -237,4 +238,14 @@ export class UserService { ); } } + async findOneById(id: string): Promise { + return await this.userRepository.findOne({ where: { uuid: id } }); + } + async deleteUser(uuid: string) { + const user = await this.findOneById(uuid); + if (!user) { + throw new BadRequestException('User not found'); + } + return await this.userRepository.update({ uuid }, { isActive: false }); + } } From c0c41cf91d0986dff0754aba923a57e54a3b77eb Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Oct 2024 16:52:42 +0300 Subject: [PATCH 18/34] google login done --- libs/common/src/auth/services/auth.service.ts | 36 +- package-lock.json | 346 +++++++++--------- package.json | 3 +- src/auth/dtos/user-auth.dto.ts | 2 +- src/auth/dtos/user-login.dto.ts | 8 +- src/auth/services/user-auth.service.ts | 44 ++- 6 files changed, 227 insertions(+), 212 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 3174b11..86bb826 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -1,4 +1,8 @@ -import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as argon2 from 'argon2'; import { HelperHashService } from '../../helper/services'; @@ -7,16 +11,20 @@ import { UserSessionRepository } from '../../../../common/src/modules/session/re import { UserSessionEntity } from '../../../../common/src/modules/session/entities'; import { ConfigService } from '@nestjs/config'; import axios from 'axios'; +import { OAuth2Client } from 'google-auth-library'; @Injectable() export class AuthService { + private client: OAuth2Client; constructor( private jwtService: JwtService, private readonly userRepository: UserRepository, private readonly sessionRepository: UserSessionRepository, private readonly helperHashService: HelperHashService, private readonly configService: ConfigService, - ) {} + ) { + this.client = new OAuth2Client(this.configService.get('GOOGLE_CLIENT_ID')); + } async validateUser( email: string, @@ -83,14 +91,13 @@ export class AuthService { roles: user?.roles, googleCode: user.googleCode, }; - if (payload.googleCode) { const profile = await this.getProfile(payload.googleCode); user = await this.userRepository.findOne({ - where: { email: profile.data.email }, + where: { email: profile.email }, }); if (!user) { - throw new UnauthorizedException('wrong credentials'); + return { profile }; } } const tokens = await this.getTokens(payload); @@ -114,19 +121,16 @@ export class AuthService { async getProfile(googleCode: string) { try { - const response = await axios.post('https://oauth2.googleapis.com/token', { - client_id: this.configService('GOOGLE_CLIENT_ID'), - client_secret: this.configService('GOOGLE_CLIENT_SECRET'), - code: googleCode, - grant_type: 'authorization_code', - redirect_uri: 'http://localhost:3000/auth/google/callback', + const ticket = await this.client.verifyIdToken({ + idToken: googleCode, + audience: this.configService.get('GOOGLE_CLIENT_ID'), }); - return axios.get( - `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${response.data.access_token}`, - ); + const payload = ticket.getPayload(); + return { + ...payload, + }; } catch (error) { - console.error('Failed to get profile:', error); - throw new UnauthorizedException('google login failed'); + throw new UnauthorizedException('Google login failed'); } } } diff --git a/package-lock.json b/package-lock.json index db7b28d..9a92384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,13 +20,14 @@ "@nestjs/websockets": "^10.3.8", "@tuya/tuya-connector-nodejs": "^2.1.2", "argon2": "^0.40.1", - "axios": "^1.6.7", + "axios": "^1.7.7", "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "crypto-js": "^4.2.0", "express-rate-limit": "^7.1.5", "firebase": "^10.12.5", + "google-auth-library": "^9.14.1", "helmet": "^7.1.0", "ioredis": "^5.3.2", "morgan": "^1.10.0", @@ -2258,6 +2259,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "optional": true, + "peer": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2490,17 +2493,6 @@ } } }, - "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", @@ -3138,7 +3130,9 @@ "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==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "optional": true, + "peer": true }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -3571,6 +3565,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -3791,11 +3797,12 @@ "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==" }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -3969,6 +3976,15 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4131,14 +4147,6 @@ "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", @@ -5902,15 +5910,16 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -6071,6 +6080,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6209,6 +6247,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-auth-library": { + "version": "9.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.14.1.tgz", + "integrity": "sha512-Rj+PMjoNFGFTmtItH7gHfbHpGVSb3vmnGK3nwNBqxQF9NoBpttSZI/rc0WiM63ma2uGDQtYEkMHkK9U6937NiA==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -6232,6 +6308,40 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gtoken/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/gtoken/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -6399,6 +6509,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6704,7 +6827,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "engines": { "node": ">=8" }, @@ -7548,6 +7670,15 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7666,14 +7797,6 @@ "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", @@ -7911,7 +8034,9 @@ "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==" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true, + "peer": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -8146,125 +8271,6 @@ "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", @@ -8304,25 +8310,6 @@ "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", @@ -9956,11 +9943,6 @@ "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", @@ -10045,6 +10027,8 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "peer": true, "dependencies": { "memory-pager": "^1.0.2" } diff --git a/package.json b/package.json index 4883516..d3c90e0 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,14 @@ "@nestjs/websockets": "^10.3.8", "@tuya/tuya-connector-nodejs": "^2.1.2", "argon2": "^0.40.1", - "axios": "^1.6.7", + "axios": "^1.7.7", "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "crypto-js": "^4.2.0", "express-rate-limit": "^7.1.5", "firebase": "^10.12.5", + "google-auth-library": "^9.14.1", "helmet": "^7.1.0", "ioredis": "^5.3.2", "morgan": "^1.10.0", diff --git a/src/auth/dtos/user-auth.dto.ts b/src/auth/dtos/user-auth.dto.ts index b934c4c..dad1e07 100644 --- a/src/auth/dtos/user-auth.dto.ts +++ b/src/auth/dtos/user-auth.dto.ts @@ -41,5 +41,5 @@ export class UserSignUpDto { @IsString() @IsOptional() - public regionUuid: string; + public regionUuid?: string; } diff --git a/src/auth/dtos/user-login.dto.ts b/src/auth/dtos/user-login.dto.ts index dedb08f..198ae12 100644 --- a/src/auth/dtos/user-login.dto.ts +++ b/src/auth/dtos/user-login.dto.ts @@ -1,16 +1,16 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsEmail, IsOptional, IsString } from 'class-validator'; export class UserLoginDto { @ApiProperty() @IsEmail() - @IsNotEmpty() - email: string; + @IsOptional() + email?: string; @ApiProperty() @IsString() @IsOptional() - password: string; + password?: string; @ApiProperty() @IsString() diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 88e989e..6289e9e 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -21,6 +21,7 @@ import * as argon2 from 'argon2'; import { differenceInSeconds } from '@app/common/helper/differenceInSeconds'; import { LessThan, MoreThan } from 'typeorm'; import { ConfigService } from '@nestjs/config'; +import { UUID } from 'typeorm/driver/mongodb/bson.typings'; @Injectable() export class UserAuthService { @@ -93,13 +94,38 @@ export class UserAuthService { async userLogin(data: UserLoginDto) { try { - const user = await this.authService.validateUser( - data.email, - data.password, - data.regionUuid, - ); - if (!user) { - throw new UnauthorizedException('Invalid login credentials.'); + let user; + if (data.googleCode) { + const googleUserData = await this.authService.login({ + googleCode: data.googleCode, + }); + const userExists = await this.userRepository.exists({ + where: { + email: googleUserData['email'], + }, + }); + user = await this.userRepository.findOne({ + where: { + email: googleUserData['email'], + }, + }); + if (!userExists) { + await this.signUp({ + email: googleUserData['email'], + firstName: googleUserData['given_name'], + lastName: googleUserData['family_name'], + password: googleUserData['email'], + }); + } + data.email = googleUserData['email']; + data.password = googleUserData['password']; + } + if (!data.googleCode) { + user = await this.authService.validateUser( + data.email, + data.password, + data.regionUuid, + ); } const session = await Promise.all([ await this.sessionRepository.update( @@ -114,7 +140,7 @@ export class UserAuthService { isLoggedOut: false, }), ]); - return await this.authService.login({ + const res = await this.authService.login({ email: user.email, userId: user.uuid, uuid: user.uuid, @@ -122,8 +148,8 @@ export class UserAuthService { return { uuid: role.uuid, type: role.roleType.type }; }), sessionId: session[1].uuid, - googleCode: data.googleCode, }); + return res; } catch (error) { throw new BadRequestException('Invalid credentials'); } From 5703f067c97d7d8f73e732a6e3fe6e875eebaa30 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:09:29 -0500 Subject: [PATCH 19/34] add product name for the get all devices endpoint --- src/device/services/device.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 82c813a..897e493 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -534,7 +534,7 @@ export class DeviceService { }); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { productName, productId, id, ...rest } = camelCaseResponse.result; + const { productId, id, ...rest } = camelCaseResponse.result; return { ...rest, From a00f78422934bab8f9cb71334e34d0666c9b5672 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:24:24 -0500 Subject: [PATCH 20/34] finished add battery value for the door sensor device --- libs/common/src/constants/product-type.enum.ts | 1 + src/device/services/device.service.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 834dac4..1cf049f 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -8,4 +8,5 @@ export enum ProductType { TWO_G = '2G', ONE_G = '1G', WH = 'WH', + DS = 'DS', } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 897e493..e69ad7a 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -826,7 +826,19 @@ export class DeviceService { battery = batteryStatus.value; } } + // Check if the device is a door sensor (DS) + if (device.productDevice.prodType === ProductType.DS) { + const doorSensorInstructionsStatus = + await this.getDevicesInstructionStatus(device.uuid); + const batteryStatus: any = doorSensorInstructionsStatus.status.find( + (status: any) => status.code === 'battery_percentage', + ); + + if (batteryStatus) { + battery = batteryStatus.value; + } + } const spaceDevice = device?.spaceDevice; const parentDevice = spaceDevice?.parent; return { From c387c5984d708f307ba343bcb5fc45fe6137da3c Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:16:50 -0500 Subject: [PATCH 21/34] finished update schedule endpoint --- .../controllers/schedule.controller.ts | 31 +++++++- src/schedule/dtos/schedule.dto.ts | 45 +++++++++++ src/schedule/services/schedule.service.ts | 75 ++++++++++++++++++- 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts index c14c6fe..63bf182 100644 --- a/src/schedule/controllers/schedule.controller.ts +++ b/src/schedule/controllers/schedule.controller.ts @@ -17,6 +17,7 @@ import { AddScheduleDto, EnableScheduleDto, GetScheduleDeviceDto, + UpdateScheduleDto, } from '../dtos/schedule.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @@ -36,7 +37,7 @@ export class ScheduleController { @Body() addScheduleDto: AddScheduleDto, ) { try { - const device = await this.scheduleService.addDeviceSchedule( + const schedule = await this.scheduleService.addDeviceSchedule( deviceUuid, addScheduleDto, ); @@ -45,7 +46,7 @@ export class ScheduleController { statusCode: HttpStatus.CREATED, success: true, message: 'schedule added successfully', - data: device, + data: schedule, }; } catch (error) { throw new HttpException( @@ -118,4 +119,30 @@ export class ScheduleController { ); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put(':deviceUuid') + async updateDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Body() updateScheduleDto: UpdateScheduleDto, + ) { + try { + const schedule = await this.scheduleService.updateDeviceSchedule( + deviceUuid, + updateScheduleDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule updated successfully', + data: schedule, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/schedule/dtos/schedule.dto.ts b/src/schedule/dtos/schedule.dto.ts index 550519c..09b2156 100644 --- a/src/schedule/dtos/schedule.dto.ts +++ b/src/schedule/dtos/schedule.dto.ts @@ -91,3 +91,48 @@ export class GetScheduleDeviceDto { @IsNotEmpty() public category: string; } + +export class UpdateScheduleDto { + @ApiProperty({ + description: 'scheduleId', + required: true, + }) + @IsString() + @IsNotEmpty() + public scheduleId: string; + @ApiProperty({ + description: 'category', + required: true, + }) + @IsString() + @IsNotEmpty() + public category: string; + + @ApiProperty({ + description: 'time', + required: true, + }) + @IsString() + @IsNotEmpty() + public time: string; + + @ApiProperty({ + description: 'function', + required: true, + type: FunctionDto, + }) + @ValidateNested() + @Type(() => FunctionDto) + public function: FunctionDto; + + @ApiProperty({ + description: 'days', + enum: WorkingDays, + isArray: true, + required: true, + }) + @IsArray() + @IsEnum(WorkingDays, { each: true }) + @IsNotEmpty() + public days: WorkingDays[]; +} diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index c4717e6..634d120 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -1,7 +1,11 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { AddScheduleDto, EnableScheduleDto } from '../dtos/schedule.dto'; +import { + AddScheduleDto, + EnableScheduleDto, + UpdateScheduleDto, +} from '../dtos/schedule.dto'; import { addScheduleDeviceInterface, getDeviceScheduleInterface, @@ -282,4 +286,73 @@ export class ScheduleService { ...(withProductDevice && { relations: ['productDevice'] }), }); } + async updateDeviceSchedule( + deviceUuid: string, + updateScheduleDto: UpdateScheduleDto, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G && + deviceDetails.productDevice.prodType !== ProductType.WH + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + await this.updateScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + updateScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Updating Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async updateScheduleDeviceInTuya( + deviceId: string, + updateScheduleDto: UpdateScheduleDto, + ): Promise { + try { + const convertedTime = convertTimestampToDubaiTime(updateScheduleDto.time); + const loops = getScheduleStatus(updateScheduleDto.days); + + const path = `/v2.0/cloud/timer/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'PUT', + path, + body: { + timer_id: updateScheduleDto.scheduleId, + time: convertedTime.time, + timezone_id: 'Asia/Dubai', + loops: `${loops}`, + functions: [ + { + code: updateScheduleDto.function.code, + value: updateScheduleDto.function.value, + }, + ], + category: `category_${updateScheduleDto.category}`, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error updating schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } From f94cb940c8367a85578ac46604cc24afe0d1ea60 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 2 Oct 2024 03:21:13 -0500 Subject: [PATCH 22/34] finished add new types to the schedule apis --- .../common/src/constants/product-type.enum.ts | 3 +++ src/schedule/services/schedule.service.ts | 25 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 1cf049f..3ed4cfa 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -9,4 +9,7 @@ export enum ProductType { ONE_G = '1G', WH = 'WH', DS = 'DS', + THREE_3TG = '3GT', + TWO_2TG = '2GT', + ONE_1TG = '1GT', } diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index 634d120..d2bb1f5 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -53,7 +53,10 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH + deviceDetails.productDevice.prodType !== ProductType.WH && + deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && + deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG ) { throw new HttpException( 'This device is not supported for schedule', @@ -107,7 +110,10 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH + deviceDetails.productDevice.prodType !== ProductType.WH && + deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && + deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG ) { throw new HttpException( 'This device is not supported for schedule', @@ -157,7 +163,10 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH + deviceDetails.productDevice.prodType !== ProductType.WH && + deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && + deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG ) { throw new HttpException( 'This device is not supported for schedule', @@ -221,7 +230,10 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH + deviceDetails.productDevice.prodType !== ProductType.WH && + deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && + deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG ) { throw new HttpException( 'This device is not supported for schedule', @@ -302,7 +314,10 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.THREE_G && deviceDetails.productDevice.prodType !== ProductType.ONE_G && deviceDetails.productDevice.prodType !== ProductType.TWO_G && - deviceDetails.productDevice.prodType !== ProductType.WH + deviceDetails.productDevice.prodType !== ProductType.WH && + deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && + deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG ) { throw new HttpException( 'This device is not supported for schedule', From 553f08641e7c07a397c972d897e75e11e9bda762 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 4 Oct 2024 22:04:19 +0300 Subject: [PATCH 23/34] added a dedicated error handler service --- libs/common/src/auth/services/auth.service.ts | 1 + libs/common/src/common.module.ts | 12 ++++-- .../http-exception.filter.spec.ts | 7 ++++ .../http-exception/http-exception.filter.ts | 38 ++++++++++++++++++ .../error-message.service.spec.ts | 18 +++++++++ src/error-message/error-message.service.ts | 40 +++++++++++++++++++ src/main.ts | 2 + src/region/region.module.ts | 3 +- src/region/services/region.service.ts | 16 ++++++-- 9 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 src/common/filters/http-exception/http-exception.filter.spec.ts create mode 100644 src/common/filters/http-exception/http-exception.filter.ts create mode 100644 src/error-message/error-message.service.spec.ts create mode 100644 src/error-message/error-message.service.ts diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index af9d047..28940f9 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -6,6 +6,7 @@ import { UserRepository } from '../../../../common/src/modules/user/repositories import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository'; import { UserSessionEntity } from '../../../../common/src/modules/session/entities'; import { ConfigService } from '@nestjs/config'; +import { OAuth2Client } from 'google-auth-library'; @Injectable() export class AuthService { diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index 8c3e78c..d156de3 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -6,10 +6,16 @@ import { AuthModule } from './auth/auth.module'; import { ConfigModule } from '@nestjs/config'; import config from './config'; import { EmailService } from './util/email.service'; - +import { ErrorMessageService } from 'src/error-message/error-message.service'; @Module({ - providers: [CommonService, EmailService], - exports: [CommonService, HelperModule, AuthModule, EmailService], + providers: [CommonService, EmailService, ErrorMessageService], + exports: [ + CommonService, + HelperModule, + AuthModule, + EmailService, + ErrorMessageService, + ], imports: [ ConfigModule.forRoot({ load: config, diff --git a/src/common/filters/http-exception/http-exception.filter.spec.ts b/src/common/filters/http-exception/http-exception.filter.spec.ts new file mode 100644 index 0000000..8f016dd --- /dev/null +++ b/src/common/filters/http-exception/http-exception.filter.spec.ts @@ -0,0 +1,7 @@ +import { HttpExceptionFilter } from './http-exception.filter'; + +describe('HttpExceptionFilter', () => { + it('should be defined', () => { + expect(new HttpExceptionFilter()).toBeDefined(); + }); +}); diff --git a/src/common/filters/http-exception/http-exception.filter.ts b/src/common/filters/http-exception/http-exception.filter.ts new file mode 100644 index 0000000..c587769 --- /dev/null +++ b/src/common/filters/http-exception/http-exception.filter.ts @@ -0,0 +1,38 @@ +import { + ExceptionFilter, + Catch, + ArgumentsHost, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { Response } from 'express'; + +@Catch() +export class HttpExceptionFilter implements ExceptionFilter { + catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const status = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + const message = + exception instanceof HttpException + ? exception.getResponse() + : 'Internal server error'; + + const errorResponse = { + statusCode: status, + timestamp: new Date().toISOString(), + path: request.url, + error: message, + }; + + // Optionally log the exception + console.error(`Error occurred:`, exception); + + response.status(status).json(errorResponse); + } +} diff --git a/src/error-message/error-message.service.spec.ts b/src/error-message/error-message.service.spec.ts new file mode 100644 index 0000000..ae9c353 --- /dev/null +++ b/src/error-message/error-message.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ErrorMessageService } from './error-message.service'; + +describe('ErrorMessageService', () => { + let service: ErrorMessageService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ErrorMessageService], + }).compile(); + + service = module.get(ErrorMessageService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/error-message/error-message.service.ts b/src/error-message/error-message.service.ts new file mode 100644 index 0000000..83f7497 --- /dev/null +++ b/src/error-message/error-message.service.ts @@ -0,0 +1,40 @@ +// src/common/services/error-message.service.ts +import { Injectable } from '@nestjs/common'; +type ErrorMessageKey = keyof typeof ErrorMessageService.prototype.messages; + +@Injectable() +export class ErrorMessageService { + public readonly messages = { + NOT_FOUND: '{entity} not found', // Single key for "not found" errors + INVALID_MINUTES: 'Invalid minutes value', + INVALID_TIME_FORMAT: 'Invalid time format', + USER_NOT_FOUND: '{entity} not found', // Can reuse NOT_FOUND if desired + INTERNAL_SERVER_ERROR: 'Internal server error', + ERROR_ADDING_TEMP_PASSWORD: + 'Error adding {type} temporary password from Tuya', + INVALID_UUID: 'Invalid {entity} UUID', + USER_ALREADY_BELONGS: 'This user already belongs to this {entity}', + USER_HAS_NO_ENTITIES: 'This user has no {entity}', + DEVICE_OPERATION_FAILED: 'All device operations failed', + REQUEST_FAILED: 'Error processing {operation} request', + COOLDOWN_ERROR: + 'Please wait {time} more seconds before requesting a new OTP.', + }; + + getMessage( + key: ErrorMessageKey, + params?: Record, + ): string { + let message = this.messages[key] || 'Unknown error'; + + // Replace placeholders with provided params + if (params) { + Object.keys(params).forEach((param) => { + const regex = new RegExp(`{${param}}`, 'g'); + message = message.replace(regex, params[param].toString()); + }); + } + + return message; + } +} diff --git a/src/main.ts b/src/main.ts index 894ecd1..6dbfc45 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,7 @@ import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.sw import { ValidationPipe } from '@nestjs/common'; import { json, urlencoded } from 'body-parser'; import { SeederService } from '@app/common/seed/services/seeder.service'; +import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -15,6 +16,7 @@ async function bootstrap() { // Set the body parser limit to 1 MB app.use(json({ limit: '1mb' })); app.use(urlencoded({ limit: '1mb', extended: true })); + app.useGlobalFilters(new HttpExceptionFilter()); app.use( rateLimit({ diff --git a/src/region/region.module.ts b/src/region/region.module.ts index c0da5ab..22eaa2d 100644 --- a/src/region/region.module.ts +++ b/src/region/region.module.ts @@ -3,9 +3,10 @@ import { RegionService } from './services/region.service'; import { RegionController } from './controllers/region.controller'; import { ConfigModule } from '@nestjs/config'; import { RegionRepository } from '@app/common/modules/region/repositories'; +import { CommonModule } from '@app/common'; @Module({ - imports: [ConfigModule], + imports: [ConfigModule, CommonModule], controllers: [RegionController], providers: [RegionService, RegionRepository], exports: [RegionService], diff --git a/src/region/services/region.service.ts b/src/region/services/region.service.ts index 580f285..9061e0a 100644 --- a/src/region/services/region.service.ts +++ b/src/region/services/region.service.ts @@ -2,23 +2,33 @@ import { BadRequestException, HttpException, HttpStatus, + Inject, Injectable, } from '@nestjs/common'; import { RegionRepository } from '@app/common/modules/region/repositories'; +import { ErrorMessageService } from 'src/error-message/error-message.service'; @Injectable() export class RegionService { - constructor(private readonly regionRepository: RegionRepository) {} + constructor( + private readonly regionRepository: RegionRepository, + @Inject(ErrorMessageService) + private readonly errorMessageService: ErrorMessageService, + ) {} async getAllRegions() { try { const regions = await this.regionRepository.find(); - return regions; } catch (err) { if (err instanceof BadRequestException) { throw err; // Re-throw BadRequestException } else { - throw new HttpException('Regions found', HttpStatus.NOT_FOUND); + throw new HttpException( + this.errorMessageService.getMessage('NOT_FOUND', { + entity: 'Regions', + }), + HttpStatus.NOT_FOUND, + ); } } } From 4c0bbd2849dbd7a514ba5a3099adfe9164a9b159 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:10:43 -0500 Subject: [PATCH 24/34] finished added battery to water leak --- libs/common/src/constants/product-type.enum.ts | 1 + src/device/services/device.service.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 3ed4cfa..7ef741c 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -12,4 +12,5 @@ export enum ProductType { THREE_3TG = '3GT', TWO_2TG = '2GT', ONE_1TG = '1GT', + WL = 'WL', } diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index e69ad7a..1020614 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -839,6 +839,19 @@ export class DeviceService { battery = batteryStatus.value; } } + // Check if the device is a water leak sensor (WL) + if (device.productDevice.prodType === ProductType.WL) { + const doorSensorInstructionsStatus = + await this.getDevicesInstructionStatus(device.uuid); + + const batteryStatus: any = doorSensorInstructionsStatus.status.find( + (status: any) => status.code === 'battery_percentage', + ); + + if (batteryStatus) { + battery = batteryStatus.value; + } + } const spaceDevice = device?.spaceDevice; const parentDevice = spaceDevice?.parent; return { From 9a868c57577e21de85e1e96f443271d5d8ac19f2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 6 Oct 2024 15:44:49 +0300 Subject: [PATCH 25/34] added enums to replace constants --- libs/common/src/config/email.config.ts | 3 +- libs/common/src/config/tuya.config.ts | 3 +- libs/common/src/constants/automation.enum.ts | 9 ++ .../src/constants/battery-status.enum.ts | 4 + .../src/constants/boolean-values.enum.ts | 4 + libs/common/src/constants/days.enum.ts | 14 +++ .../src/constants/device-status.enum.ts | 4 + libs/common/src/constants/error-codes.enum.ts | 3 + .../src/constants/hours-minutes.enum.ts | 99 +++++++++++++++++++ .../src/constants/password-type.enum.ts | 4 + libs/common/src/constants/working-days.ts | 16 +-- .../controllers/devices-status.controller.ts | 3 +- .../src/helper/convertTimestampToDubaiTime.ts | 22 ++++- libs/common/src/helper/getScheduleStatus.ts | 30 ++++-- .../controllers/authentication.controller.ts | 3 +- src/auth/controllers/user-auth.controller.ts | 3 +- .../controllers/automation.controller.ts | 3 +- src/automation/services/automation.service.ts | 21 ++-- .../controllers/building.controller.ts | 6 +- src/building/dtos/get.building.dto.ts | 3 +- src/building/services/building.service.ts | 32 +++--- .../controllers/community.controller.ts | 6 +- src/community/dtos/get.community.dto.ts | 3 +- src/community/services/community.service.ts | 26 +++-- .../controllers/device-messages.controller.ts | 3 +- .../services/device-messages.service.ts | 3 +- src/device/controllers/device.controller.ts | 6 +- src/device/services/device.service.ts | 17 ++-- .../controllers/door.lock.controller.ts | 3 +- src/door-lock/services/door.lock.service.ts | 67 +++++++++---- src/door-lock/services/encryption.services.ts | 5 +- src/floor/controllers/floor.controller.ts | 6 +- src/floor/dtos/get.floor.dto.ts | 3 +- src/floor/services/floor.service.ts | 48 ++++++--- src/group/controllers/group.controller.ts | 3 +- src/guards/building.permission.guard.ts | 3 +- src/guards/building.type.guard.ts | 3 +- src/guards/community.permission.guard.ts | 3 +- src/guards/community.type.guard.ts | 3 +- src/guards/floor.permission.guard.ts | 3 +- src/guards/floor.type.guard.ts | 3 +- src/guards/room.guard.ts | 3 +- src/guards/room.permission.guard.ts | 3 +- src/guards/unit.permission.guard.ts | 3 +- src/guards/unit.type.guard.ts | 3 +- src/guards/user.building.guard.ts | 3 +- src/guards/user.community.guard.ts | 3 +- src/guards/user.floor.guard.ts | 3 +- src/guards/user.room.guard.ts | 3 +- src/guards/user.unit.guard.ts | 3 +- src/region/controllers/region.controller.ts | 3 +- src/role/controllers/role.controller.ts | 3 +- src/role/services/role.service.ts | 3 +- src/room/controllers/room.controller.ts | 6 +- src/room/services/room.service.ts | 18 ++-- src/scene/controllers/scene.controller.ts | 3 +- src/scene/services/scene.service.ts | 14 +-- .../controllers/schedule.controller.ts | 3 +- .../controllers/timezone.controller.ts | 3 +- src/unit/controllers/unit.controller.ts | 6 +- src/unit/services/unit.service.ts | 42 ++++---- .../user-device-permission.controller.ts | 3 +- .../user-device-permission.service.ts | 5 +- .../user-notification.controller.ts | 3 +- .../services/user-notification.service.ts | 3 +- src/users/controllers/user.controller.ts | 3 +- .../visitor-password.controller.ts | 3 +- .../services/visitor-password.service.ts | 92 +++++++++++------ 68 files changed, 547 insertions(+), 202 deletions(-) create mode 100644 libs/common/src/constants/automation.enum.ts create mode 100644 libs/common/src/constants/battery-status.enum.ts create mode 100644 libs/common/src/constants/boolean-values.enum.ts create mode 100644 libs/common/src/constants/days.enum.ts create mode 100644 libs/common/src/constants/device-status.enum.ts create mode 100644 libs/common/src/constants/error-codes.enum.ts create mode 100644 libs/common/src/constants/hours-minutes.enum.ts create mode 100644 libs/common/src/constants/password-type.enum.ts diff --git a/libs/common/src/config/email.config.ts b/libs/common/src/config/email.config.ts index 11e8c6f..3a5c21e 100644 --- a/libs/common/src/config/email.config.ts +++ b/libs/common/src/config/email.config.ts @@ -1,11 +1,12 @@ import { registerAs } from '@nestjs/config'; +import { BooleanValues } from '../constants/boolean-values.enum'; export default registerAs( 'email-config', (): Record => ({ SMTP_HOST: process.env.SMTP_HOST, SMTP_PORT: parseInt(process.env.SMTP_PORT), - SMTP_SECURE: process.env.SMTP_SECURE === 'true', + SMTP_SECURE: process.env.SMTP_SECURE === BooleanValues.TRUE, SMTP_USER: process.env.SMTP_USER, SMTP_SENDER: process.env.SMTP_SENDER, SMTP_PASSWORD: process.env.SMTP_PASSWORD, diff --git a/libs/common/src/config/tuya.config.ts b/libs/common/src/config/tuya.config.ts index ba3344e..bd0ed8e 100644 --- a/libs/common/src/config/tuya.config.ts +++ b/libs/common/src/config/tuya.config.ts @@ -1,4 +1,5 @@ import { registerAs } from '@nestjs/config'; +import { BooleanValues } from '../constants/boolean-values.enum'; export default registerAs( 'tuya-config', @@ -7,6 +8,6 @@ export default registerAs( TUYA_ACCESS_KEY: process.env.TUYA_ACCESS_KEY, TUYA_EU_URL: process.env.TUYA_EU_URL, TRUN_ON_TUYA_SOCKET: - process.env.TRUN_ON_TUYA_SOCKET === 'true' ? true : false, + process.env.TRUN_ON_TUYA_SOCKET === BooleanValues.TRUE ? true : false, }), ); diff --git a/libs/common/src/constants/automation.enum.ts b/libs/common/src/constants/automation.enum.ts new file mode 100644 index 0000000..7e431eb --- /dev/null +++ b/libs/common/src/constants/automation.enum.ts @@ -0,0 +1,9 @@ +// automation.enum.ts +export enum ActionExecutorEnum { + DEVICE_ISSUE = 'device_issue', + DELAY = 'delay', +} + +export enum EntityTypeEnum { + DEVICE_REPORT = 'device_report', +} diff --git a/libs/common/src/constants/battery-status.enum.ts b/libs/common/src/constants/battery-status.enum.ts new file mode 100644 index 0000000..de500c1 --- /dev/null +++ b/libs/common/src/constants/battery-status.enum.ts @@ -0,0 +1,4 @@ +export enum BatteryStatus { + RESIDUAL_ELECTRICITY = 'residual_electricity', + BATTERY_PERCENTAGE = 'battery_percentage', +} diff --git a/libs/common/src/constants/boolean-values.enum.ts b/libs/common/src/constants/boolean-values.enum.ts new file mode 100644 index 0000000..ee0d4aa --- /dev/null +++ b/libs/common/src/constants/boolean-values.enum.ts @@ -0,0 +1,4 @@ +export enum BooleanValues { + TRUE = 'true', + FALSE = 'false', +} diff --git a/libs/common/src/constants/days.enum.ts b/libs/common/src/constants/days.enum.ts new file mode 100644 index 0000000..737c6cf --- /dev/null +++ b/libs/common/src/constants/days.enum.ts @@ -0,0 +1,14 @@ +export enum DaysEnum { + SUN = 'Sun', + MON = 'Mon', + TUE = 'Tue', + WED = 'Wed', + THU = 'Thu', + FRI = 'Fri', + SAT = 'Sat', +} + +export enum EnableDisableStatusEnum { + DISABLED = '0', + ENABLED = '1', +} diff --git a/libs/common/src/constants/device-status.enum.ts b/libs/common/src/constants/device-status.enum.ts new file mode 100644 index 0000000..c1b0ebb --- /dev/null +++ b/libs/common/src/constants/device-status.enum.ts @@ -0,0 +1,4 @@ +export enum DeviceStatuses { + REJECTED = 'rejected', + FULLFILLED = 'fulfilled', +} diff --git a/libs/common/src/constants/error-codes.enum.ts b/libs/common/src/constants/error-codes.enum.ts new file mode 100644 index 0000000..c22ed89 --- /dev/null +++ b/libs/common/src/constants/error-codes.enum.ts @@ -0,0 +1,3 @@ +export enum CommonErrorCodes { + DUPLICATE_ENTITY = '23505', +} diff --git a/libs/common/src/constants/hours-minutes.enum.ts b/libs/common/src/constants/hours-minutes.enum.ts new file mode 100644 index 0000000..cff3350 --- /dev/null +++ b/libs/common/src/constants/hours-minutes.enum.ts @@ -0,0 +1,99 @@ +export enum CommonHours { + ONE = '01:00', + ONE_THIRTY = '01:30', + TWO = '02:00', + TWO_THIRTY = '02:30', + THREE = '03:00', + THREE_THIRTY = '03:30', + FOUR = '04:00', + FOUR_THIRTY = '04:30', + FIVE = '05:00', + FIVE_THIRTY = '05:30', + SIX = '06:00', + SIX_THIRTY = '06:30', + SEVEN = '07:00', + SEVEN_THIRTY = '07:30', + EIGHT = '08:00', + EIGHT_THIRTY = '08:30', + NINE = '09:00', + NINE_THIRTY = '09:30', + TEN = '10:00', + TEN_THIRTY = '10:30', + ELEVEN = '11:00', + ELEVEN_THIRTY = '11:30', + TWELVE = '12:00', + TWELVE_THIRTY = '12:30', + THIRTEEN = '13:00', + THIRTEEN_THIRTY = '13:30', + FOURTEEN = '14:00', + FOURTEEN_THIRTY = '14:30', + FIFTEEN = '15:00', + FIFTEEN_THIRTY = '15:30', + SIXTEEN = '16:00', + SIXTEEN_THIRTY = '16:30', + SEVENTEEN = '17:00', + SEVENTEEN_THIRTY = '17:30', + EIGHTEEN = '18:00', + EIGHTEEN_THIRTY = '18:30', + NINETEEN = '19:00', + NINETEEN_THIRTY = '19:30', + TWENTY = '20:00', + TWENTY_THIRTY = '20:30', + TWENTY_ONE = '21:00', + TWENTY_ONE_THIRTY = '21:30', + TWENTY_TWO = '22:00', + TWENTY_TWO_THIRTY = '22:30', + TWENTY_THREE = '23:00', + TWENTY_THREE_THIRTY = '23:30', + TWENTY_FOUR = '24:00', +} + +export enum CommonHourMinutes { + ONE = 60, + ONE_THIRTY = 90, + TWO = 120, + TWO_THIRTY = 150, + THREE = 180, + THREE_THIRTY = 210, + FOUR = 240, + FOUR_THIRTY = 270, + FIVE = 300, + FIVE_THIRTY = 330, + SIX = 360, + SIX_THIRTY = 390, + SEVEN = 420, + SEVEN_THIRTY = 450, + EIGHT = 480, + EIGHT_THIRTY = 510, + NINE = 540, + NINE_THIRTY = 570, + TEN = 600, + TEN_THIRTY = 630, + ELEVEN = 660, + ELEVEN_THIRTY = 690, + TWELVE = 720, + TWELVE_THIRTY = 750, + THIRTEEN = 780, + THIRTEEN_THIRTY = 810, + FOURTEEN = 840, + FOURTEEN_THIRTY = 870, + FIFTEEN = 900, + FIFTEEN_THIRTY = 930, + SIXTEEN = 960, + SIXTEEN_THIRTY = 990, + SEVENTEEN = 1020, + SEVENTEEN_THIRTY = 1050, + EIGHTEEN = 1080, + EIGHTEEN_THIRTY = 1110, + NINETEEN = 1140, + NINETEEN_THIRTY = 1170, + TWENTY = 1200, + TWENTY_THIRTY = 1230, + TWENTY_ONE = 1260, + TWENTY_ONE_THIRTY = 1290, + TWENTY_TWO = 1320, + TWENTY_TWO_THIRTY = 1350, + TWENTY_THREE = 1380, + TWENTY_THREE_THIRTY = 1410, + TWENTY_FOUR = 1440, +} diff --git a/libs/common/src/constants/password-type.enum.ts b/libs/common/src/constants/password-type.enum.ts new file mode 100644 index 0000000..904b584 --- /dev/null +++ b/libs/common/src/constants/password-type.enum.ts @@ -0,0 +1,4 @@ +export enum PasswordType { + SINGLE = 'single', + MULTIPLE = 'multiple', +} diff --git a/libs/common/src/constants/working-days.ts b/libs/common/src/constants/working-days.ts index d870d3c..38fc530 100644 --- a/libs/common/src/constants/working-days.ts +++ b/libs/common/src/constants/working-days.ts @@ -1,9 +1,11 @@ +import { DaysEnum } from './days.enum'; + export enum WorkingDays { - Sun = 'Sun', - Mon = 'Mon', - Tue = 'Tue', - Wed = 'Wed', - Thu = 'Thu', - Fri = 'Fri', - Sat = 'Sat', + Sun = DaysEnum.SUN, + Mon = DaysEnum.MON, + Tue = DaysEnum.TUE, + Wed = DaysEnum.WED, + Thu = DaysEnum.THU, + Fri = DaysEnum.FRI, + Sat = DaysEnum.SAT, } 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 index 70506c2..4a3458d 100644 --- a/libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts +++ b/libs/common/src/firebase/devices-status/controllers/devices-status.controller.ts @@ -2,10 +2,11 @@ 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'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Device Status Firebase Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'device-status-firebase', }) export class DeviceStatusFirebaseController { diff --git a/libs/common/src/helper/convertTimestampToDubaiTime.ts b/libs/common/src/helper/convertTimestampToDubaiTime.ts index 225e781..e1c235b 100644 --- a/libs/common/src/helper/convertTimestampToDubaiTime.ts +++ b/libs/common/src/helper/convertTimestampToDubaiTime.ts @@ -1,3 +1,5 @@ +import { EnableDisableStatusEnum } from '../constants/days.enum'; + export function convertTimestampToDubaiTime(timestamp) { // Convert timestamp to milliseconds const date = new Date(timestamp * 1000); @@ -8,12 +10,24 @@ export function convertTimestampToDubaiTime(timestamp) { // Format the date as YYYYMMDD const year = dubaiTime.getUTCFullYear(); - const month = String(dubaiTime.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-based - const day = String(dubaiTime.getUTCDate()).padStart(2, '0'); + const month = String(dubaiTime.getUTCMonth() + 1).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); // Months are zero-based + const day = String(dubaiTime.getUTCDate()).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); // Format the time as HH:MM (24-hour format) - const hours = String(dubaiTime.getUTCHours()).padStart(2, '0'); - const minutes = String(dubaiTime.getUTCMinutes()).padStart(2, '0'); + const hours = String(dubaiTime.getUTCHours()).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); + const minutes = String(dubaiTime.getUTCMinutes()).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); // Return formatted date and time return { diff --git a/libs/common/src/helper/getScheduleStatus.ts b/libs/common/src/helper/getScheduleStatus.ts index e77496c..3daf5c3 100644 --- a/libs/common/src/helper/getScheduleStatus.ts +++ b/libs/common/src/helper/getScheduleStatus.ts @@ -1,24 +1,42 @@ -export function getScheduleStatus(daysEnabled: string[]): string { - const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; +import { DaysEnum, EnableDisableStatusEnum } from '../constants/days.enum'; - const schedule: string[] = Array(7).fill('0'); +export function getScheduleStatus(daysEnabled: string[]): string { + const daysMap: string[] = [ + DaysEnum.SUN, + DaysEnum.MON, + DaysEnum.TUE, + DaysEnum.WED, + DaysEnum.THU, + DaysEnum.FRI, + DaysEnum.SAT, + ]; + + const schedule: string[] = Array(7).fill(EnableDisableStatusEnum.DISABLED); daysEnabled.forEach((day) => { const index: number = daysMap.indexOf(day); if (index !== -1) { - schedule[index] = '1'; + schedule[index] = EnableDisableStatusEnum.ENABLED; } }); return schedule.join(''); } export function getEnabledDays(schedule: string): string[] { - const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + const daysMap: string[] = [ + DaysEnum.SUN, + DaysEnum.MON, + DaysEnum.TUE, + DaysEnum.WED, + DaysEnum.THU, + DaysEnum.FRI, + DaysEnum.SAT, + ]; const enabledDays: string[] = []; // Iterate through the schedule string for (let i = 0; i < schedule.length; i++) { - if (schedule[i] === '1') { + if (schedule[i] === EnableDisableStatusEnum.ENABLED) { enabledDays.push(daysMap[i]); } } diff --git a/src/auth/controllers/authentication.controller.ts b/src/auth/controllers/authentication.controller.ts index ace6525..6f00558 100644 --- a/src/auth/controllers/authentication.controller.ts +++ b/src/auth/controllers/authentication.controller.ts @@ -1,9 +1,10 @@ import { Controller, Post } from '@nestjs/common'; import { AuthenticationService } from '../services/authentication.service'; import { ApiTags } from '@nestjs/swagger'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'authentication', }) @ApiTags('Tuya Auth') diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index 1eee19e..e539a71 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -15,9 +15,10 @@ import { UserLoginDto } from '../dtos/user-login.dto'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'authentication', }) @ApiTags('Auth') diff --git a/src/automation/controllers/automation.controller.ts b/src/automation/controllers/automation.controller.ts index 4328602..bbe4f29 100644 --- a/src/automation/controllers/automation.controller.ts +++ b/src/automation/controllers/automation.controller.ts @@ -18,10 +18,11 @@ import { UpdateAutomationStatusDto, } from '../dtos/automation.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Automation Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'automation', }) export class AutomationController { diff --git a/src/automation/services/automation.service.ts b/src/automation/services/automation.service.ts index bbb7651..3f5a4d5 100644 --- a/src/automation/services/automation.service.ts +++ b/src/automation/services/automation.service.ts @@ -23,6 +23,11 @@ import { GetAutomationByUnitInterface, } from '../interface/automation.interface'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { + ActionExecutorEnum, + EntityTypeEnum, +} from '@app/common/constants/automation.enum'; @Injectable() export class AutomationService { @@ -64,7 +69,7 @@ export class AutomationService { ); for (const action of actions) { - if (action.action_executor === 'device_issue') { + if (action.action_executor === ActionExecutorEnum.DEVICE_ISSUE) { const device = await this.deviceService.getDeviceByDeviceUuid( action.entity_id, false, @@ -76,7 +81,7 @@ export class AutomationService { } for (const condition of conditions) { - if (condition.entity_type === 'device_report') { + if (condition.entity_type === EntityTypeEnum.DEVICE_REPORT) { const device = await this.deviceService.getDeviceByDeviceUuid( condition.entity_id, false, @@ -127,12 +132,12 @@ export class AutomationService { where: { uuid: unitUuid, spaceType: { - type: 'unit', + type: SpaceType.UNIT, }, }, relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') { + if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -240,7 +245,7 @@ export class AutomationService { })); for (const action of actions) { - if (action.actionExecutor === 'device_issue') { + if (action.actionExecutor === ActionExecutorEnum.DEVICE_ISSUE) { const device = await this.deviceService.getDeviceByDeviceTuyaUuid( action.entityId, ); @@ -249,8 +254,8 @@ export class AutomationService { action.entityId = device.uuid; } } else if ( - action.actionExecutor !== 'device_issue' && - action.actionExecutor !== 'delay' + action.actionExecutor !== ActionExecutorEnum.DEVICE_ISSUE && + action.actionExecutor !== ActionExecutorEnum.DELAY ) { const sceneDetails = await this.getTapToRunSceneDetailsTuya( action.entityId, @@ -268,7 +273,7 @@ export class AutomationService { })); for (const condition of conditions) { - if (condition.entityType === 'device_report') { + if (condition.entityType === EntityTypeEnum.DEVICE_REPORT) { const device = await this.deviceService.getDeviceByDeviceTuyaUuid( condition.entityId, ); diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index a76d620..de48d33 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -20,11 +20,13 @@ import { CheckUserBuildingGuard } from 'src/guards/user.building.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { BuildingPermissionGuard } from 'src/guards/building.permission.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @ApiTags('Building Module') @Controller({ - version: '1', - path: 'building', + version: EnableDisableStatusEnum.ENABLED, + path: SpaceType.BUILDING, }) export class BuildingController { constructor(private readonly buildingService: BuildingService) {} diff --git a/src/building/dtos/get.building.dto.ts b/src/building/dtos/get.building.dto.ts index f762469..fae0c6b 100644 --- a/src/building/dtos/get.building.dto.ts +++ b/src/building/dtos/get.building.dto.ts @@ -1,3 +1,4 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { @@ -45,7 +46,7 @@ export class GetBuildingChildDto { @IsOptional() @IsBoolean() @Transform((value) => { - return value.obj.includeSubSpaces === 'true'; + return value.obj.includeSubSpaces === BooleanValues.TRUE; }) public includeSubSpaces: boolean = false; } diff --git a/src/building/services/building.service.ts b/src/building/services/building.service.ts index c9f8273..b3de5d4 100644 --- a/src/building/services/building.service.ts +++ b/src/building/services/building.service.ts @@ -18,6 +18,8 @@ import { import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; import { UserSpaceRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class BuildingService { @@ -31,7 +33,7 @@ export class BuildingService { try { const spaceType = await this.spaceTypeRepository.findOne({ where: { - type: 'building', + type: SpaceType.BUILDING, }, }); @@ -61,7 +63,7 @@ export class BuildingService { where: { uuid: buildingUuid, spaceType: { - type: 'building', + type: SpaceType.BUILDING, }, }, relations: ['spaceType'], @@ -69,7 +71,7 @@ export class BuildingService { if ( !building || !building.spaceType || - building.spaceType.type !== 'building' + building.spaceType.type !== SpaceType.BUILDING ) { throw new BadRequestException('Invalid building UUID'); } @@ -99,7 +101,11 @@ export class BuildingService { where: { uuid: buildingUuid }, relations: ['children', 'spaceType'], }); - if (!space || !space.spaceType || space.spaceType.type !== 'building') { + if ( + !space || + !space.spaceType || + space.spaceType.type !== SpaceType.BUILDING + ) { throw new BadRequestException('Invalid building UUID'); } @@ -147,8 +153,8 @@ export class BuildingService { return children .filter( (child) => - child.spaceType.type !== 'building' && - child.spaceType.type !== 'community', + child.spaceType.type !== SpaceType.BUILDING && + child.spaceType.type !== SpaceType.COMMUNITY, ) // Filter remaining building and community types .map((child) => ({ uuid: child.uuid, @@ -161,8 +167,8 @@ export class BuildingService { children .filter( (child) => - child.spaceType.type !== 'building' && - child.spaceType.type !== 'community', + child.spaceType.type !== SpaceType.BUILDING && + child.spaceType.type !== SpaceType.COMMUNITY, ) // Filter remaining building and community types .map(async (child) => ({ uuid: child.uuid, @@ -183,7 +189,7 @@ export class BuildingService { where: { uuid: buildingUuid, spaceType: { - type: 'building', + type: SpaceType.BUILDING, }, }, relations: ['spaceType', 'parent', 'parent.spaceType'], @@ -191,7 +197,7 @@ export class BuildingService { if ( !building || !building.spaceType || - building.spaceType.type !== 'building' + building.spaceType.type !== SpaceType.BUILDING ) { throw new BadRequestException('Invalid building UUID'); } @@ -222,7 +228,7 @@ export class BuildingService { relations: ['space', 'space.spaceType'], where: { user: { uuid: userUuid }, - space: { spaceType: { type: 'building' } }, + space: { spaceType: { type: SpaceType.BUILDING } }, }, }); @@ -254,7 +260,7 @@ export class BuildingService { space: { uuid: addUserBuildingDto.buildingUuid }, }); } catch (err) { - if (err.code === '23505') { + if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'User already belongs to this building', HttpStatus.BAD_REQUEST, @@ -279,7 +285,7 @@ export class BuildingService { if ( !building || !building.spaceType || - building.spaceType.type !== 'building' + building.spaceType.type !== SpaceType.BUILDING ) { throw new BadRequestException('Invalid building UUID'); } diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index af8d137..1be0d43 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -21,12 +21,14 @@ import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; // 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 { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; // import { CommunityPermissionGuard } from 'src/guards/community.permission.guard'; @ApiTags('Community Module') @Controller({ - version: '1', - path: 'community', + version: EnableDisableStatusEnum.ENABLED, + path: SpaceType.COMMUNITY, }) export class CommunityController { constructor(private readonly communityService: CommunityService) {} diff --git a/src/community/dtos/get.community.dto.ts b/src/community/dtos/get.community.dto.ts index be614e5..cae892b 100644 --- a/src/community/dtos/get.community.dto.ts +++ b/src/community/dtos/get.community.dto.ts @@ -1,3 +1,4 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { @@ -45,7 +46,7 @@ export class GetCommunityChildDto { @IsOptional() @IsBoolean() @Transform((value) => { - return value.obj.includeSubSpaces === 'true'; + return value.obj.includeSubSpaces === BooleanValues.TRUE; }) public includeSubSpaces: boolean = false; } diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 27d7bcf..145dc13 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -18,6 +18,8 @@ import { import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { UserSpaceRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class CommunityService { @@ -31,7 +33,7 @@ export class CommunityService { try { const spaceType = await this.spaceTypeRepository.findOne({ where: { - type: 'community', + type: SpaceType.COMMUNITY, }, }); @@ -53,7 +55,7 @@ export class CommunityService { where: { uuid: communityUuid, spaceType: { - type: 'community', + type: SpaceType.COMMUNITY, }, }, relations: ['spaceType'], @@ -61,7 +63,7 @@ export class CommunityService { if ( !community || !community.spaceType || - community.spaceType.type !== 'community' + community.spaceType.type !== SpaceType.COMMUNITY ) { throw new BadRequestException('Invalid community UUID'); } @@ -83,7 +85,7 @@ export class CommunityService { async getCommunities(): Promise { try { const community = await this.spaceRepository.find({ - where: { spaceType: { type: 'community' } }, + where: { spaceType: { type: SpaceType.COMMUNITY } }, relations: ['spaceType'], }); return community.map((community) => ({ @@ -109,7 +111,11 @@ export class CommunityService { relations: ['children', 'spaceType'], }); - if (!space || !space.spaceType || space.spaceType.type !== 'community') { + if ( + !space || + !space.spaceType || + space.spaceType.type !== SpaceType.COMMUNITY + ) { throw new BadRequestException('Invalid community UUID'); } const totalCount = await this.spaceRepository.count({ @@ -152,7 +158,7 @@ export class CommunityService { if (!children || children.length === 0 || !includeSubSpaces) { return children - .filter((child) => child.spaceType.type !== 'community') // Filter remaining community type + .filter((child) => child.spaceType.type !== SpaceType.COMMUNITY) // Filter remaining community type .map((child) => ({ uuid: child.uuid, name: child.spaceName, @@ -162,7 +168,7 @@ export class CommunityService { const childHierarchies = await Promise.all( children - .filter((child) => child.spaceType.type !== 'community') // Filter remaining community type + .filter((child) => child.spaceType.type !== SpaceType.COMMUNITY) // Filter remaining community type .map(async (child) => ({ uuid: child.uuid, name: child.spaceName, @@ -182,7 +188,7 @@ export class CommunityService { relations: ['space', 'space.spaceType'], where: { user: { uuid: userUuid }, - space: { spaceType: { type: 'community' } }, + space: { spaceType: { type: SpaceType.COMMUNITY } }, }, }); @@ -215,7 +221,7 @@ export class CommunityService { space: { uuid: addUserCommunityDto.communityUuid }, }); } catch (err) { - if (err.code === '23505') { + if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'User already belongs to this community', HttpStatus.BAD_REQUEST, @@ -240,7 +246,7 @@ export class CommunityService { if ( !community || !community.spaceType || - community.spaceType.type !== 'community' + community.spaceType.type !== SpaceType.COMMUNITY ) { throw new BadRequestException('Invalid community UUID'); } diff --git a/src/device-messages/controllers/device-messages.controller.ts b/src/device-messages/controllers/device-messages.controller.ts index c8f7834..fea206c 100644 --- a/src/device-messages/controllers/device-messages.controller.ts +++ b/src/device-messages/controllers/device-messages.controller.ts @@ -14,10 +14,11 @@ import { DeviceMessagesSubscriptionService } from '../services/device-messages.s import { DeviceMessagesAddDto } from '../dtos/device-messages.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Device Messages Status Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'device-messages/subscription', }) export class DeviceMessagesSubscriptionController { diff --git a/src/device-messages/services/device-messages.service.ts b/src/device-messages/services/device-messages.service.ts index 4722b79..fea62d5 100644 --- a/src/device-messages/services/device-messages.service.ts +++ b/src/device-messages/services/device-messages.service.ts @@ -1,6 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { DeviceMessagesAddDto } from '../dtos/device-messages.dto'; import { DeviceNotificationRepository } from '@app/common/modules/device/repositories'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class DeviceMessagesSubscriptionService { @@ -21,7 +22,7 @@ export class DeviceMessagesSubscriptionService { }, }); } catch (error) { - if (error.code === '23505') { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'This User already belongs to this device', HttpStatus.BAD_REQUEST, diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 6a844d9..30be830 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -30,10 +30,12 @@ import { CheckUserHaveControllablePermission } from 'src/guards/user.device.cont import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { CheckDeviceGuard } from 'src/guards/device.guard'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @ApiTags('Device Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'device', }) export class DeviceController { @@ -73,7 +75,7 @@ export class DeviceController { } @ApiBearerAuth() @UseGuards(JwtAuthGuard, CheckRoomGuard) - @Get('room') + @Get(SpaceType.ROOM) async getDevicesByRoomId( @Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, @Req() req: any, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 1020614..b8168c6 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -35,6 +35,8 @@ import { In } from 'typeorm'; import { ProductType } from '@app/common/constants/product-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; +import { DeviceStatuses } from '@app/common/constants/device-status.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class DeviceService { @@ -106,7 +108,7 @@ export class DeviceService { } return deviceSaved; } catch (error) { - if (error.code === '23505') { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'Device already exists', HttpStatus.BAD_REQUEST, @@ -355,7 +357,7 @@ export class DeviceService { const failedResults = []; for (const result of results) { - if (result.status === 'fulfilled') { + if (result.status === DeviceStatuses.FULLFILLED) { const { deviceUuid, result: operationResult } = result.value; if (operationResult.success) { @@ -457,7 +459,7 @@ export class DeviceService { const failedResults = []; for (const result of results) { - if (result.status === 'fulfilled') { + if (result.status === DeviceStatuses.FULLFILLED) { const { deviceUuid, result: operationResult } = result.value; if (operationResult.success) { @@ -819,7 +821,8 @@ export class DeviceService { await this.getDevicesInstructionStatus(device.uuid); const batteryStatus: any = doorLockInstructionsStatus.status.find( - (status: any) => status.code === 'residual_electricity', + (status: any) => + status.code === batteryStatus.RESIDUAL_ELECTRICITY, ); if (batteryStatus) { @@ -832,7 +835,7 @@ export class DeviceService { await this.getDevicesInstructionStatus(device.uuid); const batteryStatus: any = doorSensorInstructionsStatus.status.find( - (status: any) => status.code === 'battery_percentage', + (status: any) => status.code === batteryStatus.BATTERY_PERCENTAGE, ); if (batteryStatus) { @@ -845,7 +848,7 @@ export class DeviceService { await this.getDevicesInstructionStatus(device.uuid); const batteryStatus: any = doorSensorInstructionsStatus.status.find( - (status: any) => status.code === 'battery_percentage', + (status: any) => status.code === batteryStatus.BATTERY_PERCENTAGE, ); if (batteryStatus) { @@ -877,7 +880,7 @@ export class DeviceService { // Filter out rejected promises and extract the fulfilled values const fulfilledDevices = devicesData - .filter((result) => result.status === 'fulfilled') + .filter((result) => result.status === DeviceStatuses.FULLFILLED) .map( (result) => (result as PromiseFulfilledResult).value, diff --git a/src/door-lock/controllers/door.lock.controller.ts b/src/door-lock/controllers/door.lock.controller.ts index 56ba2b8..680904b 100644 --- a/src/door-lock/controllers/door.lock.controller.ts +++ b/src/door-lock/controllers/door.lock.controller.ts @@ -16,10 +16,11 @@ import { AddDoorLockOnlineDto } from '../dtos/add.online-temp.dto'; import { AddDoorLockOfflineTempMultipleTimeDto } from '../dtos/add.offline-temp.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { UpdateDoorLockOfflineTempDto } from '../dtos/update.offline-temp.dto'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Door Lock Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'door-lock', }) export class DoorLockController { diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 5864c5d..1d4f291 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -24,6 +24,15 @@ 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'; +import { + DaysEnum, + EnableDisableStatusEnum, +} from '@app/common/constants/days.enum'; +import { PasswordType } from '@app/common/constants/password-type.enum'; +import { + CommonHourMinutes, + CommonHours, +} from '@app/common/constants/hours-minutes.enum'; @Injectable() export class DoorLockService { @@ -115,7 +124,7 @@ export class DoorLockService { ); const passwords = await this.getTemporaryOfflinePasswordsTuya( deviceDetails.deviceTuyaUuid, - 'multiple', + PasswordType.MULTIPLE, isExpired, ); if (!passwords.result.records.length && fromVisitor) { @@ -502,7 +511,7 @@ export class DoorLockService { } const createOnceOfflinePass = await this.addOfflineTemporaryPasswordTuya( deviceDetails.deviceTuyaUuid, - 'multiple', + PasswordType.MULTIPLE, addDoorLockOfflineTempMultipleTimeDto, ); if (!createOnceOfflinePass.success) { @@ -566,7 +575,7 @@ export class DoorLockService { method: 'POST', path, body: { - ...(type === 'multiple' && { + ...(type === PasswordType.MULTIPLE && { effective_time: addDoorLockOfflineTempMultipleTimeDto.effectiveTime, invalid_time: addDoorLockOfflineTempMultipleTimeDto.invalidTime, }), @@ -711,7 +720,7 @@ export class DoorLockService { schedule_list: scheduleList, }), - type: '0', + type: EnableDisableStatusEnum.DISABLED, }, }); @@ -725,7 +734,15 @@ export class DoorLockService { } getWorkingDayValue(days) { // Array representing the days of the week - const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + const weekDays = [ + DaysEnum.SAT, + DaysEnum.FRI, + DaysEnum.THU, + DaysEnum.WED, + DaysEnum.TUE, + DaysEnum.MON, + DaysEnum.SUN, + ]; // Initialize a binary string with 7 bits let binaryString = '0000000'; @@ -734,10 +751,10 @@ export class DoorLockService { days.forEach((day) => { const index = weekDays.indexOf(day); if (index !== -1) { - // Set the corresponding bit to '1' + // Set the corresponding bit to EnableDisableStatusEnum.ENABLED binaryString = binaryString.substring(0, index) + - '1' + + EnableDisableStatusEnum.ENABLED + binaryString.substring(index + 1); } }); @@ -749,17 +766,27 @@ export class DoorLockService { } getDaysFromWorkingDayValue(workingDayValue) { // Array representing the days of the week - const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + const weekDays = [ + DaysEnum.SAT, + DaysEnum.FRI, + DaysEnum.THU, + DaysEnum.WED, + DaysEnum.TUE, + DaysEnum.MON, + DaysEnum.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'); + const binaryString = workingDayValue + .toString(2) + .padStart(7, EnableDisableStatusEnum.DISABLED); // 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') { + if (binaryString[i] === EnableDisableStatusEnum.ENABLED) { days.push(weekDays[i]); } } @@ -769,8 +796,8 @@ export class DoorLockService { timeToMinutes(timeStr) { try { // Special case for "24:00" - if (timeStr === '24:00') { - return 1440; + if (timeStr === CommonHours.TWENTY_FOUR) { + return CommonHourMinutes.TWENTY_FOUR; } // Regular expression to validate the 24-hour time format (HH:MM) @@ -798,20 +825,26 @@ export class DoorLockService { if ( typeof totalMinutes !== 'number' || totalMinutes < 0 || - totalMinutes > 1440 + totalMinutes > CommonHourMinutes.TWENTY_FOUR ) { throw new Error('Invalid minutes value'); } - if (totalMinutes === 1440) { - return '24:00'; + if (totalMinutes === CommonHourMinutes.TWENTY_FOUR) { + return CommonHours.TWENTY_FOUR; } const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; - const formattedHours = String(hours).padStart(2, '0'); - const formattedMinutes = String(minutes).padStart(2, '0'); + const formattedHours = String(hours).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); + const formattedMinutes = String(minutes).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); return `${formattedHours}:${formattedMinutes}`; } catch (error) { diff --git a/src/door-lock/services/encryption.services.ts b/src/door-lock/services/encryption.services.ts index 7e56afe..b87ef03 100644 --- a/src/door-lock/services/encryption.services.ts +++ b/src/door-lock/services/encryption.services.ts @@ -1,6 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as CryptoJS from 'crypto-js'; import { ConfigService } from '@nestjs/config'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @Injectable() export class PasswordEncryptionService { @@ -43,7 +44,9 @@ export class PasswordEncryptionService { 'auth-config.SECRET_KEY', ); // The accessSecret must be 32 bytes, ensure it is properly padded or truncated - const paddedAccessSecret = accessSecret.padEnd(32, '0').slice(0, 32); + const paddedAccessSecret = accessSecret + .padEnd(32, EnableDisableStatusEnum.DISABLED) + .slice(0, 32); const plainTextTicketKey = this.decrypt(ticketKey, paddedAccessSecret); return this.encrypt(plainTextPassword, plainTextTicketKey); diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index b4940fe..c83e06b 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -20,11 +20,13 @@ import { CheckUserFloorGuard } from 'src/guards/user.floor.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { FloorPermissionGuard } from 'src/guards/floor.permission.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @ApiTags('Floor Module') @Controller({ - version: '1', - path: 'floor', + version: EnableDisableStatusEnum.ENABLED, + path: SpaceType.FLOOR, }) export class FloorController { constructor(private readonly floorService: FloorService) {} diff --git a/src/floor/dtos/get.floor.dto.ts b/src/floor/dtos/get.floor.dto.ts index 23a8e56..957f81b 100644 --- a/src/floor/dtos/get.floor.dto.ts +++ b/src/floor/dtos/get.floor.dto.ts @@ -1,3 +1,4 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { @@ -45,7 +46,7 @@ export class GetFloorChildDto { @IsOptional() @IsBoolean() @Transform((value) => { - return value.obj.includeSubSpaces === 'true'; + return value.obj.includeSubSpaces === BooleanValues.TRUE; }) public includeSubSpaces: boolean = false; } diff --git a/src/floor/services/floor.service.ts b/src/floor/services/floor.service.ts index 5113206..1f7604f 100644 --- a/src/floor/services/floor.service.ts +++ b/src/floor/services/floor.service.ts @@ -18,6 +18,8 @@ import { import { SpaceEntity } from '@app/common/modules/space/entities'; import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; import { UserSpaceRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class FloorService { @@ -31,7 +33,7 @@ export class FloorService { try { const spaceType = await this.spaceTypeRepository.findOne({ where: { - type: 'floor', + type: SpaceType.FLOOR, }, }); @@ -52,12 +54,16 @@ export class FloorService { where: { uuid: floorUuid, spaceType: { - type: 'floor', + type: SpaceType.FLOOR, }, }, relations: ['spaceType'], }); - if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') { + if ( + !floor || + !floor.spaceType || + floor.spaceType.type !== SpaceType.FLOOR + ) { throw new BadRequestException('Invalid floor UUID'); } @@ -88,7 +94,11 @@ export class FloorService { relations: ['children', 'spaceType'], }); - if (!space || !space.spaceType || space.spaceType.type !== 'floor') { + if ( + !space || + !space.spaceType || + space.spaceType.type !== SpaceType.FLOOR + ) { throw new BadRequestException('Invalid floor UUID'); } const totalCount = await this.spaceRepository.count({ @@ -135,9 +145,9 @@ export class FloorService { return children .filter( (child) => - child.spaceType.type !== 'floor' && - child.spaceType.type !== 'building' && - child.spaceType.type !== 'community', + child.spaceType.type !== SpaceType.FLOOR && + child.spaceType.type !== SpaceType.BUILDING && + child.spaceType.type !== SpaceType.COMMUNITY, ) // Filter remaining floor and building and community types .map((child) => ({ uuid: child.uuid, @@ -150,9 +160,9 @@ export class FloorService { children .filter( (child) => - child.spaceType.type !== 'floor' && - child.spaceType.type !== 'building' && - child.spaceType.type !== 'community', + child.spaceType.type !== SpaceType.FLOOR && + child.spaceType.type !== SpaceType.BUILDING && + child.spaceType.type !== SpaceType.COMMUNITY, ) // Filter remaining floor and building and community types .map(async (child) => ({ uuid: child.uuid, @@ -171,12 +181,16 @@ export class FloorService { where: { uuid: floorUuid, spaceType: { - type: 'floor', + type: SpaceType.FLOOR, }, }, relations: ['spaceType', 'parent', 'parent.spaceType'], }); - if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') { + if ( + !floor || + !floor.spaceType || + floor.spaceType.type !== SpaceType.FLOOR + ) { throw new BadRequestException('Invalid floor UUID'); } @@ -207,7 +221,7 @@ export class FloorService { relations: ['space', 'space.spaceType'], where: { user: { uuid: userUuid }, - space: { spaceType: { type: 'floor' } }, + space: { spaceType: { type: SpaceType.FLOOR } }, }, }); @@ -239,7 +253,7 @@ export class FloorService { space: { uuid: addUserFloorDto.floorUuid }, }); } catch (err) { - if (err.code === '23505') { + if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'User already belongs to this floor', HttpStatus.BAD_REQUEST, @@ -261,7 +275,11 @@ export class FloorService { relations: ['spaceType'], }); - if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') { + if ( + !floor || + !floor.spaceType || + floor.spaceType.type !== SpaceType.FLOOR + ) { throw new BadRequestException('Invalid floor UUID'); } diff --git a/src/group/controllers/group.controller.ts b/src/group/controllers/group.controller.ts index ce6fae8..0300dd2 100644 --- a/src/group/controllers/group.controller.ts +++ b/src/group/controllers/group.controller.ts @@ -11,10 +11,11 @@ import { import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { UnitPermissionGuard } from 'src/guards/unit.permission.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Group Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'group', }) export class GroupController { diff --git a/src/guards/building.permission.guard.ts b/src/guards/building.permission.guard.ts index d73f19b..5d496a9 100644 --- a/src/guards/building.permission.guard.ts +++ b/src/guards/building.permission.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; import { BadRequestException, @@ -24,7 +25,7 @@ export class BuildingPermissionGuard implements CanActivate { await this.permissionService.checkUserPermission( buildingUuid, user.uuid, - 'building', + SpaceType.BUILDING, ); return true; diff --git a/src/guards/building.type.guard.ts b/src/guards/building.type.guard.ts index 1e36d9f..dd2870e 100644 --- a/src/guards/building.type.guard.ts +++ b/src/guards/building.type.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -42,7 +43,7 @@ export class CheckBuildingTypeGuard implements CanActivate { if ( !buildingData || !buildingData.spaceType || - buildingData.spaceType.type !== 'building' + buildingData.spaceType.type !== SpaceType.BUILDING ) { throw new BadRequestException('Invalid building UUID'); } diff --git a/src/guards/community.permission.guard.ts b/src/guards/community.permission.guard.ts index 2e44cd1..a078b70 100644 --- a/src/guards/community.permission.guard.ts +++ b/src/guards/community.permission.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; import { BadRequestException, @@ -24,7 +25,7 @@ export class CommunityPermissionGuard implements CanActivate { await this.permissionService.checkUserPermission( communityUuid, user.uuid, - 'community', + SpaceType.COMMUNITY, ); return true; diff --git a/src/guards/community.type.guard.ts b/src/guards/community.type.guard.ts index e8212f4..9dc5d01 100644 --- a/src/guards/community.type.guard.ts +++ b/src/guards/community.type.guard.ts @@ -6,6 +6,7 @@ import { } from '@nestjs/common'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException } from '@nestjs/common'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckCommunityTypeGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckCommunityTypeGuard implements CanActivate { if ( !communityData || !communityData.spaceType || - communityData.spaceType.type !== 'community' + communityData.spaceType.type !== SpaceType.COMMUNITY ) { throw new BadRequestException('Invalid community UUID'); } diff --git a/src/guards/floor.permission.guard.ts b/src/guards/floor.permission.guard.ts index 5e5c6ce..7092264 100644 --- a/src/guards/floor.permission.guard.ts +++ b/src/guards/floor.permission.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; import { BadRequestException, @@ -24,7 +25,7 @@ export class FloorPermissionGuard implements CanActivate { await this.permissionService.checkUserPermission( floorUuid, user.uuid, - 'floor', + SpaceType.FLOOR, ); return true; diff --git a/src/guards/floor.type.guard.ts b/src/guards/floor.type.guard.ts index 8064cb2..3e6b875 100644 --- a/src/guards/floor.type.guard.ts +++ b/src/guards/floor.type.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -42,7 +43,7 @@ export class CheckFloorTypeGuard implements CanActivate { if ( !floorData || !floorData.spaceType || - floorData.spaceType.type !== 'floor' + floorData.spaceType.type !== SpaceType.FLOOR ) { throw new BadRequestException('Invalid floor UUID'); } diff --git a/src/guards/room.guard.ts b/src/guards/room.guard.ts index c5ed514..1fe9f93 100644 --- a/src/guards/room.guard.ts +++ b/src/guards/room.guard.ts @@ -8,6 +8,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckRoomGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckRoomGuard implements CanActivate { where: { uuid: roomUuid, spaceType: { - type: 'room', + type: SpaceType.ROOM, }, }, }); diff --git a/src/guards/room.permission.guard.ts b/src/guards/room.permission.guard.ts index 721a92f..d1e7042 100644 --- a/src/guards/room.permission.guard.ts +++ b/src/guards/room.permission.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; import { BadRequestException, @@ -24,7 +25,7 @@ export class RoomPermissionGuard implements CanActivate { await this.permissionService.checkUserPermission( roomUuid, user.uuid, - 'room', + SpaceType.ROOM, ); return true; diff --git a/src/guards/unit.permission.guard.ts b/src/guards/unit.permission.guard.ts index e0d4958..fe2b969 100644 --- a/src/guards/unit.permission.guard.ts +++ b/src/guards/unit.permission.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; import { BadRequestException, @@ -24,7 +25,7 @@ export class UnitPermissionGuard implements CanActivate { await this.permissionService.checkUserPermission( unitUuid, user.uuid, - 'unit', + SpaceType.UNIT, ); return true; diff --git a/src/guards/unit.type.guard.ts b/src/guards/unit.type.guard.ts index f1e292f..a753756 100644 --- a/src/guards/unit.type.guard.ts +++ b/src/guards/unit.type.guard.ts @@ -1,3 +1,4 @@ +import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -42,7 +43,7 @@ export class CheckUnitTypeGuard implements CanActivate { if ( !unitData || !unitData.spaceType || - unitData.spaceType.type !== 'unit' + unitData.spaceType.type !== SpaceType.UNIT ) { throw new BadRequestException('Invalid unit UUID'); } diff --git a/src/guards/user.building.guard.ts b/src/guards/user.building.guard.ts index b47124d..3f74500 100644 --- a/src/guards/user.building.guard.ts +++ b/src/guards/user.building.guard.ts @@ -7,6 +7,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserBuildingGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckUserBuildingGuard implements CanActivate { private async checkBuildingIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: 'building' } }, + where: { uuid: spaceUuid, spaceType: { type: SpaceType.BUILDING } }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.community.guard.ts b/src/guards/user.community.guard.ts index 04e08b4..e8dea71 100644 --- a/src/guards/user.community.guard.ts +++ b/src/guards/user.community.guard.ts @@ -7,6 +7,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserCommunityGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckUserCommunityGuard implements CanActivate { private async checkCommunityIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: 'community' } }, + where: { uuid: spaceUuid, spaceType: { type: SpaceType.COMMUNITY } }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.floor.guard.ts b/src/guards/user.floor.guard.ts index 9235fb8..6faa520 100644 --- a/src/guards/user.floor.guard.ts +++ b/src/guards/user.floor.guard.ts @@ -7,6 +7,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserFloorGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckUserFloorGuard implements CanActivate { private async checkFloorIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: 'floor' } }, + where: { uuid: spaceUuid, spaceType: { type: SpaceType.FLOOR } }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.room.guard.ts b/src/guards/user.room.guard.ts index 0ecf817..49c77b8 100644 --- a/src/guards/user.room.guard.ts +++ b/src/guards/user.room.guard.ts @@ -7,6 +7,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserRoomGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckUserRoomGuard implements CanActivate { private async checkRoomIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: 'room' } }, + where: { uuid: spaceUuid, spaceType: { type: SpaceType.ROOM } }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.unit.guard.ts b/src/guards/user.unit.guard.ts index 4d7b1ab..eb60a27 100644 --- a/src/guards/user.unit.guard.ts +++ b/src/guards/user.unit.guard.ts @@ -7,6 +7,7 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserUnitGuard implements CanActivate { @@ -43,7 +44,7 @@ export class CheckUserUnitGuard implements CanActivate { private async checkUnitIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: 'unit' } }, + where: { uuid: spaceUuid, spaceType: { type: SpaceType.UNIT } }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/region/controllers/region.controller.ts b/src/region/controllers/region.controller.ts index 8df4a11..bb911e2 100644 --- a/src/region/controllers/region.controller.ts +++ b/src/region/controllers/region.controller.ts @@ -2,10 +2,11 @@ import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common'; import { RegionService } from '../services/region.service'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { ControllerRoute } from '@app/common/constants/controller-route'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Region Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: ControllerRoute.REGION.ROUTE, }) export class RegionController { diff --git a/src/role/controllers/role.controller.ts b/src/role/controllers/role.controller.ts index cb2038d..90f549f 100644 --- a/src/role/controllers/role.controller.ts +++ b/src/role/controllers/role.controller.ts @@ -11,10 +11,11 @@ import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { RoleService } from '../services/role.service'; import { AddUserRoleDto } from '../dtos'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Role Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'role', }) export class RoleController { diff --git a/src/role/services/role.service.ts b/src/role/services/role.service.ts index 42076d4..ba10f44 100644 --- a/src/role/services/role.service.ts +++ b/src/role/services/role.service.ts @@ -3,6 +3,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AddUserRoleDto } from '../dtos/role.add.dto'; import { UserRoleRepository } from '@app/common/modules/user/repositories'; import { QueryFailedError } from 'typeorm'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class RoleService { @@ -24,7 +25,7 @@ export class RoleService { } catch (error) { if ( error instanceof QueryFailedError && - error.driverError.code === '23505' + error.driverError.code === CommonErrorCodes.DUPLICATE_ENTITY ) { // Postgres unique constraint violation error code throw new HttpException( diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index 0a92e57..80fb920 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -18,11 +18,13 @@ import { CheckUserRoomGuard } from 'src/guards/user.room.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { RoomPermissionGuard } from 'src/guards/room.permission.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @ApiTags('Room Module') @Controller({ - version: '1', - path: 'room', + version: EnableDisableStatusEnum.ENABLED, + path: SpaceType.ROOM, }) export class RoomController { constructor(private readonly roomService: RoomService) {} diff --git a/src/room/services/room.service.ts b/src/room/services/room.service.ts index 7babd6b..340df0f 100644 --- a/src/room/services/room.service.ts +++ b/src/room/services/room.service.ts @@ -15,6 +15,8 @@ import { } from '../interface/room.interface'; import { UpdateRoomNameDto } from '../dtos/update.room.dto'; import { UserSpaceRepository } from '@app/common/modules/user/repositories'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class RoomService { @@ -28,7 +30,7 @@ export class RoomService { try { const spaceType = await this.spaceTypeRepository.findOne({ where: { - type: 'room', + type: SpaceType.ROOM, }, }); @@ -49,12 +51,12 @@ export class RoomService { where: { uuid: roomUuid, spaceType: { - type: 'room', + type: SpaceType.ROOM, }, }, relations: ['spaceType'], }); - if (!room || !room.spaceType || room.spaceType.type !== 'room') { + if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { throw new BadRequestException('Invalid room UUID'); } @@ -80,12 +82,12 @@ export class RoomService { where: { uuid: roomUuid, spaceType: { - type: 'room', + type: SpaceType.ROOM, }, }, relations: ['spaceType', 'parent', 'parent.spaceType'], }); - if (!room || !room.spaceType || room.spaceType.type !== 'room') { + if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { throw new BadRequestException('Invalid room UUID'); } @@ -116,7 +118,7 @@ export class RoomService { relations: ['space', 'space.spaceType'], where: { user: { uuid: userUuid }, - space: { spaceType: { type: 'room' } }, + space: { spaceType: { type: SpaceType.ROOM } }, }, }); @@ -145,7 +147,7 @@ export class RoomService { space: { uuid: addUserRoomDto.roomUuid }, }); } catch (err) { - if (err.code === '23505') { + if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'User already belongs to this room', HttpStatus.BAD_REQUEST, @@ -167,7 +169,7 @@ export class RoomService { relations: ['spaceType'], }); - if (!room || !room.spaceType || room.spaceType.type !== 'room') { + if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { throw new BadRequestException('Invalid room UUID'); } diff --git a/src/scene/controllers/scene.controller.ts b/src/scene/controllers/scene.controller.ts index 40af2a8..8800f4d 100644 --- a/src/scene/controllers/scene.controller.ts +++ b/src/scene/controllers/scene.controller.ts @@ -14,10 +14,11 @@ import { import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { AddSceneTapToRunDto, UpdateSceneTapToRunDto } from '../dtos/scene.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Scene Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'scene', }) export class SceneController { diff --git a/src/scene/services/scene.service.ts b/src/scene/services/scene.service.ts index 66bb769..30e53aa 100644 --- a/src/scene/services/scene.service.ts +++ b/src/scene/services/scene.service.ts @@ -18,6 +18,8 @@ import { SceneDetailsResult, } from '../interface/scene.interface'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { ActionExecutorEnum } from '@app/common/constants/automation.enum'; @Injectable() export class SceneService { @@ -63,7 +65,7 @@ export class SceneService { const convertedData = convertKeysToSnakeCase(actions); for (const action of convertedData) { - if (action.action_executor === 'device_issue') { + if (action.action_executor === ActionExecutorEnum.DEVICE_ISSUE) { const device = await this.deviceService.getDeviceByDeviceUuid( action.entity_id, false, @@ -108,12 +110,12 @@ export class SceneService { where: { uuid: unitUuid, spaceType: { - type: 'unit', + type: SpaceType.UNIT, }, }, relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') { + if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -249,7 +251,7 @@ export class SceneService { }); for (const action of actions) { - if (action.actionExecutor === 'device_issue') { + if (action.actionExecutor === ActionExecutorEnum.DEVICE_ISSUE) { const device = await this.deviceService.getDeviceByDeviceTuyaUuid( action.entityId, ); @@ -258,8 +260,8 @@ export class SceneService { action.entityId = device.uuid; } } else if ( - action.actionExecutor !== 'device_issue' && - action.actionExecutor !== 'delay' + action.actionExecutor !== ActionExecutorEnum.DEVICE_ISSUE && + action.actionExecutor !== ActionExecutorEnum.DELAY ) { const sceneDetails = await this.getTapToRunSceneDetailsTuya( action.entityId, diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts index 63bf182..e69064a 100644 --- a/src/schedule/controllers/schedule.controller.ts +++ b/src/schedule/controllers/schedule.controller.ts @@ -21,10 +21,11 @@ import { } from '../dtos/schedule.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Schedule Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'schedule', }) export class ScheduleController { diff --git a/src/timezone/controllers/timezone.controller.ts b/src/timezone/controllers/timezone.controller.ts index fc4dd8a..28d009e 100644 --- a/src/timezone/controllers/timezone.controller.ts +++ b/src/timezone/controllers/timezone.controller.ts @@ -8,10 +8,11 @@ import { import { TimeZoneService } from '../services/timezone.service'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('TimeZone Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'timezone', }) export class TimeZoneController { diff --git a/src/unit/controllers/unit.controller.ts b/src/unit/controllers/unit.controller.ts index 1d5cbd3..67e91a6 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -23,11 +23,13 @@ import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard'; import { CheckUserUnitGuard } from 'src/guards/user.unit.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { UnitPermissionGuard } from 'src/guards/unit.permission.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { SpaceType } from '@app/common/constants/space-type.enum'; @ApiTags('Unit Module') @Controller({ - version: '1', - path: 'unit', + version: EnableDisableStatusEnum.ENABLED, + path: SpaceType.UNIT, }) export class UnitController { constructor(private readonly unitService: UnitService) {} diff --git a/src/unit/services/unit.service.ts b/src/unit/services/unit.service.ts index b5dfb8f..826c044 100644 --- a/src/unit/services/unit.service.ts +++ b/src/unit/services/unit.service.ts @@ -24,6 +24,8 @@ import { UserDevicePermissionService } from 'src/user-device-permission/services import { PermissionType } from '@app/common/constants/permission-type.enum'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; +import { SpaceType } from '@app/common/constants/space-type.enum'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class UnitService { @@ -50,7 +52,7 @@ export class UnitService { try { const spaceType = await this.spaceTypeRepository.findOne({ where: { - type: 'unit', + type: SpaceType.UNIT, }, }); const tuyaUnit = await this.addUnitTuya(addUnitDto.unitName); @@ -95,12 +97,12 @@ export class UnitService { where: { uuid: unitUuid, spaceType: { - type: 'unit', + type: SpaceType.UNIT, }, }, relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') { + if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -131,7 +133,11 @@ export class UnitService { relations: ['children', 'spaceType'], }); - if (!space || !space.spaceType || space.spaceType.type !== 'unit') { + if ( + !space || + !space.spaceType || + space.spaceType.type !== SpaceType.UNIT + ) { throw new BadRequestException('Invalid unit UUID'); } @@ -174,10 +180,10 @@ export class UnitService { return children .filter( (child) => - child.spaceType.type !== 'unit' && - child.spaceType.type !== 'floor' && - child.spaceType.type !== 'community' && - child.spaceType.type !== 'unit', + child.spaceType.type !== SpaceType.UNIT && + child.spaceType.type !== SpaceType.FLOOR && + child.spaceType.type !== SpaceType.COMMUNITY && + child.spaceType.type !== SpaceType.UNIT, ) // Filter remaining unit and floor and community and unit types .map((child) => ({ uuid: child.uuid, @@ -190,10 +196,10 @@ export class UnitService { children .filter( (child) => - child.spaceType.type !== 'unit' && - child.spaceType.type !== 'floor' && - child.spaceType.type !== 'community' && - child.spaceType.type !== 'unit', + child.spaceType.type !== SpaceType.UNIT && + child.spaceType.type !== SpaceType.FLOOR && + child.spaceType.type !== SpaceType.COMMUNITY && + child.spaceType.type !== SpaceType.UNIT, ) // Filter remaining unit and floor and community and unit types .map(async (child) => ({ uuid: child.uuid, @@ -212,12 +218,12 @@ export class UnitService { where: { uuid: unitUuid, spaceType: { - type: 'unit', + type: SpaceType.UNIT, }, }, relations: ['spaceType', 'parent', 'parent.spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') { + if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -246,7 +252,7 @@ export class UnitService { relations: ['space', 'space.spaceType'], where: { user: { uuid: userUuid }, - space: { spaceType: { type: 'unit' } }, + space: { spaceType: { type: SpaceType.UNIT } }, }, }); @@ -276,7 +282,7 @@ export class UnitService { space: { uuid: addUserUnitDto.unitUuid }, }); } catch (err) { - if (err.code === '23505') { + if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'User already belongs to this unit', HttpStatus.BAD_REQUEST, @@ -298,7 +304,7 @@ export class UnitService { relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') { + if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { throw new BadRequestException('Invalid unit UUID'); } @@ -383,7 +389,7 @@ export class UnitService { const unit = await this.spaceRepository.findOneOrFail({ where: { invitationCode: inviteCode, - spaceType: { type: 'unit' }, + spaceType: { type: SpaceType.UNIT }, }, relations: ['spaceType'], }); diff --git a/src/user-device-permission/controllers/user-device-permission.controller.ts b/src/user-device-permission/controllers/user-device-permission.controller.ts index 2f68708..bb998df 100644 --- a/src/user-device-permission/controllers/user-device-permission.controller.ts +++ b/src/user-device-permission/controllers/user-device-permission.controller.ts @@ -15,10 +15,11 @@ import { UserDevicePermissionService } from '../services/user-device-permission. import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Device Permission Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'device-permission', }) export class UserDevicePermissionController { 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 9ec93e6..aa2df6d 100644 --- a/src/user-device-permission/services/user-device-permission.service.ts +++ b/src/user-device-permission/services/user-device-permission.service.ts @@ -3,6 +3,7 @@ import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.d import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class UserDevicePermissionService { @@ -24,7 +25,7 @@ export class UserDevicePermissionService { }, }); } catch (error) { - if (error.code === '23505') { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'This User already belongs to this device', HttpStatus.BAD_REQUEST, @@ -54,7 +55,7 @@ export class UserDevicePermissionService { }, ); } catch (error) { - if (error.code === '23505') { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'This User already belongs to this device', HttpStatus.BAD_REQUEST, diff --git a/src/user-notification/controllers/user-notification.controller.ts b/src/user-notification/controllers/user-notification.controller.ts index 83fd215..900fc40 100644 --- a/src/user-notification/controllers/user-notification.controller.ts +++ b/src/user-notification/controllers/user-notification.controller.ts @@ -17,10 +17,11 @@ import { } from '../dtos/user-notification.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('User Notification Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'user-notification/subscription', }) export class UserNotificationController { diff --git a/src/user-notification/services/user-notification.service.ts b/src/user-notification/services/user-notification.service.ts index 8c9616b..b2208bb 100644 --- a/src/user-notification/services/user-notification.service.ts +++ b/src/user-notification/services/user-notification.service.ts @@ -4,6 +4,7 @@ import { UserNotificationUpdateDto, } from '../dtos/user-notification.dto'; import { UserNotificationRepository } from '@app/common/modules/user/repositories'; +import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; @Injectable() export class UserNotificationService { @@ -20,7 +21,7 @@ export class UserNotificationService { subscriptionUuid: userNotificationAddDto.subscriptionUuid, }); } catch (error) { - if (error.code === '23505') { + if (error.code === CommonErrorCodes.DUPLICATE_ENTITY) { throw new HttpException( 'This User already has this subscription uuid', HttpStatus.BAD_REQUEST, diff --git a/src/users/controllers/user.controller.ts b/src/users/controllers/user.controller.ts index c669f08..c0de10b 100644 --- a/src/users/controllers/user.controller.ts +++ b/src/users/controllers/user.controller.ts @@ -20,10 +20,11 @@ import { } from '../dtos'; import { CheckProfilePictureGuard } from 'src/guards/profile.picture.guard'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('User Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'user', }) export class UserController { diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index bdd4324..e17d7a8 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -17,10 +17,11 @@ import { AddDoorLockOnlineOneTimeDto, } from '../dtos/temp-pass.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; @ApiTags('Visitor Password Module') @Controller({ - version: '1', + version: EnableDisableStatusEnum.ENABLED, path: 'visitor-password', }) export class VisitorPasswordController { diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index f15b8e4..9fefd0d 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -20,6 +20,16 @@ import { PasswordEncryptionService } from 'src/door-lock/services/encryption.ser import { DoorLockService } from 'src/door-lock/services'; import { GetDeviceDetailsInterface } from 'src/device/interfaces/get.device.interface'; import { DeviceService } from 'src/device/services'; +import { DeviceStatuses } from '@app/common/constants/device-status.enum'; +import { + DaysEnum, + EnableDisableStatusEnum, +} from '@app/common/constants/days.enum'; +import { PasswordType } from '@app/common/constants/password-type.enum'; +import { + CommonHourMinutes, + CommonHours, +} from '@app/common/constants/hours-minutes.enum'; @Injectable() export class VisitorPasswordService { @@ -67,7 +77,7 @@ export class VisitorPasswordService { const createMultipleOfflinePass = await this.addOfflineTemporaryPasswordTuya( deviceDetails.deviceTuyaUuid, - 'multiple', + PasswordType.MULTIPLE, addDoorLockOfflineMultipleDto, addDoorLockOfflineMultipleDto.passwordName, ); @@ -117,7 +127,7 @@ export class VisitorPasswordService { const successfulResults = deviceResults .filter( (result) => - result.status === 'fulfilled' && + result.status === DeviceStatuses.FULLFILLED && (result as PromiseFulfilledResult).value.success, ) .map((result) => (result as PromiseFulfilledResult).value); @@ -125,11 +135,11 @@ export class VisitorPasswordService { const failedResults = deviceResults .filter( (result) => - result.status === 'rejected' || + result.status === DeviceStatuses.REJECTED || !(result as PromiseFulfilledResult).value.success, ) .map((result) => - result.status === 'rejected' + result.status === DeviceStatuses.REJECTED ? { success: false, error: @@ -238,7 +248,7 @@ export class VisitorPasswordService { const successfulResults = deviceResults .filter( (result) => - result.status === 'fulfilled' && + result.status === DeviceStatuses.FULLFILLED && (result as PromiseFulfilledResult).value.success, ) .map((result) => (result as PromiseFulfilledResult).value); @@ -246,11 +256,11 @@ export class VisitorPasswordService { const failedResults = deviceResults .filter( (result) => - result.status === 'rejected' || + result.status === DeviceStatuses.REJECTED || !(result as PromiseFulfilledResult).value.success, ) .map((result) => - result.status === 'rejected' + result.status === DeviceStatuses.REJECTED ? { success: false, error: @@ -298,7 +308,7 @@ export class VisitorPasswordService { method: 'POST', path, body: { - ...(type === 'multiple' && { + ...(type === PasswordType.MULTIPLE && { effective_time: addDoorLockOfflineMultipleDto.effectiveTime, invalid_time: addDoorLockOfflineMultipleDto.invalidTime, }), @@ -389,7 +399,7 @@ export class VisitorPasswordService { const successfulResults = deviceResults .filter( (result) => - result.status === 'fulfilled' && + result.status === DeviceStatuses.FULLFILLED && (result as PromiseFulfilledResult).value.success, ) .map((result) => (result as PromiseFulfilledResult).value); @@ -397,11 +407,11 @@ export class VisitorPasswordService { const failedResults = deviceResults .filter( (result) => - result.status === 'rejected' || + result.status === DeviceStatuses.REJECTED || !(result as PromiseFulfilledResult).value.success, ) .map((result) => - result.status === 'rejected' + result.status === DeviceStatuses.REJECTED ? { success: false, error: @@ -573,7 +583,7 @@ export class VisitorPasswordService { const successfulResults = deviceResults .filter( (result) => - result.status === 'fulfilled' && + result.status === DeviceStatuses.FULLFILLED && (result as PromiseFulfilledResult).value.success, ) .map((result) => (result as PromiseFulfilledResult).value); @@ -581,11 +591,11 @@ export class VisitorPasswordService { const failedResults = deviceResults .filter( (result) => - result.status === 'rejected' || + result.status === DeviceStatuses.REJECTED || !(result as PromiseFulfilledResult).value.success, ) .map((result) => - result.status === 'rejected' + result.status === DeviceStatuses.REJECTED ? { success: false, error: @@ -707,7 +717,7 @@ export class VisitorPasswordService { schedule_list: scheduleList, }), - type: '0', + type: EnableDisableStatusEnum.DISABLED, }, }); @@ -722,7 +732,15 @@ export class VisitorPasswordService { getWorkingDayValue(days) { // Array representing the days of the week - const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + const weekDays = [ + DaysEnum.SAT, + DaysEnum.FRI, + DaysEnum.THU, + DaysEnum.WED, + DaysEnum.TUE, + DaysEnum.MON, + DaysEnum.SUN, + ]; // Initialize a binary string with 7 bits let binaryString = '0000000'; @@ -731,10 +749,10 @@ export class VisitorPasswordService { days.forEach((day) => { const index = weekDays.indexOf(day); if (index !== -1) { - // Set the corresponding bit to '1' + // Set the corresponding bit to EnableDisableStatusEnum.ENABLED binaryString = binaryString.substring(0, index) + - '1' + + EnableDisableStatusEnum.ENABLED + binaryString.substring(index + 1); } }); @@ -746,17 +764,27 @@ export class VisitorPasswordService { } getDaysFromWorkingDayValue(workingDayValue) { // Array representing the days of the week - const weekDays = ['Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon', 'Sun']; + const weekDays = [ + DaysEnum.SAT, + DaysEnum.FRI, + DaysEnum.THU, + DaysEnum.WED, + DaysEnum.TUE, + DaysEnum.MON, + DaysEnum.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'); + const binaryString = workingDayValue + .toString(2) + .padStart(7, EnableDisableStatusEnum.DISABLED); // 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') { + if (binaryString[i] === EnableDisableStatusEnum.ENABLED) { days.push(weekDays[i]); } } @@ -766,8 +794,8 @@ export class VisitorPasswordService { timeToMinutes(timeStr) { try { // Special case for "24:00" - if (timeStr === '24:00') { - return 1440; + if (timeStr === CommonHours.TWENTY_FOUR) { + return CommonHourMinutes.TWENTY_FOUR; } // Regular expression to validate the 24-hour time format (HH:MM) @@ -795,20 +823,26 @@ export class VisitorPasswordService { if ( typeof totalMinutes !== 'number' || totalMinutes < 0 || - totalMinutes > 1440 + totalMinutes > CommonHourMinutes.TWENTY_FOUR ) { throw new Error('Invalid minutes value'); } - if (totalMinutes === 1440) { - return '24:00'; + if (totalMinutes === CommonHourMinutes.TWENTY_FOUR) { + return CommonHours.TWENTY_FOUR; } const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; - const formattedHours = String(hours).padStart(2, '0'); - const formattedMinutes = String(minutes).padStart(2, '0'); + const formattedHours = String(hours).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); + const formattedMinutes = String(minutes).padStart( + 2, + EnableDisableStatusEnum.DISABLED, + ); return `${formattedHours}:${formattedMinutes}`; } catch (error) { @@ -846,7 +880,7 @@ export class VisitorPasswordService { invalid_time: addDeviceObj.invalidTime, password_type: 'ticket', ticket_id: addDeviceObj.ticketId, - type: '1', + type: EnableDisableStatusEnum.ENABLED, }, }); From b16f6ceaa78c29deafa0fe6a324de380d55688f3 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:14:22 -0500 Subject: [PATCH 26/34] finish add garage door --- libs/common/src/constants/product-type.enum.ts | 1 + src/schedule/services/schedule.service.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 7ef741c..4f72a25 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -13,4 +13,5 @@ export enum ProductType { TWO_2TG = '2GT', ONE_1TG = '1GT', WL = 'WL', + GD = 'GD', } diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index d2bb1f5..6af6a76 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -56,7 +56,8 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.WH && deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && + deviceDetails.productDevice.prodType !== ProductType.GD ) { throw new HttpException( 'This device is not supported for schedule', @@ -113,7 +114,8 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.WH && deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && + deviceDetails.productDevice.prodType !== ProductType.GD ) { throw new HttpException( 'This device is not supported for schedule', @@ -166,7 +168,8 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.WH && deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && + deviceDetails.productDevice.prodType !== ProductType.GD ) { throw new HttpException( 'This device is not supported for schedule', @@ -233,7 +236,8 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.WH && deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && + deviceDetails.productDevice.prodType !== ProductType.GD ) { throw new HttpException( 'This device is not supported for schedule', @@ -317,7 +321,8 @@ export class ScheduleService { deviceDetails.productDevice.prodType !== ProductType.WH && deviceDetails.productDevice.prodType !== ProductType.ONE_1TG && deviceDetails.productDevice.prodType !== ProductType.TWO_2TG && - deviceDetails.productDevice.prodType !== ProductType.THREE_3TG + deviceDetails.productDevice.prodType !== ProductType.THREE_3TG && + deviceDetails.productDevice.prodType !== ProductType.GD ) { throw new HttpException( 'This device is not supported for schedule', From b3b83f838c4b17e38be94fb0bbf1d50be68cfac0 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:58:50 -0500 Subject: [PATCH 27/34] fix gateway issue --- src/device/services/device.service.ts | 30 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 1020614..e45152b 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -667,19 +667,25 @@ export class DeviceService { const devices = await Promise.all( response.map(async (device: any) => { - const deviceDetails = await this.getDeviceByDeviceTuyaUuid(device.id); - if (deviceDetails.deviceTuyaUuid) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, ...rest } = device; - return { - ...rest, - tuyaUuid: deviceDetails.deviceTuyaUuid, - uuid: deviceDetails.uuid, - productUuid: deviceDetails.productDevice.uuid, - productType: deviceDetails.productDevice.prodType, - }; + try { + const deviceDetails = await this.getDeviceByDeviceTuyaUuid( + device.id, + ); + if (deviceDetails.deviceTuyaUuid) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...rest } = device; + return { + ...rest, + tuyaUuid: deviceDetails.deviceTuyaUuid, + uuid: deviceDetails.uuid, + productUuid: deviceDetails.productDevice.uuid, + productType: deviceDetails.productDevice.prodType, + }; + } + return null; + } catch (error) { + return null; } - return null; }), ); From 69d2aece43241417be4aed0104560abbc6072646 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:28:06 -0500 Subject: [PATCH 28/34] remove unused imports --- libs/common/src/auth/services/auth.service.ts | 1 - src/auth/services/user-auth.service.ts | 2 -- src/community/controllers/community.controller.ts | 2 -- 3 files changed, 5 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 86bb826..5792eb0 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -10,7 +10,6 @@ import { UserRepository } from '../../../../common/src/modules/user/repositories import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository'; import { UserSessionEntity } from '../../../../common/src/modules/session/entities'; import { ConfigService } from '@nestjs/config'; -import axios from 'axios'; import { OAuth2Client } from 'google-auth-library'; @Injectable() diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 7191e3b..c924658 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -3,7 +3,6 @@ import { BadRequestException, ForbiddenException, Injectable, - UnauthorizedException, } from '@nestjs/common'; import { UserSignUpDto } from '../dtos/user-auth.dto'; import { HelperHashService } from '../../../libs/common/src/helper/services'; @@ -19,7 +18,6 @@ import * as argon2 from 'argon2'; import { differenceInSeconds } from '@app/common/helper/differenceInSeconds'; import { LessThan, MoreThan } from 'typeorm'; import { ConfigService } from '@nestjs/config'; -import { UUID } from 'typeorm/driver/mongodb/bson.typings'; @Injectable() export class UserAuthService { diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index af8d137..3c9a313 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -18,10 +18,8 @@ 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 { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -// import { CommunityPermissionGuard } from 'src/guards/community.permission.guard'; @ApiTags('Community Module') @Controller({ From f5dcdc0ed526f0b3d12f70130fb50951b7cc0441 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:41:31 -0500 Subject: [PATCH 29/34] add filter to get groups api --- .../common/src/constants/product-type.enum.ts | 1 + src/group/services/group.service.ts | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index 4f72a25..6442f87 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -14,4 +14,5 @@ export enum ProductType { ONE_1TG = '1GT', WL = 'WL', GD = 'GD', + CUR = 'CUR', } diff --git a/src/group/services/group.service.ts b/src/group/services/group.service.ts index 4f1a1f7..f549afc 100644 --- a/src/group/services/group.service.ts +++ b/src/group/services/group.service.ts @@ -7,6 +7,7 @@ import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { PermissionType } from '@app/common/constants/permission-type.enum'; import { In } from 'typeorm'; +import { ProductType } from '@app/common/constants/product-type.enum'; @Injectable() export class GroupService { @@ -44,8 +45,24 @@ export class GroupService { }); const uniqueGroupNames = [...new Set(groupNames)]; - - return uniqueGroupNames.map((groupName) => ({ groupName })); + const groups = uniqueGroupNames.map((groupName) => ({ + groupName: groupName as ProductType, + })); + const allowedProductTypes = [ + ProductType.ONE_1TG, + ProductType.TWO_2TG, + ProductType.THREE_3TG, + ProductType.THREE_G, + ProductType.TWO_G, + ProductType.ONE_G, + ProductType.GD, + ProductType.WH, + ProductType.AC, + ProductType.CUR, + ]; + return groups.filter((group) => + allowedProductTypes.includes(group.groupName), + ); } catch (error) { throw new HttpException( 'This unit does not have any groups', From 6bfb64370a665e9583179a5b600c2c237de45124 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:11:16 -0500 Subject: [PATCH 30/34] finished soft delete devices --- .../services/devices-status.service.ts | 2 ++ .../modules/device/entities/device.entity.ts | 3 ++- src/device/services/device.service.ts | 19 +++++++++++++++++-- src/door-lock/services/door.lock.service.ts | 1 + src/guards/device.product.guard.ts | 4 ++-- src/guards/room.guard.ts | 1 + ...er.device.controllable.permission.guard.ts | 6 +++++- src/guards/user.device.permission.guard.ts | 6 +++++- src/schedule/services/schedule.service.ts | 1 + .../services/visitor-password.service.ts | 3 +++ 10 files changed, 39 insertions(+), 7 deletions(-) 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 index 267d6b6..f075f34 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -85,6 +85,7 @@ export class DeviceStatusFirebaseService { return await this.deviceRepository.findOne({ where: { deviceTuyaUuid, + isActive: true, }, relations: ['productDevice'], }); @@ -139,6 +140,7 @@ export class DeviceStatusFirebaseService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index c02f8a1..5a96255 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -18,8 +18,9 @@ export class DeviceEntity extends AbstractEntity { @Column({ nullable: true, default: true, + type: 'boolean', }) - isActive: true; + isActive: boolean; @ManyToOne(() => UserEntity, (user) => user.userSpaces, { nullable: false }) user: UserEntity; diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index e45152b..68e11b5 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -62,6 +62,7 @@ export class DeviceService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); @@ -70,6 +71,7 @@ export class DeviceService { return await this.deviceRepository.findOne({ where: { deviceTuyaUuid, + isActive: true, }, relations: ['productDevice'], }); @@ -126,6 +128,7 @@ export class DeviceService { const devices = await this.deviceRepository.find({ where: { user: { uuid: userUuid }, + isActive: true, permission: { userUuid, permissionType: { @@ -172,6 +175,7 @@ export class DeviceService { const devices = await this.deviceRepository.find({ where: { spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid }, + isActive: true, permission: { userUuid, permissionType: { @@ -221,6 +225,7 @@ export class DeviceService { const device = await this.deviceRepository.findOne({ where: { uuid: updateDeviceInRoomDto.deviceUuid, + isActive: true, }, relations: ['spaceDevice', 'spaceDevice.parent'], }); @@ -407,7 +412,7 @@ export class DeviceService { } async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { const firstDevice = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[0] }, + where: { uuid: deviceUuids[0], isActive: true }, relations: ['productDevice'], }); @@ -419,7 +424,7 @@ export class DeviceService { for (let i = 1; i < deviceUuids.length; i++) { const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[i] }, + where: { uuid: deviceUuids[i], isActive: true }, relations: ['productDevice'], }); @@ -463,6 +468,11 @@ export class DeviceService { if (operationResult.success) { // Add to success results if operationResult.success is true successResults.push({ deviceUuid, result: operationResult }); + // Update isActive to false in the repository for the successfully reset device + await this.deviceRepository.update( + { uuid: deviceUuid }, + { isActive: false }, + ); } else { // Add to failed results if operationResult.success is false failedResults.push({ deviceUuid, error: operationResult.msg }); @@ -643,6 +653,7 @@ export class DeviceService { const device = await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, permission: { userUuid: userUuid, }, @@ -774,6 +785,9 @@ export class DeviceService { parent: { uuid: unitUuid, }, + devicesSpaceEntity: { + isActive: true, + }, }, relations: ['devicesSpaceEntity', 'devicesSpaceEntity.productDevice'], }); @@ -808,6 +822,7 @@ export class DeviceService { async getAllDevices(): Promise { try { const devices = await this.deviceRepository.find({ + where: { isActive: true }, relations: [ 'spaceDevice.parent', 'productDevice', diff --git a/src/door-lock/services/door.lock.service.ts b/src/door-lock/services/door.lock.service.ts index 5864c5d..4007cea 100644 --- a/src/door-lock/services/door.lock.service.ts +++ b/src/door-lock/services/door.lock.service.ts @@ -826,6 +826,7 @@ export class DoorLockService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); diff --git a/src/guards/device.product.guard.ts b/src/guards/device.product.guard.ts index 108307a..6bf84cc 100644 --- a/src/guards/device.product.guard.ts +++ b/src/guards/device.product.guard.ts @@ -28,7 +28,7 @@ export class CheckProductUuidForAllDevicesGuard implements CanActivate { async checkAllDevicesHaveSameProductUuid(deviceUuids: string[]) { const firstDevice = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[0] }, + where: { uuid: deviceUuids[0], isActive: true }, relations: ['productDevice'], }); @@ -40,7 +40,7 @@ export class CheckProductUuidForAllDevicesGuard implements CanActivate { for (let i = 1; i < deviceUuids.length; i++) { const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuids[i] }, + where: { uuid: deviceUuids[i], isActive: true }, relations: ['productDevice'], }); diff --git a/src/guards/room.guard.ts b/src/guards/room.guard.ts index c5ed514..97fb992 100644 --- a/src/guards/room.guard.ts +++ b/src/guards/room.guard.ts @@ -55,6 +55,7 @@ export class CheckRoomGuard implements CanActivate { const response = await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, }); diff --git a/src/guards/user.device.controllable.permission.guard.ts b/src/guards/user.device.controllable.permission.guard.ts index 2684c8d..3736368 100644 --- a/src/guards/user.device.controllable.permission.guard.ts +++ b/src/guards/user.device.controllable.permission.guard.ts @@ -58,7 +58,11 @@ export class CheckUserHaveControllablePermission implements CanActivate { deviceUuid: string, ): Promise { const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuid, permission: { userUuid: userUuid } }, + where: { + uuid: deviceUuid, + isActive: true, + permission: { userUuid: userUuid }, + }, relations: ['permission', 'permission.permissionType'], }); diff --git a/src/guards/user.device.permission.guard.ts b/src/guards/user.device.permission.guard.ts index e63a08e..adf78e4 100644 --- a/src/guards/user.device.permission.guard.ts +++ b/src/guards/user.device.permission.guard.ts @@ -59,7 +59,11 @@ export class CheckUserHavePermission implements CanActivate { deviceUuid: string, ): Promise { const device = await this.deviceRepository.findOne({ - where: { uuid: deviceUuid, permission: { userUuid: userUuid } }, + where: { + uuid: deviceUuid, + permission: { userUuid: userUuid }, + isActive: true, + }, relations: ['permission', 'permission.permissionType'], }); diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index 6af6a76..ee029cf 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -298,6 +298,7 @@ export class ScheduleService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index f15b8e4..f03d370 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -440,6 +440,7 @@ export class VisitorPasswordService { productDevice: { prodType: ProductType.DL, }, + isActive: true, }, }); const data = []; @@ -482,6 +483,7 @@ export class VisitorPasswordService { productDevice: { prodType: ProductType.DL, }, + isActive: true, }, relations: ['productDevice'], }); @@ -823,6 +825,7 @@ export class VisitorPasswordService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, + isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); From bd3945f2ee944865ff38903d0305e8a674377c96 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:09:21 -0500 Subject: [PATCH 31/34] remove tuya auth --- src/app.module.ts | 2 - src/auth/auth.module.ts | 7 +- .../controllers/authentication.controller.ts | 16 --- src/auth/controllers/index.ts | 1 - src/auth/services/authentication.service.ts | 120 ------------------ src/auth/services/index.ts | 1 - 6 files changed, 2 insertions(+), 145 deletions(-) delete mode 100644 src/auth/controllers/authentication.controller.ts delete mode 100644 src/auth/services/authentication.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 29d07ac..5c3a52c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,7 +2,6 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import config from './config'; import { AuthenticationModule } from './auth/auth.module'; -import { AuthenticationController } from './auth/controllers/authentication.controller'; import { UserModule } from './users/user.module'; import { RoomModule } from './room/room.module'; import { GroupModule } from './group/group.module'; @@ -53,7 +52,6 @@ import { ScheduleModule } from './schedule/schedule.module'; VisitorPasswordModule, ScheduleModule, ], - controllers: [AuthenticationController], providers: [ { provide: APP_INTERCEPTOR, diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 012312d..66d335d 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,6 +1,4 @@ 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 '@app/common/modules/user/user.repository.module'; import { CommonModule } from '../../libs/common/src'; @@ -16,9 +14,8 @@ import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; @Module({ imports: [ConfigModule, UserRepositoryModule, CommonModule], - controllers: [AuthenticationController, UserAuthController], + controllers: [UserAuthController], providers: [ - AuthenticationService, UserAuthService, UserRepository, UserSessionRepository, @@ -26,6 +23,6 @@ import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; UserRoleRepository, RoleTypeRepository, ], - exports: [AuthenticationService, UserAuthService], + exports: [UserAuthService], }) export class AuthenticationModule {} diff --git a/src/auth/controllers/authentication.controller.ts b/src/auth/controllers/authentication.controller.ts deleted file mode 100644 index ace6525..0000000 --- a/src/auth/controllers/authentication.controller.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Controller, Post } from '@nestjs/common'; -import { AuthenticationService } from '../services/authentication.service'; -import { ApiTags } from '@nestjs/swagger'; - -@Controller({ - version: '1', - path: 'authentication', -}) -@ApiTags('Tuya Auth') -export class AuthenticationController { - constructor(private readonly authenticationService: AuthenticationService) {} - @Post('auth2') - async Authentication() { - return await this.authenticationService.main(); - } -} diff --git a/src/auth/controllers/index.ts b/src/auth/controllers/index.ts index 2ce466d..f63ac56 100644 --- a/src/auth/controllers/index.ts +++ b/src/auth/controllers/index.ts @@ -1,2 +1 @@ -export * from './authentication.controller'; export * from './user-auth.controller'; diff --git a/src/auth/services/authentication.service.ts b/src/auth/services/authentication.service.ts deleted file mode 100644 index 1d5d580..0000000 --- a/src/auth/services/authentication.service.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import * as qs from 'qs'; -import * as crypto from 'crypto'; -import { ConfigService } from '@nestjs/config'; -import axios from 'axios'; -@Injectable() -export class AuthenticationService { - private token: string; - private deviceId: string; - private accessKey: string; - private secretKey: string; - - constructor(private readonly configService: ConfigService) { - (this.deviceId = this.configService.get('auth-config.DEVICE_ID')), - (this.accessKey = this.configService.get( - 'auth-config.ACCESS_KEY', - )), - (this.secretKey = this.configService.get( - 'auth-config.SECRET_KEY', - )); - } - - async main() { - await this.getToken(); - const data = await this.getDeviceInfo(this.deviceId); - console.log('fetch success: ', JSON.stringify(data)); - return JSON.stringify(data); - } - - async getToken() { - const method = 'GET'; - const timestamp = Date.now().toString(); - const signUrl = 'https://openapi.tuyaeu.com/v1.0/token?grant_type=1'; - const contentHash = crypto.createHash('sha256').update('').digest('hex'); - const stringToSign = [method, contentHash, '', signUrl].join('\n'); - const signStr = this.accessKey + timestamp + stringToSign; - - const headers = { - t: timestamp, - sign_method: 'HMAC-SHA256', - client_id: this.accessKey, - sign: await this.encryptStr(signStr, this.secretKey), - }; - - const { data: login } = await axios.get( - 'https://openapi.tuyaeu.com/v1.0/token', - { - params: { - grant_type: 1, - }, - headers, - }, - ); - - if (!login || !login.success) { - throw new Error(`fetch failed: ${login.msg}`); - } - this.token = login.result.access_token; - } - - async getDeviceInfo(deviceId: string) { - const query = {}; - const method = 'POST'; - const url = `https://openapi.tuyaeu.com/v1.0/devices/${deviceId}/commands`; - const reqHeaders: { [k: string]: string } = await this.getRequestSign( - url, - method, - {}, - query, - ); - - const { data } = await axios.post(url, {}, reqHeaders); - - if (!data || !data.success) { - throw new Error(`request api failed: ${data.msg}`); - } - - return data; - } - - async encryptStr(str: string, secret: string): Promise { - return crypto - .createHmac('sha256', secret) - .update(str, 'utf8') - .digest('hex') - .toUpperCase(); - } - - async getRequestSign( - path: string, - method: string, - query: { [k: string]: any } = {}, - body: { [k: string]: any } = {}, - ) { - const t = Date.now().toString(); - const [uri, pathQuery] = path.split('?'); - const queryMerged = Object.assign(query, qs.parse(pathQuery)); - const sortedQuery: { [k: string]: string } = {}; - Object.keys(queryMerged) - .sort() - .forEach((i) => (sortedQuery[i] = query[i])); - - const querystring = decodeURIComponent(qs.stringify(sortedQuery)); - const url = querystring ? `${uri}?${querystring}` : uri; - const contentHash = crypto - .createHash('sha256') - .update(JSON.stringify(body)) - .digest('hex'); - const stringToSign = [method, contentHash, '', url].join('\n'); - const signStr = this.accessKey + this.token + t + stringToSign; - return { - t, - path: url, - client_id: 'this.accessKey', - sign: await this.encryptStr(signStr, this.secretKey), - sign_method: 'HMAC-SHA256', - access_token: this.token, - }; - } -} diff --git a/src/auth/services/index.ts b/src/auth/services/index.ts index ac532d6..aa322a1 100644 --- a/src/auth/services/index.ts +++ b/src/auth/services/index.ts @@ -1,2 +1 @@ -export * from './authentication.service'; export * from './user-auth.service'; From 898791ae93b10e111572a65281114e0a07b1e521 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:13:00 -0500 Subject: [PATCH 32/34] remove rename from apis url --- src/building/controllers/building.controller.ts | 2 +- src/community/controllers/community.controller.ts | 2 +- src/floor/controllers/floor.controller.ts | 2 +- src/room/controllers/room.controller.ts | 2 +- src/unit/controllers/unit.controller.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index a76d620..ad61cb3 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -134,7 +134,7 @@ export class BuildingController { @ApiBearerAuth() @UseGuards(JwtAuthGuard, BuildingPermissionGuard) - @Put('rename/:buildingUuid') + @Put(':buildingUuid') async renameBuildingByUuid( @Param('buildingUuid') buildingUuid: string, @Body() updateBuildingDto: UpdateBuildingNameDto, diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 3c9a313..adc38b7 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -133,7 +133,7 @@ export class CommunityController { } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Put('rename/:communityUuid') + @Put(':communityUuid') async renameCommunityByUuid( @Param('communityUuid') communityUuid: string, @Body() updateCommunityDto: UpdateCommunityNameDto, diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index b4940fe..be7d921 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -134,7 +134,7 @@ export class FloorController { @ApiBearerAuth() @UseGuards(JwtAuthGuard, FloorPermissionGuard) - @Put('rename/:floorUuid') + @Put(':floorUuid') async renameFloorByUuid( @Param('floorUuid') floorUuid: string, @Body() updateFloorNameDto: UpdateFloorNameDto, diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index 0a92e57..8564b0e 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -110,7 +110,7 @@ export class RoomController { @ApiBearerAuth() @UseGuards(JwtAuthGuard, RoomPermissionGuard) - @Put('rename/:roomUuid') + @Put(':roomUuid') async renameRoomByUuid( @Param('roomUuid') roomUuid: string, @Body() updateRoomNameDto: UpdateRoomNameDto, diff --git a/src/unit/controllers/unit.controller.ts b/src/unit/controllers/unit.controller.ts index 1d5cbd3..9f3e50b 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -132,7 +132,7 @@ export class UnitController { @ApiBearerAuth() @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Put('rename/:unitUuid') + @Put(':unitUuid') async renameUnitByUuid( @Param('unitUuid') unitUuid: string, @Body() updateUnitNameDto: UpdateUnitNameDto, From 88f35585898249432bb48ba0e4e95514b110a831 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:17:31 -0500 Subject: [PATCH 33/34] enhance the urls name --- src/auth/controllers/user-auth.controller.ts | 2 +- .../controllers/user-device-permission.controller.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index 1eee19e..b83a016 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -83,7 +83,7 @@ export class UserAuthController { @ApiBearerAuth() @UseGuards(SuperAdminRoleGuard) - @Get('user/list') + @Get('user') async userList() { const userList = await this.userAuthService.userList(); return { diff --git a/src/user-device-permission/controllers/user-device-permission.controller.ts b/src/user-device-permission/controllers/user-device-permission.controller.ts index 2f68708..d54637e 100644 --- a/src/user-device-permission/controllers/user-device-permission.controller.ts +++ b/src/user-device-permission/controllers/user-device-permission.controller.ts @@ -28,7 +28,7 @@ export class UserDevicePermissionController { @ApiBearerAuth() @UseGuards(AdminRoleGuard) - @Post('add') + @Post() async addDevicePermission( @Body() userDevicePermissionDto: UserDevicePermissionAddDto, ) { @@ -52,7 +52,7 @@ export class UserDevicePermissionController { @ApiBearerAuth() @UseGuards(AdminRoleGuard) - @Put('edit/:devicePermissionUuid') + @Put(':devicePermissionUuid') async editDevicePermission( @Param('devicePermissionUuid') devicePermissionUuid: string, @Body() userDevicePermissionEditDto: UserDevicePermissionEditDto, @@ -76,7 +76,7 @@ export class UserDevicePermissionController { @ApiBearerAuth() @UseGuards(AdminRoleGuard) - @Get(':deviceUuid/list') + @Get(':deviceUuid') async fetchDevicePermission(@Param('deviceUuid') deviceUuid: string) { try { const deviceDetails = From 8e4f98957b67fad4afc9efb9e9e7a850a880aff3 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:18:41 -0500 Subject: [PATCH 34/34] resolve battery issue --- src/device/services/device.service.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 35ac16a..2b37df8 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -37,6 +37,7 @@ import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; +import { BatteryStatus } from '@app/common/constants/battery-status.enum'; @Injectable() export class DeviceService { @@ -64,7 +65,6 @@ export class DeviceService { return await this.deviceRepository.findOne({ where: { uuid: deviceUuid, - isActive: true, }, ...(withProductDevice && { relations: ['productDevice'] }), }); @@ -73,7 +73,6 @@ export class DeviceService { return await this.deviceRepository.findOne({ where: { deviceTuyaUuid, - isActive: true, }, relations: ['productDevice'], }); @@ -227,7 +226,6 @@ export class DeviceService { const device = await this.deviceRepository.findOne({ where: { uuid: updateDeviceInRoomDto.deviceUuid, - isActive: true, }, relations: ['spaceDevice', 'spaceDevice.parent'], }); @@ -655,7 +653,6 @@ export class DeviceService { const device = await this.deviceRepository.findOne({ where: { uuid: deviceUuid, - isActive: true, permission: { userUuid: userUuid, }, @@ -843,7 +840,7 @@ export class DeviceService { const batteryStatus: any = doorLockInstructionsStatus.status.find( (status: any) => - status.code === batteryStatus.RESIDUAL_ELECTRICITY, + status.code === BatteryStatus.RESIDUAL_ELECTRICITY, ); if (batteryStatus) { @@ -856,7 +853,7 @@ export class DeviceService { await this.getDevicesInstructionStatus(device.uuid); const batteryStatus: any = doorSensorInstructionsStatus.status.find( - (status: any) => status.code === batteryStatus.BATTERY_PERCENTAGE, + (status: any) => status.code === BatteryStatus.BATTERY_PERCENTAGE, ); if (batteryStatus) { @@ -869,7 +866,7 @@ export class DeviceService { await this.getDevicesInstructionStatus(device.uuid); const batteryStatus: any = doorSensorInstructionsStatus.status.find( - (status: any) => status.code === batteryStatus.BATTERY_PERCENTAGE, + (status: any) => status.code === BatteryStatus.BATTERY_PERCENTAGE, ); if (batteryStatus) {