diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..78c38dc 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -2,7 +2,10 @@ // for information about these interfaces declare global { namespace App { - // interface Error {} + interface Error { + message: string + cause?: number, + } // interface Locals {} // interface PageData {} // interface PageState {} diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 7a31215..a80b6e2 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -4,3 +4,22 @@ export class ArgumentError extends Error { super(message) } } + +export class DuplicateError extends Error { + fields: Array + + constructor(fields: Array, message: string) { + super(message) + this.fields = fields + } +} + +export const enum RegisterResponseCause { + Server = 1, + MalformedRequest, + EmailLength, + EmailFormat, + EmailDuplicate, + PasswordLength, + DisplayNameLength +} diff --git a/src/lib/server/log.ts b/src/lib/server/log.ts index d116111..3eb90ce 100644 --- a/src/lib/server/log.ts +++ b/src/lib/server/log.ts @@ -10,7 +10,8 @@ export enum LogSeverity { export enum LogModule { PROCESS, - USER + USER, + DATABASE } abstract class Logger { diff --git a/src/lib/server/usermgmt.ts b/src/lib/server/usermgmt.ts index 7299801..b885797 100644 --- a/src/lib/server/usermgmt.ts +++ b/src/lib/server/usermgmt.ts @@ -1,9 +1,10 @@ import Bun from "bun" -import { ArgumentError } from "$lib/errors" +import { ArgumentError, DuplicateError } from "$lib/errors" import Log from "$lib/server/log" import db from "$lib/server/database" +import { Prisma } from "@prisma/client" class UserMgmt { @@ -11,19 +12,29 @@ class UserMgmt { if (email.length == 0 || password.length == 0 || display_name.length == 0) { throw new ArgumentError("No field may be empty") } + + try { + const user = await db.user.create({ + data: { + email: email, + password_hash: await Bun.password.hash(password, "argon2id"), + display_name: display_name + } + }) - const user = await db.user.create({ - data: { - email: email, - password_hash: await Bun.password.hash(password, "argon2id"), - display_name: display_name + Log.info(Log.module.USER, `Created user with id ${user.id}`) + console.log(user) + + return user; + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code == "P2002") { + const duplicates = Array.isArray(e.meta?.target) ? e.meta.target as Array : ["unknown"] + throw new DuplicateError(duplicates, "Already exists") } - }) - Log.info(Log.module.USER, `Created user with id ${user.id}`) - console.log(user) - - return user; + Log.error(Log.module.DATABASE, `Failed to create User in database! Error: ${JSON.stringify(e)}`) + return null + } } async login(email: string, password: string) { diff --git a/src/routes/api/register/+server.ts b/src/routes/api/register/+server.ts index b1949e1..9380627 100644 --- a/src/routes/api/register/+server.ts +++ b/src/routes/api/register/+server.ts @@ -5,6 +5,7 @@ import { json, error } from "@sveltejs/kit" import UserMgmt from "$lib/server/usermgmt" import Util from "$lib/util" +import { DuplicateError, RegisterResponseCause } from "$lib/errors" export const POST: RequestHandler = async ({ request }) => { @@ -15,26 +16,32 @@ export const POST: RequestHandler = async ({ request }) => { const display_name = data.get("display_name") if (data.keys.length > 3 || !(typeof email === "string" && typeof password === "string" && typeof display_name === "string")) { - return error(400, { message: "Invalid field arguments" }) + return error(400, { cause: RegisterResponseCause.MalformedRequest, message: "Invalid field arguments" }) } if (email.length == 0) { - return error(400, { message: "email must not be empty" }) + return error(400, { cause: RegisterResponseCause.EmailLength, message: "email must not be empty" }) } if (password.length == 0) { - return error(400, { message: "password must not be empty" }) + return error(400, { cause: RegisterResponseCause.PasswordLength, message: "password must not be empty" }) } if (display_name.length == 0) { - return error(400, { message: "Display name must not be empty" }) + return error(400, { cause: RegisterResponseCause.DisplayNameLength, message: "Display name must not be empty" }) } if (!Util.check_email_format(email)) { - return error(400, { message: "invalid email format" }) + return error(400, { cause: RegisterResponseCause.EmailFormat, message: "invalid email format" }) } try { - const user = UserMgmt.register(email, password, display_name) + const user = await UserMgmt.register(email, password, display_name) } catch (e) { - return error(500) + if (e instanceof DuplicateError) { + if (e.fields.includes("email")) { + return error(409, { cause: RegisterResponseCause.EmailDuplicate, message: "email already in use" }) + } + } + + return error(500, { cause: RegisterResponseCause.Server, message: "Server failed to create user"}) } return json({})