mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 10:25:23 +00:00
Merge pull request #41 from SyncrowIOT/SP-233-be-home-members
feat: Add unit invitation code functionality and user verification us…
This commit is contained in:
@ -16,4 +16,8 @@ export class SpaceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public spaceTypeUuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public invitationCode: string;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
|
||||
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
|
||||
import { SpaceDto } from '../dtos';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { SpaceTypeEntity } from '../../space-type/entities';
|
||||
@ -6,6 +6,7 @@ import { UserSpaceEntity } from '../../user-space/entities';
|
||||
import { DeviceEntity } from '../../device/entities';
|
||||
|
||||
@Entity({ name: 'space' })
|
||||
@Unique(['invitationCode'])
|
||||
export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
@ -18,6 +19,11 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
||||
nullable: false,
|
||||
})
|
||||
public spaceName: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
public invitationCode: string;
|
||||
@ManyToOne(() => SpaceEntity, (space) => space.children, { nullable: true })
|
||||
parent: SpaceEntity;
|
||||
|
||||
|
18
package-lock.json
generated
18
package-lock.json
generated
@ -28,6 +28,7 @@
|
||||
"helmet": "^7.1.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"morgan": "^1.10.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"nodemailer": "^6.9.10",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"pg": "^8.11.3",
|
||||
@ -7165,6 +7166,23 @@
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz",
|
||||
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
|
@ -39,6 +39,7 @@
|
||||
"helmet": "^7.1.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"morgan": "^1.10.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"nodemailer": "^6.9.10",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"pg": "^8.11.3",
|
||||
|
@ -30,7 +30,7 @@ export class BuildingController {
|
||||
constructor(private readonly buildingService: BuildingService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AdminRoleGuard, CheckCommunityTypeGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckCommunityTypeGuard)
|
||||
@Post()
|
||||
async addBuilding(@Body() addBuildingDto: AddBuildingDto) {
|
||||
try {
|
||||
|
@ -32,7 +32,7 @@ export class CommunityController {
|
||||
constructor(private readonly communityService: CommunityService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AdminRoleGuard)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post()
|
||||
async addCommunity(@Body() addCommunityDto: AddCommunityDto) {
|
||||
try {
|
||||
|
@ -30,7 +30,7 @@ export class FloorController {
|
||||
constructor(private readonly floorService: FloorService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AdminRoleGuard, CheckBuildingTypeGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckBuildingTypeGuard)
|
||||
@Post()
|
||||
async addFloor(@Body() addFloorDto: AddFloorDto) {
|
||||
try {
|
||||
|
@ -28,7 +28,7 @@ export class RoomController {
|
||||
constructor(private readonly roomService: RoomService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AdminRoleGuard, CheckUnitTypeGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckUnitTypeGuard)
|
||||
@Post()
|
||||
async addRoom(@Body() addRoomDto: AddRoomDto) {
|
||||
try {
|
||||
|
@ -12,7 +12,11 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { AddUnitDto, AddUserUnitDto } from '../dtos/add.unit.dto';
|
||||
import {
|
||||
AddUnitDto,
|
||||
AddUserUnitDto,
|
||||
AddUserUnitUsingCodeDto,
|
||||
} from '../dtos/add.unit.dto';
|
||||
import { GetUnitChildDto } from '../dtos/get.unit.dto';
|
||||
import { UpdateUnitNameDto } from '../dtos/update.unit.dto';
|
||||
import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard';
|
||||
@ -30,7 +34,7 @@ export class UnitController {
|
||||
constructor(private readonly unitService: UnitService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AdminRoleGuard, CheckFloorTypeGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckFloorTypeGuard)
|
||||
@Post()
|
||||
async addUnit(@Body() addUnitDto: AddUnitDto) {
|
||||
try {
|
||||
@ -147,4 +151,39 @@ export class UnitController {
|
||||
);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, UnitPermissionGuard)
|
||||
@Get(':unitUuid/invitation-code')
|
||||
async getUnitInvitationCode(@Param('unitUuid') unitUuid: string) {
|
||||
try {
|
||||
const unit = await this.unitService.getUnitInvitationCode(unitUuid);
|
||||
return unit;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('user/verify-code')
|
||||
async verifyCodeAndAddUserUnit(
|
||||
@Body() addUserUnitUsingCodeDto: AddUserUnitUsingCodeDto,
|
||||
) {
|
||||
try {
|
||||
await this.unitService.verifyCodeAndAddUserUnit(addUserUnitUsingCodeDto);
|
||||
return {
|
||||
statusCode: HttpStatus.CREATED,
|
||||
success: true,
|
||||
message: 'user unit added successfully',
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,3 +40,29 @@ export class AddUserUnitDto {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
||||
export class AddUserUnitUsingCodeDto {
|
||||
@ApiProperty({
|
||||
description: 'unitUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public unitUuid: string;
|
||||
@ApiProperty({
|
||||
description: 'userUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public userUuid: string;
|
||||
@ApiProperty({
|
||||
description: 'inviteCode',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public inviteCode: string;
|
||||
constructor(dto: Partial<AddUserUnitDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddUnitDto, AddUserUnitDto } from '../dtos';
|
||||
import { AddUnitDto, AddUserUnitDto, AddUserUnitUsingCodeDto } from '../dtos';
|
||||
import {
|
||||
UnitChildInterface,
|
||||
UnitParentInterface,
|
||||
@ -227,7 +227,7 @@ export class UnitService {
|
||||
|
||||
async addUserUnit(addUserUnitDto: AddUserUnitDto) {
|
||||
try {
|
||||
await this.userSpaceRepository.save({
|
||||
return await this.userSpaceRepository.save({
|
||||
user: { uuid: addUserUnitDto.userUuid },
|
||||
space: { uuid: addUserUnitDto.unitUuid },
|
||||
});
|
||||
@ -282,4 +282,63 @@ export class UnitService {
|
||||
}
|
||||
}
|
||||
}
|
||||
async getUnitInvitationCode(unitUuid: string): Promise<any> {
|
||||
try {
|
||||
const { nanoid } = await import('nanoid');
|
||||
|
||||
// Generate a 6-character random invitation code
|
||||
const invitationCode = nanoid(6);
|
||||
|
||||
// Update the unit with the new invitation code
|
||||
await this.spaceRepository.update({ uuid: unitUuid }, { invitationCode });
|
||||
|
||||
// Fetch the updated unit
|
||||
const updatedUnit = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: unitUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
uuid: updatedUnit.uuid,
|
||||
invitationCode: updatedUnit.invitationCode,
|
||||
type: updatedUnit.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err;
|
||||
} else {
|
||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async verifyCodeAndAddUserUnit(
|
||||
addUserUnitUsingCodeDto: AddUserUnitUsingCodeDto,
|
||||
) {
|
||||
try {
|
||||
const unit = await this.spaceRepository.findOneOrFail({
|
||||
where: {
|
||||
invitationCode: addUserUnitUsingCodeDto.inviteCode,
|
||||
spaceType: { type: 'unit' },
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (unit.invitationCode) {
|
||||
const user = await this.addUserUnit({
|
||||
userUuid: addUserUnitUsingCodeDto.userUuid,
|
||||
unitUuid: unit.uuid,
|
||||
});
|
||||
if (user.uuid) {
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: unit.uuid },
|
||||
{ invitationCode: null },
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
throw new HttpException(
|
||||
'Invalid invitation code',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user