diff --git a/routes.ts b/routes.ts index 5c97cdac..b894201e 100644 --- a/routes.ts +++ b/routes.ts @@ -161,6 +161,7 @@ export const rawRoutes = { "/users/[uuid]/outbox": await import( serverPath + "/users/[uuid]/outbox/index.ts" ), + "/[...404]": await import(serverPath + "/[...404].ts"), }; // Returns the route filesystem path when given a URL @@ -175,10 +176,13 @@ export const matchRoute = >(url: string) => { return { // @ts-expect-error TypeScript parses this as a defined object instead of an arbitrarily editable route file - file: rawRoutes[route.name] as Promise<{ - meta: APIRouteMeta; - default: RouteHandler; - }>, + file: rawRoutes[route.name] as Promise< + | { + meta: APIRouteMeta; + default: RouteHandler; + } + | undefined + >, matchedRoute: route, }; }; diff --git a/server.ts b/server.ts index 0504e5ed..814a577e 100644 --- a/server.ts +++ b/server.ts @@ -1,4 +1,4 @@ -import { jsonResponse } from "@response"; +import { errorResponse, jsonResponse } from "@response"; import { matches } from "ip-matching"; import { getFromRequest } from "~database/entities/User"; import type { ConfigManager, ConfigType } from "config-manager"; @@ -61,10 +61,22 @@ export const createServer = ( // There shouldn't be a performance hit after bundling right? const { matchRoute } = await import("~routes"); - const { file, matchedRoute } = matchRoute(req.url); + const { file: filePromise, matchedRoute } = matchRoute(req.url); - if (matchedRoute) { - const meta = (await file).meta; + const file = await filePromise; + + if (matchedRoute && file == undefined) { + await logger.log( + LogLevel.ERROR, + "Server", + `Route file ${matchedRoute.filePath} not found or not registered in the routes file` + ); + + return errorResponse("Route not found", 500); + } + + if (matchedRoute && file != undefined) { + const meta = file.meta; // Check for allowed requests if (!meta.allowedMethods.includes(req.method as any)) { @@ -116,9 +128,7 @@ export const createServer = ( }); } - return await ( - await file - ).default(req.clone(), matchedRoute, { + return await file.default(req.clone(), matchedRoute, { auth, configManager, parsedRequest, diff --git a/server/api/[...404].ts b/server/api/[...404].ts new file mode 100644 index 00000000..871e3a3d --- /dev/null +++ b/server/api/[...404].ts @@ -0,0 +1,21 @@ +import { apiRoute, applyConfig } from "@api"; +import { errorResponse } from "@response"; + +export const meta = applyConfig({ + allowedMethods: ["POST", "GET", "PUT", "PATCH", "DELETE"], + auth: { + required: false, + }, + ratelimits: { + duration: 60, + max: 100, + }, + route: "/[...404]", +}); + +/** + * Default catch-all route, returns a 404 error. + */ +export default apiRoute(() => { + return errorResponse("This API route does not exist", 404); +});