feat(auth): Implement user signup functionality

This commit introduces a new feature for user signup. It creates `auth.controller.ts`, `auth.dto.ts`, and `auth.schema.ts` for handling user registration requests with validation. Moreover, it enhances `auth.service.ts` to handle the database interaction part and also modifies `auth.module.ts` by adding `AuthController`. Now, the application can handle user registration successfully with proper error handling.
This commit is contained in:
Mathis H (Avnyr) 2024-07-11 13:50:43 +02:00
parent 1990bedcfd
commit de3d1cca05
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
5 changed files with 173 additions and 13 deletions

View File

@ -0,0 +1,24 @@
import { Body, Controller, HttpCode, HttpStatus, Post } from "@nestjs/common";
import { SignUpDto } from "src/auth/auth.dto";
import { AuthService } from "src/auth/auth.service";
@Controller('auth')
export class AuthController {
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 signin
//GET me -- Get current user data via jwt
//DELETE me
//PATCH me
}

50
src/auth/auth.dto.ts Normal file
View File

@ -0,0 +1,50 @@
import {
IsEmail,
IsNotEmpty,
IsString,
IsStrongPassword,
MaxLength,
MinLength,
} from "class-validator";
export class SignUpDto {
@MinLength(1)
@MaxLength(24)
@IsNotEmpty()
@IsString()
firstName: string;
@MinLength(1)
@MaxLength(24)
@IsNotEmpty()
@IsString()
lastName: string;
@MaxLength(32)
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
@IsStrongPassword({
minLength: 6,
minSymbols: 1,
})
password: string;
}
export class SignInDto {
@MaxLength(32)
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
@IsStrongPassword({
minLength: 6,
minSymbols: 1,
})
password: string;
}

View File

@ -1,10 +1,12 @@
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { DrizzleModule } from "src/drizzle/drizzle.module";
import { AuthService } from "./auth.service";
import { CredentialsModule } from "src/credentials/credentials.module"; import { CredentialsModule } from "src/credentials/credentials.module";
import { DrizzleModule } from "src/drizzle/drizzle.module";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
@Module({ @Module({
imports: [DrizzleModule, CredentialsModule], imports: [DrizzleModule, CredentialsModule],
providers: [AuthService], providers: [AuthService],
controllers: [AuthController],
}) })
export class AuthModule {} export class AuthModule {}

31
src/auth/auth.schema.ts Normal file
View File

@ -0,0 +1,31 @@
import { UsersTableInsertSchema } from "src/schema";
import { z } from "zod";
export const SignUpBodySchema = z.object({
firstName: z.string({ message: "'firstName' should be a string." }).max(24),
lastName: z.string({ message: "'lastName' should be a string." }).max(24),
email: z
.string({ message: "'email' should be a string." })
.max(32, "'email' should be less than 32 characters")
.email("Of course 'email' should be an email."),
password: z
.string({ message: "'password' should be a string." })
.min(6)
//regex for strong password
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,32}$/,
)
.max(32),
});
export const SignInBodySchema = z.object({
email: z
.string({ message: "'email' should be a string." })
.max(32, "'email' should be less than 32 characters")
.email("Of course 'email' should be an email."),
password: z
.string({ message: "'password' should be a string." })
.min(6)
.max(32),
});

View File

@ -1,27 +1,80 @@
// biome-ignore lint/style/useImportType: used by Next.js import * as console from "node:console";
import { Injectable, OnModuleInit } from "@nestjs/common"; import {
// biome-ignore lint/style/useImportType: used by Next.js Injectable,
OnModuleInit,
UnauthorizedException,
} from "@nestjs/common";
import { eq } from "drizzle-orm";
import { SignUpBodySchema } from "src/auth/auth.schema";
import { CredentialsService } from "src/credentials/credentials.service";
import { DrizzleService } from "src/drizzle/drizzle.service"; import { DrizzleService } from "src/drizzle/drizzle.service";
import { UsersTable } from "src/schema"; import { UsersTable } from "src/schema";
// biome-ignore lint/style/useImportType: used by Next.js import { SignInDto, SignUpDto } from "src/auth/auth.dto";
import { CredentialsService } from "src/credentials/credentials.service";
@Injectable() @Injectable()
export class AuthService implements OnModuleInit { export class AuthService implements OnModuleInit {
constructor( constructor(
private db: DrizzleService, private db: DrizzleService,
private credentials: CredentialsService private credentials: CredentialsService,
) {} ) {}
doRegister() {} async doRegister(data: SignUpDto) {
doLogin() {} console.log(data);
const existingUser = await this.db
.use()
.select()
.from(UsersTable)
.where(eq(UsersTable.email, data.email))
.prepare("userByEmail")
.execute();
if (existingUser.length !== 0)
throw new UnauthorizedException("Already exist");
const query = await this.db
.use()
.insert(UsersTable)
.values({
firstName: data.firstName,
lastName: data.lastName,
email: data.email,
hash: await this.credentials.hash(data.password),
})
.returning()
.prepare("insertUser")
.execute()
.catch((err) => {
console.error(err);
throw new UnauthorizedException(
"Error occurred while inserting user",
err,
);
});
return {
message: "User created, check your email for validation.",
token: await this.credentials.signAuthToken({sub: query[0].uuid})
}
}
doLogin(data: SignInDto) {}
async fetchUser(userId: string) { async fetchUser(userId: string) {
const userInDb = await this.db.use().select().from(UsersTable); //TODO Pagination
console.log("Users : \n", userInDb); const usersInDb = await this.db.use().select().from(UsersTable);
const result = {
total: usersInDb.length,
users: usersInDb.map((user)=>{
delete user.hash
return {
...user
}
})
}
console.log(result)
} }
updateUser() {} updateUser() {}
deleteUser() {} deleteUser() {}
async onModuleInit() { async onModuleInit() {
await this.fetchUser("ee"); setTimeout(()=>{
this.fetchUser("ee");
}, 2000)
} }
} }