101 lines
3.3 KiB
JavaScript
101 lines
3.3 KiB
JavaScript
const express = require("express")
|
|
const app = express()
|
|
const path = require("node:path")
|
|
const Logger = require("./modules/logger")
|
|
const logger = Logger.createLogger(__dirname)
|
|
const loader = require("./modules/loader")
|
|
|
|
const routes = loader.getRecursiveFiles(path.join(__dirname, "routes"))
|
|
const schemas = loader.getRecursiveFiles(path.join(__dirname, "schemas"))
|
|
|
|
const schemaRegistry = {}
|
|
|
|
app.use(express.json())
|
|
app.use(express.urlencoded({ extended: true }))
|
|
|
|
app.set("trust proxy", true)
|
|
|
|
logger.log("Initializing routes", ["WEB", "yellow"])
|
|
|
|
for (const schemaFile of schemas) {
|
|
try {
|
|
const schemaConfig = require(schemaFile)
|
|
const routePath = loader.computeRoutePath(path.join(__dirname, "schemas"), schemaFile)
|
|
schemaRegistry[routePath] = schemaConfig
|
|
|
|
logger.log(`${routePath.cyan.bold} schema loaded in memory`, ["WEB", "yellow"])
|
|
} catch (error) {
|
|
logger.error(error.toString(), ["WEB", "yellow"])
|
|
}
|
|
}
|
|
|
|
app.all(/.*/, (req, res, next) => {
|
|
let currentPath = req.path
|
|
if (currentPath.length > 1 && currentPath.endsWith("/")) {
|
|
currentPath = currentPath.slice(0, -1)
|
|
}
|
|
|
|
const schemaConfig = schemaRegistry[currentPath]
|
|
if (!schemaConfig || !schemaConfig[req.method]) {
|
|
return next()
|
|
}
|
|
|
|
const methodConfig = schemaConfig[req.method]
|
|
const zodSchema = methodConfig.zod || methodConfig
|
|
const errorConfig = methodConfig.error || { status: 400, message: "Validation Error" }
|
|
const dataToValidate = (req.method === "GET" || req.method === "DELETE") ? req.query : req.body
|
|
const result = zodSchema.safeParse(dataToValidate)
|
|
|
|
if (result.success) {
|
|
if (req.method === "GET" || req.method === "DELETE") {
|
|
req.query = result.data
|
|
} else {
|
|
req.body = result.data
|
|
}
|
|
return next()
|
|
}
|
|
|
|
const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress
|
|
logger.warn(`Validation failed for ${req.method.cyan} ${currentPath.cyan.bold} ` + `<IP:${ip}>`.bold, ["WEB", "yellow"])
|
|
|
|
const response = {
|
|
success: false,
|
|
message: errorConfig.message,
|
|
errors: result.error.issues.map(e => ({
|
|
field: e.path.join("."),
|
|
message: e.message
|
|
}))
|
|
}
|
|
|
|
if (methodConfig.error) {
|
|
const extras = { ...methodConfig.error }
|
|
delete extras.status
|
|
delete extras.message
|
|
Object.assign(response, extras)
|
|
}
|
|
|
|
return res.status(errorConfig.status || 400).json(response)
|
|
})
|
|
|
|
for (const route of routes) {
|
|
try {
|
|
const router = require(route)
|
|
const routePath = loader.computeRoutePath(path.join(__dirname, "routes"), route)
|
|
if (router.stack) {
|
|
for (const layer of router.stack) {
|
|
if (layer.route && layer.route.methods) {
|
|
const method = Object.keys(layer.route.methods).join(", ").toUpperCase()
|
|
const subPath = routePath === "/" ? "" : routePath
|
|
logger.log(`${method.cyan} ${subPath.cyan.bold} route registered`, ["WEB", "yellow"])
|
|
}
|
|
}
|
|
}
|
|
app.use(routePath, router)
|
|
} catch (error) {
|
|
logger.error(error.toString(), ["WEB", "yellow"])
|
|
}
|
|
}
|
|
|
|
app.listen(process.env.WEB_PORT || 3000, () => {
|
|
logger.log(`Server listening at port : ${process.env.WEB_PORT || 3000}`, ["WEB", "yellow"])
|
|
}) |