Add user GET endpoint, schemas, and security middlewares
Introduces GET /users/:id endpoint with validation schema, adds tests for user and register routes, and applies security middlewares (helmet, hpp, cors) to the server. Also adds ESLint configuration and updates logger with linting comments.
This commit is contained in:
parent
7e1eaf3f1f
commit
335aef34e3
@ -16,6 +16,6 @@ It features a **recursive file loader** for routes and schemas, along with a pow
|
|||||||
|
|
||||||
1. **Clone the repository**
|
1. **Clone the repository**
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitea.azures.fr/azures04/Base-REST-API.git
|
git clone https://gitea.azures.fr/azures04/Base-REST-API.git
|
||||||
cd Base-REST-API
|
cd Base-REST-API
|
||||||
```
|
```
|
||||||
28
eslint.config.js
Normal file
28
eslint.config.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const js = require("@eslint/js")
|
||||||
|
const globals = require("globals")
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
ignores: ["node_modules", "logs", "coverage", ".env", "*.log"],
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "commonjs",
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-unused-vars": "warn",
|
||||||
|
"no-undef": "error",
|
||||||
|
"eqeqeq": "error",
|
||||||
|
"indent": ["error", 4],
|
||||||
|
"quotes": ["error", "double"],
|
||||||
|
"semi": ["error", "never"],
|
||||||
|
"no-console": "warn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@ -33,11 +33,13 @@ function write($stream, level, color, content, extraLabels = []) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(`[${date}] `.magenta + `[${level}]`[color] + consoleLabels + " " + message)
|
console.log(`[${date}] `.magenta + `[${level}]`[color] + consoleLabels + " " + message)
|
||||||
$stream.write(`[${date}] [${level}]${fileLabels} ${stripColors(message)}\n`)
|
$stream.write(`[${date}] [${level}]${fileLabels} ${stripColors(message)}\n`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLogger(root) {
|
function createLogger(root) {
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
const fileName = (/false/).test(process.env.IS_PROD.toLowerCase()) ? new Date().toLocaleString("fr-FR", { timeZone: "UTC" }).replace(/[\/:]/g, "-").replace(/ /g, "_") : "DEV-LOG"
|
const fileName = (/false/).test(process.env.IS_PROD.toLowerCase()) ? new Date().toLocaleString("fr-FR", { timeZone: "UTC" }).replace(/[\/:]/g, "-").replace(/ /g, "_") : "DEV-LOG"
|
||||||
|
|
||||||
const logsDir = path.join(root, "logs")
|
const logsDir = path.join(root, "logs")
|
||||||
@ -77,6 +79,7 @@ function stripColors(string) {
|
|||||||
if (!string || typeof string !== "string") {
|
if (!string || typeof string !== "string") {
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
return string.replace(/\x1B\[[0-9;]*[mK]/g, "")
|
return string.replace(/\x1B\[[0-9;]*[mK]/g, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1085
package-lock.json
generated
1085
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,18 +17,25 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start:dev": "nodemon .",
|
"start:dev": "nodemon .",
|
||||||
"start": "node .",
|
"start": "node .",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitea.azures.fr/azures04/Base-REST-API",
|
"homepage": "https://gitea.azures.fr/azures04/Base-REST-API",
|
||||||
"readme": "https://gitea.azures.fr/azures04/Base-REST-API/src/branch/main/README.md",
|
"readme": "https://gitea.azures.fr/azures04/Base-REST-API/src/branch/main/README.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"helmet": "^8.1.0",
|
||||||
|
"hpp": "^0.2.3",
|
||||||
"path-to-regexp": "^8.3.0",
|
"path-to-regexp": "^8.3.0",
|
||||||
"zod": "^4.2.0"
|
"zod": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.2",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"globals": "^16.5.0",
|
||||||
"nodemon": "^3.1.11"
|
"nodemon": "^3.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,14 @@ const express = require("express")
|
|||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const registerService = require("../services/register")
|
const registerService = require("../services/register")
|
||||||
|
|
||||||
router.post("/", async (req, res, next) => {
|
router.post("/", async (req, res) => {
|
||||||
try {
|
const { email, username, password } = req.body
|
||||||
const { email, username, password } = req.body
|
const registerResult = registerService.register({ email, username, password })
|
||||||
const registerResult = registerService.register({ email, username, password })
|
return res.status(200).json({
|
||||||
return res.status(200).json({
|
code: 200,
|
||||||
code: 200,
|
message: "User successfully registered",
|
||||||
message: "User successfully registered",
|
data: registerResult
|
||||||
data: registerResult
|
})
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
next(error)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
@ -1,8 +1,17 @@
|
|||||||
const express = require("express")
|
const express = require("express")
|
||||||
|
const DefaultError = require("../../errors/DefaultError")
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
router.post("/", async (req, res, next) => {
|
router.get("", async (req, res) => {
|
||||||
|
const bearer = req.headers.authorization
|
||||||
|
if (bearer == "Bearer token") {
|
||||||
|
return res.status(200).json({
|
||||||
|
id: req.params.id,
|
||||||
|
username: "johndoe"
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new DefaultError(403, "Invalid token", "", "InvalidTokenException")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
@ -3,9 +3,6 @@ const z = require("zod")
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
POST: {
|
POST: {
|
||||||
headers: z.object({
|
headers: z.object({
|
||||||
authorization: z.string()
|
|
||||||
.startsWith("Bearer ", { message: "Token d'authentification manquant ou invalide." })
|
|
||||||
.optional(),
|
|
||||||
"content-type": z.string().regex(/application\/json/i).optional()
|
"content-type": z.string().regex(/application\/json/i).optional()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
|
|||||||
15
schemas/users/[id].js
Normal file
15
schemas/users/[id].js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const z = require("zod")
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
GET: {
|
||||||
|
headers: z.object({
|
||||||
|
authorization: z.string()
|
||||||
|
.startsWith("Bearer ", { message: "Token d'authentification manquant ou invalide." }),
|
||||||
|
"content-type": z.string().regex(/application\/json/i).optional()
|
||||||
|
}),
|
||||||
|
error: {
|
||||||
|
code: 422,
|
||||||
|
message: "Invalid request data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
const express = require("express")
|
const express = require("express")
|
||||||
|
const hpp = require("hpp")
|
||||||
const app = express()
|
const app = express()
|
||||||
|
const cors = require("cors")
|
||||||
const path = require("node:path")
|
const path = require("node:path")
|
||||||
|
const utils = require("./modules/utils")
|
||||||
const Logger = require("./modules/logger")
|
const Logger = require("./modules/logger")
|
||||||
const logger = Logger.createLogger(__dirname)
|
const logger = Logger.createLogger(__dirname)
|
||||||
const utils = require("./modules/utils")
|
const helmet = require("helmet")
|
||||||
const loader = require("./modules/loader")
|
const loader = require("./modules/loader")
|
||||||
const DefaultError = require("./errors/DefaultError")
|
const DefaultError = require("./errors/DefaultError")
|
||||||
const path2regex = require("path-to-regexp")
|
const path2regex = require("path-to-regexp")
|
||||||
@ -13,6 +16,10 @@ const schemas = loader.getRecursiveFiles(path.join(__dirname, "schemas"))
|
|||||||
|
|
||||||
const schemaRegistry = {}
|
const schemaRegistry = {}
|
||||||
|
|
||||||
|
app.use(hpp())
|
||||||
|
app.use(helmet())
|
||||||
|
app.use(cors({ origin: "*" }))
|
||||||
|
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.use(express.urlencoded({ extended: true }))
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
const crypto = require("node:crypto")
|
const crypto = require("node:crypto")
|
||||||
const DefaultError = require("../errors/DefaultError")
|
const DefaultError = require("../errors/DefaultError")
|
||||||
|
|
||||||
function register({ email, username, password }) {
|
function register({ email, username }) {
|
||||||
if (true) {
|
const canRegister = true
|
||||||
|
if (canRegister === true) {
|
||||||
return {
|
return {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
username: username,
|
username: username,
|
||||||
|
|||||||
8
tests/register.rest
Normal file
8
tests/register.rest
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
POST http://localhost:3000/register HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "johndoe@exemple.com",
|
||||||
|
"username": "johndoe",
|
||||||
|
"password": "Password123"
|
||||||
|
}
|
||||||
2
tests/users/id.rest
Normal file
2
tests/users/id.rest
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
GET http://localhost:3000/users/johndoe HTTP/1.1
|
||||||
|
authorization: Bearer token
|
||||||
Loading…
x
Reference in New Issue
Block a user