From 0cf879b38318f9b27a74ddf556ea02f49ab8ea02 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Wed, 1 Nov 2023 14:12:17 +0300 Subject: [PATCH] apps/api: add swagger --- apps/api/nest-cli.json | 17 +++- apps/api/package.json | 2 + apps/api/src/account/account.controller.ts | 23 ++++- apps/api/src/account/account.service.ts | 6 +- .../api/src/account/dto/create-account.dto.ts | 7 +- apps/api/src/app.controller.ts | 2 + apps/api/src/dto/credentials.ts | 7 +- apps/api/src/ldap/ldap.controller.ts | 26 +++++- apps/api/src/ldap/ldap.service.ts | 22 +++-- apps/api/src/main.ts | 14 +++ apps/api/src/schemas/account.schema.ts | 5 ++ apps/api/src/utils/ldap.ts | 26 ++++-- yarn.lock | 90 +++++++++++++++++-- 13 files changed, 209 insertions(+), 38 deletions(-) diff --git a/apps/api/nest-cli.json b/apps/api/nest-cli.json index 2566481..686843e 100644 --- a/apps/api/nest-cli.json +++ b/apps/api/nest-cli.json @@ -1,5 +1,20 @@ { "$schema": "https://json.schemastore.org/nest-cli", "collection": "@nestjs/schematics", - "sourceRoot": "src" + "sourceRoot": "src", + "compilerOptions": { + "plugins": [ + { + "name": "@nestjs/swagger/plugin", + "options": { + "dtoFileNameSuffix": [".entity.ts", ".dto.ts"], + "controllerFileNameSuffix": [".controller.ts"], + "classValidatorShim": true, + "dtoKeyOfComment": "description", + "controllerKeyOfComment": "description", + "introspectComments": true + } + } + ] + } } diff --git a/apps/api/package.json b/apps/api/package.json index b3a73f2..15057ca 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@fastify/cookie": "^9.1.0", + "@fastify/static": "^6.12.0", "@nestjs/cache-manager": "^2.1.0", "@nestjs/cli": "^10.1.18", "@nestjs/common": "^10.2.7", @@ -33,6 +34,7 @@ "@nestjs/mongoose": "^10.0.1", "@nestjs/platform-express": "^10.2.7", "@nestjs/platform-fastify": "^10.2.7", + "@nestjs/swagger": "^7.1.14", "bcrypt": "^5.1.1", "cache-manager": "^5.2.4", "cache-manager-ioredis": "^2.1.0", diff --git a/apps/api/src/account/account.controller.ts b/apps/api/src/account/account.controller.ts index 0ac980c..72b17a7 100644 --- a/apps/api/src/account/account.controller.ts +++ b/apps/api/src/account/account.controller.ts @@ -10,17 +10,21 @@ import { Get, HttpException, HttpStatus, - Param, Post, + Query, Req, Res, + UnauthorizedException, } from '@nestjs/common'; +import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { FastifyReply, FastifyRequest } from 'fastify'; import { cookieOptions } from 'src/config/cookie'; import { env } from 'src/config/env'; import { Credentials } from 'src/dto/credentials'; +import { Account } from 'src/schemas/account.schema'; @Controller('account') +@ApiTags('account') export class AccountController { constructor(private readonly accountService: AccountService) {} @@ -35,11 +39,15 @@ export class AccountController { } @Post('/create') + @ApiResponse({ + status: HttpStatus.CREATED, + type: Account, + }) async create(@Body() createAccountDto: CreateAccountDto, @Res() reply: FastifyReply) { try { const createdAccount = await this.accountService.create(createAccountDto); - return reply.send(createdAccount); + return reply.status(HttpStatus.CREATED).send(createdAccount); } catch (error) { throw new HttpException(error, HttpStatus.BAD_REQUEST); } @@ -50,8 +58,13 @@ export class AccountController { return this.accountService.findAll(); } - @Delete('/delete/:login') - async delete(@Param('login') username: CreateAccountDto['username']) { + @Delete('/delete') + @ApiResponse({ + status: HttpStatus.OK, + type: Account, + }) + // @ApiQuery({ name: 'username', type: CreateAccountDto['username'] }) + async delete(@Query('username') username: string) { return this.accountService.delete(username); } @@ -76,8 +89,10 @@ export class AccountController { @Get('/get-user') async getUser(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { const token = req.cookies[env.COOKIE_TOKEN_NAME]; + if (!token) throw new UnauthorizedException(); const account = await this.accountService.getUser(token); + if (!account) throw new UnauthorizedException('Account not found'); return reply.send(account); } diff --git a/apps/api/src/account/account.service.ts b/apps/api/src/account/account.service.ts index 3931caf..d1b123b 100644 --- a/apps/api/src/account/account.service.ts +++ b/apps/api/src/account/account.service.ts @@ -58,6 +58,10 @@ export class AccountService { } public async getUser(token: string) { - return this.jwtService.decode(token); + try { + return this.jwtService.verify(token); + } catch { + throw new UnauthorizedException('Invalid token'); + } } } diff --git a/apps/api/src/account/dto/create-account.dto.ts b/apps/api/src/account/dto/create-account.dto.ts index 373a921..c37a53d 100644 --- a/apps/api/src/account/dto/create-account.dto.ts +++ b/apps/api/src/account/dto/create-account.dto.ts @@ -1,14 +1,17 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsNotEmpty, IsOptional, IsString, MinLength } from 'class-validator'; export class CreateAccountDto { + @ApiProperty() @IsString() @IsNotEmpty() - readonly username: string; + public readonly username: string; + @ApiPropertyOptional({}) @IsString() @MinLength(10) @IsOptional() - readonly password: string; + public readonly password: string; readonly [key: string]: unknown; } diff --git a/apps/api/src/app.controller.ts b/apps/api/src/app.controller.ts index f380f7b..490d528 100644 --- a/apps/api/src/app.controller.ts +++ b/apps/api/src/app.controller.ts @@ -1,9 +1,11 @@ import { AppService } from './app.service'; import { env } from './config/env'; import { Controller, Get, HttpStatus, Req, Res } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; import { FastifyReply, FastifyRequest } from 'fastify'; @Controller() +@ApiExcludeController() export class AppController { constructor(private readonly appService: AppService) {} @Get('auth') diff --git a/apps/api/src/dto/credentials.ts b/apps/api/src/dto/credentials.ts index 1dee2e6..858522d 100644 --- a/apps/api/src/dto/credentials.ts +++ b/apps/api/src/dto/credentials.ts @@ -1,10 +1,13 @@ -import { IsNotEmpty, IsString } from "class-validator"; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; export class Credentials { + @ApiProperty() @IsString() @IsNotEmpty() readonly login: string; - + + @ApiProperty() @IsString() @IsNotEmpty() readonly password: string; diff --git a/apps/api/src/ldap/ldap.controller.ts b/apps/api/src/ldap/ldap.controller.ts index 15ef03c..5cc45a0 100644 --- a/apps/api/src/ldap/ldap.controller.ts +++ b/apps/api/src/ldap/ldap.controller.ts @@ -4,12 +4,25 @@ import { Credentials } from '../dto/credentials'; import { LdapService } from './ldap.service'; import type { CookieSerializeOptions } from '@fastify/cookie'; -import { Body, Controller, Get, HttpException, HttpStatus, Post, Req, Res } from '@nestjs/common'; +import { + Body, + Controller, + Get, + HttpException, + HttpStatus, + Post, + Req, + Res, + UnauthorizedException, +} from '@nestjs/common'; +import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { FastifyReply, FastifyRequest } from 'fastify'; import { cookieOptions } from 'src/config/cookie'; import { env } from 'src/config/env'; +import { User } from 'src/utils/ldap'; @Controller('ldap') +@ApiTags('ldap') export class LdapController { cookieOptions: CookieSerializeOptions; constructor(private readonly ldapService: LdapService) {} @@ -25,6 +38,9 @@ export class LdapController { } @Post('/signin') + @ApiResponse({ + status: HttpStatus.OK, + }) async login(@Body() credentials: Credentials, @Res() reply: FastifyReply) { try { const token = await this.ldapService.login(credentials); @@ -46,11 +62,19 @@ export class LdapController { } @Get('/get-user') + @ApiResponse({ + status: HttpStatus.OK, + type: User, + }) async getUser(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { const token = req.cookies[env.COOKIE_TOKEN_NAME]; + if (!token) throw new UnauthorizedException(); + const user = await this.ldapService.getUser(token); + if (!user) throw new UnauthorizedException('User not found'); + return reply.send(user); } } diff --git a/apps/api/src/ldap/ldap.service.ts b/apps/api/src/ldap/ldap.service.ts index 15d3fc1..eb9dcc5 100644 --- a/apps/api/src/ldap/ldap.service.ts +++ b/apps/api/src/ldap/ldap.service.ts @@ -1,6 +1,6 @@ import type { DecodedToken, TokenPayload } from './types/jwt'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Cache } from 'cache-manager'; import { env } from 'src/config/env'; @@ -37,18 +37,22 @@ export class LdapService { } public async getUser(token: string) { - const { username } = this.jwtService.decode(token) as DecodedToken; + try { + const { username } = this.jwtService.verify(token) as DecodedToken; - const cachedUser = await this.cacheManager.get(username); + const cachedUser = (await this.cacheManager.get(username)) as ldap.User; - if (!cachedUser) { - const user = await ldap.authenticate(username); + if (!cachedUser) { + const user = await ldap.authenticate(username); - await this.cacheManager.set(username, user); + await this.cacheManager.set(username, user); - return user; + return user; + } + + return cachedUser; + } catch { + throw new UnauthorizedException('Invalid token'); } - - return cachedUser; } } diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index ff55960..fab6f67 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -3,10 +3,22 @@ import { AppModule } from './app.module'; import { env } from './config/env'; import { fastifyCookie } from '@fastify/cookie'; +import type { INestApplication } from '@nestjs/common'; import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import type { NestFastifyApplication } from '@nestjs/platform-fastify'; import { FastifyAdapter } from '@nestjs/platform-fastify'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; + +function setupOpenApi(app: INestApplication) { + const config = new DocumentBuilder() + .setTitle('Evo.Auth') + .setVersion('1.0') + // .addTag('api') + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('swagger', app, document, { useGlobalPrefix: true }); +} async function bootstrap() { const app = await NestFactory.create( @@ -22,6 +34,8 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe({ stopAtFirstError: true })); + setupOpenApi(app); + await app.listen(env.API_PORT, '0.0.0.0'); } diff --git a/apps/api/src/schemas/account.schema.ts b/apps/api/src/schemas/account.schema.ts index 5d2f973..2f57073 100644 --- a/apps/api/src/schemas/account.schema.ts +++ b/apps/api/src/schemas/account.schema.ts @@ -1,5 +1,6 @@ /* eslint-disable @babel/no-invalid-this */ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger'; import * as bcrypt from 'bcrypt'; import type { HydratedDocument } from 'mongoose'; @@ -7,9 +8,13 @@ export type UserDocument = HydratedDocument; @Schema({ strict: false }) export class Account { + @ApiResponseProperty() + @ApiProperty() @Prop({ index: { unique: true }, required: true }) username: string; + @ApiResponseProperty() + @ApiProperty() @Prop({ required: true }) password: string; } diff --git a/apps/api/src/utils/ldap.ts b/apps/api/src/utils/ldap.ts index a387b92..8b070cf 100644 --- a/apps/api/src/utils/ldap.ts +++ b/apps/api/src/utils/ldap.ts @@ -1,16 +1,24 @@ +import { ApiResponseProperty } from '@nestjs/swagger'; import type { AuthenticationOptions } from 'ldap-authentication'; import * as ldap from 'ldap-authentication'; import { env } from 'src/config/env'; -export type User = { - department: string; - displayName: string; - domain: string; - domainName: string; - mail: string; - position: string; - username: string; -}; +export class User { + @ApiResponseProperty() + public department: string; + @ApiResponseProperty() + public displayName: string; + @ApiResponseProperty() + public domain: string; + @ApiResponseProperty() + public domainName: string; + @ApiResponseProperty() + public mail: string; + @ApiResponseProperty() + public position: string; + @ApiResponseProperty() + public username: string; +} export type LdapUser = { accountExpires: string; diff --git a/yarn.lock b/yarn.lock index c413e0f..1deede1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -457,6 +457,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.51.0.tgz#6d419c240cfb2b66da37df230f7e7eef801c32fa" integrity sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg== +"@fastify/accept-negotiator@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff" + integrity sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ== + "@fastify/ajv-compiler@^3.5.0": version "3.5.0" resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz#459bff00fefbf86c96ec30e62e933d2379e46670" @@ -517,6 +522,29 @@ path-to-regexp "^6.1.0" reusify "^1.0.4" +"@fastify/send@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/send/-/send-2.1.0.tgz#1aa269ccb4b0940a2dadd1f844443b15d8224ea0" + integrity sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA== + dependencies: + "@lukeed/ms" "^2.0.1" + escape-html "~1.0.3" + fast-decode-uri-component "^1.0.1" + http-errors "2.0.0" + mime "^3.0.0" + +"@fastify/static@^6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@fastify/static/-/static-6.12.0.tgz#f3d55dda201c072bae0593e5d45dde8fd235c288" + integrity sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ== + dependencies: + "@fastify/accept-negotiator" "^1.0.0" + "@fastify/send" "^2.0.0" + content-disposition "^0.5.3" + fastify-plugin "^4.0.0" + glob "^8.0.1" + p-limit "^3.1.0" + "@fontsource/montserrat@^5.0.13": version "5.0.13" resolved "https://registry.yarnpkg.com/@fontsource/montserrat/-/montserrat-5.0.13.tgz#e9e78a726e4a452b820c8c0bdcddf4444c922db6" @@ -1027,6 +1055,11 @@ resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe" integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA== +"@lukeed/ms@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@lukeed/ms/-/ms-2.0.1.tgz#3c2bbc258affd9cc0e0cc7828477383c73afa6ee" + integrity sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA== + "@mapbox/node-pre-gyp@^1.0.11": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" @@ -1121,7 +1154,7 @@ "@types/jsonwebtoken" "9.0.2" jsonwebtoken "9.0.0" -"@nestjs/mapped-types@*": +"@nestjs/mapped-types@*", "@nestjs/mapped-types@2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz#c8a090a8d22145b85ed977414c158534210f2e4f" integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg== @@ -1166,6 +1199,17 @@ jsonc-parser "3.2.0" pluralize "8.0.0" +"@nestjs/swagger@^7.1.14": + version "7.1.14" + resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-7.1.14.tgz#492b3816308264472b3619f5c0336f378f1c9995" + integrity sha512-2Ol4S6qHeYVVmkshkWBM8E/qkmEqEOUj2QIewr0jLSyo30H7f3v81pJyks6pTLy4PK0LGUXojMvIfFIE3mmGQQ== + dependencies: + "@nestjs/mapped-types" "2.0.2" + js-yaml "4.1.0" + lodash "4.17.21" + path-to-regexp "3.2.0" + swagger-ui-dist "5.9.0" + "@nestjs/testing@^10.2.7": version "10.2.7" resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.2.7.tgz#50408ccb4c809d216a12d60ac7932fd6ad7fedf4" @@ -2846,7 +2890,7 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -content-disposition@0.5.4: +content-disposition@0.5.4, content-disposition@^0.5.3: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -4472,6 +4516,17 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@^9.2.0: version "9.3.5" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" @@ -5580,6 +5635,13 @@ js-types@^1.0.0: resolved "https://registry.yarnpkg.com/js-types/-/js-types-1.0.0.tgz#d242e6494ed572ad3c92809fc8bed7f7687cbf03" integrity sha512-bfwqBW9cC/Lp7xcRpug7YrXm0IVw+T9e3g4mCYnv0Pjr3zIzU9PCQElYU9oSGAWzXlbdl9X5SAMPejO9sxkeUw== +js-yaml@4.1.0, 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" + js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -5588,13 +5650,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -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" - jsdoc-type-pratt-parser@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" @@ -6042,6 +6097,11 @@ mime@2.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6071,6 +6131,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimatch@^8.0.2: version "8.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" @@ -7777,6 +7844,11 @@ 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-ui-dist@5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz#d52b6cf52fd0a8e6930866c402aaa793fe4e3f76" + integrity sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA== + symbol-observable@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"