From cc5b59444744a79d5b9588180c0775171d39be89 Mon Sep 17 00:00:00 2001 From: Leoj030 Date: Tue, 2 Jun 2026 11:44:19 +0800 Subject: [PATCH] feat: add exception handling utility with common HTTP error responses --- src/index.luau | 37 +++++++++++++++++++--------- src/utils/Exception.luau | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 src/utils/Exception.luau diff --git a/src/index.luau b/src/index.luau index 5db15ed..fddc5ac 100644 --- a/src/index.luau +++ b/src/index.luau @@ -28,11 +28,9 @@ Nova.__index = Nova Nova.chain = require("@utils/Chain") Nova.response = require("@utils/Response") +Nova.exception = require("@utils/Exception") -local function devMiddleware(req: Types.Request, next: Types.Next) - local res = next() - local status = res.config.status or 200 - +local function statusLog(status: number, req) local statusColor = "\27[31m" if status >= 200 and status < 300 then statusColor = "\27[32m" @@ -47,6 +45,13 @@ local function devMiddleware(req: Types.Request, next: Types.Next) end end +local function devMiddleware(req: Types.Request, next: Types.Next) + local res = next() + local status = res.config.status or 200 + + statusLog(status, req) +end + function Nova.new(port: number, middlewares: { (req: Types.Request, next: Types.Next) -> () }?) local self = setmetatable({}, Nova) self.port = port @@ -120,20 +125,30 @@ function Nova:listen(handler: () -> ()) return FAVICON_CACHED_RESPONSE or FAVICON_EMPTY_RESPONSE :: net.NetResponse end - local success, result: Types.ResponsePayload | any = pcall(rootPipeline, request) - + local success, result: Types.ResponsePayload + | { statusCode: number, message: string } | any = pcall(rootPipeline, request) + if not success then - if isDev then + statusLog(result.statusCode or 500, request) + if typeof(result) == "table" then + return { + status = result.statusCode, + body = encode("json", { error = result.message }), + headers = { ["Content-Type"] = "application/json" } + } + else local errorMessage = debug.traceback(tostring(result), 2) - print("\27[31m[Error]: " .. errorMessage .. "\27[0m") + print("\27[31m[Error]: \n" .. errorMessage .. "\27[0m") + + return INTERNAL_ERROR_RESPONSE :: net.NetResponse end - return INTERNAL_ERROR_RESPONSE :: net.NetResponse end - - local status: number + + local status: number? local headers = nil if result.config then + status = result.config.status headers = result.config.headers end diff --git a/src/utils/Exception.luau b/src/utils/Exception.luau new file mode 100644 index 0000000..ad21756 --- /dev/null +++ b/src/utils/Exception.luau @@ -0,0 +1,52 @@ +local Exception = {} + +-- // BASE EXCEPTION FUNCTION // -- + +function Exception.HttpException(statusCode: number, message: string) + return { + statusCode = statusCode, + message = message + } +end + +-- // COMMON 4xx // -- + +function Exception.BadRequest(message: string?) + return Exception.HttpException(400, message or "Bad Request") +end + +function Exception.Unauthorized(message: string?) + return Exception.HttpException(401, message or "Unauthorized") +end + +function Exception.Forbidden(message: string?) + return Exception.HttpException(403, message or "Forbidden") +end + +function Exception.NotFound(message: string?) + return Exception.HttpException(404, message or "Not Found") +end + +function Exception.Conflict(message: string?) + return Exception.HttpException(409, message or "Conflict") +end + +function Exception.TooManyRequests(message: string?) + return Exception.HttpException(429, message or "Too Many Requests") +end + +-- // COMMON 5xx // -- + +function Exception.InternalServerError(message: string?) + return Exception.HttpException(500, message or "Internal Server Error") +end + +function Exception.BadGateway(message: string?) + return Exception.HttpException(502, message or "Bad Gateway") +end + +function Exception.ServiceUnavailable(message: string?) + return Exception.HttpException(503, message or "Service Unavailable") +end + +return Exception