diff --git a/back/volume/src/api/api.module.ts b/back/volume/src/api/api.module.ts deleted file mode 100644 index e8e762b..0000000 --- a/back/volume/src/api/api.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Module } from '@nestjs/common' -import { ApiController } from './api.controller' - -@Module({ - controllers: [ApiController] -}) -export class ApiModule {} diff --git a/back/volume/src/api/api.controller.ts b/back/volume/src/app.controller.ts similarity index 72% rename from back/volume/src/api/api.controller.ts rename to back/volume/src/app.controller.ts index 6d71651..0d28677 100644 --- a/back/volume/src/api/api.controller.ts +++ b/back/volume/src/app.controller.ts @@ -1,14 +1,14 @@ import { Controller, Get, Redirect, Req, UseGuards } from '@nestjs/common' -import { User } from 'src/auth/42.decorator' -import { AuthenticatedGuard } from 'src/auth/42-auth.guard' -import { Profile } from 'passport-42' import { Request } from 'express' +import { Profile } from 'passport-42' +import { FtUser } from './auth/42.decorator' +import { AuthenticatedGuard } from './auth/42-auth.guard' @Controller() -export class ApiController { +export class AppController { @Get('profile') @UseGuards(AuthenticatedGuard) - profile (@User() user: Profile) { + profile (@FtUser() user: Profile) { return { user } } diff --git a/back/volume/src/app.module.ts b/back/volume/src/app.module.ts index 87da858..d4f94ed 100644 --- a/back/volume/src/app.module.ts +++ b/back/volume/src/app.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' import * as Joi from 'joi' -import { ApiModule } from './api/api.module' +import { AppController } from './app.controller' import { AuthModule } from './auth/auth.module' import { ChatModule } from './chat/chat.module' import { DbModule } from './db/db.module' @@ -23,12 +23,12 @@ import { UsersModule } from './users/users.module' JWT_EXPIRATION_TIME: Joi.string().required() }) }), - ApiModule, AuthModule, ChatModule, DbModule, PongModule, UsersModule - ] + ], + controllers: [AppController] }) -export class AppModule {} +export class AppModule { } diff --git a/back/volume/src/auth/42.decorator.ts b/back/volume/src/auth/42.decorator.ts index 75f0a0e..8436198 100644 --- a/back/volume/src/auth/42.decorator.ts +++ b/back/volume/src/auth/42.decorator.ts @@ -1,7 +1,7 @@ import { createParamDecorator, type ExecutionContext } from '@nestjs/common' import { type Profile } from 'passport-42' -export const User = createParamDecorator( +export const FtUser = createParamDecorator( (data: unknown, ctx: ExecutionContext): Profile => { const request = ctx.switchToHttp().getRequest() return request.user diff --git a/back/volume/src/auth/requestWithUser.interface.ts b/back/volume/src/auth/requestWithUser.interface.ts deleted file mode 100644 index 8836e11..0000000 --- a/back/volume/src/auth/requestWithUser.interface.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { type Request } from 'express' -import type User from 'src/users/user.entity' - -interface RequestWithUser extends Request { - user: User -} - -export default RequestWithUser diff --git a/back/volume/src/users/user.dto.ts b/back/volume/src/users/user.dto.ts index f5cd8a7..76370b6 100644 --- a/back/volume/src/users/user.dto.ts +++ b/back/volume/src/users/user.dto.ts @@ -1,9 +1,6 @@ import { IsString, - IsNumber, IsNotEmpty, - IsEmail, - Length, IsPositive, IsOptional } from 'class-validator' @@ -11,17 +8,7 @@ import { import { ApiProperty } from '@nestjs/swagger' import { Express } from 'express' -export class CreateUserDto { - @IsPositive() - @IsNotEmpty() - readonly id_42: number - - @IsString() - @IsNotEmpty() - readonly username: string -} - -export class UpdateUserDto { +export class UserDto { @IsPositive() @IsNotEmpty() readonly id_42: number diff --git a/back/volume/src/users/user.entity.ts b/back/volume/src/users/user.entity.ts index 58429e6..bdda256 100644 --- a/back/volume/src/users/user.entity.ts +++ b/back/volume/src/users/user.entity.ts @@ -2,10 +2,9 @@ import { Entity, PrimaryGeneratedColumn, Column, - OneToOne, OneToMany, ManyToMany, - JoinColumn + JoinTable } from 'typeorm' import Message from 'src/chat/model/message.entity' @@ -13,6 +12,9 @@ import Channel from 'src/chat/model/channel.entity' @Entity() export class User { + @PrimaryGeneratedColumn() + id: number; + @Column({ unique: true }) id_42: number @@ -32,9 +34,17 @@ export class User { rooms: Channel[] @ManyToMany(() => User) + @JoinTable() blocked: User[] - @ManyToMany(() => User) + @ManyToMany(() => User, (user) => user.following) + @JoinTable() + followers: User[] + + @ManyToMany(() => User, (user) => user.followers) + following: User[] + + @ManyToMany(() => User, (user) => user.friends) friends: User[] // @Column({ default: { wr: -1, place: -1 } }) diff --git a/back/volume/src/users/users.controller.ts b/back/volume/src/users/users.controller.ts index 3fc7743..44c2b65 100644 --- a/back/volume/src/users/users.controller.ts +++ b/back/volume/src/users/users.controller.ts @@ -19,36 +19,50 @@ import { diskStorage } from 'multer' import { type User } from './user.entity' import { UsersService } from './users.service' -import { CreateUserDto, UpdateUserDto, AvatarUploadDto } from './user.dto' +import { UserDto, AvatarUploadDto } from './user.dto' -import RequestWithUser from 'src/auth/requestWithUser.interface' -import { FtOauthGuard } from 'src/auth/42-auth.guard' -import { ApiBody, ApiConsumes } from '@nestjs/swagger' +import { AuthenticatedGuard } from 'src/auth/42-auth.guard' +import { FtUser } from 'src/auth/42.decorator' +import { Profile } from 'passport-42' -import { Response } from 'express' +import { ApiBody, ApiConsumes } from '@nestjs/swagger' +import { Request, Response } from 'express' import { createReadStream } from 'fs' import { join } from 'path' @Controller('users') export class UsersController { - constructor (private readonly usersService: UsersService) {} + constructor(private readonly usersService: UsersService) { } @Get() - async getAllUsers (): Promise { + async getAllUsers(): Promise { return await this.usersService.getAllUsers() } @Post() - async create (@Body() payload: CreateUserDto) { - return await this.usersService.create(payload) + @UseGuards(AuthenticatedGuard) + async create( + @Body() payload: UserDto, + @FtUser() profile: Profile) { + const user = await this.usersService.getOneUser42(profile.id); + if (user) { + return await this.usersService.update(user.id, payload) + } else { + return await this.usersService.create(payload) + } } - @Post(':id') - update (@Param('id', ParseIntPipe) id: number, @Body() user: UpdateUserDto) { - this.usersService.update(id, user) + @Post("follow/:target") + @UseGuards(AuthenticatedGuard) + followUser( + @FtUser() profile: Profile, + @Param('target, ParseIntPipe') target: number, + ) { + this.usersService.follow(profile.id, target); } - @Post(':id/avatar') + @Post('avatar') + @UseGuards(AuthenticatedGuard) @UseInterceptors( FileInterceptor('avatar', { storage: diskStorage({ @@ -68,19 +82,19 @@ export class UsersController { description: 'A new avatar for the user', type: AvatarUploadDto }) - async addAvatar ( - @Param('id', ParseIntPipe) id: number, + async addAvatar( + @FtUser() profile: Profile, @UploadedFile() file: Express.Multer.File ) { - await this.usersService.addAvatar(id, file.filename) + await this.usersService.addAvatar(profile.id, file.filename) } - @Get(':id/avatar') - async getAvatar ( - @Param('id', ParseIntPipe) id: number, + @Get('avatar') + async getAvatar( + @FtUser() profile: Profile, @Res({ passthrough: true }) response: Response ) { - const user = await this.usersService.findOne(id) + const user = await this.usersService.getOneUser42(profile.id) const filename = user.avatar const stream = createReadStream(join(process.cwd(), 'avatars/' + filename)) response.set({ diff --git a/back/volume/src/users/users.service.ts b/back/volume/src/users/users.service.ts index 5b5eed5..2d71898 100644 --- a/back/volume/src/users/users.service.ts +++ b/back/volume/src/users/users.service.ts @@ -2,7 +2,7 @@ import { Injectable, NotFoundException } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' import { Repository } from 'typeorm' import { User } from './user.entity' -import { type CreateUserDto, type UpdateUserDto } from './user.dto' +import { UserDto } from './user.dto' import { type Channel } from 'src/chat/model/channel.entity' @Injectable() @@ -23,7 +23,7 @@ export class UsersService { return await this.usersRepository.findOneBy({ id_42 }) } - async create (userData: CreateUserDto) { + async create (userData: UserDto) { try { const newUser = this.usersRepository.create(userData) return await this.usersRepository.save(newUser) @@ -46,7 +46,7 @@ export class UsersService { .getMany() } - async update (id: number, changes: UpdateUserDto) { + async update (id: number, changes: UserDto) { const updatedUser = await this.findOne(id) this.usersRepository.merge(updatedUser, changes) return await this.usersRepository.save(updatedUser) @@ -57,4 +57,9 @@ export class UsersService { avatar: filename }) } + + async follow(userFtId: number, targetFtId: number) { + const user = await this.getOneUser42(userFtId); + const target = await this.getOneUser42(targetFtId); + } }