feat(auth): add user management endpoint and update guards
Introduce new "me" endpoints for fetching, updating, and deleting current user data. Update guards to properly inject dependent services using @Inject decorator for better DI handling.
This commit is contained in:
parent
5d24f36133
commit
27f79d4e46
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>('CredentialsService', UserGuard);
|
||||
this.databaseService = reflector.get<DrizzleService>('DrizzleService', UserGuard);
|
||||
constructor(
|
||||
@Inject(CredentialsService) private readonly credentialService: CredentialsService,
|
||||
@Inject(DrizzleService) private readonly databaseService: DrizzleService,
|
||||
) {
|
||||
}
|
||||
|
||||
async canActivate(
|
||||
context: ExecutionContext,
|
||||
context: ExecutionContext
|
||||
): Promise<boolean> {
|
||||
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<boolean> {
|
||||
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
|
||||
|
@ -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 {}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user