From b8af5f9860ce470018fa8c17cf756172c83add69 Mon Sep 17 00:00:00 2001 From: Kai Ritthaler Date: Fri, 9 May 2025 21:08:38 +0200 Subject: [PATCH] added authentication and swagger --- code/backend/env.txt | 1 + code/backend/package.json | 8 +- .../backend/src/controllers/userController.ts | 105 ++++++++-- .../src/middleware/authenticateToken.ts | 45 ++-- code/backend/src/routes/userRoutes.ts | 97 ++++++++- code/backend/src/schemas/userSchemas.ts | 6 +- code/backend/src/server.ts | 45 +++- code/backend/yarn.lock | 196 +++++++++++++++++- 8 files changed, 447 insertions(+), 56 deletions(-) diff --git a/code/backend/env.txt b/code/backend/env.txt index a8335f9..c6c663a 100644 --- a/code/backend/env.txt +++ b/code/backend/env.txt @@ -1 +1,2 @@ DATABASE_URL="postgres://postgres:YourPassword@localhost:5432/prisma" +TOKEN_SECRET=ThisIsVeryImportantChangeThisToSomethingSecureThisIsUsedToEncryptTheJWT \ No newline at end of file diff --git a/code/backend/package.json b/code/backend/package.json index 830d300..524cb7d 100644 --- a/code/backend/package.json +++ b/code/backend/package.json @@ -5,6 +5,8 @@ "license": "MIT", "dependencies": { "@prisma/client": "^6.7.0", + "@types/bcryptjs": "^3.0.0", + "bcryptjs": "^3.0.2", "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^5.1.0", @@ -12,12 +14,16 @@ "jsonwebtoken": "^9.0.2", "node": "^22.15.0", "pg": "^8.15.6", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", "zod": "^3.24.4" }, "devDependencies": { "@types/express": "^5.0.1", "@types/jsonwebtoken": "^9.0.9", "@types/node": "^22.15.2", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", "nodemon": "^3.1.10", "prettier": "3.5.3", "prisma": "^6.7.0", @@ -26,7 +32,7 @@ "typescript": "^5.8.3" }, "scripts": { - "start": "ts-node src/server.ts", + "start": "nodemon src/server.ts", "build": "tsc", "dev": "ts-node-dev --respawn src/server.ts" } diff --git a/code/backend/src/controllers/userController.ts b/code/backend/src/controllers/userController.ts index 01d5664..92c6279 100644 --- a/code/backend/src/controllers/userController.ts +++ b/code/backend/src/controllers/userController.ts @@ -3,42 +3,107 @@ import { PrismaClient } from "@prisma/client"; import { UserLoginDto, userLoginSchema } from "../schemas/userSchemas"; import jwt from "jsonwebtoken"; import dotenv from "dotenv"; -import { string } from "zod"; +import bcrypt from "bcryptjs"; const app = express(); app.use(express.json()); const prisma = new PrismaClient(); // load environment variables from .env file dotenv.config(); -const JWT_SECRET: string = process.env.TOKEN_SECRET!; - -function generateAccessToken(username: string) { - return jwt.sign({ username }, JWT_SECRET, { expiresIn: "1800s" }); +const JWT_SECRET: string = process.env.TOKEN_SECRET!; // this secret is used to sign the JWT token +// Generate a JWT token with the username as payload and a secret from the environment variables which expires in 1800 seconds (30 minutes) +function generateAccessToken(username: string, userId: string) { + return jwt.sign( + { username: username, role: "user", sub: userId }, + JWT_SECRET, + { expiresIn: "1800s", issuer: "VogelApi" } + ); //TODO: change role to user role } - +// Endpoint to register a new user export const registerUser = async (req: Request, res: Response) => { - const userRequest = await req.body; - const user = await prisma.user.create({ data: userRequest }); - console.log(user.username); - res.json({ message: "User registered successfully", data: req.body }); + const { username, password, email } = await req.body; //gets the data from the request body + if (!username || !password || !email) { + // check if username, password and email are provided and + res + .status(400) + .json({ message: "Username, password and email are required" }); + } + const existingUser = await prisma.user.findUnique({ + // check if the user already exists + where: { + username: username, + }, + }); + if (existingUser) { + // if the user already exists, return an error message + res.status(400).json({ message: `User "${username}" already exists` }); + } + const hashedPassword = await bcrypt.hash(password, 10); // hash the password with bcrypt + if (!hashedPassword) { + // check if the password was hashed successfully + res.status(500).json({ message: "Server Error" }); + } + const userData = { + // create a new user object with the data from the request body and the hashed password + username: username, + email: email, + password: hashedPassword, + }; + const user = await prisma.user.create({ data: userData }); // create a new user in the database + if (!user) { + // check if the user was created successfully + res.status(500).json({ message: "Server Error" }); + } + res.json({ message: "User registered successfully" }); + res.status(201).json({ + message: "user created", + data: { username: username, email: email }, + }); // return the user object with the username and email }; - -export const loginUser = (req: Request, res: Response) => { - const token: string = generateAccessToken(req.body.username); - res.json({ message: "User logged in successfully", data: req.body, token }); +// Endpoint to login a user (unfinished) +export const loginUser = async (req: Request, res: Response) => { + const { username, password } = req.body; // get the data from the request body + if (!username || !password) { + // check if username and password are provided + res.status(400).json({ message: "Username and password are required" }); + } + const user = await prisma.user.findUnique({ + // check if the user exists + where: { + username: username, + }, + }); + if (!user) { + // if the user does not exist, return an error message + res.status(400).json({ message: `User "${username}" not found` }); + return; + } + const isPasswordValid = await bcrypt.compare(password, user.password); // compare the password with the hashed password in the database + if (!isPasswordValid) { + // if the password is not valid, return an error message + res.status(401).json({ message: "Invalid password" }); + } + const token: string = generateAccessToken(user.username, user.id); // generate a JWT token with the username and userId as payload + res.set("Authorization", `Bearer ${token}`); // set the token in the response header + res.json({ message: "User logged in successfully" }); }; - +// Endpoint to get user data export const getUser = async (req: Request, res: Response) => { - const username = req.body.username; - console.log(username, req.body); + const username: string = req.query.username as string; + if (!username) { + res.status(400).json({ message: "Username is required" }); + } const user = await prisma.user.findUnique({ where: { username: username, }, }); if (!user) { - res.status(404).json({ message: "User not found" }); + res.status(404).json({ message: `User "${username}" not found` }); + return; } - res.json({ message: "User found", data: user }); + res.json({ + message: "User found", + data: { username: user.username, email: user.email }, + }); }; - diff --git a/code/backend/src/middleware/authenticateToken.ts b/code/backend/src/middleware/authenticateToken.ts index 88859ea..9e8b276 100644 --- a/code/backend/src/middleware/authenticateToken.ts +++ b/code/backend/src/middleware/authenticateToken.ts @@ -1,15 +1,19 @@ import express, { NextFunction, Request, Response } from "express"; -import jwt from "jsonwebtoken"; +import jwt, { TokenExpiredError } from "jsonwebtoken"; import dotenv from "dotenv"; -import { string } from "zod"; +dotenv.config(); +// imports the JWT secret const JWT_SECRET: string = process.env.TOKEN_SECRET!; - +if (!JWT_SECRET) console.log("no JWT secret"); +// create an interface for the JWT payload +// this interface is used to define the structure of the JWT payload interface JwtPayload { username: string; iat: number; exp: number; } - +// extend the Express Request interface to include the user property +// this is used to store the JWT payload in the request object declare global { namespace Express { interface Request { @@ -17,27 +21,26 @@ declare global { } } } - +// Middleware function to authenticate the JWT token export function authenticateToken() { return (req: Request, res: Response, next: NextFunction) => { - const authHeader = req.headers["authorization"]; - const token = authHeader && authHeader.split(" ")[1]; + const authHeader = req.headers["authorization"]; // get the authorization header from the request + const token = authHeader && authHeader.split(" ")[1]; // split the header to get the token - if (token == null) res.sendStatus(401); + if (token == null) + res.sendStatus(401); // if there is no token, return 401 Unauthorized else { - jwt.verify( - token, - process.env.TOKEN_SECRET as string, - (err: any, user: any) => { - console.log(err); - - if (err) res.sendStatus(403); - - req.user = user; - - next(); - }, - ); + jwt.verify(token, JWT_SECRET, (err: any, user: any) => { + // verify the token with the secret + console.log(err); + if (err) { + if (err instanceof TokenExpiredError) + // check if the error is expired and return 401 + res.status(401).json({ message: "Token expired" }); + else res.status(403).json({ message: "Invalid token" }); + } + next(); + }); } }; } diff --git a/code/backend/src/routes/userRoutes.ts b/code/backend/src/routes/userRoutes.ts index 21af359..3e0cd20 100644 --- a/code/backend/src/routes/userRoutes.ts +++ b/code/backend/src/routes/userRoutes.ts @@ -14,17 +14,100 @@ import { loginUser, getUser, } from "../controllers/userController"; +/** + * @swagger + * components: + * schemas: + * UserRegistrationDto: + * type: object + * required: + * - username + * - email + * - password + * properties: + * username: + * type: string + * email: + * type: string + * format: email + * password: + * type: string + * format: password + * UserLoginDto: + * type: object + * required: + * - username + * - password + * properties: + * username: + * type: string + * password: + * type: string + */ +/** + * @swagger + * /api/user/register: + * post: + * summary: Register a new user + * tags: [User] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UserRegistrationDto' + * responses: + * 201: + * description: Benutzer erfolgreich registriert + * 400: + * description: Ungültige Eingabedaten + */ userRouter.post( "/register", validateData(userRegistrationSchema), - registerUser, + registerUser ); +/** + * @swagger + * /api/user/login: + * post: + * summary: Log in a user + * tags: [User] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UserLoginDto' + * responses: + * 200: + * description: Login erfolgreich + * 401: + * description: Ungültige Anmeldedaten + */ userRouter.post("/login", validateData(userLoginSchema), loginUser); -userRouter.get( - "/getUser", - authenticateToken(), - validateData(userLoginSchema), - getUser, -); +/** + * @swagger + * /api/user/getUser/{username}: + * get: + * summary: Get user data + * tags: [User] + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: username + * required: true + * schema: + * type: string + * description: Der Benutzername, nach dem gesucht werden soll + * responses: + * 200: + * description: Login erfolgreich + * 401: + * description: Ungültige Anmeldedaten + */ +userRouter.get("/getUser/:username", authenticateToken(), getUser); + export default userRouter; diff --git a/code/backend/src/schemas/userSchemas.ts b/code/backend/src/schemas/userSchemas.ts index f004045..bdae1da 100644 --- a/code/backend/src/schemas/userSchemas.ts +++ b/code/backend/src/schemas/userSchemas.ts @@ -2,14 +2,14 @@ import { z } from "zod"; export const userRegistrationSchema = z.object({ - username: z.string(), + username: z.string().regex(/^\S*$/, "Username must not contain spaces"), // No whitespaces allowed, email: z.string().email(), password: z.string().min(8), }); export const userLoginSchema = z.object({ - username: z.string(), - password: z.string().min(8), + username: z.string().regex(/^\S*$/, "Username must not contain spaces"), // No whitespaces allowed, + password: z.string(), }); // DTO-Typen aus den Schemas ableiten export type UserRegistrationDto = z.infer; diff --git a/code/backend/src/server.ts b/code/backend/src/server.ts index f1bde96..6d25189 100644 --- a/code/backend/src/server.ts +++ b/code/backend/src/server.ts @@ -1,4 +1,4 @@ -import express, { Request, Response } from "express"; +import express, { Request, Response, Application } from "express"; import dotenv from "dotenv"; import userRouter from "./routes/userRoutes"; @@ -9,9 +9,50 @@ dotenv.config(); const app = express(); const port = 3000; +//swagger configuration +import swaggerJSDoc from "swagger-jsdoc"; +import swaggerUi from "swagger-ui-express"; + +const options = { + definition: { + openapi: "3.1.0", + info: { + title: "VogelApi", + version: "0.0.1", + description: + "This is a simple CRUD API application made with Express and documented with Swagger", + }, + servers: [ + { + url: "http://localhost:3000", + }, + ], + components: { + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", // Optional, for documentation purposes + }, + }, + }, + security: [ + { + bearerAuth: [], // Apply globally if needed + }, + ], + }, + apis: ["src/routes/*.ts"], // Hier werden alle Routen-Dateien mit Swagger-Kommentaren geladen +}; +const specs = swaggerJSDoc(options); +app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(specs)); + app.use(bodyParser.json()); app.use("/api/user", userRouter); - +// Sample route +app.get("/api/hello", (req, res) => { + res.send("Hello World!"); +}); app.listen(port, () => { console.log(`Server läuft auf http://localhost:${port}`); }); diff --git a/code/backend/yarn.lock b/code/backend/yarn.lock index 6550b79..8d1ac5e 100644 --- a/code/backend/yarn.lock +++ b/code/backend/yarn.lock @@ -2,6 +2,38 @@ # yarn lockfile v1 +"@apidevtools/json-schema-ref-parser@^9.0.6": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8" + integrity sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@apidevtools/openapi-schemas@^2.0.4": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz#32057ae99487872c4dd96b314a1ab4b95d89eaf5" + integrity sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.6" + "@apidevtools/openapi-schemas" "^2.0.4" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + z-schema "^5.0.1" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -152,6 +184,11 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@prisma/client@^6.7.0": version "6.7.0" resolved "https://registry.yarnpkg.com/@prisma/client/-/client-6.7.0.tgz#69f913b0a8cfb6aa5de5dca6aa57bfd4e91e7e95" @@ -201,6 +238,11 @@ dependencies: "@prisma/debug" "6.7.0" +"@scarf/scarf@=1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972" + integrity sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ== + "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -221,6 +263,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/bcryptjs@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-3.0.0.tgz#d7be11653aa82cf17ffee4f3925f1f80cfc1add2" + integrity sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg== + dependencies: + bcryptjs "*" + "@types/body-parser@*": version "1.19.5" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -246,7 +295,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@^5.0.1": +"@types/express@*", "@types/express@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.1.tgz#138d741c6e5db8cc273bec5285cd6e9d0779fc9f" integrity sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ== @@ -260,6 +309,11 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== +"@types/json-schema@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/jsonwebtoken@^9.0.9": version "9.0.9" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz#a4c3a446c0ebaaf467a58398382616f416345fb3" @@ -322,6 +376,19 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/swagger-jsdoc@^6.0.4": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz#bb4f60f3a5f103818e022f2e29ff8935113fb83d" + integrity sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ== + +"@types/swagger-ui-express@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz#3c0e0bf2543c7efb500eaa081bfde6d92f88096c" + integrity sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g== + dependencies: + "@types/express" "*" + "@types/serve-static" "*" + accepts@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" @@ -355,11 +422,21 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bcryptjs@*, bcryptjs@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-3.0.2.tgz#caadcca1afefe372ed6e20f86db8e8546361c1ca" + integrity sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog== + binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -426,6 +503,11 @@ call-bound@^1.0.2: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + chokidar@^3.5.1, chokidar@^3.5.2: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -441,6 +523,16 @@ chokidar@^3.5.1, chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" +commander@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" + integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -498,6 +590,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +doctrine@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dotenv@^16.5.0: version "16.5.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.5.0.tgz#092b49f25f808f020050051d1ff258e404c78692" @@ -596,6 +695,11 @@ escape-html@^1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -709,6 +813,18 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -825,6 +941,13 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsonwebtoken@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" @@ -858,6 +981,11 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -868,6 +996,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -888,6 +1021,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -925,7 +1063,7 @@ mime-types@^3.0.0, mime-types@^3.0.1: dependencies: mime-db "^1.54.0" -minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -1335,6 +1473,39 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swagger-jsdoc@^6.2.8: + version "6.2.8" + resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz#6d33d9fb07ff4a7c1564379c52c08989ec7d0256" + integrity sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ== + dependencies: + commander "6.2.0" + doctrine "3.0.0" + glob "7.1.6" + lodash.mergewith "^4.6.2" + swagger-parser "^10.0.3" + yaml "2.0.0-1" + +swagger-parser@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-10.0.3.tgz#04cb01c18c3ac192b41161c77f81e79309135d03" + integrity sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg== + dependencies: + "@apidevtools/swagger-parser" "10.0.3" + +swagger-ui-dist@>=5.0.0: + version "5.21.0" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz#aed230fe6e294c9470217e67697d601e3bb8eb9d" + integrity sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg== + dependencies: + "@scarf/scarf" "=1.4.0" + +swagger-ui-express@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz#fb8c1b781d2793a6bd2f8a205a3f4bd6fa020dd8" + integrity sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA== + dependencies: + swagger-ui-dist ">=5.0.0" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -1436,6 +1607,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +validator@^13.7.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" + integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA== + vary@^1, vary@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -1451,11 +1627,27 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +yaml@2.0.0-1: + version "2.0.0-1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18" + integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ== + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +z-schema@^5.0.1: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0" + zod@^3.24.4: version "3.24.4" resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f"