Added register
This commit is contained in:
parent
3e1fea085c
commit
839339ecbf
|
|
@ -1,11 +1,17 @@
|
|||
export enum BaseErrorCodes {
|
||||
INVALID_FORM_DATA = "INVALID_FORM_DATA"
|
||||
}
|
||||
|
||||
export default class BaseError extends Error {
|
||||
public readonly errorCode: string;
|
||||
public readonly statusCode: number;
|
||||
public readonly data: object;
|
||||
|
||||
constructor(message: string = "An unexpected error occurred", errorCode: string = "GENERIC_ERROR", statusCode: number = 500) {
|
||||
constructor(message: string = "An unexpected error occurred", errorCode: string = "GENERIC_ERROR", statusCode: number = 500, data: object = {}) {
|
||||
super(message); // Pass message to the Error parent class
|
||||
this.errorCode = errorCode;
|
||||
this.statusCode = statusCode;
|
||||
this.data = data;
|
||||
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import createUser from "@/features/auth/actions/createUser";
|
||||
import {
|
||||
Paper,
|
||||
PasswordInput,
|
||||
|
|
@ -11,7 +12,7 @@ import {
|
|||
Button,
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export interface RegisterFormSchema {
|
||||
email: string,
|
||||
|
|
@ -22,6 +23,8 @@ export interface RegisterFormSchema {
|
|||
|
||||
export default function RegisterPage() {
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("")
|
||||
|
||||
const form = useForm<RegisterFormSchema>({
|
||||
initialValues: {
|
||||
email: "",
|
||||
|
|
@ -31,19 +34,46 @@ export default function RegisterPage() {
|
|||
},
|
||||
validate: {
|
||||
email: (value: string) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
|
||||
password: (value: string) => (value.length > 6 ? null : 'Password should be at least 6 characters'),
|
||||
password: (value: string) => (value.length >= 6 ? null : 'Password should be at least 6 characters'),
|
||||
passwordConfirmation: (value: string, values: RegisterFormSchema) => value === values.password ? null : 'Passwords should match',
|
||||
name: (value: string) => (value.length > 0 ? null : 'Name is required'),
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmit = async (values: RegisterFormSchema) => {
|
||||
const formData = new FormData();
|
||||
Object.entries(values)
|
||||
.forEach(([key, value]) => {
|
||||
formData.append(key, value)
|
||||
});
|
||||
|
||||
const response = await createUser(formData);
|
||||
|
||||
if (!response.success){
|
||||
setErrorMessage(response.error.message);
|
||||
|
||||
if (response.error.errors){
|
||||
const errors = Object.entries(response.error.errors)
|
||||
.reduce((prev, [k,v]) => {
|
||||
prev[k] = v[0]
|
||||
return prev;
|
||||
}, {} as {[k: string]: string})
|
||||
|
||||
form.setErrors(errors)
|
||||
console.log(form.errors)
|
||||
} else {
|
||||
form.clearErrors()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-screen h-screen flex items-center justify-center">
|
||||
<Paper radius="md" p="xl" withBorder w={400}>
|
||||
<Text size="lg" fw={500} mb={30}>
|
||||
Register
|
||||
</Text>
|
||||
<form onSubmit={form.onSubmit(() => {})}>
|
||||
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label="Name"
|
||||
|
|
|
|||
0
src/app/dashboard/layout.tsx
Normal file
0
src/app/dashboard/layout.tsx
Normal file
|
|
@ -1,4 +1,3 @@
|
|||
import { auth } from '@/features/auth'
|
||||
import React from 'react'
|
||||
|
||||
export default async function Dashboard() {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export enum AuthErrorCode {
|
|||
}
|
||||
|
||||
export default class AuthError extends BaseError {
|
||||
constructor(errorCode: AuthErrorCode, statusCode = 500, message: string = "Authentication error") {
|
||||
super(message, errorCode, statusCode);
|
||||
constructor(errorCode: AuthErrorCode, {statusCode = 500, message, data}: Partial<{statusCode: number, message: string, data: object}> = {}) {
|
||||
super(message, errorCode, statusCode, data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,100 @@
|
|||
import prisma from "@/db"
|
||||
"use server"
|
||||
import { z } from "zod";
|
||||
import prisma from "@/db";
|
||||
import AuthError, { AuthErrorCode } from "../AuthError";
|
||||
import { hashPassword } from "../authUtils";
|
||||
import BaseError, { BaseErrorCodes } from "@/BaseError";
|
||||
import { createJwtToken, hashPassword } from "../authUtils";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
/**
|
||||
* Interface for the schema of a new user.
|
||||
*/
|
||||
interface CreateUserSchema {
|
||||
name: string,
|
||||
email: string,
|
||||
plainPassword: string,
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const register = async (inputData: CreateUserSchema) => {
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: {
|
||||
email: inputData.email
|
||||
/**
|
||||
* Validation schema for creating a user.
|
||||
*/
|
||||
const createUserSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
password: z.string().min(6),
|
||||
passwordConfirmation: z.string().optional(),
|
||||
}).refine(
|
||||
(data) => data.password === data.passwordConfirmation,
|
||||
{
|
||||
message: "Password confirmation must match the password",
|
||||
path: ["passwordConfirmation"],
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a new user in the system.
|
||||
*
|
||||
* @param formData - The form data containing user details.
|
||||
* @returns An object indicating the result of the operation.
|
||||
*/
|
||||
export default async function createUser(formData: FormData){
|
||||
//TODO: Add Throttling
|
||||
//TODO: Add validation check if the user is already logged in
|
||||
|
||||
try {
|
||||
const parsedData = {
|
||||
email: formData.get("email")?.toString() ?? '',
|
||||
name: formData.get("name")?.toString() ?? '',
|
||||
password: formData.get("password")?.toString() ?? '',
|
||||
passwordConfirmation: formData.get("passwordConfirmation")?.toString()
|
||||
};
|
||||
const validatedFields = createUserSchema.safeParse(parsedData);
|
||||
|
||||
if (!validatedFields.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: "",
|
||||
errors: validatedFields.error.flatten().fieldErrors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: { email: validatedFields.data.email },
|
||||
});
|
||||
|
||||
if (existingUser) throw new AuthError(AuthErrorCode.USER_ALREADY_EXISTS, 419, "Email already exists")
|
||||
if (existingUser){
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: "",
|
||||
errors: {
|
||||
email: ["Email already exists"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
name: inputData.name,
|
||||
email: inputData.email,
|
||||
passwordHash: await hashPassword(inputData.plainPassword)
|
||||
}
|
||||
name: validatedFields.data.name,
|
||||
email: validatedFields.data.email,
|
||||
passwordHash: await hashPassword(validatedFields.data.password),
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
const token = createJwtToken({ id: user.id });
|
||||
cookies().set("token", token);
|
||||
redirect("/dashboard");
|
||||
} catch (e: unknown) {
|
||||
// Handle unexpected errors
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: "An unexpected error occurred on the server. Please try again or contact the administrator.",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default register;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import AuthError, { AuthErrorCode } from "../AuthError";
|
|||
import { comparePassword, createJwtToken } from "../authUtils";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import BaseError from "@/BaseError";
|
||||
|
||||
/**
|
||||
* Handles the sign-in process for a user.
|
||||
|
|
@ -57,7 +58,7 @@ export default async function signIn(prevState: any, rawFormData: FormData) {
|
|||
redirect("/dashboard");
|
||||
} catch (e: unknown) {
|
||||
// Custom error handling for authentication errors
|
||||
if (e instanceof AuthError) {
|
||||
if (e instanceof BaseError) {
|
||||
// Specific error handling for known authentication errors
|
||||
switch (e.errorCode) {
|
||||
case AuthErrorCode.EMAIL_NOT_FOUND:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user