Spaces:
Running
Running
import { Router, Express } from "express"; | |
import * as glob from "glob"; | |
import path from "path"; | |
import swaggerUi from "swagger-ui-express"; | |
import { BaseController } from "./lib/controllers/controller.base"; | |
import { validationErrorHandler } from "./helpers/validation.helper"; | |
import { JsonResponse } from "@lib/responses/json-response"; | |
import { errorHandlerMiddleware } from "middlewares/error-handler.middleware"; | |
import { swaggerRegistry } from "@lib/swagger/swagger"; | |
/** | |
* Sets the routes for the Express app. | |
* | |
* @param app - The Express app. | |
*/ | |
export const setAppRoutes = async (app: Express) => { | |
const mainRouter = Router(); | |
await importControllers(mainRouter); | |
await setCustomRoutes(mainRouter); | |
app.use("/api/v1", mainRouter); | |
}; | |
/* custom routes */ | |
/** | |
* Sets custom routes for the router. | |
* | |
* @param router - The router object to set the routes on. | |
*/ | |
const setCustomRoutes = async (router: Router) => { | |
// Health check route | |
router.get("/health", (_req: any, res: any) => { | |
JsonResponse.success( | |
{ | |
message: "Server is up!", | |
data: { success: true }, | |
}, | |
res | |
); | |
}); | |
// Validation error handler | |
router.use(validationErrorHandler); | |
// docs | |
router.use( | |
"/docs", | |
swaggerUi.serve, | |
swaggerUi.setup(swaggerRegistry.generateSwaggerDocument(), { | |
customCss: ".swagger-ui .topbar { display: none }", | |
customSiteTitle: "API Documentation", | |
swaggerOptions: { | |
layout: "StandaloneLayout", | |
deepLinking: true, | |
docExpansion: "none", | |
filter: true, | |
tagsSorter: "alpha", | |
operationsSorter: "alpha", | |
showMutatedRequest: true, | |
showMutatedResponse: true, | |
showRequestDuration: true, | |
persistAuthorization: true, | |
displayRequestDuration: true, | |
withCredentials: true, | |
}, | |
}) | |
); | |
// Invalid URL handler | |
router.all("*", (req: any, res: any) => { | |
JsonResponse.error( | |
{ | |
error: "Invalid URL!", | |
status: 404, | |
}, | |
res | |
); | |
}); | |
// Error handler | |
router.use(errorHandlerMiddleware); | |
}; | |
/* importing all controllers */ | |
/** | |
* Finds all controller files in the project. | |
* @returns An array of strings representing the absolute paths of the controller files. | |
*/ | |
const findControllerFiles = (): string[] => { | |
const controllersPath = path | |
.relative(process.cwd(), path.join(__dirname, "**/*.controller.{ts,js}")) | |
.replace(/\\/g, "/"); | |
return glob.sync(controllersPath, {}).map((file) => { | |
return path.resolve(file); | |
}); | |
}; | |
/** | |
* Imports controller classes from files, sets up routes for each controller, | |
* and adds them to the provided router. | |
* | |
* @param router - The router to add the routes to. | |
*/ | |
const importControllers = async (router: Router) => { | |
const files = findControllerFiles(); | |
console.log("importing controllers..."); | |
await Promise.all( | |
files.map(async (file) => { | |
const controllerClass = await importController(file); | |
if (!controllerClass) return; | |
const controller: BaseController = new (controllerClass as any)(); | |
controller.setRoutes(); | |
router.use(controller.prefix, controller.router); | |
}) | |
); | |
console.log("controllers imported!"); | |
}; | |
/** | |
* Imports a module from a file and returns the first controller that extends BaseController. | |
* @param file - The path to the file containing the module. | |
* @returns The first controller that extends BaseController. | |
*/ | |
const importController = async (file: string) => { | |
const controllers = Object.values(await import(file)); | |
return controllers.find( | |
(controller: { prototype: typeof BaseController }) => | |
controller.prototype instanceof BaseController | |
) as typeof BaseController; | |
}; | |