mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-27 00:54:55 +00:00
Merge pull request #135 from SyncrowIOT/feature/space-management
Feature/space-management
This commit is contained in:
@ -7,10 +7,12 @@ import { ConfigModule } from '@nestjs/config';
|
|||||||
import config from './config';
|
import config from './config';
|
||||||
import { EmailService } from './util/email.service';
|
import { EmailService } from './util/email.service';
|
||||||
import { ErrorMessageService } from 'src/error-message/error-message.service';
|
import { ErrorMessageService } from 'src/error-message/error-message.service';
|
||||||
|
import { TuyaService } from './integrations/tuya/services/tuya.service';
|
||||||
@Module({
|
@Module({
|
||||||
providers: [CommonService, EmailService, ErrorMessageService],
|
providers: [CommonService, EmailService, ErrorMessageService, TuyaService],
|
||||||
exports: [
|
exports: [
|
||||||
CommonService,
|
CommonService,
|
||||||
|
TuyaService,
|
||||||
HelperModule,
|
HelperModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
EmailService,
|
EmailService,
|
||||||
|
|||||||
@ -8,4 +8,231 @@ export class ControllerRoute {
|
|||||||
'Retrieve the list of all regions registered in Syncrow.';
|
'Retrieve the list of all regions registered in Syncrow.';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static COMMUNITY = class {
|
||||||
|
public static readonly ROUTE = 'communities';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_COMMUNITY_BY_ID_SUMMARY =
|
||||||
|
'Get community by community community uuid';
|
||||||
|
|
||||||
|
public static readonly GET_COMMUNITY_BY_ID_DESCRIPTION =
|
||||||
|
'Get community by community community uuid - ( [a-zA-Z0-9]{10} )';
|
||||||
|
|
||||||
|
public static readonly LIST_COMMUNITY_SUMMARY = 'Get list of community';
|
||||||
|
|
||||||
|
public static readonly LIST_COMMUNITY_DESCRIPTION =
|
||||||
|
'Return a list of community';
|
||||||
|
public static readonly UPDATE_COMMUNITY_SUMMARY = 'Update community';
|
||||||
|
|
||||||
|
public static readonly UPDATE_COMMUNITY_DESCRIPTION =
|
||||||
|
'Update community in the database and return updated community';
|
||||||
|
|
||||||
|
public static readonly DELETE_COMMUNITY_SUMMARY = 'Delete community';
|
||||||
|
|
||||||
|
public static readonly DELETE_COMMUNITY_DESCRIPTION =
|
||||||
|
'Delete community matching by community id';
|
||||||
|
|
||||||
|
public static readonly CREATE_COMMUNITY_SUMMARY = 'Create community';
|
||||||
|
|
||||||
|
public static readonly CREATE_COMMUNITY_DESCRIPTION =
|
||||||
|
'Create community in the database and return in model';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static COMMUNITY_SPACE = class {
|
||||||
|
public static readonly ROUTE = 'communities/:communityUuid/space';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY =
|
||||||
|
'Fetch hierarchical structure of spaces within a community.';
|
||||||
|
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_DESCRIPTION =
|
||||||
|
'retrieves all the spaces associated with a given community, organized into a hierarchical structure.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static USER_COMMUNITY = class {
|
||||||
|
public static readonly ROUTE = '/user/:userUuid/communities';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_USER_COMMUNITIES_SUMMARY =
|
||||||
|
'Get communities associated with a user by user UUID';
|
||||||
|
public static readonly GET_USER_COMMUNITIES_DESCRIPTION =
|
||||||
|
'This endpoint returns the list of communities a specific user is associated with';
|
||||||
|
|
||||||
|
public static readonly ASSOCIATE_USER_COMMUNITY_SUMMARY =
|
||||||
|
'Associate a user with a community';
|
||||||
|
public static readonly ASSOCIATE_USER_COMMUNITY_DESCRIPTION =
|
||||||
|
'This endpoint associates a user with a community.';
|
||||||
|
|
||||||
|
public static readonly DISASSOCIATE_USER_COMMUNITY_SUMMARY =
|
||||||
|
'Disassociate a user from a community';
|
||||||
|
public static readonly DISASSOCIATE_USER_COMMUNITY_DESCRIPTION =
|
||||||
|
'This endpoint disassociates a user from a community. It removes the relationship between the specified user and the community. If the user is not associated with the community, an error will be returned.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static USER_SPACE = class {
|
||||||
|
public static readonly ROUTE = '/user/:userUuid/spaces';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_USER_SPACES_SUMMARY =
|
||||||
|
'Retrieve list of spaces a user belongs to';
|
||||||
|
public static readonly GET_USER_SPACES_DESCRIPTION =
|
||||||
|
'This endpoint retrieves all the spaces that a user is associated with, based on the user ID. It fetches the user spaces by querying the UserSpaceEntity to find the spaces where the user has an association.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SCENE = class {
|
||||||
|
public static readonly ROUTE = 'scene';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly CREATE_TAP_TO_RUN_SCENE_SUMMARY =
|
||||||
|
'Create a Tap-to-Run Scene';
|
||||||
|
public static readonly CREATE_TAP_TO_RUN_SCENE_DESCRIPTION =
|
||||||
|
'Creates a new Tap-to-Run scene in Tuya and stores the scene in the local database.';
|
||||||
|
|
||||||
|
public static readonly DELETE_TAP_TO_RUN_SCENE_SUMMARY =
|
||||||
|
'Delete a Tap-to-Run Scene';
|
||||||
|
public static readonly DELETE_TAP_TO_RUN_SCENE_DESCRIPTION =
|
||||||
|
'Deletes a Tap-to-Run scene from Tuya and removes it from the local database.';
|
||||||
|
|
||||||
|
public static readonly TRIGGER_TAP_TO_RUN_SCENE_SUMMARY =
|
||||||
|
'Trigger a Tap-to-Run Scene';
|
||||||
|
public static readonly TRIGGER_TAP_TO_RUN_SCENE_DESCRIPTION =
|
||||||
|
'Triggers an existing Tap-to-Run scene in Tuya by scene UUID, executing its actions immediately.';
|
||||||
|
|
||||||
|
public static readonly GET_TAP_TO_RUN_SCENE_SUMMARY =
|
||||||
|
'Get Tap-to-Run Scene Details';
|
||||||
|
public static readonly GET_TAP_TO_RUN_SCENE_DESCRIPTION =
|
||||||
|
'Retrieves detailed information of a specific Tap-to-Run scene identified by the scene UUID.';
|
||||||
|
|
||||||
|
public static readonly UPDATE_TAP_TO_RUN_SCENE_SUMMARY =
|
||||||
|
'Update a Tap-to-Run Scene';
|
||||||
|
public static readonly UPDATE_TAP_TO_RUN_SCENE_DESCRIPTION =
|
||||||
|
'Updates an existing Tap-to-Run scene in Tuya and updates the scene in the local database, reflecting any new configurations or actions.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SPACE = class {
|
||||||
|
public static readonly ROUTE = '/communities/:communityUuid/spaces';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly CREATE_SPACE_SUMMARY = 'Create a new space';
|
||||||
|
public static readonly CREATE_SPACE_DESCRIPTION =
|
||||||
|
'This endpoint allows you to create a space in a specified community. Optionally, you can specify a parent space to nest the new space under it.';
|
||||||
|
|
||||||
|
public static readonly LIST_SPACE_SUMMARY = 'List spaces in community';
|
||||||
|
public static readonly LIST_SPACE_DESCRIPTION =
|
||||||
|
'List spaces in specified community by community id';
|
||||||
|
|
||||||
|
public static readonly GET_SPACE_SUMMARY = 'Get a space in community';
|
||||||
|
public static readonly GET_SPACE_DESCRIPTION =
|
||||||
|
'Get Space in specified community by community id';
|
||||||
|
|
||||||
|
public static readonly DELETE_SPACE_SUMMARY = 'Delete a space';
|
||||||
|
public static readonly DELETE_SPACE_DESCRIPTION =
|
||||||
|
'Deletes a space by its UUID and community ID. If the space has children, they will also be deleted due to cascade delete.';
|
||||||
|
|
||||||
|
public static readonly UPDATE_SPACE_SUMMARY = 'Update a space';
|
||||||
|
public static readonly UPDATE_SPACE_DESCRIPTION =
|
||||||
|
'Updates a space by its UUID and community ID. You can update the name, parent space, and other properties. If a parent space is provided and not already a parent, its `isParent` flag will be set to true.';
|
||||||
|
|
||||||
|
public static readonly GET_HEIRARCHY_SUMMARY = 'Get space hierarchy';
|
||||||
|
public static readonly GET_HEIRARCHY_DESCRIPTION =
|
||||||
|
'This endpoint retrieves the hierarchical structure of spaces under a given space ID. It returns all the child spaces nested within the specified space, organized by their parent-child relationships. ';
|
||||||
|
|
||||||
|
public static readonly CREATE_INVITATION_CODE_SPACE_SUMMARY =
|
||||||
|
'Generate a new invitation code for a specific space';
|
||||||
|
public static readonly CREATE_INVITATION_CODE_SPACE_DESCRIPTION =
|
||||||
|
'This endpoint generates a new 6-character invitation code for a space identified by its UUID and stores it in the space entity';
|
||||||
|
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY =
|
||||||
|
'Fetch hierarchical structure of spaces within a community.';
|
||||||
|
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_DESCRIPTION =
|
||||||
|
'retrieves all the spaces associated with a given community, organized into a hierarchical structure.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SPACE_SCENE = class {
|
||||||
|
public static readonly ROUTE =
|
||||||
|
'/communities/:communityUuid/spaces/:spaceUuid/scenes';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_TAP_TO_RUN_SCENE_BY_SPACE_SUMMARY =
|
||||||
|
'Retrieve Tap-to-Run Scenes by Space';
|
||||||
|
public static readonly GET_TAP_TO_RUN_SCENE_BY_SPACE_DESCRIPTION =
|
||||||
|
'Fetches all Tap-to-Run scenes associated with a specified space UUID. An optional query parameter can filter the results to show only scenes marked for the homepage display.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SPACE_USER = class {
|
||||||
|
public static readonly ROUTE =
|
||||||
|
'/communities/:communityUuid/spaces/:spaceUuid/user';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly ASSOCIATE_SPACE_USER_SUMMARY =
|
||||||
|
'Associate a user to a space';
|
||||||
|
public static readonly ASSOCIATE_SPACE_USER_DESCRIPTION =
|
||||||
|
'Associates a user with a given space by their respective UUIDs';
|
||||||
|
|
||||||
|
public static readonly DISSOCIATE_SPACE_USER_SUMMARY =
|
||||||
|
'Disassociate a user from a space';
|
||||||
|
public static readonly DISSOCIATE_SPACE_USER_DESCRIPTION =
|
||||||
|
'Disassociates a user from a space by removing the existing association.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SPACE_DEVICES = class {
|
||||||
|
public static readonly ROUTE =
|
||||||
|
'/communities/:communityUuid/spaces/:spaceUuid/devices';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly LIST_SPACE_DEVICE_SUMMARY =
|
||||||
|
'List devices in a space';
|
||||||
|
public static readonly LIST_SPACE_DEVICE_DESCRIPTION =
|
||||||
|
'Retrieves a list of all devices associated with a specified space.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SUBSPACE = class {
|
||||||
|
public static readonly ROUTE =
|
||||||
|
'/communities/:communityUuid/spaces/:spaceUuid/subspaces';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly CREATE_SUBSPACE_SUMMARY = 'Create Subspace';
|
||||||
|
public static readonly CREATE_SUBSPACE_DESCRIPTION =
|
||||||
|
'Creates a new subspace within a specific space and community.';
|
||||||
|
|
||||||
|
public static readonly LIST_SUBSPACES_SUMMARY = 'List Subspaces';
|
||||||
|
public static readonly LIST_SUBSPACES_DESCRIPTION =
|
||||||
|
'Retrieves a list of subspaces within a specified space and community.';
|
||||||
|
|
||||||
|
public static readonly GET_SUBSPACE_SUMMARY = 'Get Subspace';
|
||||||
|
public static readonly GET_SUBSPACE_DESCRIPTION =
|
||||||
|
'Fetches a specific subspace by UUID within a given space and community';
|
||||||
|
|
||||||
|
public static readonly UPDATE_SUBSPACE_SUMMARY = 'Update Subspace';
|
||||||
|
public static readonly UPDATE_SUBSPACE_DESCRIPTION =
|
||||||
|
'Updates a specific subspace within a given space and community.';
|
||||||
|
|
||||||
|
public static readonly DELETE_SUBSPACE_SUMMARY = 'Delete Subspace';
|
||||||
|
public static readonly DELETE_SUBSPACE_DESCRIPTION =
|
||||||
|
'Deletes a specific subspace within a given space and community.';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SUBSPACE_DEVICE = class {
|
||||||
|
public static readonly ROUTE =
|
||||||
|
'/communities/:communityUuid/spaces/:spaceUuid/subspaces/:subSpaceUuid/devices';
|
||||||
|
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly LIST_SUBSPACE_DEVICE_SUMMARY =
|
||||||
|
'List devices in a subspace';
|
||||||
|
public static readonly LIST_SUBSPACE_DEVICE_DESCRIPTION =
|
||||||
|
'Retrieves a list of all devices associated with a specified subspace.';
|
||||||
|
|
||||||
|
public static readonly ASSOCIATE_SUBSPACE_DEVICE_SUMMARY =
|
||||||
|
'Associate a device to a subspace';
|
||||||
|
public static readonly ASSOCIATE_SUBSPACE_DEVICE_DESCRIPTION =
|
||||||
|
'Associates a device with a specific subspace, enabling it to be managed within that subspace context.';
|
||||||
|
|
||||||
|
public static readonly DISASSOCIATE_SUBSPACE_DEVICE_SUMMARY =
|
||||||
|
'Disassociate a device from a subspace';
|
||||||
|
public static readonly DISASSOCIATE_SUBSPACE_DEVICE_DESCRIPTION =
|
||||||
|
'Removes the association of a device from a specific subspace, making it no longer managed within that subspace.';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { UserOtpEntity } from '../modules/user/entities';
|
|||||||
import { ProductEntity } from '../modules/product/entities';
|
import { ProductEntity } from '../modules/product/entities';
|
||||||
import { DeviceEntity } from '../modules/device/entities';
|
import { DeviceEntity } from '../modules/device/entities';
|
||||||
import { PermissionTypeEntity } from '../modules/permission/entities';
|
import { PermissionTypeEntity } from '../modules/permission/entities';
|
||||||
import { SpaceEntity } from '../modules/space/entities';
|
import { SpaceEntity, SubspaceEntity } from '../modules/space/entities';
|
||||||
import { SpaceTypeEntity } from '../modules/space/entities';
|
import { SpaceTypeEntity } from '../modules/space/entities';
|
||||||
import { UserSpaceEntity } from '../modules/user/entities';
|
import { UserSpaceEntity } from '../modules/user/entities';
|
||||||
import { DeviceUserPermissionEntity } from '../modules/device/entities';
|
import { DeviceUserPermissionEntity } from '../modules/device/entities';
|
||||||
@ -19,6 +19,7 @@ import { DeviceNotificationEntity } from '../modules/device/entities';
|
|||||||
import { RegionEntity } from '../modules/region/entities';
|
import { RegionEntity } from '../modules/region/entities';
|
||||||
import { TimeZoneEntity } from '../modules/timezone/entities';
|
import { TimeZoneEntity } from '../modules/timezone/entities';
|
||||||
import { VisitorPasswordEntity } from '../modules/visitor-password/entities';
|
import { VisitorPasswordEntity } from '../modules/visitor-password/entities';
|
||||||
|
import { CommunityEntity } from '../modules/community/entities';
|
||||||
import { DeviceStatusLogEntity } from '../modules/device-status-log/entities';
|
import { DeviceStatusLogEntity } from '../modules/device-status-log/entities';
|
||||||
import { SceneEntity, SceneIconEntity } from '../modules/scene/entities';
|
import { SceneEntity, SceneIconEntity } from '../modules/scene/entities';
|
||||||
|
|
||||||
@ -43,7 +44,9 @@ import { SceneEntity, SceneIconEntity } from '../modules/scene/entities';
|
|||||||
DeviceUserPermissionEntity,
|
DeviceUserPermissionEntity,
|
||||||
DeviceEntity,
|
DeviceEntity,
|
||||||
PermissionTypeEntity,
|
PermissionTypeEntity,
|
||||||
|
CommunityEntity,
|
||||||
SpaceEntity,
|
SpaceEntity,
|
||||||
|
SubspaceEntity,
|
||||||
SpaceTypeEntity,
|
SpaceTypeEntity,
|
||||||
UserSpaceEntity,
|
UserSpaceEntity,
|
||||||
DeviceUserPermissionEntity,
|
DeviceUserPermissionEntity,
|
||||||
|
|||||||
23
libs/common/src/dto/base.response.dto.ts
Normal file
23
libs/common/src/dto/base.response.dto.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { WithOptional } from '../type/optional.type';
|
||||||
|
|
||||||
|
export class BaseResponseDto {
|
||||||
|
statusCode?: number;
|
||||||
|
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
error?: string;
|
||||||
|
|
||||||
|
data?: any;
|
||||||
|
|
||||||
|
success?: boolean;
|
||||||
|
|
||||||
|
static wrap({
|
||||||
|
data,
|
||||||
|
statusCode = 200,
|
||||||
|
message = 'Success',
|
||||||
|
success = true,
|
||||||
|
error = undefined,
|
||||||
|
}: WithOptional<BaseResponseDto, 'message'>) {
|
||||||
|
return { data, statusCode, success, message, error };
|
||||||
|
}
|
||||||
|
}
|
||||||
66
libs/common/src/dto/pagination.request.dto.ts
Normal file
66
libs/common/src/dto/pagination.request.dto.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { IsDate, IsOptional } from 'class-validator';
|
||||||
|
import { IsPageRequestParam } from '../validators/is-page-request-param.validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsSizeRequestParam } from '../validators/is-size-request-param.validator';
|
||||||
|
import { Transform } from 'class-transformer';
|
||||||
|
import { parseToDate } from '../util/parseToDate';
|
||||||
|
|
||||||
|
export class PaginationRequestGetListDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsPageRequestParam({
|
||||||
|
message: 'Page must be bigger than 0',
|
||||||
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'page',
|
||||||
|
required: false,
|
||||||
|
description: 'Page request',
|
||||||
|
})
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsSizeRequestParam({
|
||||||
|
message: 'Size must not be negative',
|
||||||
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'size',
|
||||||
|
required: false,
|
||||||
|
description: 'Size request',
|
||||||
|
})
|
||||||
|
size?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'name',
|
||||||
|
required: false,
|
||||||
|
description: 'Name to be filtered',
|
||||||
|
})
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'from',
|
||||||
|
required: false,
|
||||||
|
type: Number,
|
||||||
|
description: `Start time in UNIX timestamp format to filter`,
|
||||||
|
example: 1674172800000,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => parseToDate(value))
|
||||||
|
@IsDate({
|
||||||
|
message: `From must be in UNIX timestamp format in order to parse to Date instance`,
|
||||||
|
})
|
||||||
|
from?: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'to',
|
||||||
|
required: false,
|
||||||
|
type: Number,
|
||||||
|
description: `End time in UNIX timestamp format to filter`,
|
||||||
|
example: 1674259200000,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => parseToDate(value))
|
||||||
|
@IsDate({
|
||||||
|
message: `To must be in UNIX timestamp format in order to parse to Date instance`,
|
||||||
|
})
|
||||||
|
to?: Date;
|
||||||
|
}
|
||||||
62
libs/common/src/dto/pagination.response.dto.ts
Normal file
62
libs/common/src/dto/pagination.response.dto.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { BaseResponseDto } from './base.response.dto';
|
||||||
|
|
||||||
|
export interface PageResponseDto {
|
||||||
|
// Original paging information from the request ( or default )
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
|
||||||
|
// Useful for display (N Records found)
|
||||||
|
totalItem: number;
|
||||||
|
|
||||||
|
// Use for display N Pages ( 0... N )
|
||||||
|
totalPage: number;
|
||||||
|
|
||||||
|
// Has next is false when cursor is at last page
|
||||||
|
hasNext: boolean;
|
||||||
|
|
||||||
|
// Has previous is false when cursor is at first page
|
||||||
|
hasPrevious: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageResponse<T> implements BaseResponseDto, PageResponseDto {
|
||||||
|
code?: number;
|
||||||
|
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
data: Array<T>;
|
||||||
|
|
||||||
|
page: number;
|
||||||
|
|
||||||
|
size: number;
|
||||||
|
|
||||||
|
totalItem: number;
|
||||||
|
|
||||||
|
totalPage: number;
|
||||||
|
|
||||||
|
hasNext: boolean;
|
||||||
|
|
||||||
|
hasPrevious: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
baseResponseDto: BaseResponseDto,
|
||||||
|
pageResponseDto: PageResponseDto,
|
||||||
|
) {
|
||||||
|
if (baseResponseDto.statusCode) {
|
||||||
|
this.code = baseResponseDto.statusCode;
|
||||||
|
} else {
|
||||||
|
this.code = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseResponseDto.data) {
|
||||||
|
this.data = baseResponseDto.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.message = baseResponseDto.message;
|
||||||
|
this.page = pageResponseDto.page;
|
||||||
|
this.size = pageResponseDto.size;
|
||||||
|
this.totalItem = pageResponseDto.totalItem;
|
||||||
|
this.totalPage = pageResponseDto.totalPage;
|
||||||
|
this.hasNext = pageResponseDto.hasNext;
|
||||||
|
this.hasPrevious = pageResponseDto.hasPrevious;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
libs/common/src/dto/success.response.dto.ts
Normal file
30
libs/common/src/dto/success.response.dto.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { BaseResponseDto } from './base.response.dto';
|
||||||
|
|
||||||
|
export class SuccessResponseDto<Type> implements BaseResponseDto {
|
||||||
|
@ApiProperty({
|
||||||
|
example: 200,
|
||||||
|
})
|
||||||
|
statusCode: number;
|
||||||
|
|
||||||
|
data: Type;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'Success message',
|
||||||
|
})
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: true,
|
||||||
|
description: 'Indicates that the operation was successful',
|
||||||
|
})
|
||||||
|
success: boolean;
|
||||||
|
|
||||||
|
constructor(input: BaseResponseDto) {
|
||||||
|
if (input.statusCode) this.statusCode = input.statusCode;
|
||||||
|
else this.statusCode = 200;
|
||||||
|
if (input.message) this.message = input.message;
|
||||||
|
if (input.data) this.data = input.data;
|
||||||
|
this.success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,19 +10,27 @@ import { DeviceMessagesService } from './services/device.messages.service';
|
|||||||
import { DeviceRepositoryModule } from '../modules/device/device.repository.module';
|
import { DeviceRepositoryModule } from '../modules/device/device.repository.module';
|
||||||
import { DeviceNotificationRepository } from '../modules/device/repositories';
|
import { DeviceNotificationRepository } from '../modules/device/repositories';
|
||||||
import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module';
|
import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module';
|
||||||
|
import { CommunityPermissionService } from './services/community.permission.service';
|
||||||
|
import { CommunityRepository } from '../modules/community/repositories';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
HelperHashService,
|
HelperHashService,
|
||||||
SpacePermissionService,
|
SpacePermissionService,
|
||||||
|
CommunityPermissionService,
|
||||||
SpaceRepository,
|
SpaceRepository,
|
||||||
TuyaWebSocketService,
|
TuyaWebSocketService,
|
||||||
OneSignalService,
|
OneSignalService,
|
||||||
DeviceMessagesService,
|
DeviceMessagesService,
|
||||||
DeviceNotificationRepository,
|
DeviceNotificationRepository,
|
||||||
|
CommunityRepository,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
HelperHashService,
|
||||||
|
SpacePermissionService,
|
||||||
|
CommunityPermissionService,
|
||||||
],
|
],
|
||||||
exports: [HelperHashService, SpacePermissionService],
|
|
||||||
controllers: [],
|
controllers: [],
|
||||||
imports: [
|
imports: [
|
||||||
SpaceRepositoryModule,
|
SpaceRepositoryModule,
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommunityPermissionService {
|
||||||
|
constructor(private readonly communityRepository: CommunityRepository) {}
|
||||||
|
|
||||||
|
async checkUserPermission(communityUuid: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const communityData = await this.communityRepository.findOne({
|
||||||
|
where: {
|
||||||
|
uuid: communityUuid,
|
||||||
|
},
|
||||||
|
relations: ['users'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!communityData) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
'You do not have permission to access this community',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new BadRequestException(err.message || 'Invalid UUID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,27 +9,23 @@ export class SpacePermissionService {
|
|||||||
async checkUserPermission(
|
async checkUserPermission(
|
||||||
spaceUuid: string,
|
spaceUuid: string,
|
||||||
userUuid: string,
|
userUuid: string,
|
||||||
type: string,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
const spaceData = await this.spaceRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
uuid: spaceUuid,
|
uuid: spaceUuid,
|
||||||
spaceType: {
|
|
||||||
type: type,
|
|
||||||
},
|
|
||||||
userSpaces: {
|
userSpaces: {
|
||||||
user: {
|
user: {
|
||||||
uuid: userUuid,
|
uuid: userUuid,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relations: ['spaceType', 'userSpaces', 'userSpaces.user'],
|
relations: ['userSpaces', 'userSpaces.user'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!spaceData) {
|
if (!spaceData) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`You do not have permission to access this ${type}`,
|
`You do not have permission to access this space`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
2
libs/common/src/integrations/tuya/interfaces/index.ts
Normal file
2
libs/common/src/integrations/tuya/interfaces/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './tuya.response.interface';
|
||||||
|
export * from './tap-to-run-action.interface';
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
export interface ConvertedExecutorProperty {
|
||||||
|
function_code?: string;
|
||||||
|
function_value?: any;
|
||||||
|
delay_seconds?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConvertedAction {
|
||||||
|
entity_id: string;
|
||||||
|
action_executor: string;
|
||||||
|
executor_property?: ConvertedExecutorProperty;
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export interface TuyaResponseInterface {
|
||||||
|
success: boolean;
|
||||||
|
msg?: string;
|
||||||
|
result: boolean;
|
||||||
|
}
|
||||||
177
libs/common/src/integrations/tuya/services/tuya.service.ts
Normal file
177
libs/common/src/integrations/tuya/services/tuya.service.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
||||||
|
import { ConvertedAction, TuyaResponseInterface } from '../interfaces';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TuyaService {
|
||||||
|
private tuya: TuyaContext;
|
||||||
|
|
||||||
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
||||||
|
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
|
||||||
|
const tuyaEuUrl = this.configService.get<string>('tuya-config.TUYA_EU_URL');
|
||||||
|
this.tuya = new TuyaContext({
|
||||||
|
baseUrl: tuyaEuUrl,
|
||||||
|
accessKey,
|
||||||
|
secretKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async createSpace({ name }: { name: string }) {
|
||||||
|
const path = '/v2.0/cloud/space/creation';
|
||||||
|
const body = { name };
|
||||||
|
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'POST',
|
||||||
|
path,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
return response.result as string;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
'Error creating space in Tuya',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDeviceDetails(deviceId: string) {
|
||||||
|
const path = `/v1.1/iot-03/devices/${deviceId}`;
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'GET',
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Error fetching device details: ${response.msg}`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteSceneRule(sceneId: string, spaceId: string) {
|
||||||
|
const path = `/v2.0/cloud/scene/rule?ids=${sceneId}&space_id=${spaceId}`;
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'DELETE',
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
`Error deleting scene rule: ${response.msg}`,
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSceneRule(sceneId: string) {
|
||||||
|
const path = `/v2.0/cloud/scene/rule/${sceneId}`;
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'GET',
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
`Error fetching scene rule: ${response.msg}`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addTapToRunScene(
|
||||||
|
spaceId: string,
|
||||||
|
sceneName: string,
|
||||||
|
actions: ConvertedAction[],
|
||||||
|
decisionExpr: string,
|
||||||
|
) {
|
||||||
|
const path = `/v2.0/cloud/scene/rule`;
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'POST',
|
||||||
|
path,
|
||||||
|
body: {
|
||||||
|
space_id: spaceId,
|
||||||
|
name: sceneName,
|
||||||
|
type: 'scene',
|
||||||
|
decision_expr: decisionExpr,
|
||||||
|
actions: actions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
`Error fetching scene rule: ${response.msg}`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async triggerScene(sceneId: string): Promise<TuyaResponseInterface> {
|
||||||
|
const path = `/v2.0/cloud/scene/rule/${sceneId}/actions/trigger`;
|
||||||
|
const response: TuyaResponseInterface = await this.tuya.request({
|
||||||
|
method: 'POST',
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
throw new HttpException(
|
||||||
|
response.msg || 'Error triggering scene',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAutomation(
|
||||||
|
spaceId: string,
|
||||||
|
automationName: string,
|
||||||
|
effectiveTime: any,
|
||||||
|
decisionExpr: string,
|
||||||
|
conditions: any[],
|
||||||
|
actions: any[],
|
||||||
|
) {
|
||||||
|
const path = `/v2.0/cloud/scene/rule`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.tuya.request({
|
||||||
|
method: 'POST',
|
||||||
|
path,
|
||||||
|
body: {
|
||||||
|
space_id: spaceId,
|
||||||
|
name: automationName,
|
||||||
|
effective_time: {
|
||||||
|
...effectiveTime,
|
||||||
|
timezone_id: 'Asia/Dubai',
|
||||||
|
},
|
||||||
|
type: 'automation',
|
||||||
|
decision_expr: decisionExpr,
|
||||||
|
conditions: conditions,
|
||||||
|
actions: actions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
throw new HttpException(response.msg, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
error.message || 'Failed to create automation in Tuya',
|
||||||
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
libs/common/src/models/typeOrmCustom.model.ts
Normal file
145
libs/common/src/models/typeOrmCustom.model.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { FindManyOptions, Repository } from 'typeorm';
|
||||||
|
import { InternalServerErrorException } from '@nestjs/common';
|
||||||
|
import { BaseResponseDto } from '../dto/base.response.dto';
|
||||||
|
import { PageResponseDto } from '../dto/pagination.response.dto';
|
||||||
|
import { buildTypeORMSortQuery } from '../util/buildTypeORMSortQuery';
|
||||||
|
import { buildTypeORMIncludeQuery } from '../util/buildTypeORMIncludeQuery';
|
||||||
|
import { buildTypeORMWhereClause } from '../util/buildTypeORMWhereClause';
|
||||||
|
import { getPaginationResponseDto } from '../util/getPaginationResponseDto';
|
||||||
|
|
||||||
|
export interface TypeORMCustomModelFindAllQuery {
|
||||||
|
page: number | undefined;
|
||||||
|
size: number | undefined;
|
||||||
|
sort?: string;
|
||||||
|
modelName?: string;
|
||||||
|
include?: string;
|
||||||
|
where?: { [key: string]: unknown };
|
||||||
|
select?: string[];
|
||||||
|
includeDisable?: boolean | string;
|
||||||
|
}
|
||||||
|
interface CustomFindAllQuery {
|
||||||
|
page?: number;
|
||||||
|
size?: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FindAllQueryWithDefaults extends CustomFindAllQuery {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultQueryOptions(
|
||||||
|
query: Partial<TypeORMCustomModelFindAllQuery>,
|
||||||
|
): FindManyOptions & FindAllQueryWithDefaults {
|
||||||
|
const { page, size, includeDisable, modelName, ...rest } = query;
|
||||||
|
|
||||||
|
// Set default if undefined or null
|
||||||
|
const returnPage = page ? Number(page) : 1;
|
||||||
|
const returnSize = size ? Number(size) : 10;
|
||||||
|
const returnIncludeDisable =
|
||||||
|
includeDisable === true || includeDisable === 'true';
|
||||||
|
|
||||||
|
// Return query with defaults and ensure modelName is passed through
|
||||||
|
return {
|
||||||
|
skip: (returnPage - 1) * returnSize,
|
||||||
|
take: returnSize,
|
||||||
|
where: {
|
||||||
|
...rest,
|
||||||
|
},
|
||||||
|
page: returnPage,
|
||||||
|
size: returnSize,
|
||||||
|
includeDisable: returnIncludeDisable,
|
||||||
|
modelName: modelName || query.modelName, // Ensure modelName is passed through
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeORMCustomModelFindAllQueryWithDefault
|
||||||
|
extends TypeORMCustomModelFindAllQuery {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TypeORMCustomModelFindAllResponse = {
|
||||||
|
baseResponseDto: BaseResponseDto;
|
||||||
|
paginationResponseDto: PageResponseDto;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TypeORMCustomModel(repository: Repository<any>) {
|
||||||
|
return Object.assign(repository, {
|
||||||
|
findAll: async function (
|
||||||
|
query: Partial<TypeORMCustomModelFindAllQuery>,
|
||||||
|
): Promise<TypeORMCustomModelFindAllResponse> {
|
||||||
|
// Extract values from the query
|
||||||
|
const {
|
||||||
|
page = 1,
|
||||||
|
size = 10,
|
||||||
|
sort,
|
||||||
|
modelName,
|
||||||
|
include,
|
||||||
|
where,
|
||||||
|
select,
|
||||||
|
} = getDefaultQueryOptions(query);
|
||||||
|
|
||||||
|
// Ensure modelName is set before proceeding
|
||||||
|
if (!modelName) {
|
||||||
|
console.error(
|
||||||
|
'modelName is missing after getDefaultQueryOptions:',
|
||||||
|
query,
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException(
|
||||||
|
`[TypeORMCustomModel] Cannot findAll with unknown modelName`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const skip = (page - 1) * size;
|
||||||
|
const order = buildTypeORMSortQuery(sort);
|
||||||
|
const relations = buildTypeORMIncludeQuery(modelName, include);
|
||||||
|
|
||||||
|
// Use the where clause directly, without wrapping it under 'where'
|
||||||
|
const whereClause = buildTypeORMWhereClause({ where });
|
||||||
|
console.log('Final where clause:', whereClause);
|
||||||
|
|
||||||
|
// Ensure the whereClause is passed directly to findAndCount
|
||||||
|
const [data, count] = await repository.findAndCount({
|
||||||
|
where: whereClause,
|
||||||
|
take: size,
|
||||||
|
skip: skip,
|
||||||
|
order: order,
|
||||||
|
select: select,
|
||||||
|
relations: relations,
|
||||||
|
});
|
||||||
|
|
||||||
|
const paginationResponseDto = getPaginationResponseDto(count, page, size);
|
||||||
|
const baseResponseDto: BaseResponseDto = {
|
||||||
|
data,
|
||||||
|
message: getResponseMessage(modelName, { where }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return { baseResponseDto, paginationResponseDto };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponseMessage(
|
||||||
|
modelName: string,
|
||||||
|
query?: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
where?: any;
|
||||||
|
},
|
||||||
|
): string {
|
||||||
|
if (!query) {
|
||||||
|
return `Success get list ${modelName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { where } = query;
|
||||||
|
if (modelName === 'user' && where && where?.community) {
|
||||||
|
const {
|
||||||
|
some: { communityId },
|
||||||
|
} = where.community;
|
||||||
|
if (typeof communityId === 'string') {
|
||||||
|
return `Success get list ${modelName} belong to community`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Success get list ${modelName}`;
|
||||||
|
}
|
||||||
19
libs/common/src/modules/community/dtos/community.dto.ts
Normal file
19
libs/common/src/modules/community/dtos/community.dto.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class CommunityDto {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public uuid: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public name: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
public description?: string;
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public regionId: string;
|
||||||
|
}
|
||||||
1
libs/common/src/modules/community/dtos/index.ts
Normal file
1
libs/common/src/modules/community/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './community.dto';
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { Column, Entity, OneToMany, Unique } from 'typeorm';
|
||||||
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
|
import { CommunityDto } from '../dtos';
|
||||||
|
import { SpaceEntity } from '../../space/entities';
|
||||||
|
|
||||||
|
@Entity({ name: 'community' })
|
||||||
|
@Unique(['name'])
|
||||||
|
export class CommunityEntity extends AbstractEntity<CommunityDto> {
|
||||||
|
@Column({
|
||||||
|
type: 'uuid',
|
||||||
|
default: () => 'gen_random_uuid()',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
public uuid: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
length: 255,
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ length: 255, nullable: true })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@OneToMany(() => SpaceEntity, (space) => space.community)
|
||||||
|
spaces: SpaceEntity[];
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'varchar',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
externalId: string;
|
||||||
|
}
|
||||||
1
libs/common/src/modules/community/entities/index.ts
Normal file
1
libs/common/src/modules/community/entities/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './community.entity';
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CommunityEntity } from '../entities';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommunityRepository extends Repository<CommunityEntity> {
|
||||||
|
constructor(private dataSource: DataSource) {
|
||||||
|
super(CommunityEntity, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
1
libs/common/src/modules/community/repositories/index.ts
Normal file
1
libs/common/src/modules/community/repositories/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './community.repository';
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Column, Entity, ManyToOne, OneToMany, Unique, Index } from 'typeorm';
|
import { Column, Entity, ManyToOne, OneToMany, Unique, Index, JoinColumn } from 'typeorm';
|
||||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto';
|
import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto';
|
||||||
import { SpaceEntity } from '../../space/entities';
|
import { SpaceEntity, SubspaceEntity } from '../../space/entities';
|
||||||
import { ProductEntity } from '../../product/entities';
|
import { ProductEntity } from '../../product/entities';
|
||||||
import { UserEntity } from '../../user/entities';
|
import { UserEntity } from '../../user/entities';
|
||||||
import { DeviceNotificationDto } from '../dtos';
|
import { DeviceNotificationDto } from '../dtos';
|
||||||
@ -42,7 +42,7 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
|
|||||||
)
|
)
|
||||||
deviceUserNotification: DeviceNotificationEntity[];
|
deviceUserNotification: DeviceNotificationEntity[];
|
||||||
|
|
||||||
@ManyToOne(() => SpaceEntity, (space) => space.devicesSpaceEntity, {
|
@ManyToOne(() => SpaceEntity, (space) => space.devices, {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
spaceDevice: SpaceEntity;
|
spaceDevice: SpaceEntity;
|
||||||
@ -52,6 +52,12 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
|
|||||||
})
|
})
|
||||||
productDevice: ProductEntity;
|
productDevice: ProductEntity;
|
||||||
|
|
||||||
|
@ManyToOne(() => SubspaceEntity, (subspace) => subspace.devices, {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'subspace_id' })
|
||||||
|
subspace: SubspaceEntity;
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ nullable: false })
|
@Column({ nullable: false })
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
|||||||
@ -47,7 +47,8 @@ export class SceneEntity extends AbstractEntity<SceneDto> {
|
|||||||
@Column({
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
})
|
})
|
||||||
unitUuid: string;
|
spaceUuid: string;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './space.dto';
|
export * from './space.dto';
|
||||||
|
export * from './subspace.dto';
|
||||||
|
|||||||
29
libs/common/src/modules/space/dtos/subspace.dto.ts
Normal file
29
libs/common/src/modules/space/dtos/subspace.dto.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { SpaceDto } from './space.dto';
|
||||||
|
import { DeviceDto } from '../../device/dtos';
|
||||||
|
|
||||||
|
export class SubspaceDto {
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'd7a44e8a-32d5-4f39-ae2e-013f1245aead',
|
||||||
|
description: 'UUID of the subspace',
|
||||||
|
})
|
||||||
|
uuid: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'Meeting Room 1',
|
||||||
|
description: 'Name of the subspace',
|
||||||
|
})
|
||||||
|
subspaceName: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: () => SpaceDto,
|
||||||
|
description: 'The space to which this subspace belongs',
|
||||||
|
})
|
||||||
|
space: SpaceDto;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: [],
|
||||||
|
description: 'List of devices assigned to this subspace, if any',
|
||||||
|
})
|
||||||
|
devices: DeviceDto[];
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from './space.entity';
|
export * from './space.entity';
|
||||||
|
export * from './subspace.entity';
|
||||||
|
|||||||
@ -1,8 +1,17 @@
|
|||||||
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
Unique,
|
||||||
|
} from 'typeorm';
|
||||||
import { SpaceDto, SpaceTypeDto } from '../dtos';
|
import { SpaceDto, SpaceTypeDto } from '../dtos';
|
||||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
import { UserSpaceEntity } from '../../user/entities';
|
import { UserSpaceEntity } from '../../user/entities';
|
||||||
import { DeviceEntity } from '../../device/entities';
|
import { DeviceEntity } from '../../device/entities';
|
||||||
|
import { CommunityEntity } from '../../community/entities';
|
||||||
|
import { SubspaceEntity } from './subspace.entity';
|
||||||
|
|
||||||
@Entity({ name: 'space-type' })
|
@Entity({ name: 'space-type' })
|
||||||
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
||||||
@ -18,8 +27,6 @@ export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
|||||||
})
|
})
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
@OneToMany(() => SpaceEntity, (space) => space.spaceType)
|
|
||||||
spaces: SpaceEntity[];
|
|
||||||
constructor(partial: Partial<SpaceTypeEntity>) {
|
constructor(partial: Partial<SpaceTypeEntity>) {
|
||||||
super();
|
super();
|
||||||
Object.assign(this, partial);
|
Object.assign(this, partial);
|
||||||
@ -45,6 +52,13 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
|||||||
})
|
})
|
||||||
public spaceName: string;
|
public spaceName: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => CommunityEntity, (community) => community.spaces, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'community_id' })
|
||||||
|
community: CommunityEntity;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
@ -54,19 +68,20 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
|||||||
|
|
||||||
@OneToMany(() => SpaceEntity, (space) => space.parent)
|
@OneToMany(() => SpaceEntity, (space) => space.parent)
|
||||||
children: SpaceEntity[];
|
children: SpaceEntity[];
|
||||||
@ManyToOne(() => SpaceTypeEntity, (spaceType) => spaceType.spaces, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
spaceType: SpaceTypeEntity;
|
|
||||||
|
|
||||||
@OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.space)
|
@OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.space)
|
||||||
userSpaces: UserSpaceEntity[];
|
userSpaces: UserSpaceEntity[];
|
||||||
|
|
||||||
|
@OneToMany(() => SubspaceEntity, (subspace) => subspace.space, {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
subspaces?: SubspaceEntity[];
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
() => DeviceEntity,
|
() => DeviceEntity,
|
||||||
(devicesSpaceEntity) => devicesSpaceEntity.spaceDevice,
|
(devicesSpaceEntity) => devicesSpaceEntity.spaceDevice,
|
||||||
)
|
)
|
||||||
devicesSpaceEntity: DeviceEntity[];
|
devices: DeviceEntity[];
|
||||||
|
|
||||||
constructor(partial: Partial<SpaceEntity>) {
|
constructor(partial: Partial<SpaceEntity>) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
37
libs/common/src/modules/space/entities/subspace.entity.ts
Normal file
37
libs/common/src/modules/space/entities/subspace.entity.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm';
|
||||||
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
|
import { DeviceEntity } from '../../device/entities';
|
||||||
|
import { SpaceEntity } from './space.entity';
|
||||||
|
import { SubspaceDto } from '../dtos';
|
||||||
|
|
||||||
|
@Entity({ name: 'subspace' })
|
||||||
|
export class SubspaceEntity extends AbstractEntity<SubspaceDto> {
|
||||||
|
@Column({
|
||||||
|
type: 'uuid',
|
||||||
|
default: () => 'gen_random_uuid()',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
public uuid: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
public subspaceName: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => SpaceEntity, (space) => space.subspaces, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'space_id' })
|
||||||
|
space: SpaceEntity;
|
||||||
|
|
||||||
|
@OneToMany(() => DeviceEntity, (device) => device.subspace, {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
devices: DeviceEntity[];
|
||||||
|
|
||||||
|
constructor(partial: Partial<SubspaceEntity>) {
|
||||||
|
super();
|
||||||
|
Object.assign(this, partial);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { DataSource, Repository } from 'typeorm';
|
import { DataSource, Repository } from 'typeorm';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { SpaceEntity, SpaceTypeEntity } from '../entities';
|
import { SpaceEntity, SpaceTypeEntity, SubspaceEntity } from '../entities';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SpaceRepository extends Repository<SpaceEntity> {
|
export class SpaceRepository extends Repository<SpaceEntity> {
|
||||||
@ -15,3 +15,10 @@ export class SpaceTypeRepository extends Repository<SpaceTypeEntity> {
|
|||||||
super(SpaceTypeEntity, dataSource.createEntityManager());
|
super(SpaceTypeEntity, dataSource.createEntityManager());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SubspaceRepository extends Repository<SubspaceEntity> {
|
||||||
|
constructor(private dataSource: DataSource) {
|
||||||
|
super(SubspaceEntity, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { SpaceEntity, SpaceTypeEntity } from './entities';
|
import { SpaceEntity, SpaceTypeEntity, SubspaceEntity } from './entities';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [],
|
providers: [],
|
||||||
exports: [],
|
exports: [],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
imports: [TypeOrmModule.forFeature([SpaceEntity, SpaceTypeEntity])],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([SpaceEntity, SpaceTypeEntity, SubspaceEntity]),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SpaceRepositoryModule {}
|
export class SpaceRepositoryModule {}
|
||||||
|
|||||||
@ -115,6 +115,7 @@ export class UserEntity extends AbstractEntity<UserDto> {
|
|||||||
(visitorPassword) => visitorPassword.user,
|
(visitorPassword) => visitorPassword.user,
|
||||||
)
|
)
|
||||||
public visitorPasswords: VisitorPasswordEntity[];
|
public visitorPasswords: VisitorPasswordEntity[];
|
||||||
|
|
||||||
constructor(partial: Partial<UserEntity>) {
|
constructor(partial: Partial<UserEntity>) {
|
||||||
super();
|
super();
|
||||||
Object.assign(this, partial);
|
Object.assign(this, partial);
|
||||||
|
|||||||
2
libs/common/src/type/optional.type.ts
Normal file
2
libs/common/src/type/optional.type.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
|
||||||
|
export type WithRequired<T, K extends keyof T> = Omit<T, K> & Required<T>;
|
||||||
43
libs/common/src/util/buildTypeORMIncludeQuery.ts
Normal file
43
libs/common/src/util/buildTypeORMIncludeQuery.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
type TypeORMIncludeQuery = string[];
|
||||||
|
|
||||||
|
const mappingInclude: { [key: string]: any } = {
|
||||||
|
roles: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
community: {
|
||||||
|
community: true,
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
space: true,
|
||||||
|
},
|
||||||
|
subspace: {
|
||||||
|
subspace: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildTypeORMIncludeQuery(
|
||||||
|
modelName: string,
|
||||||
|
includeParam?: string,
|
||||||
|
): TypeORMIncludeQuery | undefined {
|
||||||
|
if (includeParam) {
|
||||||
|
const relations: TypeORMIncludeQuery = [];
|
||||||
|
const fieldsToInclude: string[] = includeParam.split(',');
|
||||||
|
|
||||||
|
fieldsToInclude.forEach((field: string) => {
|
||||||
|
if (mappingInclude[field]) {
|
||||||
|
relations.push(field); // Push mapped field
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Field ${field} not found in mappingInclude for ${modelName}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined; // If no includes, return undefined
|
||||||
|
}
|
||||||
18
libs/common/src/util/buildTypeORMSortQuery.ts
Normal file
18
libs/common/src/util/buildTypeORMSortQuery.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
type TypeORMSortQuery = { [key: string]: 'ASC' | 'DESC' };
|
||||||
|
|
||||||
|
export function buildTypeORMSortQuery(
|
||||||
|
sortParam: string | undefined,
|
||||||
|
): TypeORMSortQuery {
|
||||||
|
// sortParam format: userId:asc,createdDate:desc
|
||||||
|
if (!sortParam) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditions: string[] = sortParam.split(',');
|
||||||
|
|
||||||
|
return conditions.reduce((acc: TypeORMSortQuery, condition) => {
|
||||||
|
const [field, direction] = condition.split(':').map((str) => str.trim());
|
||||||
|
acc[field] = direction.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
26
libs/common/src/util/buildTypeORMWhereClause.ts
Normal file
26
libs/common/src/util/buildTypeORMWhereClause.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export function buildTypeORMWhereClause({ where }) {
|
||||||
|
if (!where) return {};
|
||||||
|
|
||||||
|
// Remove extra nesting if `where` is wrapped within an additional `where` property
|
||||||
|
const condition = where.where ? where.where : where;
|
||||||
|
|
||||||
|
const convertToNestedObject = (condition: any): any => {
|
||||||
|
const result = {};
|
||||||
|
for (const [key, value] of Object.entries(condition)) {
|
||||||
|
if (key.includes('.')) {
|
||||||
|
const [parentKey, childKey] = key.split('.');
|
||||||
|
result[parentKey] = {
|
||||||
|
...(result[parentKey] || {}),
|
||||||
|
[childKey]: value,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Array.isArray(condition)
|
||||||
|
? condition.map((item) => convertToNestedObject(item))
|
||||||
|
: convertToNestedObject(condition);
|
||||||
|
}
|
||||||
21
libs/common/src/util/getPaginationResponseDto.ts
Normal file
21
libs/common/src/util/getPaginationResponseDto.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { PageResponseDto } from '../dto/pagination.response.dto';
|
||||||
|
|
||||||
|
export function getPaginationResponseDto(
|
||||||
|
count: number,
|
||||||
|
page: number,
|
||||||
|
size: number,
|
||||||
|
): PageResponseDto {
|
||||||
|
const totalItem = count;
|
||||||
|
const totalPage = Math.ceil(totalItem / size);
|
||||||
|
const hasNext = page < totalPage ? true : false;
|
||||||
|
const hasPrevious = page === 1 || page > totalPage ? false : true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasNext,
|
||||||
|
hasPrevious,
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
totalItem,
|
||||||
|
totalPage,
|
||||||
|
};
|
||||||
|
}
|
||||||
4
libs/common/src/util/parseToDate.ts
Normal file
4
libs/common/src/util/parseToDate.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export function parseToDate(value: unknown): Date {
|
||||||
|
const valueInNumber = Number(value);
|
||||||
|
return new Date(valueInNumber);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { ValidateBy, ValidationOptions } from 'class-validator';
|
||||||
|
|
||||||
|
export function IsPageRequestParam(
|
||||||
|
validationOptions?: ValidationOptions,
|
||||||
|
): PropertyDecorator {
|
||||||
|
return ValidateBy(
|
||||||
|
{
|
||||||
|
name: 'IsPageRequestParam',
|
||||||
|
validator: {
|
||||||
|
validate(value) {
|
||||||
|
return IsPageParam(value); // you can return a Promise<boolean> here as well, if you want to make async validation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validationOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsPageParam(value: any): boolean {
|
||||||
|
return !isNaN(Number(value)) && value > 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { ValidateBy, ValidationOptions } from 'class-validator';
|
||||||
|
|
||||||
|
export function IsSizeRequestParam(
|
||||||
|
validationOptions?: ValidationOptions,
|
||||||
|
): PropertyDecorator {
|
||||||
|
return ValidateBy(
|
||||||
|
{
|
||||||
|
name: 'IsSizeRequestParam',
|
||||||
|
validator: {
|
||||||
|
validate(value) {
|
||||||
|
return IsSizeParam(value); // you can return a Promise<boolean> here as well, if you want to make async validation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validationOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsSizeParam(value: any): boolean {
|
||||||
|
return !isNaN(Number(value)) && value > -1;
|
||||||
|
}
|
||||||
54
libs/common/src/validators/is-sort-param.validator.ts
Normal file
54
libs/common/src/validators/is-sort-param.validator.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ValidateBy, ValidationOptions } from 'class-validator';
|
||||||
|
|
||||||
|
export function IsSortParam(
|
||||||
|
validationOptions?: ValidationOptions,
|
||||||
|
allowedFieldName?: string[],
|
||||||
|
): PropertyDecorator {
|
||||||
|
return ValidateBy(
|
||||||
|
{
|
||||||
|
name: 'IsSortParam',
|
||||||
|
validator: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
||||||
|
validate(value) {
|
||||||
|
return IsValidMultipleSortParam(value, allowedFieldName); // you can return a Promise<boolean> here as well, if you want to make async validation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validationOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsValidMultipleSortParam(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
||||||
|
value: any,
|
||||||
|
allowedFieldName?: string[],
|
||||||
|
): boolean {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditions: string[] = value.split(',');
|
||||||
|
const isValid: boolean = conditions.every((condition) => {
|
||||||
|
const combination: string[] = condition.split(':');
|
||||||
|
if (combination.length !== 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const field = combination[0].trim();
|
||||||
|
const direction = combination[1].trim();
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowedFieldName?.length && !allowedFieldName.includes(field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!['asc', 'desc'].includes(direction.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
@ -3,14 +3,10 @@ import { ConfigModule } from '@nestjs/config';
|
|||||||
import config from './config';
|
import config from './config';
|
||||||
import { AuthenticationModule } from './auth/auth.module';
|
import { AuthenticationModule } from './auth/auth.module';
|
||||||
import { UserModule } from './users/user.module';
|
import { UserModule } from './users/user.module';
|
||||||
import { RoomModule } from './room/room.module';
|
|
||||||
import { GroupModule } from './group/group.module';
|
import { GroupModule } from './group/group.module';
|
||||||
import { DeviceModule } from './device/device.module';
|
import { DeviceModule } from './device/device.module';
|
||||||
import { UserDevicePermissionModule } from './user-device-permission/user-device-permission.module';
|
import { UserDevicePermissionModule } from './user-device-permission/user-device-permission.module';
|
||||||
import { CommunityModule } from './community/community.module';
|
import { CommunityModule } from './community/community.module';
|
||||||
import { BuildingModule } from './building/building.module';
|
|
||||||
import { FloorModule } from './floor/floor.module';
|
|
||||||
import { UnitModule } from './unit/unit.module';
|
|
||||||
import { RoleModule } from './role/role.module';
|
import { RoleModule } from './role/role.module';
|
||||||
import { SeederModule } from '@app/common/seed/seeder.module';
|
import { SeederModule } from '@app/common/seed/seeder.module';
|
||||||
import { UserNotificationModule } from './user-notification/user-notification.module';
|
import { UserNotificationModule } from './user-notification/user-notification.module';
|
||||||
@ -24,6 +20,7 @@ import { RegionModule } from './region/region.module';
|
|||||||
import { TimeZoneModule } from './timezone/timezone.module';
|
import { TimeZoneModule } from './timezone/timezone.module';
|
||||||
import { VisitorPasswordModule } from './vistor-password/visitor-password.module';
|
import { VisitorPasswordModule } from './vistor-password/visitor-password.module';
|
||||||
import { ScheduleModule } from './schedule/schedule.module';
|
import { ScheduleModule } from './schedule/schedule.module';
|
||||||
|
import { SpaceModule } from './space/space.module';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
@ -33,11 +30,9 @@ import { ScheduleModule } from './schedule/schedule.module';
|
|||||||
UserModule,
|
UserModule,
|
||||||
RoleModule,
|
RoleModule,
|
||||||
CommunityModule,
|
CommunityModule,
|
||||||
BuildingModule,
|
|
||||||
FloorModule,
|
SpaceModule,
|
||||||
UnitModule,
|
|
||||||
RoomModule,
|
|
||||||
RoomModule,
|
|
||||||
GroupModule,
|
GroupModule,
|
||||||
DeviceModule,
|
DeviceModule,
|
||||||
DeviceMessagesSubscriptionModule,
|
DeviceMessagesSubscriptionModule,
|
||||||
|
|||||||
@ -8,12 +8,14 @@ import { DeviceService } from 'src/device/services';
|
|||||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
import { ProductRepository } from '@app/common/modules/product/repositories';
|
import { ProductRepository } from '@app/common/modules/product/repositories';
|
||||||
import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module';
|
import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule],
|
imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule],
|
||||||
controllers: [AutomationController],
|
controllers: [AutomationController],
|
||||||
providers: [
|
providers: [
|
||||||
AutomationService,
|
AutomationService,
|
||||||
|
TuyaService,
|
||||||
SpaceRepository,
|
SpaceRepository,
|
||||||
DeviceService,
|
DeviceService,
|
||||||
DeviceRepository,
|
DeviceRepository,
|
||||||
|
|||||||
@ -18,6 +18,11 @@ import {
|
|||||||
} from '../dtos/automation.dto';
|
} from '../dtos/automation.dto';
|
||||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||||
|
import {
|
||||||
|
AutomationParamDto,
|
||||||
|
DeleteAutomationParamDto,
|
||||||
|
SpaceParamDto,
|
||||||
|
} from '../dtos';
|
||||||
|
|
||||||
@ApiTags('Automation Module')
|
@ApiTags('Automation Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
@ -42,28 +47,29 @@ export class AutomationController {
|
|||||||
}
|
}
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':unitUuid')
|
@Get(':spaceUuid')
|
||||||
async getAutomationByUnit(@Param('unitUuid') unitUuid: string) {
|
async getAutomationBySpace(@Param() param: SpaceParamDto) {
|
||||||
const automation =
|
const automation = await this.automationService.getAutomationBySpace(
|
||||||
await this.automationService.getAutomationByUnit(unitUuid);
|
param.spaceUuid,
|
||||||
|
);
|
||||||
return automation;
|
return automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('details/:automationId')
|
@Get('details/:automationUuid')
|
||||||
async getAutomationDetails(@Param('automationId') automationId: string) {
|
async getAutomationDetails(@Param() param: AutomationParamDto) {
|
||||||
const automation =
|
const automation = await this.automationService.getAutomationDetails(
|
||||||
await this.automationService.getAutomationDetails(automationId);
|
param.automationUuid,
|
||||||
|
);
|
||||||
return automation;
|
return automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Delete(':unitUuid/:automationId')
|
@Delete(':unitUuid/:automationId')
|
||||||
async deleteAutomation(
|
async deleteAutomation(@Param() param: DeleteAutomationParamDto) {
|
||||||
@Param('unitUuid') unitUuid: string,
|
await this.automationService.deleteAutomation(param);
|
||||||
@Param('automationId') automationId: string,
|
|
||||||
) {
|
|
||||||
await this.automationService.deleteAutomation(unitUuid, automationId);
|
|
||||||
return {
|
return {
|
||||||
statusCode: HttpStatus.OK,
|
statusCode: HttpStatus.OK,
|
||||||
message: 'Automation Deleted Successfully',
|
message: 'Automation Deleted Successfully',
|
||||||
@ -74,11 +80,11 @@ export class AutomationController {
|
|||||||
@Put(':automationId')
|
@Put(':automationId')
|
||||||
async updateAutomation(
|
async updateAutomation(
|
||||||
@Body() updateAutomationDto: UpdateAutomationDto,
|
@Body() updateAutomationDto: UpdateAutomationDto,
|
||||||
@Param('automationId') automationId: string,
|
@Param() param: AutomationParamDto,
|
||||||
) {
|
) {
|
||||||
const automation = await this.automationService.updateAutomation(
|
const automation = await this.automationService.updateAutomation(
|
||||||
updateAutomationDto,
|
updateAutomationDto,
|
||||||
automationId,
|
param.automationUuid,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
statusCode: HttpStatus.CREATED,
|
statusCode: HttpStatus.CREATED,
|
||||||
@ -87,16 +93,17 @@ export class AutomationController {
|
|||||||
data: automation,
|
data: automation,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Put('status/:automationId')
|
@Put('status/:automationId')
|
||||||
async updateAutomationStatus(
|
async updateAutomationStatus(
|
||||||
@Body() updateAutomationStatusDto: UpdateAutomationStatusDto,
|
@Body() updateAutomationStatusDto: UpdateAutomationStatusDto,
|
||||||
@Param('automationId') automationId: string,
|
@Param() param: AutomationParamDto,
|
||||||
) {
|
) {
|
||||||
await this.automationService.updateAutomationStatus(
|
await this.automationService.updateAutomationStatus(
|
||||||
updateAutomationStatusDto,
|
updateAutomationStatusDto,
|
||||||
automationId,
|
param.automationUuid,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
statusCode: HttpStatus.CREATED,
|
statusCode: HttpStatus.CREATED,
|
||||||
|
|||||||
@ -114,10 +114,10 @@ class Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AddAutomationDto {
|
export class AddAutomationDto {
|
||||||
@ApiProperty({ description: 'Unit ID', required: true })
|
@ApiProperty({ description: 'Space ID', required: true })
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public unitUuid: string;
|
public spaceUuid: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Automation name', required: true })
|
@ApiProperty({ description: 'Automation name', required: true })
|
||||||
@IsString()
|
@IsString()
|
||||||
@ -197,10 +197,10 @@ export class UpdateAutomationDto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class UpdateAutomationStatusDto {
|
export class UpdateAutomationStatusDto {
|
||||||
@ApiProperty({ description: 'Unit uuid', required: true })
|
@ApiProperty({ description: 'Space uuid', required: true })
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public unitUuid: string;
|
public spaceUuid: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Is enable', required: true })
|
@ApiProperty({ description: 'Is enable', required: true })
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
11
src/automation/dtos/automation.param.dto.ts
Normal file
11
src/automation/dtos/automation.param.dto.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class AutomationParamDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the automation',
|
||||||
|
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
automationUuid: string;
|
||||||
|
}
|
||||||
18
src/automation/dtos/delete.automation.param.dto.ts
Normal file
18
src/automation/dtos/delete.automation.param.dto.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class DeleteAutomationParamDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the Space',
|
||||||
|
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
spaceUuid: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the Automation',
|
||||||
|
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
automationUuid: string;
|
||||||
|
}
|
||||||
@ -1 +1,4 @@
|
|||||||
export * from './automation.dto';
|
export * from './automation.dto';
|
||||||
|
export * from './space.param.dto';
|
||||||
|
export * from './automation.param.dto';
|
||||||
|
export * from './delete.automation.param.dto';
|
||||||
|
|||||||
11
src/automation/dtos/space.param.dto.ts
Normal file
11
src/automation/dtos/space.param.dto.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class SpaceParamDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the space',
|
||||||
|
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
spaceUuid: string;
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@ export interface AddAutomationInterface {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface GetAutomationByUnitInterface {
|
export interface GetAutomationBySpaceInterface {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
msg?: string;
|
msg?: string;
|
||||||
result: {
|
result: {
|
||||||
|
|||||||
@ -7,27 +7,29 @@ import {
|
|||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import {
|
import {
|
||||||
AddAutomationDto,
|
AddAutomationDto,
|
||||||
|
DeleteAutomationParamDto,
|
||||||
UpdateAutomationDto,
|
UpdateAutomationDto,
|
||||||
UpdateAutomationStatusDto,
|
UpdateAutomationStatusDto,
|
||||||
} from '../dtos';
|
} from '../dtos';
|
||||||
import { GetUnitByUuidInterface } from 'src/unit/interface/unit.interface';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
||||||
import { convertKeysToSnakeCase } from '@app/common/helper/snakeCaseConverter';
|
import { convertKeysToSnakeCase } from '@app/common/helper/snakeCaseConverter';
|
||||||
import { DeviceService } from 'src/device/services';
|
import { DeviceService } from 'src/device/services';
|
||||||
import {
|
import {
|
||||||
|
Action,
|
||||||
AddAutomationInterface,
|
AddAutomationInterface,
|
||||||
AutomationDetailsResult,
|
AutomationDetailsResult,
|
||||||
AutomationResponseData,
|
AutomationResponseData,
|
||||||
|
Condition,
|
||||||
DeleteAutomationInterface,
|
DeleteAutomationInterface,
|
||||||
GetAutomationByUnitInterface,
|
GetAutomationBySpaceInterface,
|
||||||
} from '../interface/automation.interface';
|
} from '../interface/automation.interface';
|
||||||
import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter';
|
import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import {
|
import {
|
||||||
ActionExecutorEnum,
|
ActionExecutorEnum,
|
||||||
EntityTypeEnum,
|
EntityTypeEnum,
|
||||||
} from '@app/common/constants/automation.enum';
|
} from '@app/common/constants/automation.enum';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AutomationService {
|
export class AutomationService {
|
||||||
@ -36,6 +38,7 @@ export class AutomationService {
|
|||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
private readonly spaceRepository: SpaceRepository,
|
private readonly spaceRepository: SpaceRepository,
|
||||||
private readonly deviceService: DeviceService,
|
private readonly deviceService: DeviceService,
|
||||||
|
private readonly tuyaService: TuyaService,
|
||||||
) {
|
) {
|
||||||
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
||||||
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
|
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
|
||||||
@ -49,75 +52,39 @@ export class AutomationService {
|
|||||||
|
|
||||||
async addAutomation(addAutomationDto: AddAutomationDto, spaceTuyaId = null) {
|
async addAutomation(addAutomationDto: AddAutomationDto, spaceTuyaId = null) {
|
||||||
try {
|
try {
|
||||||
let unitSpaceTuyaId;
|
const { automationName, effectiveTime, decisionExpr } = addAutomationDto;
|
||||||
if (!spaceTuyaId) {
|
const space = await this.getSpaceByUuid(addAutomationDto.spaceUuid);
|
||||||
const unitDetails = await this.getUnitByUuid(addAutomationDto.unitUuid);
|
|
||||||
|
|
||||||
unitSpaceTuyaId = unitDetails.spaceTuyaUuid;
|
const actions = await this.processEntities<Action>(
|
||||||
if (!unitDetails) {
|
addAutomationDto.actions,
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
'actionExecutor',
|
||||||
}
|
{ [ActionExecutorEnum.DEVICE_ISSUE]: true },
|
||||||
} else {
|
this.deviceService,
|
||||||
unitSpaceTuyaId = spaceTuyaId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = addAutomationDto.actions.map((action) =>
|
|
||||||
convertKeysToSnakeCase(action),
|
|
||||||
);
|
|
||||||
const conditions = addAutomationDto.conditions.map((condition) =>
|
|
||||||
convertKeysToSnakeCase(condition),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const action of actions) {
|
const conditions = await this.processEntities<Condition>(
|
||||||
if (action.action_executor === ActionExecutorEnum.DEVICE_ISSUE) {
|
addAutomationDto.conditions,
|
||||||
const device = await this.deviceService.getDeviceByDeviceUuid(
|
'entityType',
|
||||||
action.entity_id,
|
{ [EntityTypeEnum.DEVICE_REPORT]: true },
|
||||||
false,
|
this.deviceService,
|
||||||
);
|
);
|
||||||
if (device) {
|
|
||||||
action.entity_id = device.deviceTuyaUuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const condition of conditions) {
|
const response = (await this.tuyaService.createAutomation(
|
||||||
if (condition.entity_type === EntityTypeEnum.DEVICE_REPORT) {
|
space.spaceTuyaUuid,
|
||||||
const device = await this.deviceService.getDeviceByDeviceUuid(
|
automationName,
|
||||||
condition.entity_id,
|
effectiveTime,
|
||||||
false,
|
decisionExpr,
|
||||||
);
|
conditions,
|
||||||
if (device) {
|
actions,
|
||||||
condition.entity_id = device.deviceTuyaUuid;
|
)) as AddAutomationInterface;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = `/v2.0/cloud/scene/rule`;
|
|
||||||
const response: AddAutomationInterface = await this.tuya.request({
|
|
||||||
method: 'POST',
|
|
||||||
path,
|
|
||||||
body: {
|
|
||||||
space_id: unitSpaceTuyaId,
|
|
||||||
name: addAutomationDto.automationName,
|
|
||||||
effective_time: {
|
|
||||||
...addAutomationDto.effectiveTime,
|
|
||||||
timezone_id: 'Asia/Dubai',
|
|
||||||
},
|
|
||||||
type: 'automation',
|
|
||||||
decision_expr: addAutomationDto.decisionExpr,
|
|
||||||
conditions: conditions,
|
|
||||||
actions: actions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!response.success) {
|
|
||||||
throw new HttpException(response.msg, HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
id: response.result.id,
|
id: response?.result.id,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
if (err instanceof BadRequestException) {
|
if (err instanceof BadRequestException) {
|
||||||
throw err; // Re-throw BadRequestException
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
err.message || 'Automation not found',
|
err.message || 'Automation not found',
|
||||||
@ -126,45 +93,42 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getUnitByUuid(unitUuid: string): Promise<GetUnitByUuidInterface> {
|
|
||||||
|
async getSpaceByUuid(spaceUuid: string) {
|
||||||
try {
|
try {
|
||||||
const unit = await this.spaceRepository.findOne({
|
const space = await this.spaceRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
uuid: unitUuid,
|
uuid: spaceUuid,
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.UNIT,
|
|
||||||
},
|
},
|
||||||
},
|
relations: ['community'],
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
});
|
||||||
if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) {
|
if (!space) {
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
throw new BadRequestException(`Invalid space UUID ${spaceUuid}`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
uuid: unit.uuid,
|
uuid: space.uuid,
|
||||||
createdAt: unit.createdAt,
|
createdAt: space.createdAt,
|
||||||
updatedAt: unit.updatedAt,
|
updatedAt: space.updatedAt,
|
||||||
name: unit.spaceName,
|
name: space.spaceName,
|
||||||
type: unit.spaceType.type,
|
spaceTuyaUuid: space.community.externalId,
|
||||||
spaceTuyaUuid: unit.spaceTuyaUuid,
|
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof BadRequestException) {
|
if (err instanceof BadRequestException) {
|
||||||
throw err; // Re-throw BadRequestException
|
throw err; // Re-throw BadRequestException
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
throw new HttpException('Space not found', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getAutomationByUnit(unitUuid: string) {
|
async getAutomationBySpace(spaceUuid: string) {
|
||||||
try {
|
try {
|
||||||
const unit = await this.getUnitByUuid(unitUuid);
|
const space = await this.getSpaceByUuid(spaceUuid);
|
||||||
if (!unit.spaceTuyaUuid) {
|
if (!space.spaceTuyaUuid) {
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
throw new BadRequestException(`Invalid space UUID ${spaceUuid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = `/v2.0/cloud/scene/rule?space_id=${unit.spaceTuyaUuid}&type=automation`;
|
const path = `/v2.0/cloud/scene/rule?space_id=${space.spaceTuyaUuid}&type=automation`;
|
||||||
const response: GetAutomationByUnitInterface = await this.tuya.request({
|
const response: GetAutomationBySpaceInterface = await this.tuya.request({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path,
|
path,
|
||||||
});
|
});
|
||||||
@ -192,11 +156,12 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTapToRunSceneDetailsTuya(
|
async getTapToRunSceneDetailsTuya(
|
||||||
sceneId: string,
|
sceneUuid: string,
|
||||||
): Promise<AutomationDetailsResult> {
|
): Promise<AutomationDetailsResult> {
|
||||||
try {
|
try {
|
||||||
const path = `/v2.0/cloud/scene/rule/${sceneId}`;
|
const path = `/v2.0/cloud/scene/rule/${sceneUuid}`;
|
||||||
const response = await this.tuya.request({
|
const response = await this.tuya.request({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path,
|
path,
|
||||||
@ -224,9 +189,9 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getAutomationDetails(automationId: string, withSpaceId = false) {
|
async getAutomationDetails(automationUuid: string, withSpaceId = false) {
|
||||||
try {
|
try {
|
||||||
const path = `/v2.0/cloud/scene/rule/${automationId}`;
|
const path = `/v2.0/cloud/scene/rule/${automationUuid}`;
|
||||||
const response = await this.tuya.request({
|
const response = await this.tuya.request({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path,
|
path,
|
||||||
@ -312,24 +277,21 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAutomation(
|
async deleteAutomation(param: DeleteAutomationParamDto, spaceTuyaId = null) {
|
||||||
unitUuid: string,
|
|
||||||
automationId: string,
|
|
||||||
spaceTuyaId = null,
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
let unitSpaceTuyaId;
|
const { automationUuid, spaceUuid } = param;
|
||||||
|
let tuyaSpaceId;
|
||||||
if (!spaceTuyaId) {
|
if (!spaceTuyaId) {
|
||||||
const unitDetails = await this.getUnitByUuid(unitUuid);
|
const space = await this.getSpaceByUuid(spaceUuid);
|
||||||
unitSpaceTuyaId = unitDetails.spaceTuyaUuid;
|
tuyaSpaceId = space.spaceTuyaUuid;
|
||||||
if (!unitSpaceTuyaId) {
|
if (!tuyaSpaceId) {
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
throw new BadRequestException(`Invalid space UUID ${spaceUuid}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unitSpaceTuyaId = spaceTuyaId;
|
tuyaSpaceId = spaceTuyaId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = `/v2.0/cloud/scene/rule?ids=${automationId}&space_id=${unitSpaceTuyaId}`;
|
const path = `/v2.0/cloud/scene/rule?ids=${automationUuid}&space_id=${tuyaSpaceId}`;
|
||||||
const response: DeleteAutomationInterface = await this.tuya.request({
|
const response: DeleteAutomationInterface = await this.tuya.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
path,
|
path,
|
||||||
@ -354,10 +316,10 @@ export class AutomationService {
|
|||||||
|
|
||||||
async updateAutomation(
|
async updateAutomation(
|
||||||
updateAutomationDto: UpdateAutomationDto,
|
updateAutomationDto: UpdateAutomationDto,
|
||||||
automationId: string,
|
automationUuid: string,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const spaceTuyaId = await this.getAutomationDetails(automationId, true);
|
const spaceTuyaId = await this.getAutomationDetails(automationUuid, true);
|
||||||
if (!spaceTuyaId.spaceId) {
|
if (!spaceTuyaId.spaceId) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
"Automation doesn't exist",
|
"Automation doesn't exist",
|
||||||
@ -366,14 +328,18 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
const addAutomation = {
|
const addAutomation = {
|
||||||
...updateAutomationDto,
|
...updateAutomationDto,
|
||||||
unitUuid: null,
|
spaceUuid: null,
|
||||||
};
|
};
|
||||||
const newAutomation = await this.addAutomation(
|
const newAutomation = await this.addAutomation(
|
||||||
addAutomation,
|
addAutomation,
|
||||||
spaceTuyaId.spaceId,
|
spaceTuyaId.spaceId,
|
||||||
);
|
);
|
||||||
|
const params: DeleteAutomationParamDto = {
|
||||||
|
spaceUuid: spaceTuyaId.spaceId,
|
||||||
|
automationUuid: automationUuid,
|
||||||
|
};
|
||||||
if (newAutomation.id) {
|
if (newAutomation.id) {
|
||||||
await this.deleteAutomation(null, automationId, spaceTuyaId.spaceId);
|
await this.deleteAutomation(null, params);
|
||||||
return newAutomation;
|
return newAutomation;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -389,22 +355,24 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
async updateAutomationStatus(
|
async updateAutomationStatus(
|
||||||
updateAutomationStatusDto: UpdateAutomationStatusDto,
|
updateAutomationStatusDto: UpdateAutomationStatusDto,
|
||||||
automationId: string,
|
automationUuid: string,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const unitDetails = await this.getUnitByUuid(
|
const space = await this.getSpaceByUuid(
|
||||||
updateAutomationStatusDto.unitUuid,
|
updateAutomationStatusDto.spaceUuid,
|
||||||
|
);
|
||||||
|
if (!space.spaceTuyaUuid) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`Invalid space UUID ${updateAutomationStatusDto.spaceUuid}`,
|
||||||
);
|
);
|
||||||
if (!unitDetails.spaceTuyaUuid) {
|
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = `/v2.0/cloud/scene/rule/state?space_id=${unitDetails.spaceTuyaUuid}`;
|
const path = `/v2.0/cloud/scene/rule/state?space_id=${space.spaceTuyaUuid}`;
|
||||||
const response: DeleteAutomationInterface = await this.tuya.request({
|
const response: DeleteAutomationInterface = await this.tuya.request({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
path,
|
path,
|
||||||
body: {
|
body: {
|
||||||
ids: automationId,
|
ids: automationUuid,
|
||||||
is_enable: updateAutomationStatusDto.isEnable,
|
is_enable: updateAutomationStatusDto.isEnable,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -425,4 +393,42 @@ export class AutomationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async processEntities<T extends Action | Condition>(
|
||||||
|
entities: T[], // Accepts either Action[] or Condition[]
|
||||||
|
lookupKey: keyof T, // The key to look up, specific to T
|
||||||
|
entityTypeOrExecutorMap: {
|
||||||
|
[key in ActionExecutorEnum | EntityTypeEnum]?: boolean;
|
||||||
|
},
|
||||||
|
deviceService: {
|
||||||
|
getDeviceByDeviceUuid: (
|
||||||
|
id: string,
|
||||||
|
flag: boolean,
|
||||||
|
) => Promise<{ deviceTuyaUuid: string } | null>;
|
||||||
|
},
|
||||||
|
): Promise<T[]> {
|
||||||
|
// Returns the same type as provided in the input
|
||||||
|
return Promise.all(
|
||||||
|
entities.map(async (entity) => {
|
||||||
|
// Convert keys to snake case (assuming a utility function exists)
|
||||||
|
const processedEntity = convertKeysToSnakeCase(entity) as T;
|
||||||
|
|
||||||
|
// Check if entity needs device UUID lookup
|
||||||
|
const key = processedEntity[lookupKey];
|
||||||
|
if (
|
||||||
|
entityTypeOrExecutorMap[key as ActionExecutorEnum | EntityTypeEnum]
|
||||||
|
) {
|
||||||
|
const device = await deviceService.getDeviceByDeviceUuid(
|
||||||
|
processedEntity.entityId,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
if (device) {
|
||||||
|
processedEntity.entityId = device.deviceTuyaUuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedEntity;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { BuildingService } from './services/building.service';
|
|
||||||
import { BuildingController } from './controllers/building.controller';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
|
||||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { SpaceTypeRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
|
||||||
controllers: [BuildingController],
|
|
||||||
providers: [
|
|
||||||
BuildingService,
|
|
||||||
SpaceRepository,
|
|
||||||
SpaceTypeRepository,
|
|
||||||
UserSpaceRepository,
|
|
||||||
UserRepository,
|
|
||||||
],
|
|
||||||
exports: [BuildingService],
|
|
||||||
})
|
|
||||||
export class BuildingModule {}
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
import { BuildingService } from '../services/building.service';
|
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
HttpStatus,
|
|
||||||
Param,
|
|
||||||
Post,
|
|
||||||
Put,
|
|
||||||
Query,
|
|
||||||
UseGuards,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
|
||||||
import { AddBuildingDto, AddUserBuildingDto } from '../dtos/add.building.dto';
|
|
||||||
import { GetBuildingChildDto } from '../dtos/get.building.dto';
|
|
||||||
import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
|
|
||||||
import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard';
|
|
||||||
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: EnableDisableStatusEnum.ENABLED,
|
|
||||||
path: SpaceType.BUILDING,
|
|
||||||
})
|
|
||||||
export class BuildingController {
|
|
||||||
constructor(private readonly buildingService: BuildingService) {}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, CheckCommunityTypeGuard)
|
|
||||||
@Post()
|
|
||||||
async addBuilding(@Body() addBuildingDto: AddBuildingDto) {
|
|
||||||
const building = await this.buildingService.addBuilding(addBuildingDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'Building added successfully',
|
|
||||||
data: building,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, BuildingPermissionGuard)
|
|
||||||
@Get(':buildingUuid')
|
|
||||||
async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) {
|
|
||||||
const building = await this.buildingService.getBuildingByUuid(buildingUuid);
|
|
||||||
return building;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, BuildingPermissionGuard)
|
|
||||||
@Get('child/:buildingUuid')
|
|
||||||
async getBuildingChildByUuid(
|
|
||||||
@Param('buildingUuid') buildingUuid: string,
|
|
||||||
@Query() query: GetBuildingChildDto,
|
|
||||||
) {
|
|
||||||
const building = await this.buildingService.getBuildingChildByUuid(
|
|
||||||
buildingUuid,
|
|
||||||
query,
|
|
||||||
);
|
|
||||||
return building;
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, BuildingPermissionGuard)
|
|
||||||
@Get('parent/:buildingUuid')
|
|
||||||
async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) {
|
|
||||||
const building =
|
|
||||||
await this.buildingService.getBuildingParentByUuid(buildingUuid);
|
|
||||||
return building;
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(AdminRoleGuard, CheckUserBuildingGuard)
|
|
||||||
@Post('user')
|
|
||||||
async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) {
|
|
||||||
await this.buildingService.addUserBuilding(addUserBuildingDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'user building added successfully',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Get('user/:userUuid')
|
|
||||||
async getBuildingsByUserId(@Param('userUuid') userUuid: string) {
|
|
||||||
return await this.buildingService.getBuildingsByUserId(userUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, BuildingPermissionGuard)
|
|
||||||
@Put(':buildingUuid')
|
|
||||||
async renameBuildingByUuid(
|
|
||||||
@Param('buildingUuid') buildingUuid: string,
|
|
||||||
@Body() updateBuildingDto: UpdateBuildingNameDto,
|
|
||||||
) {
|
|
||||||
const building = await this.buildingService.renameBuildingByUuid(
|
|
||||||
buildingUuid,
|
|
||||||
updateBuildingDto,
|
|
||||||
);
|
|
||||||
return building;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './building.controller';
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class AddBuildingDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'buildingName',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public buildingName: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'communityUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public communityUuid: string;
|
|
||||||
constructor(dto: Partial<AddBuildingDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class AddUserBuildingDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'buildingUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public buildingUuid: string;
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'userUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public userUuid: string;
|
|
||||||
constructor(dto: Partial<AddUserBuildingDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import { BooleanValues } from '@app/common/constants/boolean-values.enum';
|
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { Transform } from 'class-transformer';
|
|
||||||
import {
|
|
||||||
IsBoolean,
|
|
||||||
IsInt,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
|
||||||
IsString,
|
|
||||||
Min,
|
|
||||||
} from 'class-validator';
|
|
||||||
|
|
||||||
export class GetBuildingDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'buildingUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public buildingUuid: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GetBuildingChildDto {
|
|
||||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
|
||||||
@IsInt({ message: 'Page must be a number' })
|
|
||||||
@Min(1, { message: 'Page must not be less than 1' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
public page: number;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: 10,
|
|
||||||
description: 'Number of items per page',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsInt({ message: 'Page size must be a number' })
|
|
||||||
@Min(1, { message: 'Page size must not be less than 1' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
public pageSize: number;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: true,
|
|
||||||
description: 'Flag to determine whether to fetch full hierarchy',
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsBoolean()
|
|
||||||
@Transform((value) => {
|
|
||||||
return value.obj.includeSubSpaces === BooleanValues.TRUE;
|
|
||||||
})
|
|
||||||
public includeSubSpaces: boolean = false;
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './add.building.dto';
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class UpdateBuildingNameDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'buildingName',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public buildingName: string;
|
|
||||||
|
|
||||||
constructor(dto: Partial<UpdateBuildingNameDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
export interface GetBuildingByUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildingChildInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
totalCount?: number;
|
|
||||||
children?: BuildingChildInterface[];
|
|
||||||
}
|
|
||||||
export interface BuildingParentInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
parent?: BuildingParentInterface;
|
|
||||||
}
|
|
||||||
export interface RenameBuildingByUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
export interface GetBuildingByUserUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
@ -1,317 +0,0 @@
|
|||||||
import { GetBuildingChildDto } from '../dtos/get.building.dto';
|
|
||||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository';
|
|
||||||
import {
|
|
||||||
Injectable,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
BadRequestException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { AddBuildingDto, AddUserBuildingDto } from '../dtos';
|
|
||||||
import {
|
|
||||||
BuildingChildInterface,
|
|
||||||
BuildingParentInterface,
|
|
||||||
GetBuildingByUserUuidInterface,
|
|
||||||
GetBuildingByUuidInterface,
|
|
||||||
RenameBuildingByUuidInterface,
|
|
||||||
} from '../interface/building.interface';
|
|
||||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
|
||||||
import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class BuildingService {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
|
||||||
private readonly userSpaceRepository: UserSpaceRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async addBuilding(addBuildingDto: AddBuildingDto) {
|
|
||||||
try {
|
|
||||||
const spaceType = await this.spaceTypeRepository.findOne({
|
|
||||||
where: {
|
|
||||||
type: SpaceType.BUILDING,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!spaceType) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
const building = await this.spaceRepository.save({
|
|
||||||
spaceName: addBuildingDto.buildingName,
|
|
||||||
parent: { uuid: addBuildingDto.communityUuid },
|
|
||||||
spaceType: { uuid: spaceType.uuid },
|
|
||||||
});
|
|
||||||
return building;
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuildingByUuid(
|
|
||||||
buildingUuid: string,
|
|
||||||
): Promise<GetBuildingByUuidInterface> {
|
|
||||||
try {
|
|
||||||
const building = await this.spaceRepository.findOne({
|
|
||||||
where: {
|
|
||||||
uuid: buildingUuid,
|
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.BUILDING,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!building ||
|
|
||||||
!building.spaceType ||
|
|
||||||
building.spaceType.type !== SpaceType.BUILDING
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
uuid: building.uuid,
|
|
||||||
createdAt: building.createdAt,
|
|
||||||
updatedAt: building.updatedAt,
|
|
||||||
name: building.spaceName,
|
|
||||||
type: building.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getBuildingChildByUuid(
|
|
||||||
buildingUuid: string,
|
|
||||||
getBuildingChildDto: GetBuildingChildDto,
|
|
||||||
): Promise<BuildingChildInterface> {
|
|
||||||
try {
|
|
||||||
const { includeSubSpaces, page, pageSize } = getBuildingChildDto;
|
|
||||||
|
|
||||||
const space = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: buildingUuid },
|
|
||||||
relations: ['children', 'spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!space ||
|
|
||||||
!space.spaceType ||
|
|
||||||
space.spaceType.type !== SpaceType.BUILDING
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalCount = await this.spaceRepository.count({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const children = await this.buildHierarchy(
|
|
||||||
space,
|
|
||||||
includeSubSpaces,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: space.uuid,
|
|
||||||
name: space.spaceName,
|
|
||||||
type: space.spaceType.type,
|
|
||||||
totalCount,
|
|
||||||
children,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async buildHierarchy(
|
|
||||||
space: SpaceEntity,
|
|
||||||
includeSubSpaces: boolean,
|
|
||||||
page: number,
|
|
||||||
pageSize: number,
|
|
||||||
): Promise<BuildingChildInterface[]> {
|
|
||||||
const children = await this.spaceRepository.find({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
skip: (page - 1) * pageSize,
|
|
||||||
take: pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
|
||||||
return children
|
|
||||||
.filter(
|
|
||||||
(child) =>
|
|
||||||
child.spaceType.type !== SpaceType.BUILDING &&
|
|
||||||
child.spaceType.type !== SpaceType.COMMUNITY,
|
|
||||||
) // Filter remaining building and community types
|
|
||||||
.map((child) => ({
|
|
||||||
uuid: child.uuid,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const childHierarchies = await Promise.all(
|
|
||||||
children
|
|
||||||
.filter(
|
|
||||||
(child) =>
|
|
||||||
child.spaceType.type !== SpaceType.BUILDING &&
|
|
||||||
child.spaceType.type !== SpaceType.COMMUNITY,
|
|
||||||
) // Filter remaining building and community types
|
|
||||||
.map(async (child) => ({
|
|
||||||
uuid: child.uuid,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
return childHierarchies;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuildingParentByUuid(
|
|
||||||
buildingUuid: string,
|
|
||||||
): Promise<BuildingParentInterface> {
|
|
||||||
try {
|
|
||||||
const building = await this.spaceRepository.findOne({
|
|
||||||
where: {
|
|
||||||
uuid: buildingUuid,
|
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.BUILDING,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!building ||
|
|
||||||
!building.spaceType ||
|
|
||||||
building.spaceType.type !== SpaceType.BUILDING
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
uuid: building.uuid,
|
|
||||||
name: building.spaceName,
|
|
||||||
type: building.spaceType.type,
|
|
||||||
parent: {
|
|
||||||
uuid: building.parent.uuid,
|
|
||||||
name: building.parent.spaceName,
|
|
||||||
type: building.parent.spaceType.type,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuildingsByUserId(
|
|
||||||
userUuid: string,
|
|
||||||
): Promise<GetBuildingByUserUuidInterface[]> {
|
|
||||||
try {
|
|
||||||
const buildings = await this.userSpaceRepository.find({
|
|
||||||
relations: ['space', 'space.spaceType'],
|
|
||||||
where: {
|
|
||||||
user: { uuid: userUuid },
|
|
||||||
space: { spaceType: { type: SpaceType.BUILDING } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (buildings.length === 0) {
|
|
||||||
throw new HttpException(
|
|
||||||
'this user has no buildings',
|
|
||||||
HttpStatus.NOT_FOUND,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const spaces = buildings.map((building) => ({
|
|
||||||
uuid: building.space.uuid,
|
|
||||||
name: building.space.spaceName,
|
|
||||||
type: building.space.spaceType.type,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return spaces;
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof HttpException) {
|
|
||||||
throw err;
|
|
||||||
} else {
|
|
||||||
throw new HttpException('user not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async addUserBuilding(addUserBuildingDto: AddUserBuildingDto) {
|
|
||||||
try {
|
|
||||||
await this.userSpaceRepository.save({
|
|
||||||
user: { uuid: addUserBuildingDto.userUuid },
|
|
||||||
space: { uuid: addUserBuildingDto.buildingUuid },
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) {
|
|
||||||
throw new HttpException(
|
|
||||||
'User already belongs to this building',
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new HttpException(
|
|
||||||
err.message || 'Internal Server Error',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async renameBuildingByUuid(
|
|
||||||
buildingUuid: string,
|
|
||||||
updateBuildingNameDto: UpdateBuildingNameDto,
|
|
||||||
): Promise<RenameBuildingByUuidInterface> {
|
|
||||||
try {
|
|
||||||
const building = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: buildingUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!building ||
|
|
||||||
!building.spaceType ||
|
|
||||||
building.spaceType.type !== SpaceType.BUILDING
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.spaceRepository.update(
|
|
||||||
{ uuid: buildingUuid },
|
|
||||||
{ spaceName: updateBuildingNameDto.buildingName },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fetch the updated building
|
|
||||||
const updatedBuilding = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: buildingUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: updatedBuilding.uuid,
|
|
||||||
name: updatedBuilding.spaceName,
|
|
||||||
type: updatedBuilding.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './building.service';
|
|
||||||
@ -4,11 +4,11 @@ import { CommunityController } from './controllers/community.controller';
|
|||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import { SpaceTypeRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
||||||
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { SpacePermissionService } from '@app/common/helper/services';
|
import { SpacePermissionService } from '@app/common/helper/services';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
||||||
@ -16,9 +16,9 @@ import { SpacePermissionService } from '@app/common/helper/services';
|
|||||||
providers: [
|
providers: [
|
||||||
CommunityService,
|
CommunityService,
|
||||||
SpaceRepository,
|
SpaceRepository,
|
||||||
SpaceTypeRepository,
|
|
||||||
UserSpaceRepository,
|
UserSpaceRepository,
|
||||||
UserRepository,
|
TuyaService,
|
||||||
|
CommunityRepository,
|
||||||
SpacePermissionService,
|
SpacePermissionService,
|
||||||
],
|
],
|
||||||
exports: [CommunityService, SpacePermissionService],
|
exports: [CommunityService, SpacePermissionService],
|
||||||
|
|||||||
@ -2,31 +2,28 @@ import { CommunityService } from '../services/community.service';
|
|||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
HttpStatus,
|
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||||
import {
|
import { AddCommunityDto } from '../dtos/add.community.dto';
|
||||||
AddCommunityDto,
|
import { GetCommunityParams } from '../dtos/get.community.dto';
|
||||||
AddUserCommunityDto,
|
|
||||||
} from '../dtos/add.community.dto';
|
|
||||||
import { GetCommunityChildDto } from '../dtos/get.community.dto';
|
|
||||||
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
||||||
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
|
// import { CheckUserCommunityGuard } from 'src/guards/user.community.guard';
|
||||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
// import { CommunityPermissionGuard } from 'src/guards/community.permission.guard';
|
import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto';
|
||||||
|
|
||||||
@ApiTags('Community Module')
|
@ApiTags('Community Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
version: EnableDisableStatusEnum.ENABLED,
|
version: '1',
|
||||||
path: SpaceType.COMMUNITY,
|
path: ControllerRoute.COMMUNITY.ROUTE,
|
||||||
})
|
})
|
||||||
export class CommunityController {
|
export class CommunityController {
|
||||||
constructor(private readonly communityService: CommunityService) {}
|
constructor(private readonly communityService: CommunityService) {}
|
||||||
@ -34,73 +31,70 @@ export class CommunityController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Post()
|
@Post()
|
||||||
async addCommunity(@Body() addCommunityDto: AddCommunityDto) {
|
@ApiOperation({
|
||||||
const community = await this.communityService.addCommunity(addCommunityDto);
|
summary: ControllerRoute.COMMUNITY.ACTIONS.CREATE_COMMUNITY_SUMMARY,
|
||||||
return {
|
description: ControllerRoute.COMMUNITY.ACTIONS.CREATE_COMMUNITY_DESCRIPTION,
|
||||||
statusCode: HttpStatus.CREATED,
|
})
|
||||||
success: true,
|
async createCommunity(
|
||||||
message: 'Community added successfully',
|
@Body() addCommunityDto: AddCommunityDto,
|
||||||
data: community,
|
): Promise<BaseResponseDto> {
|
||||||
};
|
return await this.communityService.createCommunity(addCommunityDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':communityUuid')
|
@ApiOperation({
|
||||||
async getCommunityByUuid(@Param('communityUuid') communityUuid: string) {
|
summary: ControllerRoute.COMMUNITY.ACTIONS.GET_COMMUNITY_BY_ID_SUMMARY,
|
||||||
const community =
|
description:
|
||||||
await this.communityService.getCommunityByUuid(communityUuid);
|
ControllerRoute.COMMUNITY.ACTIONS.GET_COMMUNITY_BY_ID_DESCRIPTION,
|
||||||
return community;
|
})
|
||||||
|
@Get('/:communityUuid')
|
||||||
|
async getCommunityByUuid(
|
||||||
|
@Param() params: GetCommunityParams,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.communityService.getCommunityById(params.communityUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.COMMUNITY.ACTIONS.LIST_COMMUNITY_SUMMARY,
|
||||||
|
description: ControllerRoute.COMMUNITY.ACTIONS.LIST_COMMUNITY_DESCRIPTION,
|
||||||
|
})
|
||||||
@Get()
|
@Get()
|
||||||
async getCommunities() {
|
async getCommunities(
|
||||||
const communities = await this.communityService.getCommunities();
|
@Query() query: PaginationRequestGetListDto,
|
||||||
return communities;
|
): Promise<BaseResponseDto> {
|
||||||
}
|
return this.communityService.getCommunities(query);
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Get('child/:communityUuid')
|
|
||||||
async getCommunityChildByUuid(
|
|
||||||
@Param('communityUuid') communityUuid: string,
|
|
||||||
@Query() query: GetCommunityChildDto,
|
|
||||||
) {
|
|
||||||
const community = await this.communityService.getCommunityChildByUuid(
|
|
||||||
communityUuid,
|
|
||||||
query,
|
|
||||||
);
|
|
||||||
return community;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('user/:userUuid')
|
@ApiOperation({
|
||||||
async getCommunitiesByUserId(@Param('userUuid') userUuid: string) {
|
summary: ControllerRoute.COMMUNITY.ACTIONS.UPDATE_COMMUNITY_SUMMARY,
|
||||||
return await this.communityService.getCommunitiesByUserId(userUuid);
|
description: ControllerRoute.COMMUNITY.ACTIONS.UPDATE_COMMUNITY_DESCRIPTION,
|
||||||
}
|
})
|
||||||
@ApiBearerAuth()
|
@Put('/:communityUuid')
|
||||||
@UseGuards(AdminRoleGuard)
|
async updateCommunity(
|
||||||
@Post('user')
|
@Param() param: GetCommunityParams,
|
||||||
async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) {
|
|
||||||
await this.communityService.addUserCommunity(addUserCommunityDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'user community added successfully',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Put(':communityUuid')
|
|
||||||
async renameCommunityByUuid(
|
|
||||||
@Param('communityUuid') communityUuid: string,
|
|
||||||
@Body() updateCommunityDto: UpdateCommunityNameDto,
|
@Body() updateCommunityDto: UpdateCommunityNameDto,
|
||||||
) {
|
) {
|
||||||
const community = await this.communityService.renameCommunityByUuid(
|
return this.communityService.updateCommunity(
|
||||||
communityUuid,
|
param.communityUuid,
|
||||||
updateCommunityDto,
|
updateCommunityDto,
|
||||||
);
|
);
|
||||||
return community;
|
}
|
||||||
|
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Delete('/:communityUuid')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.COMMUNITY.ACTIONS.DELETE_COMMUNITY_SUMMARY,
|
||||||
|
description: ControllerRoute.COMMUNITY.ACTIONS.DELETE_COMMUNITY_DESCRIPTION,
|
||||||
|
})
|
||||||
|
async deleteCommunity(
|
||||||
|
@Param() param: GetCommunityParams,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return this.communityService.deleteCommunity(param.communityUuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,30 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
export class AddCommunityDto {
|
export class AddCommunityDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'communityName',
|
description: 'The name of the community',
|
||||||
|
example: 'Community A',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public communityName: string;
|
public name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'A description of the community',
|
||||||
|
example: 'This is a community for developers.',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
public description?: string;
|
||||||
|
|
||||||
constructor(dto: Partial<AddCommunityDto>) {
|
constructor(dto: Partial<AddCommunityDto>) {
|
||||||
Object.assign(this, dto);
|
Object.assign(this, dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AddUserCommunityDto {
|
export class AddUserCommunityDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'communityUuid',
|
description: 'communityUuid',
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
|
IsUUID,
|
||||||
Min,
|
Min,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
@ -20,6 +21,18 @@ export class GetCommunityDto {
|
|||||||
public communityUuid: string;
|
public communityUuid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GetCommunityParams {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Community id of the specific community',
|
||||||
|
required: true,
|
||||||
|
name: 'communityUuid',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public communityUuid: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class GetCommunityChildDto {
|
export class GetCommunityChildDto {
|
||||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
||||||
@IsInt({ message: 'Page must be a number' })
|
@IsInt({ message: 'Page must be a number' })
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import { IsNotEmpty, IsString } from 'class-validator';
|
|||||||
|
|
||||||
export class UpdateCommunityNameDto {
|
export class UpdateCommunityNameDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'communityName',
|
description: 'community name',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public communityName: string;
|
public name: string;
|
||||||
|
|
||||||
constructor(dto: Partial<UpdateCommunityNameDto>) {
|
constructor(dto: Partial<UpdateCommunityNameDto>) {
|
||||||
Object.assign(this, dto);
|
Object.assign(this, dto);
|
||||||
|
|||||||
@ -1,278 +1,172 @@
|
|||||||
import { GetCommunityChildDto } from './../dtos/get.community.dto';
|
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||||
import { SpaceTypeRepository } from './../../../libs/common/src/modules/space/repositories/space.repository';
|
import { AddCommunityDto } from '../dtos';
|
||||||
import {
|
|
||||||
Injectable,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
BadRequestException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { AddCommunityDto, AddUserCommunityDto } from '../dtos';
|
|
||||||
import {
|
|
||||||
CommunityChildInterface,
|
|
||||||
GetCommunitiesInterface,
|
|
||||||
GetCommunityByUserUuidInterface,
|
|
||||||
GetCommunityByUuidInterface,
|
|
||||||
RenameCommunityByUuidInterface,
|
|
||||||
} from '../interface/community.interface';
|
|
||||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
|
||||||
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
import {
|
||||||
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
TypeORMCustomModel,
|
||||||
|
TypeORMCustomModelFindAllQuery,
|
||||||
|
} from '@app/common/models/typeOrmCustom.model';
|
||||||
|
import { PageResponse } from '@app/common/dto/pagination.response.dto';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { CommunityDto } from '@app/common/modules/community/dtos';
|
||||||
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommunityService {
|
export class CommunityService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly spaceRepository: SpaceRepository,
|
private readonly communityRepository: CommunityRepository,
|
||||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
private readonly tuyaService: TuyaService,
|
||||||
private readonly userSpaceRepository: UserSpaceRepository,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async addCommunity(addCommunityDto: AddCommunityDto) {
|
async createCommunity(dto: AddCommunityDto): Promise<BaseResponseDto> {
|
||||||
try {
|
const { name, description } = dto;
|
||||||
const spaceType = await this.spaceTypeRepository.findOne({
|
|
||||||
where: {
|
const existingCommunity = await this.communityRepository.findOneBy({
|
||||||
type: SpaceType.COMMUNITY,
|
name,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
if (existingCommunity) {
|
||||||
const community = await this.spaceRepository.save({
|
|
||||||
spaceName: addCommunityDto.communityName,
|
|
||||||
spaceType: { uuid: spaceType.uuid },
|
|
||||||
});
|
|
||||||
return community;
|
|
||||||
} catch (err) {
|
|
||||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCommunityByUuid(
|
|
||||||
communityUuid: string,
|
|
||||||
): Promise<GetCommunityByUuidInterface> {
|
|
||||||
try {
|
|
||||||
const community = await this.spaceRepository.findOne({
|
|
||||||
where: {
|
|
||||||
uuid: communityUuid,
|
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.COMMUNITY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!community ||
|
|
||||||
!community.spaceType ||
|
|
||||||
community.spaceType.type !== SpaceType.COMMUNITY
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid community UUID');
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
uuid: community.uuid,
|
|
||||||
createdAt: community.createdAt,
|
|
||||||
updatedAt: community.updatedAt,
|
|
||||||
name: community.spaceName,
|
|
||||||
type: community.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getCommunities(): Promise<GetCommunitiesInterface> {
|
|
||||||
try {
|
|
||||||
const community = await this.spaceRepository.find({
|
|
||||||
where: { spaceType: { type: SpaceType.COMMUNITY } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
return community.map((community) => ({
|
|
||||||
uuid: community.uuid,
|
|
||||||
createdAt: community.createdAt,
|
|
||||||
updatedAt: community.updatedAt,
|
|
||||||
name: community.spaceName,
|
|
||||||
type: community.spaceType.type,
|
|
||||||
}));
|
|
||||||
} catch (err) {
|
|
||||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getCommunityChildByUuid(
|
|
||||||
communityUuid: string,
|
|
||||||
getCommunityChildDto: GetCommunityChildDto,
|
|
||||||
): Promise<CommunityChildInterface> {
|
|
||||||
try {
|
|
||||||
const { includeSubSpaces, page, pageSize } = getCommunityChildDto;
|
|
||||||
|
|
||||||
const space = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: communityUuid },
|
|
||||||
relations: ['children', 'spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!space ||
|
|
||||||
!space.spaceType ||
|
|
||||||
space.spaceType.type !== SpaceType.COMMUNITY
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid community UUID');
|
|
||||||
}
|
|
||||||
const totalCount = await this.spaceRepository.count({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
});
|
|
||||||
const children = await this.buildHierarchy(
|
|
||||||
space,
|
|
||||||
includeSubSpaces,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
uuid: space.uuid,
|
|
||||||
name: space.spaceName,
|
|
||||||
type: space.spaceType.type,
|
|
||||||
totalCount,
|
|
||||||
children,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async buildHierarchy(
|
|
||||||
space: SpaceEntity,
|
|
||||||
includeSubSpaces: boolean,
|
|
||||||
page: number,
|
|
||||||
pageSize: number,
|
|
||||||
): Promise<CommunityChildInterface[]> {
|
|
||||||
const children = await this.spaceRepository.find({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
skip: (page - 1) * pageSize,
|
|
||||||
take: pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
|
||||||
return children
|
|
||||||
.filter((child) => child.spaceType.type !== SpaceType.COMMUNITY) // Filter remaining community type
|
|
||||||
.map((child) => ({
|
|
||||||
uuid: child.uuid,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const childHierarchies = await Promise.all(
|
|
||||||
children
|
|
||||||
.filter((child) => child.spaceType.type !== SpaceType.COMMUNITY) // Filter remaining community type
|
|
||||||
.map(async (child) => ({
|
|
||||||
uuid: child.uuid,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
return childHierarchies;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCommunitiesByUserId(
|
|
||||||
userUuid: string,
|
|
||||||
): Promise<GetCommunityByUserUuidInterface[]> {
|
|
||||||
try {
|
|
||||||
const communities = await this.userSpaceRepository.find({
|
|
||||||
relations: ['space', 'space.spaceType'],
|
|
||||||
where: {
|
|
||||||
user: { uuid: userUuid },
|
|
||||||
space: { spaceType: { type: SpaceType.COMMUNITY } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (communities.length === 0) {
|
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'this user has no communities',
|
`A community with the name '${name}' already exists.`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new community entity
|
||||||
|
const community = this.communityRepository.create({
|
||||||
|
name: name,
|
||||||
|
description: description,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save the community to the database
|
||||||
|
try {
|
||||||
|
const externalId = await this.createTuyaSpace(name);
|
||||||
|
community.externalId = externalId;
|
||||||
|
await this.communityRepository.save(community);
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
statusCode: HttpStatus.CREATED,
|
||||||
|
success: true,
|
||||||
|
data: community,
|
||||||
|
message: 'Community created successfully',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCommunityById(communityUuid: string): Promise<BaseResponseDto> {
|
||||||
|
const community = await this.communityRepository.findOneBy({
|
||||||
|
uuid: communityUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the community is not found, throw a 404 NotFoundException
|
||||||
|
if (!community) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Community with ID ${communityUuid} not found.`,
|
||||||
HttpStatus.NOT_FOUND,
|
HttpStatus.NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const spaces = communities.map((community) => ({
|
|
||||||
uuid: community.space.uuid,
|
|
||||||
name: community.space.spaceName,
|
|
||||||
type: community.space.spaceType.type,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return spaces;
|
// Return a success response
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
data: community,
|
||||||
|
message: 'Community fetched successfully',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCommunities(
|
||||||
|
pageable: Partial<TypeORMCustomModelFindAllQuery>,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
pageable.modelName = 'community';
|
||||||
|
|
||||||
|
const customModel = TypeORMCustomModel(this.communityRepository);
|
||||||
|
|
||||||
|
const { baseResponseDto, paginationResponseDto } =
|
||||||
|
await customModel.findAll(pageable);
|
||||||
|
|
||||||
|
return new PageResponse<CommunityDto>(
|
||||||
|
baseResponseDto,
|
||||||
|
paginationResponseDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCommunity(
|
||||||
|
communityUuid: string,
|
||||||
|
updateCommunityDto: UpdateCommunityNameDto,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
const community = await this.communityRepository.findOne({
|
||||||
|
where: { uuid: communityUuid },
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the community doesn't exist, throw a 404 error
|
||||||
|
if (!community) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Community with ID ${communityUuid} not found`,
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { name } = updateCommunityDto;
|
||||||
|
|
||||||
|
community.name = name;
|
||||||
|
|
||||||
|
const updatedCommunity = await this.communityRepository.save(community);
|
||||||
|
|
||||||
|
return new SuccessResponseDto<CommunityDto>({
|
||||||
|
message: 'Success update Community',
|
||||||
|
data: updatedCommunity,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// Catch and handle any errors
|
||||||
|
if (err instanceof HttpException) {
|
||||||
|
throw err; // If it's an HttpException, rethrow it
|
||||||
|
} else {
|
||||||
|
// Throw a generic 404 error if anything else goes wrong
|
||||||
|
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCommunity(communityUuid: string): Promise<BaseResponseDto> {
|
||||||
|
const community = await this.communityRepository.findOne({
|
||||||
|
where: { uuid: communityUuid },
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the community is not found, throw an error
|
||||||
|
if (!community) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Community with ID ${communityUuid} not found`,
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.communityRepository.remove(community);
|
||||||
|
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
message: `Community with ID ${communityUuid} has been successfully deleted`,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof HttpException) {
|
if (err instanceof HttpException) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException('user not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addUserCommunity(addUserCommunityDto: AddUserCommunityDto) {
|
|
||||||
try {
|
|
||||||
await this.userSpaceRepository.save({
|
|
||||||
user: { uuid: addUserCommunityDto.userUuid },
|
|
||||||
space: { uuid: addUserCommunityDto.communityUuid },
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) {
|
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'User already belongs to this community',
|
'An error occurred while deleting the community',
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new HttpException(
|
|
||||||
err.message || 'Internal Server Error',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async renameCommunityByUuid(
|
}
|
||||||
communityUuid: string,
|
|
||||||
updateCommunityDto: UpdateCommunityNameDto,
|
private async createTuyaSpace(name: string): Promise<string> {
|
||||||
): Promise<RenameCommunityByUuidInterface> {
|
|
||||||
try {
|
try {
|
||||||
const community = await this.spaceRepository.findOneOrFail({
|
const response = await this.tuyaService.createSpace({ name });
|
||||||
where: { uuid: communityUuid },
|
return response;
|
||||||
relations: ['spaceType'],
|
} catch (error) {
|
||||||
});
|
throw new HttpException(
|
||||||
|
'Failed to create a Tuya space',
|
||||||
if (
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
!community ||
|
|
||||||
!community.spaceType ||
|
|
||||||
community.spaceType.type !== SpaceType.COMMUNITY
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid community UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.spaceRepository.update(
|
|
||||||
{ uuid: communityUuid },
|
|
||||||
{ spaceName: updateCommunityDto.communityName },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch the updated community
|
|
||||||
const updatedCommunity = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: communityUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: updatedCommunity.uuid,
|
|
||||||
name: updatedCommunity.spaceName,
|
|
||||||
type: updatedCommunity.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,8 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
import { AddDeviceDto, UpdateDeviceInRoomDto } from '../dtos/add.device.dto';
|
import { AddDeviceDto, UpdateDeviceInSpaceDto } from '../dtos/add.device.dto';
|
||||||
import {
|
import { GetDeviceLogsDto } from '../dtos/get.device.dto';
|
||||||
GetDeviceByRoomUuidDto,
|
|
||||||
GetDeviceLogsDto,
|
|
||||||
} from '../dtos/get.device.dto';
|
|
||||||
import {
|
import {
|
||||||
ControlDeviceDto,
|
ControlDeviceDto,
|
||||||
BatchControlDevicesDto,
|
BatchControlDevicesDto,
|
||||||
@ -24,13 +21,10 @@ import {
|
|||||||
BatchFactoryResetDevicesDto,
|
BatchFactoryResetDevicesDto,
|
||||||
} from '../dtos/control.device.dto';
|
} from '../dtos/control.device.dto';
|
||||||
import { CheckRoomGuard } from 'src/guards/room.guard';
|
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';
|
|
||||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
import { CheckDeviceGuard } from 'src/guards/device.guard';
|
import { CheckDeviceGuard } from 'src/guards/device.guard';
|
||||||
import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
|
import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
|
||||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
|
|
||||||
@ApiTags('Device Module')
|
@ApiTags('Device Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
@ -58,33 +52,21 @@ export class DeviceController {
|
|||||||
async getDevicesByUser(@Param('userUuid') userUuid: string) {
|
async getDevicesByUser(@Param('userUuid') userUuid: string) {
|
||||||
return await this.deviceService.getDevicesByUser(userUuid);
|
return await this.deviceService.getDevicesByUser(userUuid);
|
||||||
}
|
}
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, CheckRoomGuard)
|
|
||||||
@Get(SpaceType.ROOM)
|
|
||||||
async getDevicesByRoomId(
|
|
||||||
@Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
|
|
||||||
@Req() req: any,
|
|
||||||
) {
|
|
||||||
const userUuid = req.user.uuid;
|
|
||||||
return await this.deviceService.getDevicesByRoomId(
|
|
||||||
getDeviceByRoomUuidDto,
|
|
||||||
userUuid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('unit/:unitUuid')
|
@Get('space/:spaceUuid')
|
||||||
async getDevicesByUnitId(@Param('unitUuid') unitUuid: string) {
|
async getDevicesByUnitId(@Param('spaceUuid') spaceUuid: string) {
|
||||||
return await this.deviceService.getDevicesByUnitId(unitUuid);
|
return await this.deviceService.getDevicesBySpaceUuid(spaceUuid);
|
||||||
}
|
}
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, CheckRoomGuard)
|
@UseGuards(JwtAuthGuard, CheckRoomGuard)
|
||||||
@Put('room')
|
@Put('space')
|
||||||
async updateDeviceInRoom(
|
async updateDeviceInRoom(
|
||||||
@Body() updateDeviceInRoomDto: UpdateDeviceInRoomDto,
|
@Body() updateDeviceInSpaceDto: UpdateDeviceInSpaceDto,
|
||||||
) {
|
) {
|
||||||
const device = await this.deviceService.updateDeviceInRoom(
|
const device = await this.deviceService.updateDeviceInSpace(
|
||||||
updateDeviceInRoomDto,
|
updateDeviceInSpaceDto,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -96,7 +78,7 @@ export class DeviceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':deviceUuid')
|
@Get(':deviceUuid')
|
||||||
async getDeviceDetailsByDeviceId(
|
async getDeviceDetailsByDeviceId(
|
||||||
@Param('deviceUuid') deviceUuid: string,
|
@Param('deviceUuid') deviceUuid: string,
|
||||||
@ -109,7 +91,7 @@ export class DeviceController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':deviceUuid/functions')
|
@Get(':deviceUuid/functions')
|
||||||
async getDeviceInstructionByDeviceId(
|
async getDeviceInstructionByDeviceId(
|
||||||
@Param('deviceUuid') deviceUuid: string,
|
@Param('deviceUuid') deviceUuid: string,
|
||||||
@ -117,14 +99,14 @@ export class DeviceController {
|
|||||||
return await this.deviceService.getDeviceInstructionByDeviceId(deviceUuid);
|
return await this.deviceService.getDeviceInstructionByDeviceId(deviceUuid);
|
||||||
}
|
}
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':deviceUuid/functions/status')
|
@Get(':deviceUuid/functions/status')
|
||||||
async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) {
|
async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) {
|
||||||
return await this.deviceService.getDevicesInstructionStatus(deviceUuid);
|
return await this.deviceService.getDevicesInstructionStatus(deviceUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Post(':deviceUuid/control')
|
@Post(':deviceUuid/control')
|
||||||
async controlDevice(
|
async controlDevice(
|
||||||
@Body() controlDeviceDto: ControlDeviceDto,
|
@Body() controlDeviceDto: ControlDeviceDto,
|
||||||
@ -156,6 +138,7 @@ export class DeviceController {
|
|||||||
async getAllDevices() {
|
async getAllDevices() {
|
||||||
return await this.deviceService.getAllDevices();
|
return await this.deviceService.getAllDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('report-logs/:deviceUuid')
|
@Get('report-logs/:deviceUuid')
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export class AddDeviceDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public userUuid: string;
|
public userUuid: string;
|
||||||
}
|
}
|
||||||
export class UpdateDeviceInRoomDto {
|
export class UpdateDeviceInSpaceDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'deviceUuid',
|
description: 'deviceUuid',
|
||||||
required: true,
|
required: true,
|
||||||
@ -28,10 +28,10 @@ export class UpdateDeviceInRoomDto {
|
|||||||
public deviceUuid: string;
|
public deviceUuid: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'roomUuid',
|
description: 'spaceUuid',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public roomUuid: string;
|
public spaceUuid: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class GetDeviceByRoomUuidDto {
|
export class GetDeviceBySpaceUuidDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'roomUuid',
|
description: 'spaceUuid',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
public roomUuid: string;
|
public spaceUuid: string;
|
||||||
}
|
}
|
||||||
export class GetDeviceLogsDto {
|
export class GetDeviceLogsDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export interface GetDeviceDetailsInterface {
|
export interface GetDeviceDetailsInterface {
|
||||||
activeTime: number;
|
activeTime: number;
|
||||||
assetId: string;
|
assetId?: string;
|
||||||
category: string;
|
category: string;
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
createTime: number;
|
createTime: number;
|
||||||
@ -13,6 +13,7 @@ export interface GetDeviceDetailsInterface {
|
|||||||
lon: string;
|
lon: string;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
battery?: number;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
productId?: string;
|
productId?: string;
|
||||||
@ -23,6 +24,18 @@ export interface GetDeviceDetailsInterface {
|
|||||||
uuid: string;
|
uuid: string;
|
||||||
productType: string;
|
productType: string;
|
||||||
productUuid: string;
|
productUuid: string;
|
||||||
|
spaces?: SpaceInterface[];
|
||||||
|
community?: CommunityInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpaceInterface {
|
||||||
|
uuid: string;
|
||||||
|
spaceName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommunityInterface {
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface addDeviceInRoomInterface {
|
export interface addDeviceInRoomInterface {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { AddDeviceDto, UpdateDeviceInRoomDto } from '../dtos/add.device.dto';
|
import { AddDeviceDto, UpdateDeviceInSpaceDto } from '../dtos/add.device.dto';
|
||||||
import {
|
import {
|
||||||
DeviceInstructionResponse,
|
DeviceInstructionResponse,
|
||||||
GetDeviceDetailsFunctionsInterface,
|
GetDeviceDetailsFunctionsInterface,
|
||||||
@ -20,7 +20,7 @@ import {
|
|||||||
updateDeviceFirmwareInterface,
|
updateDeviceFirmwareInterface,
|
||||||
} from '../interfaces/get.device.interface';
|
} from '../interfaces/get.device.interface';
|
||||||
import {
|
import {
|
||||||
GetDeviceByRoomUuidDto,
|
GetDeviceBySpaceUuidDto,
|
||||||
GetDeviceLogsDto,
|
GetDeviceLogsDto,
|
||||||
} from '../dtos/get.device.dto';
|
} from '../dtos/get.device.dto';
|
||||||
import {
|
import {
|
||||||
@ -39,6 +39,7 @@ import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status
|
|||||||
import { DeviceStatuses } from '@app/common/constants/device-status.enum';
|
import { DeviceStatuses } from '@app/common/constants/device-status.enum';
|
||||||
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
||||||
import { BatteryStatus } from '@app/common/constants/battery-status.enum';
|
import { BatteryStatus } from '@app/common/constants/battery-status.enum';
|
||||||
|
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeviceService {
|
export class DeviceService {
|
||||||
@ -70,6 +71,7 @@ export class DeviceService {
|
|||||||
...(withProductDevice && { relations: ['productDevice'] }),
|
...(withProductDevice && { relations: ['productDevice'] }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) {
|
async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) {
|
||||||
return await this.deviceRepository.findOne({
|
return await this.deviceRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
@ -78,6 +80,7 @@ export class DeviceService {
|
|||||||
relations: ['productDevice'],
|
relations: ['productDevice'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async addDeviceUser(addDeviceDto: AddDeviceDto) {
|
async addDeviceUser(addDeviceDto: AddDeviceDto) {
|
||||||
try {
|
try {
|
||||||
const device = await this.getDeviceDetailsByDeviceIdTuya(
|
const device = await this.getDeviceDetailsByDeviceIdTuya(
|
||||||
@ -117,12 +120,13 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
error.message || 'Failed to add device in room',
|
error.message || 'Failed to add device in space',
|
||||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDevicesByUser(
|
async getDevicesByUser(
|
||||||
userUuid: string,
|
userUuid: string,
|
||||||
): Promise<GetDeviceDetailsInterface[]> {
|
): Promise<GetDeviceDetailsInterface[]> {
|
||||||
@ -169,14 +173,15 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getDevicesByRoomId(
|
|
||||||
getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
|
async getDevicesBySpaceId(
|
||||||
|
getDeviceBySpaceUuidDto: GetDeviceBySpaceUuidDto,
|
||||||
userUuid: string,
|
userUuid: string,
|
||||||
): Promise<GetDeviceDetailsInterface[]> {
|
): Promise<GetDeviceDetailsInterface[]> {
|
||||||
try {
|
try {
|
||||||
const devices = await this.deviceRepository.find({
|
const devices = await this.deviceRepository.find({
|
||||||
where: {
|
where: {
|
||||||
spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid },
|
spaceDevice: { uuid: getDeviceBySpaceUuidDto.spaceUuid },
|
||||||
isActive: true,
|
isActive: true,
|
||||||
permission: {
|
permission: {
|
||||||
userUuid,
|
userUuid,
|
||||||
@ -211,22 +216,22 @@ export class DeviceService {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle the error here
|
// Handle the error here
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'Error fetching devices by room',
|
'Error fetching devices by space',
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async updateDeviceInRoom(updateDeviceInRoomDto: UpdateDeviceInRoomDto) {
|
async updateDeviceInSpace(updateDeviceInSpaceDto: UpdateDeviceInSpaceDto) {
|
||||||
try {
|
try {
|
||||||
await this.deviceRepository.update(
|
await this.deviceRepository.update(
|
||||||
{ uuid: updateDeviceInRoomDto.deviceUuid },
|
{ uuid: updateDeviceInSpaceDto.deviceUuid },
|
||||||
{
|
{
|
||||||
spaceDevice: { uuid: updateDeviceInRoomDto.roomUuid },
|
spaceDevice: { uuid: updateDeviceInSpaceDto.spaceUuid },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const device = await this.deviceRepository.findOne({
|
const device = await this.deviceRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
uuid: updateDeviceInRoomDto.deviceUuid,
|
uuid: updateDeviceInSpaceDto.deviceUuid,
|
||||||
},
|
},
|
||||||
relations: ['spaceDevice', 'spaceDevice.parent'],
|
relations: ['spaceDevice', 'spaceDevice.parent'],
|
||||||
});
|
});
|
||||||
@ -239,15 +244,16 @@ export class DeviceService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
uuid: device.uuid,
|
uuid: device.uuid,
|
||||||
roomUuid: device.spaceDevice.uuid,
|
spaceUuid: device.spaceDevice.uuid,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'Failed to add device in room',
|
'Failed to add device in space',
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async transferDeviceInSpacesTuya(
|
async transferDeviceInSpacesTuya(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
spaceId: string,
|
spaceId: string,
|
||||||
@ -295,6 +301,7 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async factoryResetDeviceTuya(
|
async factoryResetDeviceTuya(
|
||||||
deviceUuid: string,
|
deviceUuid: string,
|
||||||
): Promise<controlDeviceInterface> {
|
): Promise<controlDeviceInterface> {
|
||||||
@ -337,6 +344,7 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async batchControlDevices(batchControlDevicesDto: BatchControlDevicesDto) {
|
async batchControlDevices(batchControlDevicesDto: BatchControlDevicesDto) {
|
||||||
const { devicesUuid } = batchControlDevicesDto;
|
const { devicesUuid } = batchControlDevicesDto;
|
||||||
|
|
||||||
@ -562,13 +570,12 @@ export class DeviceService {
|
|||||||
async getDeviceInstructionByDeviceId(
|
async getDeviceInstructionByDeviceId(
|
||||||
deviceUuid: string,
|
deviceUuid: string,
|
||||||
): Promise<DeviceInstructionResponse> {
|
): Promise<DeviceInstructionResponse> {
|
||||||
try {
|
|
||||||
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
|
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
|
||||||
|
|
||||||
if (!deviceDetails) {
|
if (!deviceDetails) {
|
||||||
throw new NotFoundException('Device Not Found');
|
throw new NotFoundException('Device Not Found');
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
const response = await this.getDeviceInstructionByDeviceIdTuya(
|
const response = await this.getDeviceInstructionByDeviceIdTuya(
|
||||||
deviceDetails.deviceTuyaUuid,
|
deviceDetails.deviceTuyaUuid,
|
||||||
);
|
);
|
||||||
@ -623,6 +630,7 @@ export class DeviceService {
|
|||||||
status: deviceStatus.result[0].status,
|
status: deviceStatus.result[0].status,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'Error fetching device functions status',
|
'Error fetching device functions status',
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
@ -778,22 +786,22 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getDevicesByUnitId(unitUuid: string) {
|
async getDevicesBySpaceUuid(SpaceUuid: string) {
|
||||||
try {
|
try {
|
||||||
const spaces = await this.spaceRepository.find({
|
const spaces = await this.spaceRepository.find({
|
||||||
where: {
|
where: {
|
||||||
parent: {
|
parent: {
|
||||||
uuid: unitUuid,
|
uuid: SpaceUuid,
|
||||||
},
|
},
|
||||||
devicesSpaceEntity: {
|
devices: {
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relations: ['devicesSpaceEntity', 'devicesSpaceEntity.productDevice'],
|
relations: ['devices', 'devices.productDevice'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const devices = spaces.flatMap((space) => {
|
const devices = spaces.flatMap((space) => {
|
||||||
return space.devicesSpaceEntity.map((device) => device);
|
return space.devices.map((device) => device);
|
||||||
});
|
});
|
||||||
|
|
||||||
const devicesData = await Promise.all(
|
const devicesData = await Promise.all(
|
||||||
@ -814,7 +822,7 @@ export class DeviceService {
|
|||||||
return devicesData;
|
return devicesData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'This unit does not have any devices',
|
'This space does not have any devices',
|
||||||
HttpStatus.NOT_FOUND,
|
HttpStatus.NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -825,11 +833,13 @@ export class DeviceService {
|
|||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
relations: [
|
relations: [
|
||||||
'spaceDevice.parent',
|
'spaceDevice.parent',
|
||||||
|
'spaceDevice.community',
|
||||||
'productDevice',
|
'productDevice',
|
||||||
'permission',
|
'permission',
|
||||||
'permission.permissionType',
|
'permission.permissionType',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const devicesData = await Promise.allSettled(
|
const devicesData = await Promise.allSettled(
|
||||||
devices.map(async (device) => {
|
devices.map(async (device) => {
|
||||||
let battery = null;
|
let battery = null;
|
||||||
@ -874,20 +884,24 @@ export class DeviceService {
|
|||||||
battery = batteryStatus.value;
|
battery = batteryStatus.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const spaceDevice = device?.spaceDevice;
|
|
||||||
const parentDevice = spaceDevice?.parent;
|
const spaceHierarchy = await this.getFullSpaceHierarchy(
|
||||||
|
device?.spaceDevice,
|
||||||
|
);
|
||||||
|
const orderedHierarchy = spaceHierarchy.reverse();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
room: {
|
spaces: orderedHierarchy.map((space) => ({
|
||||||
uuid: spaceDevice?.uuid,
|
uuid: space.uuid,
|
||||||
name: spaceDevice?.spaceName,
|
spaceName: space.spaceName,
|
||||||
},
|
})),
|
||||||
unit: {
|
|
||||||
uuid: parentDevice?.uuid,
|
|
||||||
name: parentDevice?.spaceName,
|
|
||||||
},
|
|
||||||
productUuid: device.productDevice.uuid,
|
productUuid: device.productDevice.uuid,
|
||||||
productType: device.productDevice.prodType,
|
productType: device.productDevice.prodType,
|
||||||
permissionType: device.permission[0].permissionType.type,
|
community: {
|
||||||
|
uuid: device.spaceDevice.community.uuid,
|
||||||
|
name: device.spaceDevice.community.name,
|
||||||
|
},
|
||||||
|
// permissionType: device.permission[0].permissionType.type,
|
||||||
...(await this.getDeviceDetailsByDeviceIdTuya(
|
...(await this.getDeviceDetailsByDeviceIdTuya(
|
||||||
device.deviceTuyaUuid,
|
device.deviceTuyaUuid,
|
||||||
)),
|
)),
|
||||||
@ -913,6 +927,7 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDeviceLogs(deviceUuid: string, query: GetDeviceLogsDto) {
|
async getDeviceLogs(deviceUuid: string, query: GetDeviceLogsDto) {
|
||||||
try {
|
try {
|
||||||
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
|
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
|
||||||
@ -966,6 +981,40 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFullSpaceHierarchy(
|
||||||
|
space: SpaceEntity,
|
||||||
|
): Promise<{ uuid: string; spaceName: string }[]> {
|
||||||
|
try {
|
||||||
|
// Fetch only the relevant spaces, starting with the target space
|
||||||
|
const targetSpace = await this.spaceRepository.findOne({
|
||||||
|
where: { uuid: space.uuid },
|
||||||
|
relations: ['parent', 'children'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch only the ancestors of the target space
|
||||||
|
const ancestors = await this.fetchAncestors(targetSpace);
|
||||||
|
|
||||||
|
// Optionally, fetch descendants if required
|
||||||
|
const descendants = await this.fetchDescendants(targetSpace);
|
||||||
|
|
||||||
|
const fullHierarchy = [...ancestors, targetSpace, ...descendants].map(
|
||||||
|
(space) => ({
|
||||||
|
uuid: space.uuid,
|
||||||
|
spaceName: space.spaceName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return fullHierarchy;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching space hierarchy:', error.message);
|
||||||
|
throw new HttpException(
|
||||||
|
'Error fetching space hierarchy',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getPowerClampInstructionStatus(powerClampUuid: string) {
|
async getPowerClampInstructionStatus(powerClampUuid: string) {
|
||||||
try {
|
try {
|
||||||
const deviceDetails = await this.getDeviceByDeviceUuid(powerClampUuid);
|
const deviceDetails = await this.getDeviceByDeviceUuid(powerClampUuid);
|
||||||
@ -1043,4 +1092,48 @@ export class DeviceService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async fetchAncestors(space: SpaceEntity): Promise<SpaceEntity[]> {
|
||||||
|
const ancestors: SpaceEntity[] = [];
|
||||||
|
|
||||||
|
let currentSpace = space;
|
||||||
|
while (currentSpace && currentSpace.parent) {
|
||||||
|
// Fetch the parent space
|
||||||
|
const parent = await this.spaceRepository.findOne({
|
||||||
|
where: { uuid: currentSpace.parent.uuid },
|
||||||
|
relations: ['parent'], // To continue fetching upwards
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
ancestors.push(parent);
|
||||||
|
currentSpace = parent;
|
||||||
|
} else {
|
||||||
|
currentSpace = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the ancestors in reverse order to have the root at the start
|
||||||
|
return ancestors.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchDescendants(space: SpaceEntity): Promise<SpaceEntity[]> {
|
||||||
|
const descendants: SpaceEntity[] = [];
|
||||||
|
|
||||||
|
// Fetch the immediate children of the current space
|
||||||
|
const children = await this.spaceRepository.find({
|
||||||
|
where: { parent: { uuid: space.uuid } },
|
||||||
|
relations: ['children'], // To continue fetching downwards
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
// Add the child to the descendants list
|
||||||
|
descendants.push(child);
|
||||||
|
|
||||||
|
// Recursively fetch the child's descendants
|
||||||
|
const childDescendants = await this.fetchDescendants(child);
|
||||||
|
descendants.push(...childDescendants);
|
||||||
|
}
|
||||||
|
|
||||||
|
return descendants;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
import { FloorService } from '../services/floor.service';
|
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
HttpStatus,
|
|
||||||
Param,
|
|
||||||
Post,
|
|
||||||
Put,
|
|
||||||
Query,
|
|
||||||
UseGuards,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
|
||||||
import { AddFloorDto, AddUserFloorDto } from '../dtos/add.floor.dto';
|
|
||||||
import { GetFloorChildDto } from '../dtos/get.floor.dto';
|
|
||||||
import { UpdateFloorNameDto } from '../dtos/update.floor.dto';
|
|
||||||
import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard';
|
|
||||||
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: EnableDisableStatusEnum.ENABLED,
|
|
||||||
path: SpaceType.FLOOR,
|
|
||||||
})
|
|
||||||
export class FloorController {
|
|
||||||
constructor(private readonly floorService: FloorService) {}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, CheckBuildingTypeGuard)
|
|
||||||
@Post()
|
|
||||||
async addFloor(@Body() addFloorDto: AddFloorDto) {
|
|
||||||
const floor = await this.floorService.addFloor(addFloorDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'Floor added successfully',
|
|
||||||
data: floor,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, FloorPermissionGuard)
|
|
||||||
@Get(':floorUuid')
|
|
||||||
async getFloorByUuid(@Param('floorUuid') floorUuid: string) {
|
|
||||||
const floor = await this.floorService.getFloorByUuid(floorUuid);
|
|
||||||
return floor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, FloorPermissionGuard)
|
|
||||||
@Get('child/:floorUuid')
|
|
||||||
async getFloorChildByUuid(
|
|
||||||
@Param('floorUuid') floorUuid: string,
|
|
||||||
@Query() query: GetFloorChildDto,
|
|
||||||
) {
|
|
||||||
const floor = await this.floorService.getFloorChildByUuid(floorUuid, query);
|
|
||||||
return floor;
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, FloorPermissionGuard)
|
|
||||||
@Get('parent/:floorUuid')
|
|
||||||
async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) {
|
|
||||||
const floor = await this.floorService.getFloorParentByUuid(floorUuid);
|
|
||||||
return floor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(AdminRoleGuard, CheckUserFloorGuard)
|
|
||||||
@Post('user')
|
|
||||||
async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) {
|
|
||||||
await this.floorService.addUserFloor(addUserFloorDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'user floor added successfully',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Get('user/:userUuid')
|
|
||||||
async getFloorsByUserId(@Param('userUuid') userUuid: string) {
|
|
||||||
return await this.floorService.getFloorsByUserId(userUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, FloorPermissionGuard)
|
|
||||||
@Put(':floorUuid')
|
|
||||||
async renameFloorByUuid(
|
|
||||||
@Param('floorUuid') floorUuid: string,
|
|
||||||
@Body() updateFloorNameDto: UpdateFloorNameDto,
|
|
||||||
) {
|
|
||||||
const floor = await this.floorService.renameFloorByUuid(
|
|
||||||
floorUuid,
|
|
||||||
updateFloorNameDto,
|
|
||||||
);
|
|
||||||
return floor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './floor.controller';
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class AddFloorDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'floorName',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public floorName: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'buildingUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public buildingUuid: string;
|
|
||||||
constructor(dto: Partial<AddFloorDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class AddUserFloorDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'floorUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public floorUuid: string;
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'userUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public userUuid: string;
|
|
||||||
constructor(dto: Partial<AddUserFloorDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import { BooleanValues } from '@app/common/constants/boolean-values.enum';
|
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { Transform } from 'class-transformer';
|
|
||||||
import {
|
|
||||||
IsBoolean,
|
|
||||||
IsInt,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
|
||||||
IsString,
|
|
||||||
Min,
|
|
||||||
} from 'class-validator';
|
|
||||||
|
|
||||||
export class GetFloorDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'floorUuid',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public floorUuid: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GetFloorChildDto {
|
|
||||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
|
||||||
@IsInt({ message: 'Page must be a number' })
|
|
||||||
@Min(1, { message: 'Page must not be less than 1' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
public page: number;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: 10,
|
|
||||||
description: 'Number of items per page',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsInt({ message: 'Page size must be a number' })
|
|
||||||
@Min(1, { message: 'Page size must not be less than 1' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
public pageSize: number;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: true,
|
|
||||||
description: 'Flag to determine whether to fetch full hierarchy',
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsBoolean()
|
|
||||||
@Transform((value) => {
|
|
||||||
return value.obj.includeSubSpaces === BooleanValues.TRUE;
|
|
||||||
})
|
|
||||||
public includeSubSpaces: boolean = false;
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './add.floor.dto';
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class UpdateFloorNameDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'floorName',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
public floorName: string;
|
|
||||||
|
|
||||||
constructor(dto: Partial<UpdateFloorNameDto>) {
|
|
||||||
Object.assign(this, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { FloorService } from './services/floor.service';
|
|
||||||
import { FloorController } from './controllers/floor.controller';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
|
||||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { SpaceTypeRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
|
||||||
controllers: [FloorController],
|
|
||||||
providers: [
|
|
||||||
FloorService,
|
|
||||||
SpaceRepository,
|
|
||||||
SpaceTypeRepository,
|
|
||||||
UserSpaceRepository,
|
|
||||||
UserRepository,
|
|
||||||
],
|
|
||||||
exports: [FloorService],
|
|
||||||
})
|
|
||||||
export class FloorModule {}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
export interface GetFloorByUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FloorChildInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
totalCount?: number;
|
|
||||||
children?: FloorChildInterface[];
|
|
||||||
}
|
|
||||||
export interface FloorParentInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
parent?: FloorParentInterface;
|
|
||||||
}
|
|
||||||
export interface RenameFloorByUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetFloorByUserUuidInterface {
|
|
||||||
uuid: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
@ -1,310 +0,0 @@
|
|||||||
import { GetFloorChildDto } from '../dtos/get.floor.dto';
|
|
||||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository';
|
|
||||||
import {
|
|
||||||
Injectable,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
BadRequestException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import { AddFloorDto, AddUserFloorDto } from '../dtos';
|
|
||||||
import {
|
|
||||||
FloorChildInterface,
|
|
||||||
FloorParentInterface,
|
|
||||||
GetFloorByUserUuidInterface,
|
|
||||||
GetFloorByUuidInterface,
|
|
||||||
RenameFloorByUuidInterface,
|
|
||||||
} from '../interface/floor.interface';
|
|
||||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
|
||||||
import { UpdateFloorNameDto } from '../dtos/update.floor.dto';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class FloorService {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
|
||||||
private readonly userSpaceRepository: UserSpaceRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async addFloor(addFloorDto: AddFloorDto) {
|
|
||||||
try {
|
|
||||||
const spaceType = await this.spaceTypeRepository.findOne({
|
|
||||||
where: {
|
|
||||||
type: SpaceType.FLOOR,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const floor = await this.spaceRepository.save({
|
|
||||||
spaceName: addFloorDto.floorName,
|
|
||||||
parent: { uuid: addFloorDto.buildingUuid },
|
|
||||||
spaceType: { uuid: spaceType.uuid },
|
|
||||||
});
|
|
||||||
return floor;
|
|
||||||
} catch (err) {
|
|
||||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFloorByUuid(floorUuid: string): Promise<GetFloorByUuidInterface> {
|
|
||||||
try {
|
|
||||||
const floor = await this.spaceRepository.findOne({
|
|
||||||
where: {
|
|
||||||
uuid: floorUuid,
|
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.FLOOR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!floor ||
|
|
||||||
!floor.spaceType ||
|
|
||||||
floor.spaceType.type !== SpaceType.FLOOR
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid floor UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: floor.uuid,
|
|
||||||
createdAt: floor.createdAt,
|
|
||||||
updatedAt: floor.updatedAt,
|
|
||||||
name: floor.spaceName,
|
|
||||||
type: floor.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getFloorChildByUuid(
|
|
||||||
floorUuid: string,
|
|
||||||
getFloorChildDto: GetFloorChildDto,
|
|
||||||
): Promise<FloorChildInterface> {
|
|
||||||
try {
|
|
||||||
const { includeSubSpaces, page, pageSize } = getFloorChildDto;
|
|
||||||
|
|
||||||
const space = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: floorUuid },
|
|
||||||
relations: ['children', 'spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!space ||
|
|
||||||
!space.spaceType ||
|
|
||||||
space.spaceType.type !== SpaceType.FLOOR
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid floor UUID');
|
|
||||||
}
|
|
||||||
const totalCount = await this.spaceRepository.count({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const children = await this.buildHierarchy(
|
|
||||||
space,
|
|
||||||
includeSubSpaces,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: space.uuid,
|
|
||||||
name: space.spaceName,
|
|
||||||
type: space.spaceType.type,
|
|
||||||
totalCount,
|
|
||||||
children,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async buildHierarchy(
|
|
||||||
space: SpaceEntity,
|
|
||||||
includeSubSpaces: boolean,
|
|
||||||
page: number,
|
|
||||||
pageSize: number,
|
|
||||||
): Promise<FloorChildInterface[]> {
|
|
||||||
const children = await this.spaceRepository.find({
|
|
||||||
where: { parent: { uuid: space.uuid } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
skip: (page - 1) * pageSize,
|
|
||||||
take: pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
|
||||||
return children
|
|
||||||
.filter(
|
|
||||||
(child) =>
|
|
||||||
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,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const childHierarchies = await Promise.all(
|
|
||||||
children
|
|
||||||
.filter(
|
|
||||||
(child) =>
|
|
||||||
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,
|
|
||||||
name: child.spaceName,
|
|
||||||
type: child.spaceType.type,
|
|
||||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
return childHierarchies;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFloorParentByUuid(floorUuid: string): Promise<FloorParentInterface> {
|
|
||||||
try {
|
|
||||||
const floor = await this.spaceRepository.findOne({
|
|
||||||
where: {
|
|
||||||
uuid: floorUuid,
|
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.FLOOR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!floor ||
|
|
||||||
!floor.spaceType ||
|
|
||||||
floor.spaceType.type !== SpaceType.FLOOR
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid floor UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: floor.uuid,
|
|
||||||
name: floor.spaceName,
|
|
||||||
type: floor.spaceType.type,
|
|
||||||
parent: {
|
|
||||||
uuid: floor.parent.uuid,
|
|
||||||
name: floor.parent.spaceName,
|
|
||||||
type: floor.parent.spaceType.type,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFloorsByUserId(
|
|
||||||
userUuid: string,
|
|
||||||
): Promise<GetFloorByUserUuidInterface[]> {
|
|
||||||
try {
|
|
||||||
const floors = await this.userSpaceRepository.find({
|
|
||||||
relations: ['space', 'space.spaceType'],
|
|
||||||
where: {
|
|
||||||
user: { uuid: userUuid },
|
|
||||||
space: { spaceType: { type: SpaceType.FLOOR } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (floors.length === 0) {
|
|
||||||
throw new HttpException(
|
|
||||||
'this user has no floors',
|
|
||||||
HttpStatus.NOT_FOUND,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const spaces = floors.map((floor) => ({
|
|
||||||
uuid: floor.space.uuid,
|
|
||||||
name: floor.space.spaceName,
|
|
||||||
type: floor.space.spaceType.type,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return spaces;
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof HttpException) {
|
|
||||||
throw err;
|
|
||||||
} else {
|
|
||||||
throw new HttpException('user not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async addUserFloor(addUserFloorDto: AddUserFloorDto) {
|
|
||||||
try {
|
|
||||||
await this.userSpaceRepository.save({
|
|
||||||
user: { uuid: addUserFloorDto.userUuid },
|
|
||||||
space: { uuid: addUserFloorDto.floorUuid },
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) {
|
|
||||||
throw new HttpException(
|
|
||||||
'User already belongs to this floor',
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new HttpException(
|
|
||||||
err.message || 'Internal Server Error',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async renameFloorByUuid(
|
|
||||||
floorUuid: string,
|
|
||||||
updateFloorDto: UpdateFloorNameDto,
|
|
||||||
): Promise<RenameFloorByUuidInterface> {
|
|
||||||
try {
|
|
||||||
const floor = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: floorUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!floor ||
|
|
||||||
!floor.spaceType ||
|
|
||||||
floor.spaceType.type !== SpaceType.FLOOR
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid floor UUID');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.spaceRepository.update(
|
|
||||||
{ uuid: floorUuid },
|
|
||||||
{ spaceName: updateFloorDto.floorName },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fetch the updated floor
|
|
||||||
const updatedFloor = await this.spaceRepository.findOneOrFail({
|
|
||||||
where: { uuid: floorUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
uuid: updatedFloor.uuid,
|
|
||||||
name: updatedFloor.spaceName,
|
|
||||||
type: updatedFloor.spaceType.type,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof BadRequestException) {
|
|
||||||
throw err; // Re-throw BadRequestException
|
|
||||||
} else {
|
|
||||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './floor.service';
|
|
||||||
@ -14,13 +14,14 @@ export class GroupController {
|
|||||||
constructor(private readonly groupService: GroupService) {}
|
constructor(private readonly groupService: GroupService) {}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, UnitPermissionGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':unitUuid')
|
@Get(':spaceUuid')
|
||||||
async getGroupsBySpaceUuid(@Param('unitUuid') unitUuid: string) {
|
async getGroupsBySpaceUuid(@Param('spaceUuid') spaceUuid: string) {
|
||||||
return await this.groupService.getGroupsByUnitUuid(unitUuid);
|
return await this.groupService.getGroupsByUnitUuid(spaceUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, UnitPermissionGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':unitUuid/devices/:groupName')
|
@Get(':unitUuid/devices/:groupName')
|
||||||
async getUnitDevicesByGroupName(
|
async getUnitDevicesByGroupName(
|
||||||
@Param('unitUuid') unitUuid: string,
|
@Param('unitUuid') unitUuid: string,
|
||||||
|
|||||||
@ -31,17 +31,13 @@ export class GroupService {
|
|||||||
try {
|
try {
|
||||||
const spaces = await this.spaceRepository.find({
|
const spaces = await this.spaceRepository.find({
|
||||||
where: {
|
where: {
|
||||||
parent: {
|
|
||||||
uuid: unitUuid,
|
uuid: unitUuid,
|
||||||
},
|
},
|
||||||
},
|
relations: ['devices', 'devices.productDevice'],
|
||||||
relations: ['devicesSpaceEntity', 'devicesSpaceEntity.productDevice'],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupNames = spaces.flatMap((space) => {
|
const groupNames = spaces.flatMap((space) => {
|
||||||
return space.devicesSpaceEntity.map(
|
return space.devices.map((device) => device.productDevice.prodType);
|
||||||
(device) => device.productDevice.prodType,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const uniqueGroupNames = [...new Set(groupNames)];
|
const uniqueGroupNames = [...new Set(groupNames)];
|
||||||
@ -82,7 +78,7 @@ export class GroupService {
|
|||||||
parent: {
|
parent: {
|
||||||
uuid: unitUuid,
|
uuid: unitUuid,
|
||||||
},
|
},
|
||||||
devicesSpaceEntity: {
|
devices: {
|
||||||
productDevice: {
|
productDevice: {
|
||||||
prodType: groupName,
|
prodType: groupName,
|
||||||
},
|
},
|
||||||
@ -95,18 +91,18 @@ export class GroupService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
relations: [
|
relations: [
|
||||||
'devicesSpaceEntity',
|
'devices',
|
||||||
'devicesSpaceEntity.productDevice',
|
'devices.productDevice',
|
||||||
'devicesSpaceEntity.spaceDevice',
|
'devices.spaceDevice',
|
||||||
'devicesSpaceEntity.permission',
|
'devices.permission',
|
||||||
'devicesSpaceEntity.permission.permissionType',
|
'devices.permission.permissionType',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const devices = await Promise.all(
|
const devices = await Promise.all(
|
||||||
spaces.flatMap(async (space) => {
|
spaces.flatMap(async (space) => {
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
space.devicesSpaceEntity.map(async (device) => {
|
space.devices.map(async (device) => {
|
||||||
const deviceDetails = await this.getDeviceDetailsByDeviceIdTuya(
|
const deviceDetails = await this.getDeviceDetailsByDeviceIdTuya(
|
||||||
device.deviceTuyaUuid,
|
device.deviceTuyaUuid,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
|
||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class BuildingPermissionGuard implements CanActivate {
|
|
||||||
constructor(private readonly permissionService: SpacePermissionService) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { buildingUuid } = req.params;
|
|
||||||
const { user } = req;
|
|
||||||
|
|
||||||
if (!buildingUuid) {
|
|
||||||
throw new BadRequestException('buildingUuid is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.permissionService.checkUserPermission(
|
|
||||||
buildingUuid,
|
|
||||||
user.uuid,
|
|
||||||
SpaceType.BUILDING,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
||||||
import {
|
|
||||||
Injectable,
|
|
||||||
CanActivate,
|
|
||||||
HttpStatus,
|
|
||||||
BadRequestException,
|
|
||||||
ExecutionContext,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CheckBuildingTypeGuard implements CanActivate {
|
|
||||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { floorName, buildingUuid } = req.body;
|
|
||||||
|
|
||||||
if (!floorName) {
|
|
||||||
throw new BadRequestException('floorName is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!buildingUuid) {
|
|
||||||
throw new BadRequestException('buildingUuid is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.checkBuildingIsBuildingType(buildingUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkBuildingIsBuildingType(buildingUuid: string) {
|
|
||||||
const buildingData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: buildingUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!buildingData ||
|
|
||||||
!buildingData.spaceType ||
|
|
||||||
buildingData.spaceType.type !== SpaceType.BUILDING
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid building UUID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
console.error(error);
|
|
||||||
|
|
||||||
if (error instanceof BadRequestException) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.BAD_REQUEST)
|
|
||||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.NOT_FOUND).json({
|
|
||||||
statusCode: HttpStatus.NOT_FOUND,
|
|
||||||
message: 'Building not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
import { RoleType } from '@app/common/constants/role.type.enum';
|
||||||
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
import { CommunityPermissionService } from '@app/common/helper/services/community.permission.service';
|
||||||
import {
|
import {
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
CanActivate,
|
CanActivate,
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommunityPermissionGuard implements CanActivate {
|
export class CommunityPermissionGuard implements CanActivate {
|
||||||
constructor(private readonly permissionService: SpacePermissionService) {}
|
constructor(private readonly permissionService: CommunityPermissionService) {}
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const req = context.switchToHttp().getRequest();
|
const req = context.switchToHttp().getRequest();
|
||||||
@ -18,15 +18,22 @@ export class CommunityPermissionGuard implements CanActivate {
|
|||||||
const { communityUuid } = req.params;
|
const { communityUuid } = req.params;
|
||||||
const { user } = req;
|
const { user } = req;
|
||||||
|
|
||||||
|
if (
|
||||||
|
user &&
|
||||||
|
user.roles &&
|
||||||
|
user.roles.some(
|
||||||
|
(role) =>
|
||||||
|
role.type === RoleType.ADMIN || role.type === RoleType.SUPER_ADMIN,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!communityUuid) {
|
if (!communityUuid) {
|
||||||
throw new BadRequestException('communityUuid is required');
|
throw new BadRequestException('communityUuid is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.permissionService.checkUserPermission(
|
await this.permissionService.checkUserPermission(communityUuid);
|
||||||
communityUuid,
|
|
||||||
user.uuid,
|
|
||||||
SpaceType.COMMUNITY,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
HttpStatus,
|
|
||||||
} 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 {
|
|
||||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { buildingName, communityUuid } = req.body;
|
|
||||||
|
|
||||||
if (!buildingName) {
|
|
||||||
throw new BadRequestException('buildingName is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!communityUuid) {
|
|
||||||
throw new BadRequestException('communityUuid is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.checkCommunityIsCommunityType(communityUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkCommunityIsCommunityType(communityUuid: string) {
|
|
||||||
const communityData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: communityUuid },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!communityData ||
|
|
||||||
!communityData.spaceType ||
|
|
||||||
communityData.spaceType.type !== SpaceType.COMMUNITY
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid community UUID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
console.error(error);
|
|
||||||
|
|
||||||
if (error instanceof BadRequestException) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.BAD_REQUEST)
|
|
||||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.NOT_FOUND).json({
|
|
||||||
statusCode: HttpStatus.NOT_FOUND,
|
|
||||||
message: 'Community not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
|
||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class FloorPermissionGuard implements CanActivate {
|
|
||||||
constructor(private readonly permissionService: SpacePermissionService) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { floorUuid } = req.params;
|
|
||||||
const { user } = req;
|
|
||||||
|
|
||||||
if (!floorUuid) {
|
|
||||||
throw new BadRequestException('floorUuid is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.permissionService.checkUserPermission(
|
|
||||||
floorUuid,
|
|
||||||
user.uuid,
|
|
||||||
SpaceType.FLOOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import {
|
import {
|
||||||
Injectable,
|
Injectable,
|
||||||
@ -37,14 +36,9 @@ export class CheckFloorTypeGuard implements CanActivate {
|
|||||||
async checkFloorIsFloorType(floorUuid: string) {
|
async checkFloorIsFloorType(floorUuid: string) {
|
||||||
const floorData = await this.spaceRepository.findOne({
|
const floorData = await this.spaceRepository.findOne({
|
||||||
where: { uuid: floorUuid },
|
where: { uuid: floorUuid },
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (!floorData) {
|
||||||
!floorData ||
|
|
||||||
!floorData.spaceType ||
|
|
||||||
floorData.spaceType.type !== SpaceType.FLOOR
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid floor UUID');
|
throw new BadRequestException('Invalid floor UUID');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {
|
|||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CheckRoomGuard implements CanActivate {
|
export class CheckRoomGuard implements CanActivate {
|
||||||
@ -43,9 +42,6 @@ export class CheckRoomGuard implements CanActivate {
|
|||||||
const room = await this.spaceRepository.findOne({
|
const room = await this.spaceRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
uuid: roomUuid,
|
uuid: roomUuid,
|
||||||
spaceType: {
|
|
||||||
type: SpaceType.ROOM,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!room) {
|
if (!room) {
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
|
||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class RoomPermissionGuard implements CanActivate {
|
|
||||||
constructor(private readonly permissionService: SpacePermissionService) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { roomUuid } = req.params;
|
|
||||||
const { user } = req;
|
|
||||||
|
|
||||||
if (!roomUuid) {
|
|
||||||
throw new BadRequestException('roomUuid is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.permissionService.checkUserPermission(
|
|
||||||
roomUuid,
|
|
||||||
user.uuid,
|
|
||||||
SpaceType.ROOM,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
|
||||||
import {
|
import {
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
@ -22,11 +21,7 @@ export class UnitPermissionGuard implements CanActivate {
|
|||||||
throw new BadRequestException('unitUuid is required');
|
throw new BadRequestException('unitUuid is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.permissionService.checkUserPermission(
|
await this.permissionService.checkUserPermission(unitUuid, user.uuid);
|
||||||
unitUuid,
|
|
||||||
user.uuid,
|
|
||||||
SpaceType.UNIT,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import {
|
import {
|
||||||
Injectable,
|
Injectable,
|
||||||
@ -34,18 +33,13 @@ export class CheckUnitTypeGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkFloorIsFloorType(unitUuid: string) {
|
async checkFloorIsFloorType(spaceUuid: string) {
|
||||||
const unitData = await this.spaceRepository.findOne({
|
const space = await this.spaceRepository.findOne({
|
||||||
where: { uuid: unitUuid },
|
where: { uuid: spaceUuid },
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (!space) {
|
||||||
!unitData ||
|
throw new BadRequestException(`Invalid space UUID ${spaceUuid}`);
|
||||||
!unitData.spaceType ||
|
|
||||||
unitData.spaceType.type !== SpaceType.UNIT
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Invalid unit UUID');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
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 {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly userRepository: UserRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { userUuid, buildingUuid } = req.body;
|
|
||||||
|
|
||||||
await this.checkUserIsFound(userUuid);
|
|
||||||
|
|
||||||
await this.checkBuildingIsFound(buildingUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkUserIsFound(userUuid: string) {
|
|
||||||
const userData = await this.userRepository.findOne({
|
|
||||||
where: { uuid: userUuid },
|
|
||||||
});
|
|
||||||
if (!userData) {
|
|
||||||
throw new NotFoundException('User not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkBuildingIsFound(spaceUuid: string) {
|
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: spaceUuid, spaceType: { type: SpaceType.BUILDING } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (!spaceData) {
|
|
||||||
throw new NotFoundException('Building not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
if (
|
|
||||||
error instanceof BadRequestException ||
|
|
||||||
error instanceof NotFoundException
|
|
||||||
) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.NOT_FOUND)
|
|
||||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.BAD_REQUEST).json({
|
|
||||||
statusCode: HttpStatus.BAD_REQUEST,
|
|
||||||
message: 'invalid userUuid or buildingUuid',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,7 +7,6 @@ import {
|
|||||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||||
import { SpaceType } from '@app/common/constants/space-type.enum';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CheckUserCommunityGuard implements CanActivate {
|
export class CheckUserCommunityGuard implements CanActivate {
|
||||||
@ -44,8 +43,7 @@ export class CheckUserCommunityGuard implements CanActivate {
|
|||||||
|
|
||||||
private async checkCommunityIsFound(spaceUuid: string) {
|
private async checkCommunityIsFound(spaceUuid: string) {
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
const spaceData = await this.spaceRepository.findOne({
|
||||||
where: { uuid: spaceUuid, spaceType: { type: SpaceType.COMMUNITY } },
|
where: { uuid: spaceUuid },
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
});
|
||||||
if (!spaceData) {
|
if (!spaceData) {
|
||||||
throw new NotFoundException('Community not found');
|
throw new NotFoundException('Community not found');
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
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 {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly userRepository: UserRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { userUuid, floorUuid } = req.body;
|
|
||||||
|
|
||||||
await this.checkUserIsFound(userUuid);
|
|
||||||
|
|
||||||
await this.checkFloorIsFound(floorUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkUserIsFound(userUuid: string) {
|
|
||||||
const userData = await this.userRepository.findOne({
|
|
||||||
where: { uuid: userUuid },
|
|
||||||
});
|
|
||||||
if (!userData) {
|
|
||||||
throw new NotFoundException('User not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkFloorIsFound(spaceUuid: string) {
|
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: spaceUuid, spaceType: { type: SpaceType.FLOOR } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (!spaceData) {
|
|
||||||
throw new NotFoundException('Floor not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
if (
|
|
||||||
error instanceof BadRequestException ||
|
|
||||||
error instanceof NotFoundException
|
|
||||||
) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.NOT_FOUND)
|
|
||||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.BAD_REQUEST).json({
|
|
||||||
statusCode: HttpStatus.BAD_REQUEST,
|
|
||||||
message: 'invalid userUuid or floorUuid',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
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 {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly userRepository: UserRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { userUuid, roomUuid } = req.body;
|
|
||||||
|
|
||||||
await this.checkUserIsFound(userUuid);
|
|
||||||
|
|
||||||
await this.checkRoomIsFound(roomUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkUserIsFound(userUuid: string) {
|
|
||||||
const userData = await this.userRepository.findOne({
|
|
||||||
where: { uuid: userUuid },
|
|
||||||
});
|
|
||||||
if (!userData) {
|
|
||||||
throw new NotFoundException('User not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkRoomIsFound(spaceUuid: string) {
|
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: spaceUuid, spaceType: { type: SpaceType.ROOM } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (!spaceData) {
|
|
||||||
throw new NotFoundException('Room not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
if (
|
|
||||||
error instanceof BadRequestException ||
|
|
||||||
error instanceof NotFoundException
|
|
||||||
) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.NOT_FOUND)
|
|
||||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.BAD_REQUEST).json({
|
|
||||||
statusCode: HttpStatus.BAD_REQUEST,
|
|
||||||
message: 'invalid userUuid or roomUuid',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
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 {
|
|
||||||
constructor(
|
|
||||||
private readonly spaceRepository: SpaceRepository,
|
|
||||||
private readonly userRepository: UserRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const req = context.switchToHttp().getRequest();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { userUuid, unitUuid } = req.body;
|
|
||||||
|
|
||||||
await this.checkUserIsFound(userUuid);
|
|
||||||
|
|
||||||
await this.checkUnitIsFound(unitUuid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.handleGuardError(error, context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkUserIsFound(userUuid: string) {
|
|
||||||
const userData = await this.userRepository.findOne({
|
|
||||||
where: { uuid: userUuid },
|
|
||||||
});
|
|
||||||
if (!userData) {
|
|
||||||
throw new NotFoundException('User not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkUnitIsFound(spaceUuid: string) {
|
|
||||||
const spaceData = await this.spaceRepository.findOne({
|
|
||||||
where: { uuid: spaceUuid, spaceType: { type: SpaceType.UNIT } },
|
|
||||||
relations: ['spaceType'],
|
|
||||||
});
|
|
||||||
if (!spaceData) {
|
|
||||||
throw new NotFoundException('Unit not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
|
||||||
const response = context.switchToHttp().getResponse();
|
|
||||||
if (
|
|
||||||
error instanceof BadRequestException ||
|
|
||||||
error instanceof NotFoundException
|
|
||||||
) {
|
|
||||||
response
|
|
||||||
.status(HttpStatus.NOT_FOUND)
|
|
||||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
|
||||||
} else {
|
|
||||||
response.status(HttpStatus.BAD_REQUEST).json({
|
|
||||||
statusCode: HttpStatus.BAD_REQUEST,
|
|
||||||
message: 'invalid userUuid or unitUuid',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './room.controller';
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
import { RoomService } from '../services/room.service';
|
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
HttpStatus,
|
|
||||||
Param,
|
|
||||||
Post,
|
|
||||||
Put,
|
|
||||||
UseGuards,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
|
||||||
import { AddRoomDto, AddUserRoomDto } from '../dtos/add.room.dto';
|
|
||||||
import { UpdateRoomNameDto } from '../dtos/update.room.dto';
|
|
||||||
import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard';
|
|
||||||
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: EnableDisableStatusEnum.ENABLED,
|
|
||||||
path: SpaceType.ROOM,
|
|
||||||
})
|
|
||||||
export class RoomController {
|
|
||||||
constructor(private readonly roomService: RoomService) {}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, CheckUnitTypeGuard)
|
|
||||||
@Post()
|
|
||||||
async addRoom(@Body() addRoomDto: AddRoomDto) {
|
|
||||||
const room = await this.roomService.addRoom(addRoomDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'Room added successfully',
|
|
||||||
data: room,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, RoomPermissionGuard)
|
|
||||||
@Get(':roomUuid')
|
|
||||||
async getRoomByUuid(@Param('roomUuid') roomUuid: string) {
|
|
||||||
const room = await this.roomService.getRoomByUuid(roomUuid);
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, RoomPermissionGuard)
|
|
||||||
@Get('parent/:roomUuid')
|
|
||||||
async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) {
|
|
||||||
const room = await this.roomService.getRoomParentByUuid(roomUuid);
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(AdminRoleGuard, CheckUserRoomGuard)
|
|
||||||
@Post('user')
|
|
||||||
async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) {
|
|
||||||
await this.roomService.addUserRoom(addUserRoomDto);
|
|
||||||
return {
|
|
||||||
statusCode: HttpStatus.CREATED,
|
|
||||||
success: true,
|
|
||||||
message: 'user room added successfully',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Get('user/:userUuid')
|
|
||||||
async getRoomsByUserId(@Param('userUuid') userUuid: string) {
|
|
||||||
return await this.roomService.getRoomsByUserId(userUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(JwtAuthGuard, RoomPermissionGuard)
|
|
||||||
@Put(':roomUuid')
|
|
||||||
async renameRoomByUuid(
|
|
||||||
@Param('roomUuid') roomUuid: string,
|
|
||||||
@Body() updateRoomNameDto: UpdateRoomNameDto,
|
|
||||||
) {
|
|
||||||
const room = await this.roomService.renameRoomByUuid(
|
|
||||||
roomUuid,
|
|
||||||
updateRoomNameDto,
|
|
||||||
);
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user