Skip to content
3 changes: 3 additions & 0 deletions src/db/entities/user-group.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export class UserGroupEntity {
@Column({ name: ColumnName.UserGroup.userId })
userId: string;

@Column({ name: ColumnName.UserGroup.addedBy })
addedById: string;

@ManyToOne(() => UserEntity, (user) => user.userGroups)
@JoinColumn({ name: ColumnName.UserGroup.userId })
user: UserEntity;
Expand Down
16 changes: 16 additions & 0 deletions src/modules/group/dto/group-request.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { InvitationStatus } from "@utils";
import { Type } from "class-transformer";
import { IsEnum, IsOptional } from "class-validator";

export class GroupRequestQuery {
@ApiPropertyOptional({
example: InvitationStatus.PENDING,
enum: InvitationStatus,
description: "Query by task status (0-Pending, 1-Accepted, 2-Declined)",
})
@IsEnum(InvitationStatus)
@IsOptional()
@Type(() => Number)
status: InvitationStatus;
}
1 change: 1 addition & 0 deletions src/modules/group/dto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./create.request";
export * from "./query";
export * from "./update.request";
export * from "./response";
export * from "./group-request.query";
16 changes: 15 additions & 1 deletion src/modules/group/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { CreateGroupRequest, UpdateGroupRequest } from "./dto";
import { GroupNotExistedError } from "./errors";
import { DevChatCls } from "@utils";
import { ClsService } from "nestjs-cls";
import { ChannelRepository, GroupRepository } from "@db/repositories";
import {
ChannelRepository,
GroupRepository,
UserGroupRepository,
} from "@db/repositories";
import { Transactional } from "typeorm-transactional";

@Injectable()
export class GroupService {
constructor(
private readonly groupRepo: GroupRepository,
private readonly userGroupRepo: UserGroupRepository,
private readonly channelRepo: ChannelRepository,
private readonly cls: ClsService<DevChatCls>,
) {}
Expand All @@ -33,6 +38,15 @@ export class GroupService {
createdAt: new Date(),
});

await this.userGroupRepo.insert({
groupId: group.id,
invitedAt: new Date(),
joinedAt: new Date(),
userId: createdBy,
addedById: createdBy,
status: 1,
});

return await this.groupRepo.findOne({
where: { id: insertResult.identifiers[0].id },
});
Expand Down
1 change: 1 addition & 0 deletions src/modules/task/dto/create.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class CreateTaskRequest {
description: "ID of the user assigned to this task",
})
@IsString()
@IsOptional()
@IsUUID()
assigneeId: string;
}
6 changes: 6 additions & 0 deletions src/modules/user-friend/dto/friend-request.response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ export class FriendRequestResponseDto {
}
return dto;
}

static fromEntities(
entities: UserFriendEntity[],
): FriendRequestResponseDto[] {
return entities.map((entity) => this.fromEntity(entity));
}
}
17 changes: 17 additions & 0 deletions src/modules/user/dto/get-friend.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { FriendRequestStatus, TaskStatusEnum } from "@utils";
import { Type } from "class-transformer";
import { IsEnum, IsNumber, IsOptional } from "class-validator";

export class GetFriendRequestQuery {
@ApiPropertyOptional({
example: FriendRequestStatus.PENDING,
enum: FriendRequestStatus,
description:
"Filter by friend request status (0-Pending, 1-Accepted, 2-Declined, 3-Cancelled)",
})
@IsEnum(FriendRequestStatus)
@IsOptional()
@Type(() => Number)
status: FriendRequestStatus = FriendRequestStatus.PENDING;
}
16 changes: 16 additions & 0 deletions src/modules/user/dto/group-request.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { InvitationStatus } from "@utils";
import { Type } from "class-transformer";
import { IsEnum, IsOptional } from "class-validator";

export class GroupRequestQuery {
@ApiPropertyOptional({
example: InvitationStatus.PENDING,
enum: InvitationStatus,
description: "Query by task status (0-Pending, 1-Accepted, 2-Declined)",
})
@IsEnum(InvitationStatus)
@IsOptional()
@Type(() => Number)
status: InvitationStatus = InvitationStatus.PENDING;
}
2 changes: 2 additions & 0 deletions src/modules/user/dto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from "./create-user.request";
export * from "./update-user.request";
export * from "./user.response";
export * from "./user.query";
export * from "./get-friend.query";
export * from "./group-request.query";
59 changes: 58 additions & 1 deletion src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import { UserService } from "./user.service";
import {
ApiMessageResponseDto,
ApiResponseDto,
FriendRequestStatus,
SkipAuth,
SwaggerApiMessageResponse,
SwaggerApiResponse,
} from "@utils";
import { CreateUserRequest } from "./dto/create-user.request";
import { ApiBearerAuth, ApiOperation, ApiParam } from "@nestjs/swagger";
import { UpdateUserRequest, UserQuery } from "./dto";
import { GroupRequestQuery, UpdateUserRequest, UserQuery } from "./dto";
import { UserResponse } from "./dto/user.response";
import { FriendRequestResponseDto } from "@modules/user-friend/dto";
import { UserGroupResponse } from "@modules/user-group/dto";
import { GetFriendRequestQuery } from "./dto/get-friend.query";

