diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 1e8e598..a6a33a9 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,4 +1,4 @@ -import BaseError from "@/core/error/BaseError"; +import applicationJsonOnly from "@/core/utils/applicationJsonOnly"; import handleCatchApi from "@/core/utils/handleCatchApi"; import AuthError from "@/modules/auth/error/AuthError"; import signInSchema from "@/modules/auth/formSchemas/signInSchema"; @@ -9,13 +9,7 @@ export const dynamic = "force-dynamic"; export async function POST(request: NextRequest) { try { - if (request.headers.get("Content-Type") !== "application/json") - throw new BaseError({ - errorCode: "UNSUPPORTED_CONTENT_TYPE", - message: - "This content type is not supported. Please use application/json instead", - statusCode: 400 - }); + applicationJsonOnly(request.headers) const data = signInSchema.safeParse(await request.json()); if (!data.success){ diff --git a/src/app/api/register/route.ts b/src/app/api/register/route.ts new file mode 100644 index 0000000..e5a3207 --- /dev/null +++ b/src/app/api/register/route.ts @@ -0,0 +1,38 @@ +import BaseError from "@/core/error/BaseError"; +import applicationJsonOnly from "@/core/utils/applicationJsonOnly"; +import handleCatchApi from "@/core/utils/handleCatchApi"; +import { createUserSchema } from "@/modules/auth/formSchemas/CreateUserFormSchema"; +import createUser from "@/modules/auth/services/createUser"; +import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue"; +import { NextRequest, NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function POST(request: NextRequest) { + try { + applicationJsonOnly(request.headers); + const data = createUserSchema.safeParse(await request.json()); + + if (!data.success) { + throw new BaseError({ + errorCode: "INVALID_FORM_DATA", + message: "", + formErrors: mapObjectToFirstValue( + data.error.flatten().fieldErrors + ), + statusCode: 422, + }); + } + + const result = await createUser({ + email: data.data.email, + name: data.data.name, + password: data.data.password, + passwordConfirmation: data.data.passwordConfirmation ?? "", + }); + + return NextResponse.json(result); + } catch (e) { + return handleCatchApi(e); + } +} diff --git a/src/core/error/BaseError.ts b/src/core/error/BaseError.ts index 6e0fefc..595de4f 100644 --- a/src/core/error/BaseError.ts +++ b/src/core/error/BaseError.ts @@ -1,21 +1,24 @@ import logger from "../logger/Logger"; -export const BaseErrorCodes = ["UNKNOWN_ERROR", "UNSUPPORTED_CONTENT_TYPE"] as const; +export const BaseErrorCodes = ["UNKNOWN_ERROR", "UNSUPPORTED_CONTENT_TYPE", "INVALID_FORM_DATA"] as const; interface ErrorOptions { message?: string; errorCode: (typeof BaseErrorCodes)[number] | (string & {}); - statusCode?: number + statusCode?: number, + formErrors?: Record } class BaseError extends Error { public readonly errorCode: (typeof BaseErrorCodes)[number] | (string & {}); public readonly statusCode: number; + public readonly formErrors?: ErrorOptions['formErrors']; constructor(options: ErrorOptions) { super(options.message ?? "Undetermined Error"); this.errorCode = options.errorCode ?? "UNKNOWN_ERROR"; this.statusCode = options.statusCode ?? 500; + this.formErrors = options.formErrors; Object.setPrototypeOf(this, new.target.prototype); diff --git a/src/core/error/ClientError.ts b/src/core/error/ClientError.ts index e23488f..fa1b72e 100644 --- a/src/core/error/ClientError.ts +++ b/src/core/error/ClientError.ts @@ -11,7 +11,7 @@ interface ErrorOptions { class ClientError extends Error { public readonly errorCode: ErrorOptions['errorCode']; public readonly statusCode: ErrorOptions['statusCode']; - public readonly formErrors?: ErrorOptions['formErrors'] + public readonly formErrors?: ErrorOptions['formErrors']; constructor(options: ErrorOptions) { super(options.message ?? "Undetermined Error"); diff --git a/src/core/utils/applicationJsonOnly.ts b/src/core/utils/applicationJsonOnly.ts new file mode 100644 index 0000000..18496d1 --- /dev/null +++ b/src/core/utils/applicationJsonOnly.ts @@ -0,0 +1,11 @@ +import BaseError from "../error/BaseError"; + +export default function applicationJsonOnly(headers: Headers) { + if (headers.get("Content-Type") !== "application/json") + throw new BaseError({ + errorCode: "UNSUPPORTED_CONTENT_TYPE", + message: + "This content type is not supported. Please use application/json instead", + statusCode: 400, + }); +} diff --git a/src/core/utils/handleCatchApi.ts b/src/core/utils/handleCatchApi.ts index 76352ab..e0cb05f 100644 --- a/src/core/utils/handleCatchApi.ts +++ b/src/core/utils/handleCatchApi.ts @@ -6,6 +6,7 @@ export default function handleCatchApi(e: unknown): NextResponse { return NextResponse.json({ code: e.errorCode, message: e.message, + formErrors: e.formErrors }, {status: e.statusCode}); } if (e instanceof Error) { diff --git a/src/modules/auth/formSchemas/CreateUserFormSchema.ts b/src/modules/auth/formSchemas/CreateUserFormSchema.ts index 0885c2c..e3f44bb 100644 --- a/src/modules/auth/formSchemas/CreateUserFormSchema.ts +++ b/src/modules/auth/formSchemas/CreateUserFormSchema.ts @@ -6,8 +6,8 @@ import {z} from "zod" export interface CreateUserSchema { name: string; email: string; - plainPassword: string; - plainPasswordConfirmation: string; + password: string; + passwordConfirmation: string; } export const createUserSchema = z diff --git a/src/modules/auth/services/createUser.ts b/src/modules/auth/services/createUser.ts index e777464..d335465 100644 --- a/src/modules/auth/services/createUser.ts +++ b/src/modules/auth/services/createUser.ts @@ -1,4 +1,3 @@ -import DashboardError from "@/modules/dashboard/errors/DashboardError"; import { CreateUserSchema, createUserSchema, @@ -8,7 +7,7 @@ import db from "@/core/db"; import AuthError from "../error/AuthError"; import hashPassword from "../utils/hashPassword"; import { createJwtToken } from "../utils/createJwtToken"; -import { cookies } from "next/headers"; +import BaseError from "@/core/error/BaseError"; /** * Creates a new user in the database after validating the input data. @@ -25,11 +24,12 @@ export default async function createUser(userData: CreateUserSchema) { //Validate form input if (!validatedFields.success) { - throw new DashboardError({ + throw new BaseError({ errorCode: "INVALID_FORM_DATA", formErrors: mapObjectToFirstValue( validatedFields.error.flatten().fieldErrors ), + statusCode: 422, }); } @@ -42,6 +42,7 @@ export default async function createUser(userData: CreateUserSchema) { throw new AuthError({ errorCode: "USER_ALREADY_EXISTS", message: "This email already exists", + statusCode: 422, }); } @@ -59,5 +60,10 @@ export default async function createUser(userData: CreateUserSchema) { return { token, + user: { + name: user.name, + email: user.email, + profilePhotoUrl: user.photoProfile, + }, }; }