diff --git a/apps/api/src/features/auth/controller/auth.controller.ts b/apps/api/src/features/auth/controller/auth.controller.ts index ca1e132..0d0c943 100644 --- a/apps/api/src/features/auth/controller/auth.controller.ts +++ b/apps/api/src/features/auth/controller/auth.controller.ts @@ -19,7 +19,7 @@ import { LoginDto } from '../dto/login.dto'; import { ApiTags } from '@nestjs/swagger'; @Controller('auth') -@ApiTags('conf') +@ApiTags('auth') export class AuthController { constructor( private authService: AuthService, diff --git a/apps/api/src/features/conf/conf.module.ts b/apps/api/src/features/conf/conf.module.ts deleted file mode 100644 index 8e18d9b..0000000 --- a/apps/api/src/features/conf/conf.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ConfService } from './service/conf.service'; -import { ConfController } from './controller/conf.controller'; - -import { forwardRef, Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Conf, ConfSchema } from './schema/conf.schema'; -import { AuthModule } from '../auth/auth.module'; -import { ConfGateway } from './gateway/conf.gateway'; -import { MessagesModule } from '../messages/messages.module'; -import { SharedModule } from '../../shared/shared.module'; -import { SponsorModule } from '../sponsor/sponsor.module'; - -@Module({ - imports: [ - AuthModule, - forwardRef(() => SponsorModule), - forwardRef(() => MessagesModule), - MongooseModule.forFeature([ - { - name: Conf.name, - schema: ConfSchema, - }, - ]), - SharedModule, - ], - controllers: [ConfController], - providers: [ConfService, ConfGateway], - exports: [ConfService, ConfGateway], -}) -export class ConfModule {} diff --git a/apps/api/src/features/conf/controller/conf.controller.ts b/apps/api/src/features/conf/controller/conf.controller.ts deleted file mode 100644 index 811c6f1..0000000 --- a/apps/api/src/features/conf/controller/conf.controller.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { - Put, - Body, - Get, - Post, - Param, - Delete, - UseGuards, - Controller, -} from '@nestjs/common'; -import { ParseObjectIdPipe } from '../../../shared/pipe/parse-object-id.pipe'; -import { SponsorService } from '../../sponsor/service/sponsor.service'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { ConfService } from '../service/conf.service'; -import { User } from '../../user/schema/user.schema'; -import { ConfDto } from '../dto/conf.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('conf') -@ApiTags('conf') -export class ConfController { - constructor( - private confService: ConfService, - private sponsorSrvice: SponsorService - ) {} - - @Get() - @ApiBearerAuth('access-token') - getUserConfs(@CurrentUser() user: User) { - return this.confService.getConfsByOwner(user); - } - - @Get('public') - @ApiBearerAuth('access-token') - getPublicConfs() { - return this.confService.getPublicConfs(); - } - - @Get('member') - @ApiBearerAuth('access-token') - getConfsByMember(@CurrentUser() user: User) { - return this.confService.getConfsByMember(user); - } - - @Get(':id') - @ApiBearerAuth('access-token') - get(@Param('id', ParseObjectIdPipe) id: string) { - return this.confService.getConf(id); - } - - @Get(':id/sponsors') - @ApiBearerAuth('access-token') - getSponsorsByConf(@Param('id', ParseObjectIdPipe) id: string) { - // return this.confService.getSp .getConfsBySponsor(id); - } - - @Get('sponsor/:id') - @ApiBearerAuth('access-token') - getConfsBySponsor(@Param('id', ParseObjectIdPipe) id: string) { - return this.confService.getConfsBySponsor(id); - } - - @Delete('delete/:id') - @ApiBearerAuth('access-token') - async delete( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.confService.delete( - await this.confService.validateConfByIdAndOwner(id, user), - user - ); - } - - @Post() - @ApiBearerAuth('access-token') - async create(@Body() conf: ConfDto, @CurrentUser() user: User) { - return this.confService.create(conf, user); - } - - @Put(':id') - @ApiBearerAuth('access-token') - async update( - @Param('id', ParseObjectIdPipe) id: string, - @Body() body: ConfDto, - @CurrentUser() user: User - ) { - return this.confService.update( - await this.confService.validateConfByIdAndOwner(id, user), - body, - user - ); - } - - @Post('join') - @ApiBearerAuth('access-token') - async join( - @Body('confId', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.confService.join(id, user); - } - - @Delete('leave/:id') - @ApiBearerAuth('access-token') - async leave( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.confService.leave( - user, - await this.confService.validateConf(id) - ); - } -} diff --git a/apps/api/src/features/conf/dto/conf.dto.ts b/apps/api/src/features/conf/dto/conf.dto.ts deleted file mode 100644 index a41da21..0000000 --- a/apps/api/src/features/conf/dto/conf.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { IsBoolean, IsDateString, IsNotEmpty, IsString } from 'class-validator'; -import { User } from '../../user/schema/user.schema'; -import { ApiProperty } from '@nestjs/swagger'; - -export class ConfDto { - @ApiProperty({ - type: String, - required: true, - }) - @IsNotEmpty() - @IsString() - title: string; - - @ApiProperty({ - type: String, - }) - @IsString() - description: string; - - @ApiProperty({ - type: String, - required: true, - }) - @IsString() - @IsNotEmpty() - slug: string; - - members: User[]; - - owner: User; - - @ApiProperty({ - type: Date, - required: true, - }) - @IsDateString() - start: Date; - - @ApiProperty({ - type: Date, - required: true, - }) - @IsDateString() - end: Date; - - @ApiProperty({ - type: Boolean, - required: true, - }) - @IsNotEmpty() - @IsBoolean() - isPublic: boolean; - - createdAt: Date; - - updatedAt: Date; -} diff --git a/apps/api/src/features/conf/gateway/conf.gateway.ts b/apps/api/src/features/conf/gateway/conf.gateway.ts deleted file mode 100644 index 6cdc74d..0000000 --- a/apps/api/src/features/conf/gateway/conf.gateway.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - forwardRef, - Inject, - UseFilters, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { - ConnectedSocket, - MessageBody, - OnGatewayDisconnect, - SubscribeMessage, - WebSocketGateway, - WebSocketServer, -} from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; -import { ExceptionsFilter } from '../../../core/filter/exceptions.filter'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { ConfService } from '../service/conf.service'; - -@UsePipes(new ValidationPipe()) -@UseFilters(new ExceptionsFilter()) -@UseGuards(JwtAuthGuard) -@WebSocketGateway() -export class ConfGateway implements OnGatewayDisconnect { - @WebSocketServer() server: Server; - - constructor( - @Inject(forwardRef(() => ConfService)) private confService: ConfService - ) {} - - handleDisconnect(socket: Socket) { - this.confService.unsubscribeSocket(socket); - } - - @SubscribeMessage('conf:subscribe') - async subscribe( - @ConnectedSocket() client: Socket, - @MessageBody() confId: string - ) { - return this.confService.subscribeSocket( - client, - await this.confService.validateConf(confId) - ); - } -} diff --git a/apps/api/src/features/conf/schema/conf.schema.ts b/apps/api/src/features/conf/schema/conf.schema.ts deleted file mode 100644 index 1c23c58..0000000 --- a/apps/api/src/features/conf/schema/conf.schema.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { createSchemaForClassWithMethods } from '../../../shared/mongoose/create-schema'; -import { ObjectId } from '../../../shared/mongoose/object-id'; -import { User } from '../../user/schema/user.schema'; -import { Prop, Schema } from '@nestjs/mongoose'; -import { ApiProperty } from '@nestjs/swagger'; -import { Document } from 'mongoose'; - -@Schema() -export class Conf extends Document { - @ApiProperty({ - type: String, - }) - @Prop({ - required: true, - }) - title: string; - - @ApiProperty({ - type: String, - }) - @Prop() - description: string; - - @ApiProperty({ - type: String, - required: true, - }) - @Prop({ - required: true, - }) - slug: string; - - @ApiProperty({ - type: [ - { - type: ObjectId, - ref: User.name, - }, - ], - }) - @Prop({ - type: [ - { - type: ObjectId, - ref: User.name, - }, - ], - }) - members: User[]; - - @ApiProperty({ - type: ObjectId, - }) - @Prop({ - type: ObjectId, - ref: User.name, - }) - owner: User; - - @ApiProperty({ - type: Date, - required: true, - }) - @Prop({ - type: Date, - required: true, - }) - start: Date; - - @ApiProperty({ - type: Date, - required: true, - }) - @Prop({ - type: Date, - required: true, - }) - end: Date; - - @ApiProperty({ - type: Date, - }) - @Prop({ - type: Date, - default: Date.now, - }) - createdAt: Date; - - @ApiProperty({ - type: Date, - }) - @Prop({ - type: Date, - default: Date.now, - }) - updatedAt: Date; - - @Prop({ - type: Boolean, - required: true, - }) - isPublic: boolean; -} - -export const ConfSchema = createSchemaForClassWithMethods(Conf); diff --git a/apps/api/src/features/conf/service/conf.service.ts b/apps/api/src/features/conf/service/conf.service.ts deleted file mode 100644 index c680be7..0000000 --- a/apps/api/src/features/conf/service/conf.service.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { - Inject, - Injectable, - forwardRef, - NotFoundException, -} from '@nestjs/common'; -import { getSocketClient } from '../../../shared/utils/get-socket-client'; -import { MessageService } from '../../messages/service/message.service'; -import { SponsorService } from '../../sponsor/service/sponsor.service'; -import { UserService } from '../../user/service/user.service'; -import { ConfGateway } from '../gateway/conf.gateway'; -import { remove } from '../../../shared/utils/remove'; -import { User } from '../../user/schema/user.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model, UpdateQuery } from 'mongoose'; -import { Conf } from '../schema/conf.schema'; -import { ConfDto } from '../dto/conf.dto'; -import { Socket } from 'socket.io'; - -@Injectable() -export class ConfService { - private blockedFields: (keyof Conf)[] = ['members', 'owner']; - - unpopulatedFields = '-' + this.blockedFields.join(' -'); - - constructor( - @InjectModel(Conf.name) private confModel: Model, - private confGateway: ConfGateway, - private userService: UserService, - private sponsorService: SponsorService, - @Inject(forwardRef(() => MessageService)) - private messageService: MessageService - ) {} - - async create(conf: ConfDto, user: User) { - const object = await this.confModel.create({ ...conf, owner: user._id }); - - return object - .populate('owner', this.userService.unpopulatedFields) - .execPopulate(); - } - - async update(conf: Conf, body: UpdateQuery, user: User) { - this.handleUpdateConf(conf, body as Conf); - - return this.confModel - .findOneAndUpdate({ _id: conf._id, owner: user._id }, body) - .populate('owner', this.userService.unpopulatedFields); - } - - handleUpdateConf(conf: Conf, body: Partial) { - this.sendMessage(conf, 'conf:update', Object.assign(conf, body)); - } - - delete(conf: Conf, user: User) { - this.handleDeleteConf(conf); - - return Promise.all([ - this.confModel.findOneAndDelete({ _id: conf._id, owner: user._id }), - this.messageService.deleteRoomMessages(conf), - ]); - } - - handleDeleteConf(conf: Conf) { - this.sendMessage(conf, 'conf:delete', conf); - } - - getConfByIdAndOwner(confId: string, owner: User) { - return this.confModel - .findOne({ _id: confId, owner: owner._id }) - .populate('members', this.userService.unpopulatedFields) - .populate('sponsors', this.sponsorService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - async validateConfByIdAndOwner(confId: string, owner: User) { - const conf = await this.getConfByIdAndOwner(confId, owner); - - if (!conf) { - throw new NotFoundException('Conf não encontrada'); - } - - return conf; - } - - getConf(confId: string) { - return this.confModel - .findById(confId) - .populate('members', this.userService.unpopulatedFields) - .populate('sponsors', this.sponsorService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - async validateConf(confId: string) { - const conf = await this.getConf(confId); - - if (!conf) { - throw new NotFoundException('Conf não encontrada'); - } - - return conf; - } - - getConfsByMember(user: User) { - return this.confModel - .find({ members: { $in: user._id } }) - .populate('owner', this.userService.unpopulatedFields); - } - - getConfsBySponsor(sponsorId: string) { - return this.confModel - .find({ sponsors: { $in: sponsorId } }) - .populate('sponsors', this.sponsorService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - getPublicConfs() { - return this.confModel - .find({ isPublic: true }) - .populate('owner', this.userService.unpopulatedFields); - } - - getConfsByOwner(user: User) { - return this.confModel.find({ owner: user._id }); - } - - getSockets(conf: Conf) { - return this.confGateway.server.in(`conf_${conf._id}`).allSockets(); - } - - subscribeSocket(socket: Socket, conf: Conf) { - return socket.join(`conf_${conf._id}`); - } - - unsubscribeSocket(socket: Socket) { - const conf = getSocketClient(socket).room; - - if (!conf) { - return; - } - - return socket.leave(`conf_${conf._id}`); - } - - sendMessage(conf: Conf, event: string, message?: T) { - return this.confGateway.server.to(`conf_${conf._id}`).emit(event, message); - } - - sendMessageExcept(except: Socket, conf: Conf, event: string, message: T) { - return except.broadcast.to(`conf_${conf._id}`).emit(event, message); - } - - async join(confId: string, user: User) { - const conf = await this.validateConf(confId); - - if (!conf.members.some((member) => user.id === member.id)) { - conf.members.push(user._id); - - this.handleJoinConf(user, conf); - - return conf.save(); - } - - return conf - .populate('members', this.userService.unpopulatedFields) - .execPopulate(); - } - - handleJoinConf(user: User, conf: Conf) { - this.sendMessage(conf, 'conf:join', this.userService.filterUser(user)); - } - - async leave(user: User, conf: Conf) { - remove(conf.members, (member) => member.id === user.id); - - this.handleLeaveConf(user, conf); - - return conf.save(); - } - - handleLeaveConf(user: User, conf: Conf) { - this.sendMessage(conf, 'conf:leave', this.userService.filterUser(user)); - } -} diff --git a/apps/api/src/features/features.module.ts b/apps/api/src/features/features.module.ts index 678c447..a052e05 100644 --- a/apps/api/src/features/features.module.ts +++ b/apps/api/src/features/features.module.ts @@ -1,11 +1,6 @@ import { Module } from '@nestjs/common'; import { AuthModule } from './auth/auth.module'; -import { MessagesModule } from './messages/messages.module'; -import { NotificationModule } from './notification/notification.module'; -import { SponsorModule } from './sponsor/sponsor.module'; -import { SpeakersModule } from './speakers/speakers.module'; -import { RoomModule } from './room/room.module'; -import { ConfModule } from './conf/conf.module'; +import { SponsorsModule } from './sponsors/sponsors.module'; import { UserModule } from './user/user.module'; import { TalksModule } from './talks/talks.module'; @@ -13,21 +8,13 @@ import { TalksModule } from './talks/talks.module'; imports: [ AuthModule, UserModule, - // RoomModule, - // ConfModule, - // MessagesModule, - // NotificationModule, - SponsorModule, - // SpeakersModule, + SponsorsModule, TalksModule, ], controllers: [], exports: [ AuthModule, UserModule, - // RoomModule, - // MessagesModule, - // NotificationModule, ], }) export class FeaturesModule {} diff --git a/apps/api/src/features/messages/controller/message.controller.ts b/apps/api/src/features/messages/controller/message.controller.ts deleted file mode 100644 index 40a7310..0000000 --- a/apps/api/src/features/messages/controller/message.controller.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { - Body, - Get, - Param, - Delete, - UseGuards, - Controller, - UnauthorizedException, -} from '@nestjs/common'; -import { Query } from '@nestjs/common'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { RoomService } from '../../room/service/room.service'; -import { UserService } from '../../user/service/user.service'; -import { MessageService } from '../service/message.service'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { User } from '../../user/schema/user.schema'; -import { - DeleteDirectMessageDto, - DeleteRoomMessageDto, - FetchMessagesDto, -} from '../dto'; - -@UseGuards(JwtAuthGuard) -@Controller('message') -@ApiTags('message') -export class MessageController { - constructor( - private userService: UserService, - private roomService: RoomService, - private messageService: MessageService - ) {} - - @Get('direct-first-message/:userId') - @ApiBearerAuth('access-token') - async getFirstDirectMessage( - @CurrentUser() user: User, - @Param('userId') to: string - ) { - return this.messageService.getFirstDirectMessage( - user, - await this.userService.validateUserById(to) - ); - } - - @Get('direct/:userId') - @ApiBearerAuth('access-token') - async getDirectMessages( - @CurrentUser() user: User, - @Param('userId') to: string, - @Query() query: FetchMessagesDto - ) { - return this.messageService.getDirectMessages( - user, - await this.userService.validateUserById(to), - query.limit, - query.before - ); - } - - @Delete('direct') - @ApiBearerAuth('access-token') - async deleteDirectMessage( - @Body() body: DeleteDirectMessageDto, - @CurrentUser() from: User - ) { - await this.userService.validateUserById(body.to); - - const message = await this.messageService.validatePopulatedMessage( - body.messageId - ); - - if (message.from.id !== from.id && message.to.id !== from.id) { - throw new UnauthorizedException('Você não tem acesso a este chat'); - } - - return this.messageService.deleteDirectMessage(message); - } - - @Delete('direct/all') - @ApiBearerAuth('access-token') - async deleteDirectMessages( - @Body() body: DeleteDirectMessageDto, - @CurrentUser() from: User - ) { - const to = await this.userService.validateUserById(body.to); - - return this.messageService.deleteDirectMessages(from, to); - } - - @Get('room-first-message/:roomId') - @ApiBearerAuth('access-token') - async getFirstRoomMessage(@Param('roomId') roomId: string) { - return this.messageService.getFirstRoomMessage( - await this.roomService.validateRoom(roomId) - ); - } - - @Get('room/:roomId') - @ApiBearerAuth('access-token') - async getRoomMessages( - @Param('roomId') roomId: string, - @Query() query: FetchMessagesDto - ) { - return this.messageService.getRoomMessages( - await this.roomService.validateRoom(roomId), - query.limit, - query.before - ); - } - - @Delete('room') - @ApiBearerAuth('access-token') - async deleteRoomMessage( - @Body() body: DeleteRoomMessageDto, - @CurrentUser() user: User - ) { - const room = await this.roomService.validateRoom(body.roomId); - - const message = await this.messageService.validateMessage(body.messageId); - - if (room.owner.id !== user.id && message.from.id !== user.id) { - throw new UnauthorizedException('Você não é o dono da mensagem'); - } - - return this.messageService.deleteRoomMessage(room, body.messageId); - } - - @Delete('room/all') - @ApiBearerAuth('access-token') - async deleteRoomMessages( - @Body() body: DeleteRoomMessageDto, - @CurrentUser() user: User - ) { - const room = await this.roomService.validateRoom(body.roomId); - - if (user.id !== room.owner.id) { - throw new UnauthorizedException('Você não é o dono da sala'); - } - - return this.messageService.deleteRoomMessages(room); - } -} diff --git a/apps/api/src/features/messages/dto/delete-direct-message.dto.ts b/apps/api/src/features/messages/dto/delete-direct-message.dto.ts deleted file mode 100644 index 2671721..0000000 --- a/apps/api/src/features/messages/dto/delete-direct-message.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsMongoId, IsOptional } from 'class-validator'; - -export class DeleteDirectMessageDto { - @IsMongoId() - @ApiProperty({ - required: true, - }) - to: string; - - @IsOptional() - @IsMongoId() - @ApiProperty({ - required: false, - }) - messageId?: string; -} diff --git a/apps/api/src/features/messages/dto/delete-room-message.dto.ts b/apps/api/src/features/messages/dto/delete-room-message.dto.ts deleted file mode 100644 index d9b0bfa..0000000 --- a/apps/api/src/features/messages/dto/delete-room-message.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsMongoId, IsOptional } from 'class-validator'; - -export class DeleteRoomMessageDto { - @IsOptional() - @IsMongoId() - @ApiProperty({ - required: false, - }) - messageId?: string; - - @IsMongoId() - @ApiProperty({ - required: false, - }) - roomId: string; -} diff --git a/apps/api/src/features/messages/dto/direct-message.dto.ts b/apps/api/src/features/messages/dto/direct-message.dto.ts deleted file mode 100644 index 4a15b4c..0000000 --- a/apps/api/src/features/messages/dto/direct-message.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsMongoId, IsString, MaxLength } from 'class-validator'; - -export class DirectMessageDto { - @IsString() - @MaxLength(2000) - @ApiProperty({ - required: false, - }) - message: string; - - @IsMongoId() - @ApiProperty({ - required: true, - }) - to: string; -} diff --git a/apps/api/src/features/messages/dto/fetch-messages.dto.ts b/apps/api/src/features/messages/dto/fetch-messages.dto.ts deleted file mode 100644 index 05f3975..0000000 --- a/apps/api/src/features/messages/dto/fetch-messages.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsDate, IsNumber, IsOptional } from 'class-validator'; - -export class FetchMessagesDto { - @IsOptional() - @Type(() => Number) - @IsNumber() - @ApiProperty({ - required: false, - }) - limit = 30; - - @IsOptional() - @Type(() => Date) - @IsDate() - @ApiProperty({ - required: false, - }) - before: Date; -} diff --git a/apps/api/src/features/messages/dto/index.ts b/apps/api/src/features/messages/dto/index.ts deleted file mode 100644 index 81edfc8..0000000 --- a/apps/api/src/features/messages/dto/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './delete-direct-message.dto'; -export * from './delete-room-message.dto'; -export * from './direct-message.dto'; -export * from './fetch-messages.dto'; -export * from './room-message.dto'; diff --git a/apps/api/src/features/messages/dto/room-message.dto.ts b/apps/api/src/features/messages/dto/room-message.dto.ts deleted file mode 100644 index cc878b8..0000000 --- a/apps/api/src/features/messages/dto/room-message.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsMongoId, IsString, MaxLength } from 'class-validator'; - -export class RoomMessageDto { - @IsString() - @MaxLength(2000) - @ApiProperty({ - required: true, - }) - message: string; - - @IsMongoId() - @ApiProperty({ - required: true, - }) - roomId: string; -} diff --git a/apps/api/src/features/messages/gateway/message.gateway.ts b/apps/api/src/features/messages/gateway/message.gateway.ts deleted file mode 100644 index e7171a3..0000000 --- a/apps/api/src/features/messages/gateway/message.gateway.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { - UseFilters, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { - ConnectedSocket, - MessageBody, - SubscribeMessage, - WebSocketGateway, - WebSocketServer, -} from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; -import { ExceptionsFilter } from '../../../core/filter/exceptions.filter'; -import { environments } from '../../../environments/environments'; -import { ParseObjectIdPipe } from '../../../shared/pipe/parse-object-id.pipe'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { RoomService } from '../../room/service/room.service'; -import { User } from '../../user/schema/user.schema'; -import { - NotificationType, - SubscriptionService, -} from '../../user/service/subscription.service'; -import { UserService } from '../../user/service/user.service'; -import { DirectMessageDto } from '../dto/direct-message.dto'; -import { RoomMessageDto } from '../dto/room-message.dto'; -import { MessageService } from '../service/message.service'; - -@UsePipes(new ValidationPipe()) -@UseFilters(new ExceptionsFilter()) -@UseGuards(JwtAuthGuard) -@WebSocketGateway() -export class MessageGateway { - @WebSocketServer() server: Server; - - constructor( - private userService: UserService, - private roomService: RoomService, - private messageService: MessageService, - private subscriptionService: SubscriptionService - ) {} - - @SubscribeMessage('message:direct') - async sendDirectMessage( - @MessageBody() body: DirectMessageDto, - @CurrentUser() user: User - ) { - const userTo = await this.userService.validateUserById(body.to); - - const message = await this.messageService.createDirectMessage( - user, - userTo, - body.message - ); - - this.userService.sendMessage(user, 'message:direct', message); - this.userService.sendMessage(userTo, 'message:direct', message); - - if (userTo.id === user.id) { - return true; - } - - const url = environments.frontEndUrl; - - this.subscriptionService.sendNotification(userTo, { - notification: { - title: user.username, - body: message.message, - }, - mobileData: { - type: NotificationType.Direct, - routeName: '/direct-message', - username: user.username, - }, - webData: { - onActionClick: { - default: { - operation: 'navigateLastFocusedOrOpen', - url: `${url}/direct-message/${user.username}`, - }, - }, - }, - }); - - return true; - } - - @SubscribeMessage('message:direct:typing') - async sendDirectTyping( - @MessageBody(new ParseObjectIdPipe()) userId: string, - @CurrentUser() user: User - ) { - return this.userService.sendMessage( - await this.userService.validateUserById(userId), - 'message:direct:typing', - { user: this.userService.filterUser(user) } - ); - } - - @SubscribeMessage('message:room') - async sendRoomMessage( - @MessageBody() body: RoomMessageDto, - @CurrentUser() user: User - ) { - const room = await this.roomService.validateRoom(body.roomId); - - const message = await this.messageService.createRoomMessage( - user, - room, - body.message - ); - - const url = environments.frontEndUrl; - - for (const member of room.members) { - if (member.id === user.id) { - continue; - } - - this.subscriptionService.sendNotification(member, { - notification: { - title: room.title, - body: `${user.username}: ${message.message}`, - }, - mobileData: { - type: NotificationType.Room, - routeName: '/rooms', - roomId: room.id, - roomTitle: room.title, - }, - webData: { - onActionClick: { - default: { - operation: 'navigateLastFocusedOrOpen', - url: `${url}/room/${room._id}`, - }, - }, - }, - }); - } - - return this.roomService.sendMessage(room, 'message:room', message); - } - - @SubscribeMessage('message:room:typing') - async sendRoomTyping( - @MessageBody(new ParseObjectIdPipe()) roomId: string, - @ConnectedSocket() socket: Socket, - @CurrentUser() user: User - ) { - const room = await this.roomService.validateRoom(roomId); - - return this.roomService.sendMessageExcept( - socket, - room, - 'message:room:typing', - { room, user: this.userService.filterUser(user) } - ); - } -} diff --git a/apps/api/src/features/messages/messages.module.ts b/apps/api/src/features/messages/messages.module.ts deleted file mode 100644 index 87d9bae..0000000 --- a/apps/api/src/features/messages/messages.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MessageService } from './service/message.service'; - -import { Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Message, MessageSchema } from './schema/message.schema'; -import { AuthModule } from '../auth/auth.module'; -import { RoomModule } from '../room/room.module'; -import { MessageController } from './controller/message.controller'; -import { MessageGateway } from './gateway/message.gateway'; - -@Module({ - imports: [ - MongooseModule.forFeature([ - { - name: Message.name, - schema: MessageSchema, - }, - ]), - AuthModule, - RoomModule, - ], - controllers: [MessageController], - providers: [MessageService, MessageGateway], - exports: [MessageService], -}) -export class MessagesModule {} diff --git a/apps/api/src/features/messages/schema/message.schema.ts b/apps/api/src/features/messages/schema/message.schema.ts deleted file mode 100644 index 77adc56..0000000 --- a/apps/api/src/features/messages/schema/message.schema.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createSchemaForClassWithMethods } from '../../../shared/mongoose/create-schema'; -import { ObjectId } from '../../../shared/mongoose/object-id'; -import { Room } from '../../room/schema/room.schema'; -import { User } from '../../user/schema/user.schema'; -import { Prop, Schema } from '@nestjs/mongoose'; -import { Document } from 'mongoose'; - -@Schema() -export class Message extends Document { - @Prop({ - required: true, - }) - message: string; - - @Prop({ type: ObjectId, ref: Room.name }) - room?: Room; - - @Prop() - order: number; - - @Prop({ type: ObjectId, ref: User.name }) - from: User; - - @Prop({ type: ObjectId, ref: User.name }) - to?: User; - - @Prop({ - type: Date, - default: Date.now, - }) - createdAt: Date; - - @Prop({ - type: Date, - default: Date.now, - }) - updatedAt: Date; -} - -export const MessageSchema = createSchemaForClassWithMethods(Message); diff --git a/apps/api/src/features/messages/service/message.service.ts b/apps/api/src/features/messages/service/message.service.ts deleted file mode 100644 index c8be0ad..0000000 --- a/apps/api/src/features/messages/service/message.service.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { - Inject, - Injectable, - forwardRef, - NotFoundException, -} from '@nestjs/common'; -import { RoomService } from '../../room/service/room.service'; -import { UserService } from '../../user/service/user.service'; -import { Room } from '../../room/schema/room.schema'; -import { User } from '../../user/schema/user.schema'; -import { Message } from '../schema/message.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { FilterQuery, Model } from 'mongoose'; - -@Injectable() -export class MessageService { - constructor( - @InjectModel(Message.name) private messageModel: Model, - @Inject(forwardRef(() => RoomService)) private roomService: RoomService, - private userService: UserService - ) {} - - getMessage(id: string) { - return this.messageModel - .findById(id) - .populate('from', this.userService.unpopulatedFields); - } - - async validateMessage(id: string) { - const message = await this.getMessage(id); - - if (!message) { - throw new NotFoundException('Messagem não encontrada'); - } - - return message; - } - - getPopulatedMessage(id: string) { - return this.messageModel - .findById(id) - .populate('from', this.userService.unpopulatedFields) - .populate('to', this.userService.unpopulatedFields) - .populate('room'); - } - - async validatePopulatedMessage(id: string) { - const message = await this.getPopulatedMessage(id); - - if (!message) { - throw new NotFoundException('Messagem não encontrada'); - } - - return message; - } - - getFirstRoomMessage(room: Room) { - return this.messageModel - .findOne({ room: room._id }) - .populate('from', this.userService.unpopulatedFields); - } - - async getRoomMessages(room: Room, limit?: number, before?: Date) { - const filter: FilterQuery = { - room: room._id, - createdAt: { $lte: before }, - }; - - if (!before) { - delete filter.createdAt; - } - - return this.getMessages(filter, limit); - } - - getDirectMessages(from: User, to: User, limit = 30, before?: Date) { - const filter: FilterQuery = { - ...this.getDirectMessageFilter(from, to), - createdAt: { $lte: before }, - }; - - if (!before) { - delete filter.createdAt; - } - - return this.getMessages(filter, limit); - } - - private async getMessages(filter: FilterQuery, limit: number) { - return this.sortMessages( - await this.messageModel - .find(filter) - .limit(limit) - .sort({ createdAt: -1 }) - .populate('from', this.userService.unpopulatedFields) - ); - } - - sortMessages(messages: Message[]) { - return messages.sort( - (a, b) => a.createdAt.getTime() - b.createdAt.getTime() - ); - } - - getFirstDirectMessage(from: User, to: User) { - return this.messageModel - .findOne(this.getDirectMessageFilter(from, to)) - .populate('from', this.userService.unpopulatedFields); - } - - private getDirectMessageFilter(from: User, to: User): FilterQuery { - return { - $or: [ - { - from: from._id, - to: to._id, - }, - { - to: from._id, - from: to._id, - }, - ], - }; - } - - async createRoomMessage(from: User, room: Room, message: string) { - const object = await this.messageModel.create({ - from: from._id, - room: room._id, - message, - }); - - return object - .populate('from', this.userService.unpopulatedFields) - .execPopulate(); - } - - async deleteRoomMessages(room: Room) { - this.roomService.sendMessage(room, 'room:delete_messages', room.id); - - return this.messageModel.deleteMany({ room: room._id }); - } - - async createDirectMessage(from: User, to: User, message: string) { - const object = await this.messageModel.create({ - from: from._id, - to: to._id, - message, - }); - - return object - .populate('from', this.userService.unpopulatedFields) - .execPopulate(); - } - - async deleteDirectMessage(message: Message) { - this.userService.sendMessage( - message.from, - 'direct:delete_message', - message._id - ); - - this.userService.sendMessage( - message.to, - 'direct:delete_message', - message._id - ); - - return this.messageModel.findOneAndDelete({ - _id: message._id, - to: message.to._id, - }); - } - - async deleteRoomMessage(room: Room, messageId: string) { - this.roomService.sendMessage(room, 'room:delete_message', messageId); - - return this.messageModel.findOneAndDelete({ - _id: messageId, - room: room._id, - }); - } - - async deleteDirectMessages(from: User, to: User) { - this.userService.sendMessage(from, 'direct:delete_messages', to.id); - this.userService.sendMessage(to, 'direct:delete_messages', from.id); - - return this.messageModel.findOneAndDelete({ from: from._id, to: to._id }); - } -} diff --git a/apps/api/src/features/room/controller/room.controller.ts b/apps/api/src/features/room/controller/room.controller.ts deleted file mode 100644 index 6752603..0000000 --- a/apps/api/src/features/room/controller/room.controller.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { - Body, - Controller, - Delete, - Get, - Param, - Post, - Put, - UseGuards, -} from '@nestjs/common'; -import { ParseObjectIdPipe } from '../../../shared/pipe/parse-object-id.pipe'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { User } from '../../user/schema/user.schema'; -import { RoomService } from '../service/room.service'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { RoomDto } from '../dto/room.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('room') -@ApiTags('room') -export class RoomController { - constructor(private roomService: RoomService) {} - - @Get() - @ApiBearerAuth('access-token') - getUserRooms(@CurrentUser() user: User) { - return this.roomService.getRoomsByOwner(user); - } - - @Get('id/:id') - get(@Param('id', ParseObjectIdPipe) id: string) { - return this.roomService.getRoom(id); - } - - @Get('public') - @ApiBearerAuth('access-token') - getPublicRooms() { - return this.roomService.getPublicRooms(); - } - - @Get('member') - getRoomsByMember(@CurrentUser() user: User) { - return this.roomService.getRoomsByMember(user); - } - - @Delete('delete/:id') - async delete( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.roomService.delete( - await this.roomService.validateRoomByIdAndOwner(id, user), - user - ); - } - - @Post() - async create(@Body() room: RoomDto, @CurrentUser() user: User) { - return this.roomService.create(room, user); - } - - @Put(':id') - async update( - @Param('id', ParseObjectIdPipe) id: string, - @Body() body: RoomDto, - @CurrentUser() user: User - ) { - return this.roomService.update( - await this.roomService.validateRoomByIdAndOwner(id, user), - body, - user - ); - } - - @Post('join') - async join( - @Body('roomId', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.roomService.join(id, user); - } - - @Delete('leave/:id') - async leave( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.roomService.leave( - user, - await this.roomService.validateRoom(id) - ); - } -} diff --git a/apps/api/src/features/room/dto/room.dto.ts b/apps/api/src/features/room/dto/room.dto.ts deleted file mode 100644 index fec8b35..0000000 --- a/apps/api/src/features/room/dto/room.dto.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IsBoolean, IsNotEmpty, IsString } from 'class-validator'; -import { User } from '../../user/schema/user.schema'; -import { ApiProperty } from '@nestjs/swagger'; - -export class RoomDto { - @ApiProperty({ - type: String, - required: true, - }) - @IsNotEmpty() - @IsString() - title: string; - - members: User[]; - - owner: User; - - @ApiProperty({ - type: Boolean, - required: true, - }) - @IsNotEmpty() - @IsBoolean() - isPublic: boolean; -} diff --git a/apps/api/src/features/room/gateway/room.gateway.ts b/apps/api/src/features/room/gateway/room.gateway.ts deleted file mode 100644 index 32f327d..0000000 --- a/apps/api/src/features/room/gateway/room.gateway.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - forwardRef, - Inject, - UseFilters, - UseGuards, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { - ConnectedSocket, - MessageBody, - OnGatewayDisconnect, - SubscribeMessage, - WebSocketGateway, - WebSocketServer, -} from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; -import { ExceptionsFilter } from '../../../core/filter/exceptions.filter'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { RoomService } from '../service/room.service'; - -@UsePipes(new ValidationPipe()) -@UseFilters(new ExceptionsFilter()) -@UseGuards(JwtAuthGuard) -@WebSocketGateway() -export class RoomGateway implements OnGatewayDisconnect { - @WebSocketServer() server: Server; - - constructor( - @Inject(forwardRef(() => RoomService)) private roomService: RoomService - ) {} - - handleDisconnect(socket: Socket) { - this.roomService.unsubscribeSocket(socket); - } - - @SubscribeMessage('room:subscribe') - async subscribe( - @ConnectedSocket() client: Socket, - @MessageBody() roomId: string - ) { - return this.roomService.subscribeSocket( - client, - await this.roomService.validateRoom(roomId) - ); - } -} diff --git a/apps/api/src/features/room/room.module.ts b/apps/api/src/features/room/room.module.ts deleted file mode 100644 index 4271d5c..0000000 --- a/apps/api/src/features/room/room.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RoomService } from './service/room.service'; -import { RoomController } from './controller/room.controller'; - -import { forwardRef, Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Room, RoomSchema } from './schema/room.schema'; -import { AuthModule } from '../auth/auth.module'; -import { RoomGateway } from './gateway/room.gateway'; -import { MessagesModule } from '../messages/messages.module'; -import { SharedModule } from '../../shared/shared.module'; - -@Module({ - imports: [ - AuthModule, - forwardRef(() => MessagesModule), - MongooseModule.forFeature([ - { - name: Room.name, - schema: RoomSchema, - }, - ]), - SharedModule, - ], - controllers: [RoomController], - providers: [RoomService, RoomGateway], - exports: [RoomService, RoomGateway], -}) -export class RoomModule {} diff --git a/apps/api/src/features/room/schema/room.schema.ts b/apps/api/src/features/room/schema/room.schema.ts deleted file mode 100644 index 1bf1301..0000000 --- a/apps/api/src/features/room/schema/room.schema.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createSchemaForClassWithMethods } from '../../../shared/mongoose/create-schema'; -import { ObjectId } from '../../../shared/mongoose/object-id'; -import { User } from '../../user/schema/user.schema'; -import { Prop, Schema } from '@nestjs/mongoose'; -import { Document } from 'mongoose'; - -@Schema() -export class Room extends Document { - @Prop({ - required: true, - }) - title: string; - - @Prop({ type: [{ type: ObjectId, ref: User.name }] }) - members: User[]; - - @Prop({ type: ObjectId, ref: User.name }) - owner: User; - - @Prop({ - required: true, - }) - isPublic: boolean; -} - -export const RoomSchema = createSchemaForClassWithMethods(Room); diff --git a/apps/api/src/features/room/service/room.service.ts b/apps/api/src/features/room/service/room.service.ts deleted file mode 100644 index e87ae2a..0000000 --- a/apps/api/src/features/room/service/room.service.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - Inject, - Injectable, - forwardRef, - NotFoundException, -} from '@nestjs/common'; -import { getSocketClient } from '../../../shared/utils/get-socket-client'; -import { MessageService } from '../../messages/service/message.service'; -import { UserService } from '../../user/service/user.service'; -import { RoomGateway } from '../gateway/room.gateway'; -import { remove } from '../../../shared/utils/remove'; -import { User } from '../../user/schema/user.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model, UpdateQuery } from 'mongoose'; -import { Room } from '../schema/room.schema'; -import { RoomDto } from '../dto/room.dto'; -import { Socket } from 'socket.io'; - -@Injectable() -export class RoomService { - constructor( - @InjectModel(Room.name) private roomModel: Model, - private roomGateway: RoomGateway, - private userService: UserService, - @Inject(forwardRef(() => MessageService)) - private messageService: MessageService - ) {} - - async create(room: RoomDto, user: User) { - const object = await this.roomModel.create({ ...room, owner: user._id }); - - return object - .populate('owner', this.userService.unpopulatedFields) - .execPopulate(); - } - - async update(room: Room, body: UpdateQuery, user: User) { - this.handleUpdateRoom(room, body as Room); - - return this.roomModel - .findOneAndUpdate({ _id: room._id, owner: user._id }, body) - .populate('owner', this.userService.unpopulatedFields); - } - - handleUpdateRoom(room: Room, body: Partial) { - this.sendMessage(room, 'room:update', Object.assign(room, body)); - } - - delete(room: Room, user: User) { - this.handleDeleteRoom(room); - - return Promise.all([ - this.roomModel.findOneAndDelete({ _id: room._id, owner: user._id }), - this.messageService.deleteRoomMessages(room), - ]); - } - - handleDeleteRoom(room: Room) { - this.sendMessage(room, 'room:delete', room); - } - - getRoomByIdAndOwner(roomId: string, owner: User) { - return this.roomModel - .findOne({ _id: roomId, owner: owner._id }) - .populate('members', this.userService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - async validateRoomByIdAndOwner(roomId: string, owner: User) { - const room = await this.getRoomByIdAndOwner(roomId, owner); - - if (!room) { - throw new NotFoundException('Room not found'); - } - - return room; - } - - getRoom(roomId: string) { - return this.roomModel - .findById(roomId) - .populate('members', this.userService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - async validateRoom(roomId: string) { - const room = await this.getRoom(roomId); - - if (!room) { - throw new NotFoundException('Room not found'); - } - - return room; - } - - getRoomsByMember(user: User) { - return this.roomModel - .find({ members: { $in: user._id } }) - .populate('owner', this.userService.unpopulatedFields); - } - - getPublicRooms() { - return this.roomModel - .find({ isPublic: true }) - .populate('owner', this.userService.unpopulatedFields); - } - - getRoomsByOwner(user: User) { - return this.roomModel.find({ owner: user._id }); - } - - getSockets(room: Room) { - return this.roomGateway.server.in(`room_${room._id}`).allSockets(); - } - - subscribeSocket(socket: Socket, room: Room) { - return socket.join(`room_${room._id}`); - } - - unsubscribeSocket(socket: Socket) { - const room = getSocketClient(socket).room; - - if (!room) { - return; - } - - return socket.leave(`room_${room._id}`); - } - - sendMessage(room: Room, event: string, message?: T) { - return this.roomGateway.server.to(`room_${room._id}`).emit(event, message); - } - - sendMessageExcept(except: Socket, room: Room, event: string, message: T) { - return except.broadcast.to(`room_${room._id}`).emit(event, message); - } - - async join(roomId: string, user: User) { - const room = await this.validateRoom(roomId); - - if (!room.members.some((member) => user.id === member.id)) { - room.members.push(user._id); - - this.handleJoinRoom(user, room); - - return room.save(); - } - - return room - .populate('members', this.userService.unpopulatedFields) - .execPopulate(); - } - - handleJoinRoom(user: User, room: Room) { - this.sendMessage(room, 'room:join', this.userService.filterUser(user)); - } - - async leave(user: User, room: Room) { - remove(room.members, (member) => member.id === user.id); - - this.handleLeaveRoom(user, room); - - return room.save(); - } - - handleLeaveRoom(user: User, room: Room) { - this.sendMessage(room, 'room:leave', this.userService.filterUser(user)); - } -} diff --git a/apps/api/src/features/speakers/controller/speaker.controller.ts b/apps/api/src/features/speakers/controller/speaker.controller.ts deleted file mode 100644 index baf9577..0000000 --- a/apps/api/src/features/speakers/controller/speaker.controller.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - Body, - Get, - Param, - Delete, - UseGuards, - Controller, - UnauthorizedException, - Post, -} from '@nestjs/common'; -import { Query } from '@nestjs/common'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { ConfService } from '../../conf/service/conf.service'; -import { UserService } from '../../user/service/user.service'; -import { SpeakerService } from '../service/speaker.service'; -import { User } from '../../user/schema/user.schema'; -import { DeleteConfSpeakerDto, FetchSpeakersDto } from '../dto'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; - -@UseGuards(JwtAuthGuard) -@Controller('speaker') -@ApiTags('speaker') -export class SpeakerController { - constructor( - private userService: UserService, - private confService: ConfService, - private speakerService: SpeakerService - ) {} - - @Get('conf-next-speaker/:confId') - @ApiBearerAuth('access-token') - async getFirstConfSpeaker(@Param('confId') confId: string) { - return this.speakerService.getNextConfSpeakers( - await this.confService.validateConf(confId) - ); - } - - @Get('conf/:confId') - @ApiBearerAuth('access-token') - async getConfSpeakers( - @Param('confId') confId: string, - @Query() query: FetchSpeakersDto - ) { - return this.speakerService.getConfSpeakers( - await this.confService.validateConf(confId) - ); - } - - // @Post('conf/:confId') - // async createConfSpeakers( - // @Param('confId') confId: string, - // @Post() createSpeakerDto: FetchSpeakersDto - // ) { - // return this.speakerService.createConfSpeaker( - // await this.confService.validateConf(confId), - // query.limit, - // query.before - // ); - // } - - @Delete('conf') - @ApiBearerAuth('access-token') - async deleteConfSpeaker( - @Body() body: DeleteConfSpeakerDto, - @CurrentUser() user: User - ) { - const conf = await this.confService.validateConf(body.confId); - - const speaker = await this.speakerService.validateSpeaker(body.speakerId); - - if (conf.owner.id !== user.id && speaker.id !== user.id) { - throw new UnauthorizedException('Você não é o dono da conf'); - } - - return this.speakerService.deleteConfSpeaker(conf, body.speakerId); - } -} diff --git a/apps/api/src/features/speakers/dto/conf-speaker.dto.ts b/apps/api/src/features/speakers/dto/conf-speaker.dto.ts deleted file mode 100644 index ac7741d..0000000 --- a/apps/api/src/features/speakers/dto/conf-speaker.dto.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ObjectId } from '../../../shared/mongoose/object-id'; -import { ApiProperty } from '@nestjs/swagger'; -import { - IsDateString, - IsMongoId, - IsNotEmpty, - IsString, - MaxLength, -} from 'class-validator'; - -export class ConfSpeakerDto { - @ApiProperty({ - type: String, - required: true, - }) - @IsNotEmpty() - @IsString() - name: string; - - @ApiProperty({ - type: String, - maxLength: 2000, - }) - @IsString() - @MaxLength(2000) - bio: string; - - @ApiProperty({ - type: Date, - required: true, - }) - @IsDateString() - @IsNotEmpty() - start: Date; - - @ApiProperty({ - type: Date, - required: false, - }) - @IsDateString() - end: Date; - - @ApiProperty({ - type: ObjectId, - required: true, - }) - @IsNotEmpty() - @IsMongoId() - confId: string; -} diff --git a/apps/api/src/features/speakers/dto/delete-conf-speaker.dto.ts b/apps/api/src/features/speakers/dto/delete-conf-speaker.dto.ts deleted file mode 100644 index d88dde2..0000000 --- a/apps/api/src/features/speakers/dto/delete-conf-speaker.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IsMongoId, IsOptional } from 'class-validator'; - -export class DeleteConfSpeakerDto { - @IsOptional() - @IsMongoId() - speakerId?: string; - - @IsMongoId() - confId: string; -} diff --git a/apps/api/src/features/speakers/dto/fetch-speakers.dto.ts b/apps/api/src/features/speakers/dto/fetch-speakers.dto.ts deleted file mode 100644 index f28a287..0000000 --- a/apps/api/src/features/speakers/dto/fetch-speakers.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Type } from 'class-transformer'; -import { IsDate, IsNumber, IsOptional } from 'class-validator'; - -export class FetchSpeakersDto { - @IsOptional() - @Type(() => Number) - @IsNumber() - limit = 30; - - @IsOptional() - @Type(() => Date) - @IsDate() - before: Date; -} diff --git a/apps/api/src/features/speakers/dto/index.ts b/apps/api/src/features/speakers/dto/index.ts deleted file mode 100644 index 58b236c..0000000 --- a/apps/api/src/features/speakers/dto/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './delete-conf-speaker.dto'; -export * from './fetch-speakers.dto'; -export * from './conf-speaker.dto'; diff --git a/apps/api/src/features/speakers/schema/speaker.schema.ts b/apps/api/src/features/speakers/schema/speaker.schema.ts deleted file mode 100644 index f0eb541..0000000 --- a/apps/api/src/features/speakers/schema/speaker.schema.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { createSchemaForClassWithMethods } from '../../../shared/mongoose/create-schema'; -import { ObjectId } from '../../../shared/mongoose/object-id'; -import { Conf } from '../../conf/schema/conf.schema'; -import { Prop, Schema } from '@nestjs/mongoose'; -import { Document } from 'mongoose'; -import { ApiProperty } from '@nestjs/swagger'; - -@Schema() -export class Speaker extends Document { - @ApiProperty({ - required: true, - }) - @Prop({ - required: true, - }) - name: string; - - @ApiProperty() - @Prop() - bio: string; - - @ApiProperty() - @Prop() - email: string; - - @ApiProperty() - @Prop({ - type: ObjectId, - ref: Conf.name, - }) - conf: Conf; - - @ApiProperty() - @Prop() - order: number; - - @ApiProperty({ - required: true, - }) - @Prop({ - type: Date, - required: true, - }) - start: Date; - - @ApiProperty({ - required: true, - }) - @Prop({ - type: Date, - required: true, - }) - end: Date; - - @Prop({ - type: Date, - default: Date.now, - }) - createdAt: Date; - - @Prop({ - type: Date, - default: Date.now, - }) - updatedAt: Date; -} - -export const SpeakerSchema = createSchemaForClassWithMethods(Speaker); diff --git a/apps/api/src/features/speakers/service/speaker.service.ts b/apps/api/src/features/speakers/service/speaker.service.ts deleted file mode 100644 index f88acf3..0000000 --- a/apps/api/src/features/speakers/service/speaker.service.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { - Inject, - Injectable, - forwardRef, - NotFoundException, -} from '@nestjs/common'; -import { ConfService } from '../../conf/service/conf.service'; -import { UserService } from '../../user/service/user.service'; -import { Conf } from '../../conf/schema/conf.schema'; -import { Speaker } from '../schema/speaker.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { FilterQuery, Model } from 'mongoose'; - -@Injectable() -export class SpeakerService { - constructor( - @InjectModel(Speaker.name) private speakerModel: Model, - @Inject(forwardRef(() => ConfService)) private confService: ConfService, - private userService: UserService - ) {} - - getSpeaker(id: string) { - return this.speakerModel.findById(id); - } - - async validateSpeaker(id: string) { - const speaker = await this.getSpeaker(id); - - if (!speaker) { - throw new NotFoundException('Palestrante não encontrado'); - } - - return speaker; - } - - getPopulatedSpeaker(id: string) { - return this.speakerModel.findById(id).populate('conf'); - } - - async validatePopulatedSpeaker(id: string) { - const speaker = await this.getPopulatedSpeaker(id); - - if (!speaker) { - throw new NotFoundException('Palestrante não encontrado'); - } - - return speaker; - } - - getConfSpeakers(conf: Conf) { - return this.speakerModel.find({ conf: conf._id }); - } - - async getNextConfSpeakers(conf: Conf, limit?: number, before?: Date) { - const filter: FilterQuery = { - conf: conf._id, - start: { $lte: before }, - }; - - if (!before) { - delete filter.start; - } - - return this.getSpeakers(filter, limit); - } - - private async getSpeakers(filter: FilterQuery, limit: number) { - return this.sortSpeakers( - await this.speakerModel.find(filter).limit(limit).sort({ start: -1 }) - ); - } - - sortSpeakers(speakers: Speaker[]) { - return speakers.sort((a, b) => a.start.getTime() - b.start.getTime()); - } - - private getSpeakerFilter(start: Date, end: Date): FilterQuery { - return { - $or: [ - { start: start, end: end }, - { end: start, start: end }, - ], - }; - } - - async createConfSpeaker(start: Date, conf: Conf, speaker: string) { - const object = await this.speakerModel.create({ - start: start, - conf: conf._id, - speaker, - }); - - return object - .populate('start', this.userService.unpopulatedFields) - .execPopulate(); - } - - async deleteConfSpeaker(conf: Conf, speakerId: string) { - this.confService.sendMessage(conf, 'conf:delete_speaker', speakerId); - - return this.speakerModel.findOneAndDelete({ - _id: speakerId, - conf: conf._id, - }); - } -} diff --git a/apps/api/src/features/speakers/speakers.module.ts b/apps/api/src/features/speakers/speakers.module.ts deleted file mode 100644 index 125e52e..0000000 --- a/apps/api/src/features/speakers/speakers.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { SpeakerService } from './service/speaker.service'; - -import { Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { Speaker, SpeakerSchema } from './schema/speaker.schema'; -import { AuthModule } from '../auth/auth.module'; -import { ConfModule } from '../conf/conf.module'; -import { SpeakerController } from './controller/speaker.controller'; - -@Module({ - imports: [ - MongooseModule.forFeature([ - { - name: Speaker.name, - schema: SpeakerSchema, - }, - ]), - AuthModule, - ConfModule, - ], - controllers: [SpeakerController], - providers: [SpeakerService], - exports: [SpeakerService], -}) -export class SpeakersModule {} diff --git a/apps/api/src/features/sponsor/controller/sponsor.controller.ts b/apps/api/src/features/sponsor/controller/sponsor.controller.ts deleted file mode 100644 index 7702d5a..0000000 --- a/apps/api/src/features/sponsor/controller/sponsor.controller.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { - Put, - Body, - Get, - Post, - Param, - Delete, - UseGuards, - Controller, -} from '@nestjs/common'; -import { ParseObjectIdPipe } from '../../../shared/pipe/parse-object-id.pipe'; -import { CurrentUser } from '../../auth/decorators/current-user.decorator'; -import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { User } from '../../user/schema/user.schema'; -import { SponsorService } from '../service/sponsor.service'; -import { SponsorDto } from '../dto/sponsor.dto'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; - -@UseGuards(JwtAuthGuard) -@Controller('sponsor') -@ApiTags('sponsor') -export class SponsorController { - constructor(private sponsorService: SponsorService) {} - - @Get() - @ApiBearerAuth('access-token') - getUserSponsors(@CurrentUser() user: User) { - return this.sponsorService.getSponsorsByOwner(user); - } - - @Get('id/:id') - @ApiBearerAuth('access-token') - get(@Param('id', ParseObjectIdPipe) id: string) { - return this.sponsorService.getSponsor(id); - } - - @Get('public') - getPublicSponsors() { - return this.sponsorService.getPublicSponsors(); - } - - @Get('member') - @ApiBearerAuth('access-token') - getSponsorsByMember(@CurrentUser() user: User) { - return this.sponsorService.getSponsorsByMember(user); - } - - @Delete('delete/:id') - @ApiBearerAuth('access-token') - async delete( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.sponsorService.delete( - await this.sponsorService.validateSponsorByIdAndOwner(id, user), - user - ); - } - - @Post() - @ApiBearerAuth('access-token') - async create(@Body() sponsor: SponsorDto, @CurrentUser() user: User) { - return this.sponsorService.create(sponsor, user); - } - - @Put(':id') - @ApiBearerAuth('access-token') - async update( - @Param('id', ParseObjectIdPipe) id: string, - @Body() body: SponsorDto, - @CurrentUser() user: User - ) { - return this.sponsorService.update( - await this.sponsorService.validateSponsorByIdAndOwner(id, user), - body, - user - ); - } - - @Post('join') - @ApiBearerAuth('access-token') - async join( - @Body('sponsorId', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.sponsorService.join(id, user); - } - - @Delete('leave/:id') - @ApiBearerAuth('access-token') - async leave( - @Param('id', ParseObjectIdPipe) id: string, - @CurrentUser() user: User - ) { - return this.sponsorService.leave( - user, - await this.sponsorService.validateSponsor(id) - ); - } -} diff --git a/apps/api/src/features/sponsor/dto/sponsor.dto.ts b/apps/api/src/features/sponsor/dto/sponsor.dto.ts deleted file mode 100644 index 32885f0..0000000 --- a/apps/api/src/features/sponsor/dto/sponsor.dto.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class SponsorDto { - @ApiProperty({ - type: String, - required: true, - }) - @IsNotEmpty() - @IsString() - name: string; - - @ApiProperty({ - type: String, - }) - @IsString() - description: string; - - @ApiProperty({ - type: String, - required: true, - }) - @IsString() - @IsNotEmpty() - slug: string; - - @ApiProperty({ - type: String, - required: true, - }) - @IsString() - @IsNotEmpty() - color: string; - - @ApiProperty({ - type: String, - required: false, - }) - @IsString() - website: string; - - @ApiProperty({ - type: String, - required: false, - }) - @IsString() - youtube: string; -} diff --git a/apps/api/src/features/sponsor/service/sponsor.service.ts b/apps/api/src/features/sponsor/service/sponsor.service.ts deleted file mode 100644 index da44ef0..0000000 --- a/apps/api/src/features/sponsor/service/sponsor.service.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { - Inject, - Injectable, - forwardRef, - NotFoundException, -} from '@nestjs/common'; -import { getSocketClient } from '../../../shared/utils/get-socket-client'; -import { MessageService } from '../../messages/service/message.service'; -import { UserService } from '../../user/service/user.service'; -import { SponsorGateway } from '../gateway/sponsor.gateway'; -import { remove } from '../../../shared/utils/remove'; -import { User } from '../../user/schema/user.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model, ObjectId, UpdateQuery } from 'mongoose'; -import { Sponsor } from '../schema/sponsor.schema'; -import { SponsorDto } from '../dto/sponsor.dto'; -import { Socket } from 'socket.io'; -import { Conf } from '../../conf/schema/conf.schema'; -import { ConfService } from '../../conf/service/conf.service'; - -@Injectable() -export class SponsorService { - private blockedFields: (keyof Sponsor)[] = ['members', 'owner', 'conf']; - - unpopulatedFields = '-' + this.blockedFields.join(' -'); - - constructor( - @InjectModel(Sponsor.name) private sponsorModel: Model, - // private confService: ConfService, - private sponsorGateway: SponsorGateway, - private userService: UserService, - @Inject(forwardRef(() => MessageService)) - private messageService: MessageService - ) {} - - async create(sponsor: SponsorDto, user: User) { - const object = await this.sponsorModel.create({ - ...sponsor, - owner: user._id, - }); - - return object - .populate('owner', this.userService.unpopulatedFields) - .execPopulate(); - } - - async update(sponsor: Sponsor, body: UpdateQuery, user: User) { - this.handleUpdateSponsor(sponsor, body as Sponsor); - - return this.sponsorModel - .findOneAndUpdate({ _id: sponsor._id, owner: user._id }, body) - .populate('owner', this.userService.unpopulatedFields); - } - - handleUpdateSponsor(sponsor: Sponsor, body: Partial) { - this.sendMessage(sponsor, 'sponsor:update', Object.assign(sponsor, body)); - } - - delete(sponsor: Sponsor, user: User) { - this.handleDeleteSponsor(sponsor); - - return Promise.all([ - this.sponsorModel.findOneAndDelete({ _id: sponsor._id, owner: user._id }), - ]); - } - - handleDeleteSponsor(sponsor: Sponsor) { - this.sendMessage(sponsor, 'sponsor:delete', sponsor); - } - - getSponsorByIdAndOwner(sponsorId: string, owner: User) { - return this.sponsorModel - .findOne({ _id: sponsorId, owner: owner._id }) - .populate('members', this.userService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - } - - async validateSponsorByIdAndOwner(sponsorId: string, owner: User) { - const sponsor = await this.getSponsorByIdAndOwner(sponsorId, owner); - - if (!sponsor) { - throw new NotFoundException('Sponsor não encontrada'); - } - - return sponsor; - } - - getSponsor(sponsorId: string) { - return this.sponsorModel - .findById(sponsorId) - .populate('members', this.userService.unpopulatedFields) - .populate('owner', this.userService.unpopulatedFields); - // .populate('conf', this.confService.unpopulatedFields); - } - - getSponsorsByConf(conf: Conf) { - return this.sponsorModel.findOne({ conf: conf._id }); - // .populate('conf', this.confService.unpopulatedFields); - } - - async validateSponsor(sponsorId: string) { - const sponsor = await this.getSponsor(sponsorId); - - if (!sponsor) { - throw new NotFoundException('Sponsor não encontrada'); - } - - return sponsor; - } - - getSponsorsByMember(user: User) { - return this.sponsorModel - .find({ members: { $in: user._id } }) - .populate('owner', this.userService.unpopulatedFields); - } - - getPublicSponsors() { - return this.sponsorModel - .find() - .populate('owner', this.userService.unpopulatedFields); - } - - getSponsorsByOwner(user: User) { - return this.sponsorModel.find({ owner: user._id }); - } - - getSockets(sponsor: Sponsor) { - return this.sponsorGateway.server.in(`sponsor_${sponsor._id}`).allSockets(); - } - - subscribeSocket(socket: Socket, sponsor: Sponsor) { - return socket.join(`sponsor_${sponsor._id}`); - } - - unsubscribeSocket(socket: Socket) { - const sponsor = getSocketClient(socket).room; - - if (!sponsor) { - return; - } - - return socket.leave(`sponsor_${sponsor._id}`); - } - - sendMessage(sponsor: Sponsor, event: string, message?: T) { - return this.sponsorGateway.server - .to(`sponsor_${sponsor._id}`) - .emit(event, message); - } - - sendMessageExcept( - except: Socket, - sponsor: Sponsor, - event: string, - message: T - ) { - return except.broadcast.to(`sponsor_${sponsor._id}`).emit(event, message); - } - - async join(sponsorId: string, user: User) { - const sponsor = await this.validateSponsor(sponsorId); - - if (!sponsor.members.some((member) => user.id === member.id)) { - sponsor.members.push(user._id); - - this.handleJoinSponsor(user, sponsor); - - return sponsor.save(); - } - - return sponsor - .populate('members', this.userService.unpopulatedFields) - .execPopulate(); - } - - handleJoinSponsor(user: User, sponsor: Sponsor) { - this.sendMessage( - sponsor, - 'sponsor:join', - this.userService.filterUser(user) - ); - } - - async leave(user: User, sponsor: Sponsor) { - remove(sponsor.members, (member) => member.id === user.id); - - this.handleLeaveSponsor(user, sponsor); - - return sponsor.save(); - } - - handleLeaveSponsor(user: User, sponsor: Sponsor) { - this.sendMessage( - sponsor, - 'sponsor:leave', - this.userService.filterUser(user) - ); - } -} diff --git a/apps/api/src/features/sponsor/sponsor.module.ts b/apps/api/src/features/sponsor/sponsor.module.ts deleted file mode 100644 index 0922bfb..0000000 --- a/apps/api/src/features/sponsor/sponsor.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { SponsorService } from './service/sponsor.service'; -import { SponsorController } from './controller/sponsor.controller'; - -import { SponsorGateway } from './gateway/sponsor.gateway'; -import { MessagesModule } from '../messages/messages.module'; -import { SharedModule } from '../../shared/shared.module'; -import { Sponsor, SponsorSchema } from './schema/sponsor.schema'; -import { forwardRef, Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { AuthModule } from '../auth/auth.module'; -import { ConfModule } from '../conf/conf.module'; - -@Module({ - imports: [ - AuthModule, - forwardRef(() => ConfModule), - forwardRef(() => MessagesModule), - MongooseModule.forFeature([ - { - name: Sponsor.name, - schema: SponsorSchema, - }, - ]), - SharedModule, - ], - controllers: [SponsorController], - providers: [SponsorService, SponsorGateway], - exports: [SponsorService, SponsorGateway], -}) -export class SponsorModule {} diff --git a/apps/api/src/features/sponsors/controller/sponsors.controller.ts b/apps/api/src/features/sponsors/controller/sponsors.controller.ts new file mode 100644 index 0000000..5aabf2e --- /dev/null +++ b/apps/api/src/features/sponsors/controller/sponsors.controller.ts @@ -0,0 +1,96 @@ +import { ParseObjectIdPipe } from '../../../shared/pipe/parse-object-id.pipe'; +import { CurrentUser } from '../../auth/decorators/current-user.decorator'; +import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { SponsorsService } from '../service/sponsors.service'; +import { CreateSponsorDto } from '../dto/create-sponsor.dto'; +import { UpdateSponsorDto } from '../dto/update-sponsor.dto'; +import { User } from '../../user/schema/user.schema'; +import { + Get, + Put, + Post, + Body, + Param, + Delete, + UseGuards, + Controller, +} from '@nestjs/common'; + + +@Controller('sponsors') +@ApiTags('sponsors') +export class SponsorsController { + constructor(private readonly sponsorsService: SponsorsService) {} + + @Post() + @UseGuards(JwtAuthGuard) + @ApiBearerAuth('access-token') + create(@Body() createSponsorDto: CreateSponsorDto) { + return this.sponsorsService.create(createSponsorDto); + } + + @Get() + findAll() { + return this.sponsorsService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.sponsorsService.getSponsorById(id); + } + + @Put(':id') + @UseGuards(JwtAuthGuard) + @ApiBearerAuth('access-token') + async update( + @Param('id', ParseObjectIdPipe) id: string, + @Body() body: UpdateSponsorDto, + @CurrentUser() user: User, + ) { + return this.sponsorsService.update( + await this.sponsorsService.validateSponsorById(id), + body, + user + ); + } + + @Post('join') + @UseGuards(JwtAuthGuard) + @ApiBearerAuth('access-token') + async join( + @Body('sponsorId', ParseObjectIdPipe) id: string, + @CurrentUser() user: User + ) { + return this.sponsorsService.join(id, user); + } + + @Delete('leave/:id') + @UseGuards(JwtAuthGuard) + @ApiBearerAuth('access-token') + async leave( + @Param('id', ParseObjectIdPipe) id: string, + @CurrentUser() user: User + ) { + console.log(id, user); + + return this.sponsorsService.leave( + user, + await this.sponsorsService.validateSponsorById(id) + ); + } + + + @Get('member') + getSponsorsByMember(@CurrentUser() user: User) { + return this.sponsorsService.getSponsorsByMember(user); + } + + + // @Delete(':id') + // @UseGuards(JwtAuthGuard) + // @ApiBearerAuth('access-token') + // remove(@Param('id') id: string) { + // return this.sponsorsService.remove(id); + // } +} diff --git a/apps/api/src/features/sponsors/dto/create-sponsor.dto.ts b/apps/api/src/features/sponsors/dto/create-sponsor.dto.ts new file mode 100644 index 0000000..ae128c4 --- /dev/null +++ b/apps/api/src/features/sponsors/dto/create-sponsor.dto.ts @@ -0,0 +1,80 @@ +import { IsNotEmpty, IsOptional, IsString, IsUrl } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateSponsorDto { + @ApiProperty({ + type: String, + required: true, + }) + @IsNotEmpty() + @IsString() + name: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsString() + description: string; + + @ApiProperty({ + type: String, + required: true, + }) + @IsString() + logo: string; + + @ApiProperty({ + type: String, + required: true, + }) + @IsString() + slug: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsString() + color: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsUrl() + @IsOptional() + website: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsUrl() + @IsOptional() + youtube: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsUrl() + @IsOptional() + calendlyUrl: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsUrl() + @IsOptional() + videoUrl: string; + + @ApiProperty({ + type: String, + required: false, + }) + @IsUrl() + @IsOptional() + formUrl: string; +} diff --git a/apps/api/src/features/sponsors/dto/update-sponsor.dto.ts b/apps/api/src/features/sponsors/dto/update-sponsor.dto.ts new file mode 100644 index 0000000..e7f1bf0 --- /dev/null +++ b/apps/api/src/features/sponsors/dto/update-sponsor.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateSponsorDto } from './create-sponsor.dto'; + +export class UpdateSponsorDto extends PartialType(CreateSponsorDto) {} diff --git a/apps/api/src/features/sponsor/gateway/sponsor.gateway.ts b/apps/api/src/features/sponsors/gateway/sponsors.gateway.ts similarity index 68% rename from apps/api/src/features/sponsor/gateway/sponsor.gateway.ts rename to apps/api/src/features/sponsors/gateway/sponsors.gateway.ts index 34811b7..a5b896f 100644 --- a/apps/api/src/features/sponsor/gateway/sponsor.gateway.ts +++ b/apps/api/src/features/sponsors/gateway/sponsors.gateway.ts @@ -1,9 +1,9 @@ import { - forwardRef, Inject, - UseFilters, - UseGuards, UsePipes, + UseGuards, + UseFilters, + forwardRef, ValidationPipe, } from '@nestjs/common'; import { @@ -17,22 +17,21 @@ import { import { Server, Socket } from 'socket.io'; import { ExceptionsFilter } from '../../../core/filter/exceptions.filter'; import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { SponsorService } from '../service/sponsor.service'; +import { SponsorsService } from '../service/sponsors.service'; @UsePipes(new ValidationPipe()) @UseFilters(new ExceptionsFilter()) @UseGuards(JwtAuthGuard) @WebSocketGateway() -export class SponsorGateway implements OnGatewayDisconnect { +export class SponsorsGateway implements OnGatewayDisconnect { @WebSocketServer() server: Server; constructor( - @Inject(forwardRef(() => SponsorService)) - private sponsorService: SponsorService + @Inject(forwardRef(() => SponsorsService)) private sponsorsService: SponsorsService ) {} handleDisconnect(socket: Socket) { - this.sponsorService.unsubscribeSocket(socket); + this.sponsorsService.unsubscribeSocket(socket); } @SubscribeMessage('sponsor:subscribe') @@ -40,9 +39,9 @@ export class SponsorGateway implements OnGatewayDisconnect { @ConnectedSocket() client: Socket, @MessageBody() sponsorId: string ) { - return this.sponsorService.subscribeSocket( + return this.sponsorsService.subscribeSocket( client, - await this.sponsorService.validateSponsor(sponsorId) + await this.sponsorsService.validateSponsorById(sponsorId) ); } } diff --git a/apps/api/src/features/sponsor/schema/sponsor.schema.ts b/apps/api/src/features/sponsors/schema/sponsor.schema.ts similarity index 74% rename from apps/api/src/features/sponsor/schema/sponsor.schema.ts rename to apps/api/src/features/sponsors/schema/sponsor.schema.ts index 7d1a18b..0dba078 100644 --- a/apps/api/src/features/sponsor/schema/sponsor.schema.ts +++ b/apps/api/src/features/sponsors/schema/sponsor.schema.ts @@ -1,9 +1,7 @@ import { createSchemaForClassWithMethods } from '../../../shared/mongoose/create-schema'; import { ObjectId } from '../../../shared/mongoose/object-id'; import { User } from '../../user/schema/user.schema'; -import { Conf } from '../../conf/schema/conf.schema'; import { Prop, Schema } from '@nestjs/mongoose'; -import { ApiProperty } from '@nestjs/swagger'; import { Document } from 'mongoose'; @Schema() @@ -41,18 +39,18 @@ export class Sponsor extends Document { }) members: User[]; - @ApiProperty() - @Prop({ - type: ObjectId, - ref: Conf.name, - }) - conf: Conf; - - @Prop({ - type: ObjectId, - ref: User.name, - }) - owner: User; + // @ApiProperty() + // @Prop({ + // type: ObjectId, + // ref: Conf.name, + // }) + // conf: Conf; + + // @Prop({ + // type: ObjectId, + // ref: User.name, + // }) + // owner: User; } export const SponsorSchema = createSchemaForClassWithMethods(Sponsor); diff --git a/apps/api/src/features/sponsors/service/sponsors.service.ts b/apps/api/src/features/sponsors/service/sponsors.service.ts new file mode 100644 index 0000000..e7f95fa --- /dev/null +++ b/apps/api/src/features/sponsors/service/sponsors.service.ts @@ -0,0 +1,153 @@ +import { getSocketClient } from '../../../shared/utils/get-socket-client'; +import { Injectable, NotFoundException } from '@nestjs/common'; +import { SponsorsGateway } from '../gateway/sponsors.gateway'; +import { UserService } from '../../user/service/user.service'; +import { remove } from '../../../shared/utils/remove'; +import { Sponsor } from '../schema/sponsor.schema'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model, UpdateQuery } from 'mongoose'; +import { ObjectId } from 'mongodb'; +import { Socket } from 'socket.io'; + +import { User } from '../../user/schema/user.schema'; + +@Injectable() +export class SponsorsService { + private blockedFields: (keyof Sponsor)[] = []; + + constructor( + @InjectModel(Sponsor.name) private sponsorModel: Model, + private sponsorsGateway: SponsorsGateway, + private userService: UserService, + ) {} + + findAll() { + return this.sponsorModel.find().sort({ start: 'asc' }); + } + + remove(id: ObjectId | string) { + return this.sponsorModel.findByIdAndRemove(id); + } + + getSponsorById(id: ObjectId | string) { + return this.sponsorModel.findById(id) + .populate('members', this.userService.unpopulatedFields); + } + + getSponsorsByMember(user: User) { + return this.sponsorModel + .find({ members: { $in: user._id } }); + } + + async update(sponsor: Sponsor, body: UpdateQuery, user: User) { + this.handleUpdateSponsor(sponsor, body as Sponsor); + + return this.sponsorModel + .findOneAndUpdate({ _id: sponsor._id, owner: user._id }, body); + } + + handleUpdateSponsor(sponsor: Sponsor, body: Partial) { + this.sendMessage(sponsor, 'sponsor:update', Object.assign(sponsor, body)); + } + + updateSponsor(sponsor: Sponsor, data: UpdateQuery) { + return this.sponsorModel.findByIdAndUpdate(sponsor._id, data); + } + + async validateSponsorById(sponsorId: ObjectId | string) { + const room = await this.getSponsorById(sponsorId); + + if (!room) { + throw new NotFoundException('Sponsor não encontrada'); + } + + return room; + } + + filterSponsor(sponsor: Sponsor, allowedFields: (keyof Sponsor)[] = []) { + const sponsorObject = sponsor.toObject({ virtuals: true }); + + for (const field of this.blockedFields) { + if (allowedFields.includes(field)) { + continue; + } + + delete sponsorObject[field]; + } + + return sponsorObject; + } + + async updateSponsorObject(sponsor: Sponsor) { + const newInput = await this.getSponsorById(sponsor._id); + + return Object.assign(sponsor, newInput); + } + + async create(body: Partial) { + const sponsor = await this.sponsorModel.create(body); + + return sponsor.save(); + } + + getSockets(sponsor: Sponsor) { + return this.sponsorsGateway.server.in(`sponsor_${sponsor._id}`).allSockets(); + } + + subscribeSocket(socket: Socket, sponsor: Sponsor) { + return socket.join(`sponsor_${sponsor._id}`); + } + + unsubscribeSocket(socket: Socket) { + const sponsor = getSocketClient(socket).sponsor; + + if (!sponsor) { + return; + } + + return socket.leave(`sponsor_${sponsor._id}`); + } + + sendMessage(sponsor: Sponsor, event: string, message?: T) { + return this.sponsorsGateway.server.to(`sponsor_${sponsor._id}`).emit(event, message); + } + + sendMessageExcept(except: Socket, sponsor: Sponsor, event: string, message: T) { + return except.broadcast.to(`sponsor_${sponsor._id}`).emit(event, message); + } + + async join(sponsorId: string, user: User) { + const sponsor = await this.validateSponsorById(sponsorId); + + if (!sponsor.members.some((member) => user.id === member.id)) { + sponsor.members.push(user.id); + + this.handleJoinSponsor(user, sponsor); + + return sponsor.save(); + } + + return sponsor + .populate('members', this.userService.unpopulatedFields) + .execPopulate(); + } + + handleJoinSponsor(user: User, sponsor: Sponsor) { + this.sendMessage(sponsor, 'sponsor:join', this.userService.filterUser(user)); + } + + async leave(user: User, sponsor: Sponsor) { + console.log(sponsor); + console.log(user); + + remove(sponsor.members, (member) => member.id === user.id); + + this.handleLeaveSponsor(user, sponsor); + + return sponsor.save(); + } + + handleLeaveSponsor(user: User, sponsor: Sponsor) { + this.sendMessage(sponsor, 'sponsor:leave', this.userService.filterUser(user)); + } +} diff --git a/apps/api/src/features/sponsors/sponsors.module.ts b/apps/api/src/features/sponsors/sponsors.module.ts new file mode 100644 index 0000000..3ba6de8 --- /dev/null +++ b/apps/api/src/features/sponsors/sponsors.module.ts @@ -0,0 +1,25 @@ +import { Sponsor, SponsorSchema } from './schema/sponsor.schema'; +import { SponsorsController } from './controller/sponsors.controller'; +import { SharedModule } from '../../shared/shared.module'; +import { SponsorsService } from './service/sponsors.service'; +import { SponsorsGateway } from './gateway/sponsors.gateway'; +import { MongooseModule } from '@nestjs/mongoose'; +import { AuthModule } from '../auth/auth.module'; +import { Module } from '@nestjs/common'; + +@Module({ + imports: [ + AuthModule, + MongooseModule.forFeature([ + { + name: Sponsor.name, + schema: SponsorSchema, + }, + ]), + SharedModule + ], + controllers: [SponsorsController], + providers: [SponsorsService, SponsorsGateway], + exports: [SponsorsService, SponsorsGateway], +}) +export class SponsorsModule {} diff --git a/apps/api/src/features/talks/service/talks.service.ts b/apps/api/src/features/talks/service/talks.service.ts index 07d5b1c..72dbec6 100644 --- a/apps/api/src/features/talks/service/talks.service.ts +++ b/apps/api/src/features/talks/service/talks.service.ts @@ -3,7 +3,6 @@ import { TalksGateway } from '../gateway/talks.gateway'; import { InjectModel } from '@nestjs/mongoose'; import { Model, UpdateQuery } from 'mongoose'; import { getSocketClient } from '../../../shared/utils/get-socket-client'; -import { MessageService } from '../../messages/service/message.service'; import { UserService } from '../../user/service/user.service'; import { remove } from '../../../shared/utils/remove'; import { Talk } from '../schema/talk.schema'; diff --git a/apps/api/src/features/user/gateway/user.gateway.ts b/apps/api/src/features/user/gateway/user.gateway.ts index 01a0e54..1f2c2bd 100644 --- a/apps/api/src/features/user/gateway/user.gateway.ts +++ b/apps/api/src/features/user/gateway/user.gateway.ts @@ -12,8 +12,8 @@ import { Server, Socket } from 'socket.io'; import { getSocketUser } from '../../../shared/utils/get-socket-user'; import { CurrentUser } from '../../auth/decorators/current-user.decorator'; import { JwtAuthGuard } from '../../auth/guard/jwt-auth.guard'; -import { User } from '../schema/user.schema'; import { UserService } from '../service/user.service'; +import { User } from '../schema/user.schema'; @UseGuards(JwtAuthGuard) @WebSocketGateway() diff --git a/apps/api/src/shared/utils/get-client.ts b/apps/api/src/shared/utils/get-client.ts index c7e6778..4e3b4c6 100644 --- a/apps/api/src/shared/utils/get-client.ts +++ b/apps/api/src/shared/utils/get-client.ts @@ -1,13 +1,13 @@ import { ExecutionContext } from '@nestjs/common'; import { Dictionary } from 'code-config'; -import { Room } from '../../features/room/schema/room.schema'; +import { Sponsor } from '../../features/sponsors/schema/sponsor.schema'; import { Talk } from '../../features/talks/schema/talk.schema'; import { User } from '../../features/user/schema/user.schema'; export interface Client { headers: Dictionary; user: User; - room?: Room; + sponsor?: Sponsor; talk?: Talk; } diff --git a/libs/app/data-access/src/lib/+state/sponsor.facade.ts b/libs/app/data-access/src/lib/+state/sponsor.facade.ts index 0eba6f2..7e8f7c2 100644 --- a/libs/app/data-access/src/lib/+state/sponsor.facade.ts +++ b/libs/app/data-access/src/lib/+state/sponsor.facade.ts @@ -1,118 +1,126 @@ +import { DomSanitizer } from '@angular/platform-browser'; import { SponsorDataService } from '../infrastructure'; -import { Injectable } from '@angular/core'; +import { SponsorVM, SponsorRaw } from '../interfaces'; import { BaseState } from './base.state'; -import { Sponsor } from '../interfaces'; +import { Injectable } from '@angular/core'; +import { map } from 'rxjs/operators'; export interface SponsorState { loading: boolean; - publicSponsors: Sponsor[]; - memberSponsors: Sponsor[]; - userSponsors: Sponsor[]; - sponsor: Sponsor | null; + sponsors: SponsorVM[]; + sponsor: SponsorVM | null; } @Injectable() export class SponsorFacade extends BaseState { loading$ = this.select((state) => state.loading); - publicSponsors$ = this.select((state) => state.publicSponsors); - memberSponsors$ = this.select((state) => state.memberSponsors); - userSponsors$ = this.select((state) => state.userSponsors); + sponsors$ = this.select((state) => state.sponsors); sponsor$ = this.select((state) => state.sponsor); - constructor(private service: SponsorDataService) { + constructor( + private service: SponsorDataService, + private sanitizer: DomSanitizer + ) { super({ loading: false, - publicSponsors: [], - memberSponsors: [], - userSponsors: [], + sponsors: [], sponsor: null, }); } - createSponsor(sponsor: Partial) { - this.setState({ loading: true }); - this.service.createSponsor(sponsor).subscribe((sponsor) => { - this.setState({ - userSponsors: [...this.state.userSponsors, sponsor], - }); - this.setState({ loading: false }); - }); - } - loadUserSponsors() { this.setState({ loading: true }); - this.service.getUserSponsors().subscribe((userSponsors) => { - this.setState({ userSponsors }); - this.setState({ loading: false }); - }); - } - - loadPublicSponsors() { - this.setState({ loading: true }); - this.service.getPublicSponsors().subscribe((publicSponsors) => { - this.setState({ publicSponsors }); - this.setState({ loading: false }); - }); + this.service + .getUserSponsors() + .pipe(map((sponsors) => sponsors.map(this.mapTo))) + .subscribe((sponsors) => { + this.setState({ sponsors }); + this.setState({ loading: false }); + }); } - loadMemberSponsors() { + loadSponsors() { this.setState({ loading: true }); - this.service.getSponsorsByMember().subscribe((memberSponsors) => { - this.setState({ memberSponsors }); - this.setState({ loading: false }); - }); + this.service + .getSponsors() + .pipe(map((sponsors) => sponsors.map(this.mapTo))) + .subscribe((sponsors) => { + this.setState({ sponsors }); + this.setState({ loading: false }); + }); } - // loadSponsors() { - // this.setState({ loading: true }); - // this.service.getPublicSponsors().subscribe((sponsors) => { - // this.setState({ sponsors }); - // this.setState({ loading: false }); - // }); - // } - loadSponsor(id: string) { this.setState({ loading: true }); - this.service.getSponsor(id).subscribe((sponsor) => { - this.setState({ sponsor }); - this.setState({ loading: false }); - }); + this.service + .getSponsor(id) + .pipe(map(this.mapTo)) + .subscribe((sponsor) => { + this.setState({ sponsor }); + this.setState({ loading: false }); + }); } - updateSponsor(id: string, sponsor: Sponsor) { + updateSponsor(id: string, sponsor: SponsorRaw) { this.setState({ loading: true }); - this.service.updateSponsor(id, sponsor).subscribe((sponsor) => { - this.setState({ sponsor }); - this.setState({ loading: false }); - }); + this.service + .updateSponsor(id, sponsor) + .pipe(map(this.mapTo)) + .subscribe((sponsor) => { + this.setState({ sponsor }); + this.setState({ loading: false }); + }); } deleteSponsor(id: string) { this.setState({ loading: true }); - this.service.getSponsor(id).subscribe((sponsor) => { - this.setState({ sponsor }); - this.setState({ loading: false }); - }); + this.service + .getSponsor(id) + .pipe(map(this.mapTo)) + .subscribe((sponsor) => { + this.setState({ sponsor }); + this.setState({ loading: false }); + }); } leaveSponsor(id: string) { this.setState({ loading: true }); - this.service.leaveSponsor(id).subscribe((sponsor) => { - this.setState({ sponsor }); - this.setState({ loading: false }); - }); + this.service + .leaveSponsor(id) + .pipe(map(this.mapTo)) + .subscribe((sponsor) => { + this.setState({ sponsor }); + this.setState({ loading: false }); + }); } - subscribeSponsor(sponsor: Sponsor) { + subscribeSponsor(sponsor: SponsorRaw) { this.service.subscribeSponsor(sponsor); } joinSponsor(sponsorId: string) { this.setState({ loading: true }); - this.service.joinSponsor(sponsorId).subscribe((sponsor) => { - this.setState({ sponsor }); - this.setState({ loading: false }); - }); + this.service + .joinSponsor(sponsorId) + .pipe(map(this.mapTo)) + .subscribe((sponsor) => { + this.setState({ sponsor }); + this.setState({ loading: false }); + }); + } + + mapTo({ _id, calendlyUrl, formUrl, videoUrl, ...sponsor }: SponsorRaw) { + return { + ...sponsor, + id: _id, + calendlyUrl: this.sanitize(calendlyUrl), + videoUrl: this.sanitize(videoUrl), + formUrl: this.sanitize(formUrl), + }; + } + + private sanitize(url: string) { + return this.sanitizer.bypassSecurityTrustResourceUrl(url); } } diff --git a/libs/app/data-access/src/lib/infrastructure/sponsor-data.service.ts b/libs/app/data-access/src/lib/infrastructure/sponsor-data.service.ts index 1c0aba3..bfd5142 100644 --- a/libs/app/data-access/src/lib/infrastructure/sponsor-data.service.ts +++ b/libs/app/data-access/src/lib/infrastructure/sponsor-data.service.ts @@ -3,7 +3,7 @@ import { SocketService } from '../services/socket.service'; import { getEntityWithSortedMembers } from '../utils'; import { Inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Sponsor, User } from '../interfaces'; +import { SponsorRaw, User } from '../interfaces'; import { map } from 'rxjs/operators'; @Injectable({ @@ -19,50 +19,50 @@ export class SponsorDataService { getSponsor(sponsorId: string) { return this.http - .get(`${this.config.api}/sponsor/id/${sponsorId}`) + .get(`${this.config.api}/sponsor/id/${sponsorId}`) .pipe(map(getEntityWithSortedMembers)); } getPublicSponsors() { - return this.http.get(`${this.config.api}/sponsor/public`); + return this.http.get(`${this.config.api}/sponsor/public`); } getSponsorsByMember() { - return this.http.get(`${this.config.api}/sponsor/member`); + return this.http.get(`${this.config.api}/sponsor/member`); } getUserSponsors() { - return this.http.get(`${this.config.api}/sponsor`); + return this.http.get(`${this.config.api}/sponsor`); } getSponsors() { - return this.http.get(`${this.config.api}/sponsor`); + return this.http.get(`${this.config.api}/sponsor`); } - createSponsor(sponsor: Partial) { - return this.http.post(`${this.config.api}/sponsor`, sponsor); + createSponsor(sponsor: Partial) { + return this.http.post(`${this.config.api}/sponsor`, sponsor); } - deleteSponsor(sponsor: Sponsor) { + deleteSponsor(sponsor: SponsorRaw) { return this.http.delete(`${this.config.api}/sponsor/delete/${sponsor._id}`); } - updateSponsor(id: string, sponsor: Sponsor) { - return this.http.put(`${this.config.api}/sponsor/${id}`, sponsor); + updateSponsor(id: string, sponsor: SponsorRaw) { + return this.http.put(`${this.config.api}/sponsor/${id}`, sponsor); } joinSponsor(sponsorId: string) { return this.http - .post(`${this.config.api}/sponsor/join`, { sponsorId }) + .post(`${this.config.api}/sponsor/join`, { sponsorId }) .pipe(map(getEntityWithSortedMembers)); } leaveSponsor(sponsorId: string) { - return this.http.delete( + return this.http.delete( `${this.config.api}/sponsor/leave/${sponsorId}` ); } - subscribeSponsor(sponsor: Sponsor) { + subscribeSponsor(sponsor: SponsorRaw) { return this.socket.emit('sponsor:subscribe', sponsor._id); } @@ -76,11 +76,11 @@ export class SponsorDataService { onUpdateEvent() { return this.socket - .fromEvent('sponsor:update') + .fromEvent('sponsor:update') .pipe(map(getEntityWithSortedMembers)); } onDeleteEvent() { - return this.socket.fromEvent('sponsor:delete'); + return this.socket.fromEvent('sponsor:delete'); } } diff --git a/libs/app/data-access/src/lib/interfaces/sponsor.ts b/libs/app/data-access/src/lib/interfaces/sponsor.ts index 0051107..61313a4 100644 --- a/libs/app/data-access/src/lib/interfaces/sponsor.ts +++ b/libs/app/data-access/src/lib/interfaces/sponsor.ts @@ -1,10 +1,9 @@ import { User } from './user'; export interface Sponsor { - _id: string; name: string; description: string; - logo?: string; + logo: string; slug: string; color: string; website: string; @@ -12,3 +11,20 @@ export interface Sponsor { members: User[] | string[]; owner: User | string; } + +export interface SponsorRaw extends Sponsor { + _id: string; + calendlyUrl: string; + videoUrl: string; + formUrl: string; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface SafeUrl {} + +export interface SponsorVM extends Sponsor { + id: string; + calendlyUrl: SafeUrl; + videoUrl: SafeUrl; + formUrl: SafeUrl; +} diff --git a/apps/api/src/features/speakers/dto/create-speaker.dto.ts b/libs/app/data-access/src/lib/providers/sanitizer.provider.ts similarity index 100% rename from apps/api/src/features/speakers/dto/create-speaker.dto.ts rename to libs/app/data-access/src/lib/providers/sanitizer.provider.ts diff --git a/libs/app/feature-home/src/lib/pages/home-page/home-page.component.html b/libs/app/feature-home/src/lib/pages/home-page/home-page.component.html index 01e8e6d..b02b678 100644 --- a/libs/app/feature-home/src/lib/pages/home-page/home-page.component.html +++ b/libs/app/feature-home/src/lib/pages/home-page/home-page.component.html @@ -14,6 +14,8 @@ +
{{ facade.sponsors$ | async | json }}
+ Speak Out Group diff --git a/libs/app/feature-home/src/lib/pages/home-page/home-page.component.ts b/libs/app/feature-home/src/lib/pages/home-page/home-page.component.ts index 91b6423..606df1d 100644 --- a/libs/app/feature-home/src/lib/pages/home-page/home-page.component.ts +++ b/libs/app/feature-home/src/lib/pages/home-page/home-page.component.ts @@ -1,8 +1,18 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { SponsorFacade } from '@speak-out/app-data-access'; @Component({ templateUrl: './home-page.component.html', styleUrls: ['./home-page.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class HomePageComponent {} +export class HomePageComponent implements OnInit { + constructor(readonly facade: SponsorFacade ) { + + } + + ngOnInit() { + this.facade.loadSponsors(); + } + +} diff --git a/libs/app/feature-home/src/lib/pages/sponsor-page/sponsor-page.component.ts b/libs/app/feature-home/src/lib/pages/sponsor-page/sponsor-page.component.ts index 35811f5..fc59bb5 100644 --- a/libs/app/feature-home/src/lib/pages/sponsor-page/sponsor-page.component.ts +++ b/libs/app/feature-home/src/lib/pages/sponsor-page/sponsor-page.component.ts @@ -1,5 +1,5 @@ import { ActivatedRoute, Router } from '@angular/router'; -import { AwardFacade } from '@speak-out/app-data-access'; +import { AwardFacade, SponsorFacade } from '@speak-out/app-data-access'; import { Component, OnInit } from '@angular/core'; @Component({ @@ -9,6 +9,7 @@ import { Component, OnInit } from '@angular/core'; export class SponsorPageComponent implements OnInit { constructor( private route: ActivatedRoute, + readonly sponsorFacade: SponsorFacade, readonly facade: AwardFacade, private router: Router ) {}