@Controller("user")
export class UserController {
Expand Down Expand Up @@ -84,4 +88,57 @@ export class UserController {
"Users retrieved successfully",
);
}

@Get("/friend-request/sent")
@ApiBearerAuth()
@ApiOperation({ summary: "Get friend requests sent by the user" })
async getFriendRequests(@Query() query: GetFriendRequestQuery) {
const response = await this.userService.getSentFriendRequests(query);

return new ApiResponseDto<FriendRequestResponseDto[]>(
FriendRequestResponseDto.fromEntities(response),
null,
"Friend request retrieved successully",
);
}

@Get("friend-requests/received")
@ApiBearerAuth()
@ApiOperation({ summary: "Get friend requests received by the user" })
async getReceivedFriendRequests(@Query() query: GetFriendRequestQuery) {
const response = await this.userService.getReceivedFriendRequests(query);
return new ApiResponseDto<FriendRequestResponseDto[]>(
FriendRequestResponseDto.fromEntities(response),
null,
"Received friend requests retrieved successfully",
);
}

@Get("group-requests/sent")
@ApiBearerAuth()
@ApiOperation({ summary: "Get all sent group requests" })
@SwaggerApiResponse(UserGroupResponse)
async getSentGroupRequests(@Query() query: GroupRequestQuery) {
const response = await this.userService.getSentGroupRequests(query);

return new ApiResponseDto<UserGroupResponse[]>(
UserGroupResponse.fromEntities(response),
null,
"Users retrieved successfully",
);
}

@Get("group-requests/received")
@ApiBearerAuth()
@ApiOperation({ summary: "Get all received group requests" })
@SwaggerApiResponse(UserGroupResponse)
async getReceivedGroupRequests(@Query() query: GroupRequestQuery) {
const response = await this.userService.getReceiveGroupRequests(query);

return new ApiResponseDto<UserGroupResponse[]>(
UserGroupResponse.fromEntities(response),
null,
"Users retrieved successfully",
);
}
}
85 changes: 80 additions & 5 deletions src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import { UserRepository } from "@db/repositories";
import {
UserFriendRepository,
UserRepository,
UserGroupRepository,
} from "@db/repositories";
import { Injectable } from "@nestjs/common";
import { UserExistedError } from "./errors/user-existed.error";
import * as bcrypt from "bcryptjs";
import { PaginationDto } from "@utils";
import { CreateUserRequest, UpdateUserRequest, UserQuery } from "./dto";
import {
GetFriendRequestQuery,
GroupRequestQuery,
UpdateUserRequest,
UserQuery,
CreateUserRequest,
} from "./dto";
import { DevChatCls, PaginationDto } from "@utils";
import { UserNotFoundError } from "./errors";
import { randomBytes } from "crypto";
import { sendVerificationEmail } from "src/utils/mailer";
import { ClsService } from "nestjs-cls";

const emailToken = randomBytes(32).toString("hex");

@Injectable()
export class UserService {
constructor(private readonly userRepo: UserRepository) {}
constructor(
private readonly userRepo: UserRepository,
private readonly userGroupRepo: UserGroupRepository,
private readonly cls: ClsService<DevChatCls>,
private readonly userFriendRepo: UserFriendRepository,
) {}

async validateBeforeCreate(dto: CreateUserRequest) {
const user = await this.userRepo.findOne({
Expand Down Expand Up @@ -123,4 +138,64 @@ export class UserService {
user.isActive = false;
await this.userRepo.save(user);
}

async getSentFriendRequests(query: GetFriendRequestQuery) {
const userId = this.cls.get("profile").id;

const { status } = query;
const friendRequests = await this.userFriendRepo.find({
where: {
senderId: userId,
status,
},
relations: ["sender", "receiver"],
});

return friendRequests;
}

async getReceivedFriendRequests(query: GetFriendRequestQuery) {
const userId = this.cls.get("profile").id;

const { status } = query;
const friendRequests = await this.userFriendRepo.find({
where: {
receiverId: userId,
status,
},
relations: ["sender", "receiver"],
});

return friendRequests;
}

async getReceiveGroupRequests(query: GroupRequestQuery) {
const userId = this.cls.get("profile").id;
const { status } = query;

const groupRequests = this.userGroupRepo.find({
where: {
userId,
status,
},
relations: ["user", "group", "addedBy"],
});

return groupRequests;
}

async getSentGroupRequests(query: GroupRequestQuery) {
const userId = this.cls.get("profile").id;
const { status } = query;

const groupRequests = this.userGroupRepo.find({
where: {
addedById: userId,
status,
},
relations: ["user", "group", "addedBy"],
});

return groupRequests;
}
}