diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 063b82d..0b41d37 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,28 +1,74 @@ -import { Body, Controller, HttpCode, HttpStatus, Post } from "@nestjs/common"; -import { SignInDto, SignUpDto } from "src/auth/auth.dto"; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, Patch, + Post, + UnauthorizedException, + UseGuards +} from "@nestjs/common"; +import { SignInDto, SignUpDto, UpdateUserDto } from "src/auth/auth.dto"; import { AuthService } from "src/auth/auth.service"; -import * as console from "node:console"; +import { UserGuard } from "./auth.guard"; @Controller("auth") export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor(private readonly authService: AuthService) { } - //POST signup - @HttpCode(HttpStatus.CREATED) - @Post("signup") - async signUp(@Body() dto: SignUpDto) { - console.log(dto); - return this.authService.doRegister(dto); - } + //POST signup + @HttpCode(HttpStatus.CREATED) + @Post("signup") + async signUp(@Body() dto: SignUpDto) { + console.log(dto); + return this.authService.doRegister(dto); + } - //POST signin - @HttpCode(HttpStatus.OK) + //POST signin + @HttpCode(HttpStatus.OK) @Post("signin") async signIn(@Body() dto: SignInDto) { console.log(dto); return this.authService.doLogin(dto); } - //GET me -- Get current user data via jwt - //DELETE me - //PATCH me + //GET me -- Get current user data via jwt + @HttpCode(HttpStatus.OK) + @Get("me") + @UseGuards(UserGuard) + async getMe(@Body() body: object) { + // @ts-ignore + const targetId = body.sourceUserId + const userData = await this.authService.fetchUserById(targetId) + if (!userData) { + throw new UnauthorizedException(); + } + return userData; + } + //DELETE me + @HttpCode(HttpStatus.FOUND) + @Delete("me") + @UseGuards(UserGuard) + async deleteMe(@Body() body: object) { + // @ts-ignore + const targetId = body.sourceUserId + try { + await this.authService.deleteUser(targetId) + } catch (err) { + throw new UnauthorizedException(); + } + } + + + //PATCH me + @HttpCode(HttpStatus.OK) + @Patch("me") + @UseGuards(UserGuard) + async patchMe(@Body() body: UpdateUserDto) { + console.log(body); + // @ts-ignore + const targetId = body.sourceUserId; + await this.authService.updateUser(targetId, body); + return await this.authService.fetchUserById(targetId) + } } diff --git a/src/auth/auth.dto.ts b/src/auth/auth.dto.ts index faf1beb..5bc10ea 100644 --- a/src/auth/auth.dto.ts +++ b/src/auth/auth.dto.ts @@ -48,3 +48,17 @@ export class SignInDto { }) password: string; } + +export class UpdateUserDto { + @MinLength(1) + @MaxLength(24) + @IsNotEmpty() + @IsString() + firstName: string; + + @MinLength(1) + @MaxLength(24) + @IsNotEmpty() + @IsString() + lastName: string; +} diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 93422b6..c24bdf1 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -1,5 +1,5 @@ -import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; -import { Request } from 'express'; +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, Inject } from "@nestjs/common"; +import { Request } from "express"; import { CredentialsService } from "src/credentials/credentials.service"; import { DrizzleService } from "src/drizzle/drizzle.service"; import { UsersTable } from "src/schema"; @@ -8,37 +8,35 @@ import { Reflector } from "@nestjs/core"; @Injectable() export class UserGuard implements CanActivate { - private readonly credentialService: CredentialsService; - private readonly databaseService: DrizzleService; - constructor() { - const reflector = new Reflector(); - this.credentialService = reflector.get('CredentialsService', UserGuard); - this.databaseService = reflector.get('DrizzleService', UserGuard); + constructor( + @Inject(CredentialsService) private readonly credentialService: CredentialsService, + @Inject(DrizzleService) private readonly databaseService: DrizzleService, + ) { } async canActivate( - context: ExecutionContext, + context: ExecutionContext ): Promise { const request: Request = context.switchToHttp().getRequest(); const authHeader = request.headers.authorization; if (!authHeader) - throw new UnauthorizedException('No authorization header found.'); + throw new UnauthorizedException("No authorization header found."); - const token = authHeader.split(' ')[1]; - const vToken = await this.credentialService.verifyAuthToken(token) + const token = authHeader.split(" ")[1]; + const vToken = await this.credentialService.verifyAuthToken(token); const user = await this.databaseService.use() .select() .from(UsersTable) - .where(eq(UsersTable.uuid, vToken.payload.sub)) + .where(eq(UsersTable.uuid, vToken.payload.sub)); if (user.length !== 1) - throw new UnauthorizedException('No such user found.'); + throw new UnauthorizedException("No such user found."); if (user[0].emailCode) - throw new UnauthorizedException('Email not verified.'); + throw new UnauthorizedException("Email not verified."); // Inject user ID into request body request.body.sourceUserId = vToken.payload.sub; @@ -49,33 +47,34 @@ export class UserGuard implements CanActivate { @Injectable() export class AdminGuard implements CanActivate { - constructor( - private readonly credentialService: CredentialsService, - private readonly databaseService: DrizzleService - ) {} + constructor( + @Inject(CredentialsService) private readonly credentialService: CredentialsService, + @Inject(DrizzleService) private readonly databaseService: DrizzleService, + ) {} async canActivate( - context: ExecutionContext, + context: ExecutionContext ): Promise { const request: Request = context.switchToHttp().getRequest(); + const authHeader = request.headers.authorization; + if (!authHeader) { + throw new UnauthorizedException("No authorization header found."); - if (!authHeader) - throw new UnauthorizedException('No authorization header found.'); - - const token = authHeader.split(' ')[1]; - const vToken = await this.credentialService.verifyAuthToken(token) + } + const token = authHeader.split(" ")[1]; + const vToken = await this.credentialService.verifyAuthToken(token); const user = await this.databaseService.use() .select() .from(UsersTable) - .where(eq(UsersTable.uuid, vToken.payload.sub)) + .where(eq(UsersTable.uuid, vToken.payload.sub)); if (user.length !== 1) - throw new UnauthorizedException('No such user found.'); + throw new UnauthorizedException("No such user found."); if (!user[0].isAdmin) { - throw new UnauthorizedException('Administrator only..'); + throw new UnauthorizedException("Administrator only.."); } // Inject user ID into request body diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index a384b07..087574e 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -3,11 +3,12 @@ import { CredentialsModule } from "src/credentials/credentials.module"; import { DrizzleModule } from "src/drizzle/drizzle.module"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; -import { AdminGuard } from "src/auth/auth.guard"; +import { AdminGuard, UserGuard } from "src/auth/auth.guard"; +import { CredentialsService } from "src/credentials/credentials.service"; @Module({ imports: [DrizzleModule, CredentialsModule], - providers: [AuthService, AdminGuard], + providers: [AuthService, CredentialsService, AdminGuard, UserGuard], controllers: [AuthController], }) export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 2c241ae..eee5e42 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -75,7 +75,23 @@ export class AuthService implements OnModuleInit { }; } - async fetchUser() { + async fetchUserById(userId: string) { + const user = await this.db + .use() + .select() + .from(UsersTable) + .where(eq(UsersTable.uuid, userId)) + .prepare("userById") + .execute(); + if (user.length !== 1) { + throw new UnauthorizedException("User not found"); + } + delete user[0].hash; + delete user[0].emailCode; + return user[0]; + } + + async fetchUsers() { //TODO Pagination const usersInDb = await this.db.use().select().from(UsersTable); const result = { @@ -134,7 +150,7 @@ export class AuthService implements OnModuleInit { async onModuleInit() { setTimeout(() => { - this.fetchUser(); + this.fetchUsers(); }, 2000); } }