generated from azures04/Base-REST-API
Initial commit
This commit is contained in:
52
modules/loader.js
Normal file
52
modules/loader.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const fs = require("node:fs")
|
||||
const path = require("node:path")
|
||||
|
||||
function getRecursiveFiles(dir) {
|
||||
let results = []
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
return results
|
||||
}
|
||||
|
||||
const list = fs.readdirSync(dir)
|
||||
|
||||
for (const file of list) {
|
||||
const fullPath = path.join(dir, file)
|
||||
const stat = fs.statSync(fullPath)
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
results = results.concat(getRecursiveFiles(fullPath))
|
||||
} else {
|
||||
if (fullPath.endsWith(".js")) {
|
||||
results.push(fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
function computeRoutePath(baseDir, filePath) {
|
||||
const relativePath = path.relative(baseDir, filePath)
|
||||
let route = "/" + relativePath.split(path.sep).join("/")
|
||||
|
||||
if (route.endsWith(".js")) {
|
||||
route = route.slice(0, -3)
|
||||
}
|
||||
if (route.endsWith("/index")) {
|
||||
route = route.slice(0, -6)
|
||||
}
|
||||
|
||||
route = route.replace(/\[([^\]]+)\]/g, ":$1")
|
||||
|
||||
if (route === "") {
|
||||
return "/"
|
||||
}
|
||||
|
||||
return route
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getRecursiveFiles,
|
||||
computeRoutePath
|
||||
}
|
||||
89
modules/logger.js
Normal file
89
modules/logger.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const fs = require("node:fs")
|
||||
const path = require("node:path")
|
||||
const utils = require("./utils")
|
||||
require("colors")
|
||||
require("dotenv").config({
|
||||
quiet: true
|
||||
})
|
||||
|
||||
function cleanup($stream) {
|
||||
if (!$stream.destroyed) {
|
||||
$stream.end()
|
||||
}
|
||||
}
|
||||
|
||||
function write($stream, level, color, content, extraLabels = []) {
|
||||
const date = new Date().toISOString()
|
||||
const message = typeof content === "string" ? content : JSON.stringify(content, null, 2)
|
||||
|
||||
let consoleLabels = ""
|
||||
let fileLabels = ""
|
||||
|
||||
if (Array.isArray(extraLabels) && extraLabels.length > 0) {
|
||||
for (let i = 0; i < extraLabels.length; i += 2) {
|
||||
const labelName = extraLabels[i]
|
||||
const labelColor = extraLabels[i + 1]
|
||||
if (labelName) {
|
||||
fileLabels += ` [${labelName}]`
|
||||
if (labelColor && labelName[labelColor]) {
|
||||
consoleLabels += ` [${labelName[labelColor]}]`
|
||||
} else {
|
||||
consoleLabels += ` [${labelName.white}]`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[${date}] `.magenta + `[${level}]`[color] + consoleLabels + " " + message)
|
||||
$stream.write(`[${date}] [${level}]${fileLabels} ${stripColors(message)}\n`)
|
||||
}
|
||||
|
||||
function createLogger(root) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const fileName = utils.isTrueFromDotEnv("IS_PROD") ? new Date().toLocaleString("fr-FR", { timeZone: "UTC" }).replace(/[\/:]/g, "-").replace(/ /g, "_") : "DEV-LOG"
|
||||
|
||||
const logsDir = path.join(root, "logs")
|
||||
|
||||
if (!fs.existsSync(logsDir)) {
|
||||
fs.mkdirSync(logsDir, { recursive: true })
|
||||
}
|
||||
|
||||
const stream = fs.createWriteStream(path.join(logsDir, `${fileName}.log`), { flags: "a" })
|
||||
|
||||
process.on("exit", () => {
|
||||
cleanup(stream)
|
||||
})
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
cleanup(stream)
|
||||
process.exit()
|
||||
})
|
||||
|
||||
return {
|
||||
log: (content, labels) => {
|
||||
write(stream, "INFO", "green", content, labels)
|
||||
},
|
||||
error: (content, labels) => {
|
||||
write(stream, "ERROR", "red", content, labels)
|
||||
},
|
||||
warn: (content, labels) => {
|
||||
write(stream, "WARN", "yellow", content, labels)
|
||||
},
|
||||
debug: (content, labels) => {
|
||||
write(stream, "DEBUG", "white", content, labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stripColors(string) {
|
||||
if (!string || typeof string !== "string") {
|
||||
return string
|
||||
}
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return string.replace(/\x1B\[[0-9;]*[mK]/g, "")
|
||||
}
|
||||
|
||||
const logger = createLogger(process.cwd())
|
||||
|
||||
module.exports = logger
|
||||
35
modules/utils.js
Normal file
35
modules/utils.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const path = require("node:path")
|
||||
const Logger = require("./logger")
|
||||
const logger = Logger.createLogger(path.join(__dirname, ".."))
|
||||
|
||||
function sendValidationError(req, res, zodResult, type, path, errorConfig) {
|
||||
const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress
|
||||
logger.warn(`Validation failed for ${req.method.cyan} ${path.cyan.bold} (${type}) ` + `<IP:${ip}>`.bold, ["WEB", "yellow"])
|
||||
|
||||
const response = {
|
||||
success: false,
|
||||
message: errorConfig.message || `Validation failed in ${type}`,
|
||||
errors: zodResult.error.issues.map(e => ({
|
||||
field: e.path.join("."),
|
||||
message: e.message
|
||||
}))
|
||||
}
|
||||
|
||||
if (errorConfig) {
|
||||
const extras = { ...errorConfig }
|
||||
delete extras.status
|
||||
delete extras.message
|
||||
Object.assign(response, extras)
|
||||
}
|
||||
|
||||
return res.status(errorConfig.status || 400).json(response)
|
||||
}
|
||||
|
||||
function isTrueFromDotEnv(key) {
|
||||
return (process.env[key] || "").trim().toLowerCase() === "true"
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendValidationError,
|
||||
isTrueFromDotEnv
|
||||
}
|
||||
Reference in New Issue
Block a user