added authentication and swagger

This commit is contained in:
Kai Ritthaler 2025-05-09 21:08:38 +02:00 committed by Rudi Regentonne
parent c311e3adda
commit 64c0d79438
8 changed files with 447 additions and 56 deletions

View file

@ -1 +1,2 @@
DATABASE_URL="postgres://postgres:YourPassword@localhost:5432/prisma"
TOKEN_SECRET=ThisIsVeryImportantChangeThisToSomethingSecureThisIsUsedToEncryptTheJWT

View file

@ -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"
}

View file

@ -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 },
});
};

View file

@ -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();
});
}
};
}

View file

@ -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;

View file

@ -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<typeof userRegistrationSchema>;

View file

@ -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}`);
});

View file

@ -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"