diff --git a/.github/workflows/ci-on-develop.yml b/.github/workflows/ci-on-develop.yml new file mode 100644 index 0000000..737604a --- /dev/null +++ b/.github/workflows/ci-on-develop.yml @@ -0,0 +1,90 @@ +name: ci-on-develop + +on: + pull_request: + branches: ["develop", "staging", "main"] + +jobs: + + determine-affected-projects: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Cache node modules + id: cache + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + - uses: actions/setup-node@v1 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: 18.18.0 + - name: yarn install + continue-on-error: true + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --pure-lockfile + + - name: Fetch missing history + run: git fetch + + - name: 'Determine affected projects' + id: affected + run: | + OUTPUT=$(npx nx show projects --affected --base=origin/${{github.event.pull_request.base.ref}} --with-target=container) + echo "Affected projects: $OUTPUT" + OUTPUT="${OUTPUT//[$'\r\n']/ }" + echo "::set-output name=affected::$OUTPUT" + - name: Set output matrix excluding e2e + id: set-matrix + run: | + IFS=' ' + read -ra PROJECTS <<< "${{ steps.affected.outputs.affected }}" + COMPONENTS=() + for PROJECT in "${PROJECTS[@]}"; do + if ! [[ "$PROJECT" =~ -e2e$ ]] && [[ "$PROJECT" != "contracts" ]]; then + COMPONENTS+=("$PROJECT") + fi + done + MATRIX='{"component":[' + for COMPONENT in "${COMPONENTS[@]}"; do + MATRIX+="\"$COMPONENT\"," + done + MATRIX="${MATRIX%,}]}" + echo "::set-output name=matrix::$MATRIX" + build_and_push: + needs: determine-affected-projects + runs-on: ubuntu-latest + if: ${{ needs.determine-affected-projects.outputs.matrix != '{"component":[]}' }} + strategy: + matrix: + component: ${{fromJson(needs.determine-affected-projects.outputs.matrix).component}} + steps: + - name: Debug affected projects + run: echo "${{ needs.determine-affected-projects.outputs.matrix }}" + + - name: Checkout code + uses: actions/checkout@v2 + - run: git fetch --no-tags --prune --depth=1 origin develop + - name: Cache node modules + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + - name: Debug component name + env: + COMPONENT_NAME: ${{ matrix.component }} + run: echo ${COMPONENT_NAME} + - name: 'Build images' + run: | + + npx nx container ${COMPONENT_NAME} --prod + env: + INPUT_PUSH: false + COMPONENT_NAME: ${{ matrix.component }} + INPUT_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/ci-on-main.yml b/.github/workflows/ci-on-main.yml new file mode 100644 index 0000000..e744090 --- /dev/null +++ b/.github/workflows/ci-on-main.yml @@ -0,0 +1,104 @@ +name: ci-on-main + +on: + push: + branches: [ "main" ] + +jobs: + determine-affected-projects: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Cache node modules + id: cache + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + - uses: actions/setup-node@v1 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: 18.18.0 + - name: yarn install + if: steps.cache.outputs.cache-hit != 'true' + continue-on-error: true + run: yarn install --pure-lockfile + + + - name: Fetch missing history + run: git fetch + + - name: 'Determine affected projects' + id: affected + run: | + OUTPUT=$(npx nx show projects --affected --base=origin/main~1 --head=origin/main --with-target=container) + echo "Affected projects: $OUTPUT" + OUTPUT="${OUTPUT//[$'\r\n']/ }" + echo "::set-output name=affected::$OUTPUT" + - name: Set output matrix excluding e2e + id: set-matrix + run: | + IFS=' ' + read -ra PROJECTS <<< "${{ steps.affected.outputs.affected }}" + COMPONENTS=() + for PROJECT in "${PROJECTS[@]}"; do + COMPONENTS+=("$PROJECT") + done + MATRIX='{"component":[' + for COMPONENT in "${COMPONENTS[@]}"; do + MATRIX+="\"$COMPONENT\"," + done + MATRIX="${MATRIX%,}]}" + echo "::set-output name=matrix::$MATRIX" + build_and_push: + needs: determine-affected-projects + runs-on: ubuntu-latest + if: ${{ needs.determine-affected-projects.outputs.matrix != '{"component":[]}' }} + strategy: + matrix: + component: ${{fromJson(needs.determine-affected-projects.outputs.matrix).component}} + steps: + - name: Debug affected projects + run: echo "${{ needs.determine-affected-projects.outputs.matrix }}" + + - name: Checkout code + uses: actions/checkout@v2 + - run: git fetch --no-tags --prune --depth=1 origin main + + - name: Cache node modules + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-central-1 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Debug component name + env: + COMPONENT_NAME: ${{ matrix.component }} + run: echo ${COMPONENT_NAME} + - name: 'Build images' + run: | + + npx nx container ${COMPONENT_NAME} --prod + env: + COMPONENT_NAME: ${{ matrix.component }} + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + INPUT_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + INPUT_VC_API_IMAGES: ${{ steps.login-ecr.outputs.registry }}/justaname-production/vc-api + INPUT_TAGS: latest + INPUT_PUSH: true diff --git a/.github/workflows/ci-on-staging.yml b/.github/workflows/ci-on-staging.yml new file mode 100644 index 0000000..b48bf66 --- /dev/null +++ b/.github/workflows/ci-on-staging.yml @@ -0,0 +1,104 @@ +name: ci-on-staging + +on: + push: + branches: [ "staging" ] + +jobs: + determine-affected-projects: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Cache node modules + id: cache + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + - uses: actions/setup-node@v1 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: 18.18.0 + - name: yarn install + if: steps.cache.outputs.cache-hit != 'true' + continue-on-error: true + run: yarn install --pure-lockfile + + + - name: Fetch missing history + run: git fetch + + - name: 'Determine affected projects' + id: affected + run: | + OUTPUT=$(npx nx show projects --affected --base=origin/staging~1 --head=origin/staging --with-target=container) + echo "Affected projects: $OUTPUT" + OUTPUT="${OUTPUT//[$'\r\n']/ }" + echo "::set-output name=affected::$OUTPUT" + - name: Set output matrix + id: set-matrix + run: | + IFS=' ' + read -ra PROJECTS <<< "${{ steps.affected.outputs.affected }}" + COMPONENTS=() + for PROJECT in "${PROJECTS[@]}"; do + COMPONENTS+=("$PROJECT") + done + MATRIX='{"component":[' + for COMPONENT in "${COMPONENTS[@]}"; do + MATRIX+="\"$COMPONENT\"," + done + MATRIX="${MATRIX%,}]}" + echo "::set-output name=matrix::$MATRIX" + build_and_push: + needs: determine-affected-projects + runs-on: ubuntu-latest + if: ${{ needs.determine-affected-projects.outputs.matrix != '{"component":[]}' }} + strategy: + matrix: + component: ${{fromJson(needs.determine-affected-projects.outputs.matrix).component}} + steps: + - name: Debug affected projects + run: echo "${{ needs.determine-affected-projects.outputs.matrix }}" + + - name: Checkout code + uses: actions/checkout@v2 + - run: git fetch --no-tags --prune --depth=1 origin staging + + - name: Cache node modules + uses: actions/cache@v2 + with: + path: node_modules + key: cache-node-modules-${{ hashFiles('yarn.lock') }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-central-1 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Debug component name + env: + COMPONENT_NAME: ${{ matrix.component }} + run: echo ${COMPONENT_NAME} + - name: 'Build images' + run: | + + npx nx container ${COMPONENT_NAME} --prod + env: + COMPONENT_NAME: ${{ matrix.component }} + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + INPUT_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + INPUT_VC_API_IMAGES: ${{ steps.login-ecr.outputs.registry }}/justaname/vc-api + INPUT_TAGS: latest + INPUT_PUSH: true diff --git a/apps/vc-api/.env.example b/apps/vc-api/.env.example index 151fc80..55e4207 100644 --- a/apps/vc-api/.env.example +++ b/apps/vc-api/.env.example @@ -1,6 +1,8 @@ INFURA_PROJECT_ID= SIGNING_PRIVATE_KEY= ENVIRONMENT=# (development, production, staging, test) +ENS_DOMAIN= +CHAIN_ID= # 1 or 11155111 API_DOMAIN= GITHUB_CLIENT_ID= diff --git a/apps/vc-api/Dockerfile b/apps/vc-api/Dockerfile new file mode 100644 index 0000000..9afd7eb --- /dev/null +++ b/apps/vc-api/Dockerfile @@ -0,0 +1,33 @@ +# Stage 1: Install dependencies in a separate layer to leverage Docker cache +FROM node:lts-alpine as deps +WORKDIR /usr/src/app +COPY dist/apps/vc-api/package.json dist/apps/vc-api/yarn.lock ./ +RUN apk add --no-cache --virtual .build-deps python3 make g++ && \ + echo "@community http://dl-cdn.alpinelinux.org/alpine/v3.18/community" >> /etc/apk/repositories && \ + yarn install --production && \ + apk del .build-deps + +# Stage 2: Build the application +# Note: Assuming main.js doesn't require a build process. If it does, you would need to copy source files and build here. +FROM node:lts-alpine as build +WORKDIR /usr/src/app +COPY --from=deps /usr/src/app/node_modules ./node_modules +COPY dist/apps/vc-api/ ./ + +# Stage 3: Production image, copy all the files and run the app +FROM node:lts-alpine as runner +RUN apk add --no-cache dumb-init && \ + addgroup -S appgroup && adduser -S appuser -G appgroup +ENV NODE_ENV production +ENV PORT 3009 + +WORKDIR /usr/src/app +# Copy only necessary runtime files +COPY --from=build /usr/src/app/ ./ +COPY --from=build /usr/src/app/node_modules ./node_modules + +# Use non-root user for better security +USER appuser + +EXPOSE 3009 +CMD [ "npm", "run", "start" ] diff --git a/apps/vc-api/package.json b/apps/vc-api/package.json index 2c63c08..ed1aed4 100644 --- a/apps/vc-api/package.json +++ b/apps/vc-api/package.json @@ -1,2 +1,25 @@ { + "scripts": { + "start": "node main.js" + }, + "dependencies": { + "@nestjs/axios": "^3.0.3", + "@nestjs/common": "^10.0.2", + "@nestjs/config": "^3.2.3", + "@nestjs/core": "^10.0.2", + "@nestjs/platform-express": "^10.0.2", + "@nx/webpack": "19.7.2", + "@veramo/core": "^6.0.0", + "@veramo/credential-eip712": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "did-resolver": "4.1.0", + "ens-did-resolver": "^1.0.4", + "ethers": "^6.13.2", + "ethr-did-resolver": "^10.1.10", + "express": "4.20.0", + "moment": "^2.30.1", + "web-did-resolver": "^2.0.27", + "@nestjs/swagger": "^7.4.0" + } } diff --git a/apps/vc-api/project.json b/apps/vc-api/project.json index f84a638..0965529 100644 --- a/apps/vc-api/project.json +++ b/apps/vc-api/project.json @@ -22,7 +22,7 @@ "development": {}, "production": {} }, - "dependsOn": ["prisma-generate","lint","^test","test"] + "dependsOn": ["lint"] }, "serve": { "executor": "@nx/js:node", @@ -44,6 +44,27 @@ "options": { "fix": true } + }, + "container": { + "executor": "@nx-tools/nx-container:build", + "dependsOn": ["build"], + "options": { + "engine": "docker", + "metadata": { + "images": ["justaname/vc-api"], + "load": true, + "tags": [ + "type=schedule", + "type=ref,event=branch", + "type=ref,event=tag", + "type=ref,event=pr", + "type=semver,pattern={{version}}", + "type=semver,pattern={{major}}.{{minor}}", + "type=semver,pattern={{major}}", + "type=sha,prefix=sha-" + ] + } + } } } } diff --git a/apps/vc-api/src/api/auth/auth.callback.response.api.ts b/apps/vc-api/src/api/auth/auth.callback.response.api.ts new file mode 100644 index 0000000..a3f901d --- /dev/null +++ b/apps/vc-api/src/api/auth/auth.callback.response.api.ts @@ -0,0 +1,179 @@ +import { ApiProperty, ApiExtraModels } from '@nestjs/swagger'; +import { IsString, IsDate, IsArray, IsObject, ValidateNested, IsOptional } from 'class-validator'; +import { Type } from 'class-transformer'; + +export type PrimitiveValueApiResponse = string | number | boolean | null; + +export type CredentialSubjectValueApiResponse = Record; + + +export class CredentialSubjectElementApiResponse { + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + type: string; +} + +@ApiExtraModels(CredentialSubjectElementApiResponse) +export class TypesApiResponse { + @ApiProperty({ type: [CredentialSubjectElementApiResponse] }) + @ValidateNested({ each: true }) + @Type(() => CredentialSubjectElementApiResponse) + EIP712Domain: CredentialSubjectElementApiResponse[]; + + @ApiProperty({ type: [CredentialSubjectElementApiResponse] }) + @ValidateNested({ each: true }) + @Type(() => CredentialSubjectElementApiResponse) + CredentialSubject: CredentialSubjectElementApiResponse[]; + + @ApiProperty({ type: [CredentialSubjectElementApiResponse] }) + @ValidateNested({ each: true }) + @Type(() => CredentialSubjectElementApiResponse) + Issuer: CredentialSubjectElementApiResponse[]; + + @ApiProperty({ type: [CredentialSubjectElementApiResponse] }) + @ValidateNested({ each: true }) + @Type(() => CredentialSubjectElementApiResponse) + Proof: CredentialSubjectElementApiResponse[]; + + @ApiProperty({ type: [CredentialSubjectElementApiResponse] }) + @ValidateNested({ each: true }) + @Type(() => CredentialSubjectElementApiResponse) + VerifiableCredential: CredentialSubjectElementApiResponse[]; + +} + + +export class CredentialSubjectApiResponse { + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + id?: string; +} + +export class IssuerApiResponse { + @ApiProperty() + @IsString() + id: string; + +} + + + +export class DomainApiResponse { + @ApiProperty() + chainId: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + version: string; + +} + +@ApiExtraModels(DomainApiResponse, TypesApiResponse) +export class Eip712ApiResponse { + @ApiProperty({ type: DomainApiResponse }) + @ValidateNested() + @Type(() => DomainApiResponse) + domain: DomainApiResponse; + + @ApiProperty({ type: TypesApiResponse }) + @ValidateNested() + @Type(() => TypesApiResponse) + types: TypesApiResponse; + + @ApiProperty() + @IsString() + primaryType: string; + +} + +@ApiExtraModels(Eip712ApiResponse) +export class ProofApiResponse { + @ApiProperty() + @IsString() + verificationMethod: string; + + @ApiProperty() + @IsDate() + created: Date; + + @ApiProperty() + @IsString() + proofPurpose: string; + + @ApiProperty() + @IsString() + type: string; + + @ApiProperty() + @IsString() + proofValue: string; + + @ApiProperty({ type: Eip712ApiResponse }) + @ValidateNested() + @Type(() => Eip712ApiResponse) + eip712: Eip712ApiResponse; +} + +@ApiExtraModels(CredentialSubjectApiResponse) +/* eslint-disable @typescript-eslint/ban-types */ +export class EthereumEip712Signature2021ApiResponse { + @ApiProperty({ type: CredentialSubjectApiResponse }) + @ValidateNested() + @Type(() => CredentialSubjectApiResponse) + credentialSubject: CredentialSubjectApiResponse & T; + + @ApiProperty() + @IsDate() + issuanceDate: Date; + + @ApiProperty() + @IsDate() + expirationDate: Date; + + @ApiProperty() + @IsArray() + "@context": string | Record | (string | Record)[]; + + @ApiProperty() + @IsArray() + type: string[] | string; + +} + +@ApiExtraModels(ProofApiResponse, IssuerApiResponse) +/* eslint-disable @typescript-eslint/ban-types */ +export class VerifiedEthereumEip712Signature2021ApiResponse extends EthereumEip712Signature2021ApiResponse { + @ApiProperty({ type: ProofApiResponse }) + @ValidateNested() + @Type(() => ProofApiResponse) + proof: ProofApiResponse; + + @ApiProperty({ type: IssuerApiResponse }) + @ValidateNested() + @Type(() => IssuerApiResponse) + issuer: IssuerApiResponse; + +} + + +@ApiExtraModels(VerifiedEthereumEip712Signature2021ApiResponse) +export class AuthCallbackApiResponse { + + @ApiProperty() + @IsString() + dataKey: string; + + @ApiProperty({ type: VerifiedEthereumEip712Signature2021ApiResponse }) + @ValidateNested() + @Type(() => VerifiedEthereumEip712Signature2021ApiResponse) + verifiedCredential: VerifiedEthereumEip712Signature2021ApiResponse; +} diff --git a/apps/vc-api/src/api/auth/auth.controller.ts b/apps/vc-api/src/api/auth/auth.controller.ts new file mode 100644 index 0000000..fd1d15f --- /dev/null +++ b/apps/vc-api/src/api/auth/auth.controller.ts @@ -0,0 +1,51 @@ +import { Controller, Get, Inject, Param, Query, Res } from '@nestjs/common'; +import { + CREDENTIAL_CREATOR_FACADE, + ICredentialCreatorFacade +} from '../../core/applications/credentials/facade/icredential.facade'; +import { Response } from 'express'; +import { AuthCallbackApiResponse } from './auth.callback.response.api'; +import { AUTH_CONTROLLER_MAPPER, IAuthControllerMapper } from './mapper/iauth.controller.mapper'; +import { AuthGetAuthUrlRequestApiRequestParam } from './auth.get-auth-url.request.api'; +@Controller('auth') +export class AuthController { + + constructor( + @Inject(CREDENTIAL_CREATOR_FACADE) + private readonly credentialCreatorFacade: ICredentialCreatorFacade, + + @Inject(AUTH_CONTROLLER_MAPPER) + private readonly authControllerMapper: IAuthControllerMapper + ) {} + + @Get('') + async welcomeToJustaNameVerifications(): Promise { + return ['Welcome to JustaName Verifications! Please use the /auth/:authName endpoint to get started.'] + } + + @Get(':authName') + async getAuthUrl( + @Param() authGetAuthUrlRequestApi: AuthGetAuthUrlRequestApiRequestParam, + @Res() res: Response + ): Promise { + const redirect = await this.credentialCreatorFacade.getAuthUrl(authGetAuthUrlRequestApi.authName) + if(redirect.startsWith('http')) { + res.redirect(redirect) + return + } + res.send(redirect) + } + + @Get(':authName/callback') + async callback( + @Param() authGetAuthUrlRequestApiParam: AuthGetAuthUrlRequestApiRequestParam, + @Query() authGetAuthUrlRequestApiQuery: any, + ): Promise { + const verifiedEthereumEip712Signature2021 = await this.credentialCreatorFacade.callback( + this.authControllerMapper.mapAuthCallbackApiRequestToCredentialCallbackRequest( + authGetAuthUrlRequestApiQuery, + authGetAuthUrlRequestApiParam) + ) + return this.authControllerMapper.mapCredentialCallbackResponseToAuthCallbackApiResponse(verifiedEthereumEip712Signature2021) + } +} diff --git a/apps/vc-api/src/api/auth/auth.get-auth-url.request.api.ts b/apps/vc-api/src/api/auth/auth.get-auth-url.request.api.ts index 2868e48..f73e45c 100644 --- a/apps/vc-api/src/api/auth/auth.get-auth-url.request.api.ts +++ b/apps/vc-api/src/api/auth/auth.get-auth-url.request.api.ts @@ -1,22 +1,20 @@ import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; enum AuthName { - Google = 'google', - Facebook = 'facebook', Twitter = 'twitter', Github = 'github', Discord = 'discord', Telegram = 'telegram', } -export class AuthGetAuthUrlRequestApi +export class AuthGetAuthUrlRequestApiRequestParam { @IsNotEmpty() @IsEnum(AuthName) authName: AuthName } -export class AuthGetAuthUrlRequestQueryApi +export class AuthGetAuthUrlApiRequestQuery { @IsOptional() @IsString() diff --git a/apps/vc-api/src/api/auth/auth.read.controller.ts b/apps/vc-api/src/api/auth/auth.read.controller.ts deleted file mode 100644 index 11a1da7..0000000 --- a/apps/vc-api/src/api/auth/auth.read.controller.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Controller, Get, Inject, Param, Query, Res } from '@nestjs/common'; -import { - CREDENTIAL_CREATOR_FACADE, - ICredentialCreatorFacade -} from '../../core/applications/credentials/facade/icredential.facade'; -import { AuthGetAuthUrlRequestApi, AuthGetAuthUrlRequestQueryApi } from './auth.get-auth-url.request.api'; -import { Response } from 'express'; -@Controller('auth') -export class AuthReadController { - - constructor( - @Inject(CREDENTIAL_CREATOR_FACADE) - private readonly credentialCreatorFacade: ICredentialCreatorFacade, - ) {} - - @Get(':authName') - async getAuthUrl( - @Param() authGetAuthUrlRequestApi: AuthGetAuthUrlRequestApi, - @Res() res: Response - ): Promise { - const redirect = await this.credentialCreatorFacade.getAuthUrl(authGetAuthUrlRequestApi.authName) - if(redirect.startsWith('http')) { - res.redirect(redirect) - return - } - res.send(redirect) - } - - @Get(':authName/callback') - async callback( - @Param() authGetAuthUrlRequestApi: AuthGetAuthUrlRequestApi, - @Query() query: AuthGetAuthUrlRequestQueryApi, - @Res() res: Response - ): Promise { - const url = await this.credentialCreatorFacade.callback(authGetAuthUrlRequestApi.authName, query) - res.status(200).send('Success') - } -} diff --git a/apps/vc-api/src/api/auth/mapper/auth.controller.mapper.ts b/apps/vc-api/src/api/auth/mapper/auth.controller.mapper.ts new file mode 100644 index 0000000..230953e --- /dev/null +++ b/apps/vc-api/src/api/auth/mapper/auth.controller.mapper.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { IAuthControllerMapper } from './iauth.controller.mapper'; +import { AuthCallbackApiResponse } from '../auth.callback.response.api'; +import { CredentialCallbackResponse } from '../../../core/applications/credentials/facade/credential.callback.response'; +import { AuthGetAuthUrlApiRequestQuery, AuthGetAuthUrlRequestApiRequestParam } from '../auth.get-auth-url.request.api'; +import { CredentialCallbackRequest } from '../../../core/applications/credentials/facade/credential.callback.request'; + +@Injectable() +export class AuthControllerMapper implements IAuthControllerMapper { + + constructor() {} + + mapCredentialCallbackResponseToAuthCallbackApiResponse( + credentialCallbackResponse: CredentialCallbackResponse, + ): AuthCallbackApiResponse { + return { + dataKey: credentialCallbackResponse.dataKey, + verifiedCredential: { + type: credentialCallbackResponse.verifiableCredential.type, + '@context': credentialCallbackResponse.verifiableCredential['@context'], + credentialSubject: credentialCallbackResponse.verifiableCredential.credentialSubject, + issuer: credentialCallbackResponse.verifiableCredential.issuer, + proof: credentialCallbackResponse.verifiableCredential.proof, + expirationDate: credentialCallbackResponse.verifiableCredential.expirationDate, + issuanceDate: credentialCallbackResponse.verifiableCredential.issuanceDate, + }, + } + } + + mapAuthCallbackApiRequestToCredentialCallbackRequest( + authCallbackApiRequestQuery: AuthGetAuthUrlApiRequestQuery, + authCallbackApiRequestParams: AuthGetAuthUrlRequestApiRequestParam + ): CredentialCallbackRequest { + return { + credentialName: authCallbackApiRequestParams.authName, + callbackData: authCallbackApiRequestQuery + } + } +} diff --git a/apps/vc-api/src/api/auth/mapper/iauth.controller.mapper.ts b/apps/vc-api/src/api/auth/mapper/iauth.controller.mapper.ts new file mode 100644 index 0000000..4b7e849 --- /dev/null +++ b/apps/vc-api/src/api/auth/mapper/iauth.controller.mapper.ts @@ -0,0 +1,17 @@ +import { AuthCallbackApiResponse } from '../auth.callback.response.api'; +import { CredentialCallbackResponse } from '../../../core/applications/credentials/facade/credential.callback.response'; +import { AuthGetAuthUrlApiRequestQuery, AuthGetAuthUrlRequestApiRequestParam } from '../auth.get-auth-url.request.api'; +import { CredentialCallbackRequest } from '../../../core/applications/credentials/facade/credential.callback.request'; + +export const AUTH_CONTROLLER_MAPPER = 'AUTH_CONTROLLER_MAPPER'; + +export interface IAuthControllerMapper { + mapCredentialCallbackResponseToAuthCallbackApiResponse( + credentialCallbackResponse: CredentialCallbackResponse, + ): AuthCallbackApiResponse; + + mapAuthCallbackApiRequestToCredentialCallbackRequest( + authCallbackApiRequestQuery: AuthGetAuthUrlApiRequestQuery, + authCallbackApiRequestParams: AuthGetAuthUrlRequestApiRequestParam + ): CredentialCallbackRequest; +} diff --git a/apps/vc-api/src/config/env.validation.ts b/apps/vc-api/src/config/env.validation.ts index 9fd836c..ae4094a 100644 --- a/apps/vc-api/src/config/env.validation.ts +++ b/apps/vc-api/src/config/env.validation.ts @@ -1,14 +1,14 @@ -import { plainToClass } from 'class-transformer'; +import { plainToClass, Transform } from 'class-transformer'; import { IsEnum, - IsNumber, + IsNumber, IsNumberString, IsString, registerDecorator, validateSync, - ValidationOptions, + ValidationOptions } from 'class-validator'; import { ethers } from 'ethers'; -import { Environment, EnvironmentType } from '../core/domain/entities/environment'; +import { ChainId, Environment, EnvironmentType, SupportedChainIds } from '../core/domain/entities/environment'; class EnvironmentVariables implements Environment{ @IsString({ message: 'SIGNING_PRIVATE_KEY must be a string' }) @@ -19,6 +19,23 @@ class EnvironmentVariables implements Environment{ @IsEnum(EnvironmentType, { message: 'ENVIRONMENT must be a valid environment' }) ENVIRONMENT!: EnvironmentType; + @Transform(({ value }) => { + const intValue = parseInt(value) + if (isNaN(intValue)) { + throw new Error('CHAIN_ID must be a number') + } + + const isValidChainId = (x: any): x is ChainId => SupportedChainIds.includes(x); + + if (!isValidChainId(intValue)) { + throw new Error('CHAIN_ID must be a valid chain id (1, 11155111)') + } + }) + CHAIN_ID!: ChainId; + + @IsString({ message: 'ENS_DOMAIN must be a string' }) + ENS_DOMAIN!: string; + @IsString({message: 'INFURA_PROJECT_ID must be a string'}) INFURA_PROJECT_ID!: string; diff --git a/apps/vc-api/src/core/applications/credentials/facade/credential.callback.request.ts b/apps/vc-api/src/core/applications/credentials/facade/credential.callback.request.ts new file mode 100644 index 0000000..fbb4947 --- /dev/null +++ b/apps/vc-api/src/core/applications/credentials/facade/credential.callback.request.ts @@ -0,0 +1,6 @@ +import { AllCallback } from './subjects-resolvers/subjects/callback/all.callback'; + +export class CredentialCallbackRequest { + credentialName: string; + callbackData: AllCallback +} diff --git a/apps/vc-api/src/core/applications/credentials/facade/credential.callback.response.ts b/apps/vc-api/src/core/applications/credentials/facade/credential.callback.response.ts new file mode 100644 index 0000000..233ee90 --- /dev/null +++ b/apps/vc-api/src/core/applications/credentials/facade/credential.callback.response.ts @@ -0,0 +1,6 @@ +import { VerifiedEthereumEip712Signature2021 } from '../../../domain/entities/eip712'; + +export class CredentialCallbackResponse { + dataKey: string; + verifiableCredential: VerifiedEthereumEip712Signature2021; +} diff --git a/apps/vc-api/src/core/applications/credentials/facade/credential.facade.ts b/apps/vc-api/src/core/applications/credentials/facade/credential.facade.ts index 8d42486..24d83c5 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/credential.facade.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/credential.facade.ts @@ -1,8 +1,9 @@ -import {Inject, Injectable} from "@nestjs/common"; -import {ICredentialCreatorFacade} from "./icredential.facade"; -import {VerifiedEthereumEip712Signature2021} from "../../../domain/entities/eip712"; -import {ICredentialFacadeRequest} from "./icredential.facade.request"; -import {ISubjectResolver, SUBJECT_RESOLVER} from "./subjects-resolvers/isubject.resolver"; +import { Inject, Injectable } from '@nestjs/common'; +import { ICredentialCreatorFacade } from './icredential.facade'; +import { VerifiedEthereumEip712Signature2021 } from '../../../domain/entities/eip712'; +import { ISubjectResolver, SUBJECT_RESOLVER } from './subjects-resolvers/isubject.resolver'; +import { CredentialCallbackRequest } from './credential.callback.request'; +import { CredentialCallbackResponse } from './credential.callback.response'; @Injectable() export class CredentialCreatorFacade implements ICredentialCreatorFacade { @@ -26,7 +27,7 @@ export class CredentialCreatorFacade implements ICredentialCreatorFacade { return this.getResolver(credentialName).getAuthUrl(); } - async callback(credentialName: string, body: T): Promise { - return this.getResolver(credentialName).callback(body); + async callback(credentialCallbackRequest: CredentialCallbackRequest): Promise { + return await this.getResolver(credentialCallbackRequest.credentialName).callback(credentialCallbackRequest.callbackData); } } diff --git a/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.request.ts b/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.request.ts deleted file mode 100644 index 9e006b7..0000000 --- a/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.request.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {CredentialSubject} from "../../../domain/entities/eip712"; - -export class ICredentialFacadeRequest { - credentialName: string; - credentialSubject: CredentialSubject -} diff --git a/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.ts b/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.ts index 3c73a1e..ffda2e6 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/icredential.facade.ts @@ -1,10 +1,11 @@ import {AbstractSubjectResolver} from "./subjects-resolvers/subjects/abstract.subject.resolver"; -import { AllCallback } from './subjects-resolvers/subjects/callback/all.callback'; +import { CredentialCallbackRequest } from './credential.callback.request'; +import { CredentialCallbackResponse } from './credential.callback.response'; export const CREDENTIAL_CREATOR_FACADE = 'CREDENTIAL_CREATOR_FACADE'; export interface ICredentialCreatorFacade { getResolver(credentialName: string): AbstractSubjectResolver ; getAuthUrl(credentialName: string): Promise; - callback(credentialName: string,code: AllCallback): Promise; + callback(credentialCallbackRequest: CredentialCallbackRequest): Promise; } diff --git a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/abstract.subject.resolver.ts b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/abstract.subject.resolver.ts index ed55e99..af60cfe 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/abstract.subject.resolver.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/abstract.subject.resolver.ts @@ -1,22 +1,27 @@ import { CredentialSubject, CredentialSubjectValue, - EthereumEip712Signature2021, VerifiedEthereumEip712Signature2021 + EthereumEip712Signature2021, + VerifiedEthereumEip712Signature2021, } from '../../../../../domain/entities/eip712'; import { ICredentialCreator } from '../../../creator/icredential.creator'; import { TimeGenerator } from '../../../../time.generator'; import { AllCallback } from './callback/all.callback'; import { IEnvironmentGetter } from '../../../../environment/ienvironment.getter'; +import { CredentialCallbackResponse } from '../../credential.callback.response'; -export abstract class AbstractSubjectResolver { - +/* eslint-disable @typescript-eslint/ban-types */ +export abstract class AbstractSubjectResolver< + T extends AllCallback = {}, + K extends CredentialSubjectValue = {} +> { credentialCreator: ICredentialCreator; - dateGenerator: TimeGenerator + dateGenerator: TimeGenerator; environmentGetter: IEnvironmentGetter; protected constructor( injectedCredentialCreator: ICredentialCreator, injectedDateGenerator: TimeGenerator, - injectedEnvironmentGetter: IEnvironmentGetter, + injectedEnvironmentGetter: IEnvironmentGetter ) { this.credentialCreator = injectedCredentialCreator; this.dateGenerator = injectedDateGenerator; @@ -31,43 +36,74 @@ export abstract class AbstractSubjectResolver; + abstract callbackSuccessful( + data: T + ): Promise; abstract getCallbackParameters(): string[]; + getDataKey(): string { + return ( + this.getCredentialName().toLowerCase() + + '_' + + this.environmentGetter.getEnsDomain().toLowerCase() + ); + } + getCallbackUrl(): string { return `${this.environmentGetter.getApiDomain()}/auth/${this.getCredentialName()}/callback`; } - callback(data: T): Promise { - console.log("Callback data", data); + async callback(data: T): Promise { if (this.checkCallbackParametersHaveAllRequiredFields(data)) { - return this.callbackSuccessful(data); + const vc = await this.callbackSuccessful(data); + this.successfulVerification(vc); + return { + dataKey: this.getDataKey(), + verifiableCredential: vc, + }; } else { - throw new Error("Callback parameters are missing"); + throw new Error('Callback parameters are missing'); } } checkCallbackParametersHaveAllRequiredFields(data: T): boolean { - return this.getCallbackParameters().every((param) => data[param] !== undefined); + return this.getCallbackParameters().every( + (param) => data[param] !== undefined + ); } getExpirationPeriod(): number { return 3; } - async generateCredentialSubject(credentialSubject: CredentialSubject & K): Promise> { + // TODO: Implement this + successfulVerification( + vc: VerifiedEthereumEip712Signature2021 + ): Promise { + return Promise.resolve(); + } + + async generateCredentialSubject( + credentialSubject: CredentialSubject & K + ): Promise> { const ethereumEip712Signature2021 = new EthereumEip712Signature2021({ type: this.getType(), context: this.getContext(), credentialSubject: credentialSubject, issuanceDate: this.dateGenerator.generate(), - expirationDate: new Date(this.dateGenerator.generateWithOffset(this.getExpirationPeriod(), "months")), + expirationDate: new Date( + this.dateGenerator.generateWithOffset( + this.getExpirationPeriod(), + 'months' + ) + ), }); - const verified = await this.credentialCreator.createCredential(ethereumEip712Signature2021) as VerifiedEthereumEip712Signature2021 + const verified = (await this.credentialCreator.createCredential( + ethereumEip712Signature2021 + )) as VerifiedEthereumEip712Signature2021; return verified; } - } diff --git a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/discord.subject.resolver.ts b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/discord.subject.resolver.ts index 43e0547..b7b5283 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/discord.subject.resolver.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/discord.subject.resolver.ts @@ -1,19 +1,28 @@ import { AbstractSubjectResolver } from './abstract.subject.resolver'; import { DiscordCredential } from '../../../../../domain/credentials/discord.credential'; import { Inject } from '@nestjs/common'; -import { ENVIRONMENT_GETTER, IEnvironmentGetter } from '../../../../environment/ienvironment.getter'; +import { + ENVIRONMENT_GETTER, + IEnvironmentGetter, +} from '../../../../environment/ienvironment.getter'; import { HttpService } from '@nestjs/axios'; -import { CREDENTIAL_CREATOR, ICredentialCreator } from '../../../creator/icredential.creator'; +import { + CREDENTIAL_CREATOR, + ICredentialCreator, +} from '../../../creator/icredential.creator'; import { TIME_GENERATOR, TimeGenerator } from '../../../../time.generator'; import { DiscordCallback } from './callback/discord.callback'; import { DiscordToken } from './token/discord.token'; import { DiscordAuth } from './auth/discord.auth'; +import { VerifiedEthereumEip712Signature2021 } from '../../../../../domain/entities/eip712'; -export class DiscordSubjectResolver extends AbstractSubjectResolver { - - discordAuthUrl = "https://discord.com/api/oauth2/authorize"; - discordTokenUrl = "https://discord.com/api/oauth2/token"; - discordUserUrl = "https://discord.com/api/users/@me"; +export class DiscordSubjectResolver extends AbstractSubjectResolver< + DiscordCallback, + DiscordCredential +> { + discordAuthUrl = 'https://discord.com/api/oauth2/authorize'; + discordTokenUrl = 'https://discord.com/api/oauth2/token'; + discordUserUrl = 'https://discord.com/api/users/@me'; constructor( @Inject(ENVIRONMENT_GETTER) @@ -25,50 +34,49 @@ export class DiscordSubjectResolver extends AbstractSubjectResolver { + async callbackSuccessful( + params: DiscordCallback + ): Promise { const response = await this.httpService.axiosRef.post( this.discordTokenUrl, new URLSearchParams({ client_id: this.environmentGetter.getDiscordClientId(), client_secret: this.environmentGetter.getDiscordClientSecret(), - grant_type: "authorization_code", + grant_type: 'authorization_code', code: params.code, redirect_uri: `${this.environmentGetter.getApiDomain()}/auth/discord/callback`, - scope: "identify", + scope: 'identify', }).toString(), { headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, } ); @@ -77,17 +85,18 @@ export class DiscordSubjectResolver extends AbstractSubjectResolver( - this.discordUserUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); + this.discordUserUrl, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); const verifiedCredential = await this.generateCredentialSubject({ - // TODO: global_name or username? username: userResponse.data.global_name, }); - console.log(verifiedCredential); + return verifiedCredential; } } diff --git a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/github.subject.resolver.ts b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/github.subject.resolver.ts index bb95659..c14f920 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/github.subject.resolver.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/github.subject.resolver.ts @@ -1,18 +1,28 @@ import { AbstractSubjectResolver } from './abstract.subject.resolver'; import { GithubCredential } from '../../../../../domain/credentials/github.credential'; import { Inject } from '@nestjs/common'; -import { ENVIRONMENT_GETTER, IEnvironmentGetter } from '../../../../environment/ienvironment.getter'; +import { + ENVIRONMENT_GETTER, + IEnvironmentGetter, +} from '../../../../environment/ienvironment.getter'; import { HttpService } from '@nestjs/axios'; -import { CREDENTIAL_CREATOR, ICredentialCreator } from '../../../creator/icredential.creator'; +import { + CREDENTIAL_CREATOR, + ICredentialCreator, +} from '../../../creator/icredential.creator'; import { TIME_GENERATOR, TimeGenerator } from '../../../../time.generator'; import { GithubCallback } from './callback/github.callback'; import { GithubToken } from './token/github.token'; import { GithubAuth } from './auth/github.auth'; +import { VerifiedEthereumEip712Signature2021 } from '../../../../../domain/entities/eip712'; -export class GithubSubjectResolver extends AbstractSubjectResolver { - githubAuthUrl = "https://github.com/login/oauth/authorize"; - githubTokenUrl = "https://github.com/login/oauth/access_token"; - githubUserUrl = "https://api.github.com/user"; +export class GithubSubjectResolver extends AbstractSubjectResolver< + GithubCallback, + GithubCredential +> { + githubAuthUrl = 'https://github.com/login/oauth/authorize'; + githubTokenUrl = 'https://github.com/login/oauth/access_token'; + githubUserUrl = 'https://api.github.com/user'; constructor( @Inject(ENVIRONMENT_GETTER) @@ -24,27 +34,23 @@ export class GithubSubjectResolver extends AbstractSubjectResolver { + async callbackSuccessful( + params: GithubCallback + ): Promise { const response = await this.httpService.axiosRef.post( this.githubTokenUrl, { @@ -65,25 +73,26 @@ export class GithubSubjectResolver extends AbstractSubjectResolver( - this.githubUserUrl, { - headers: { - Authorization: `token ${accessToken}`, - }, - }); - + const userResponse = await this.httpService.axiosRef.get( + this.githubUserUrl, + { + headers: { + Authorization: `token ${accessToken}`, + }, + } + ); const verifiedCredential = await this.generateCredentialSubject({ username: userResponse.data.login, }); - console.log(verifiedCredential); + return verifiedCredential; } } diff --git a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/telegram.subject.resolver.ts b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/telegram.subject.resolver.ts index c9a513c..b30f8b1 100644 --- a/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/telegram.subject.resolver.ts +++ b/apps/vc-api/src/core/applications/credentials/facade/subjects-resolvers/subjects/telegram.subject.resolver.ts @@ -1,14 +1,24 @@ import { AbstractSubjectResolver } from './abstract.subject.resolver'; import { Inject } from '@nestjs/common'; -import { ENVIRONMENT_GETTER, IEnvironmentGetter } from '../../../../environment/ienvironment.getter'; -import { CREDENTIAL_CREATOR, ICredentialCreator } from '../../../creator/icredential.creator'; +import { + ENVIRONMENT_GETTER, + IEnvironmentGetter, +} from '../../../../environment/ienvironment.getter'; +import { + CREDENTIAL_CREATOR, + ICredentialCreator, +} from '../../../creator/icredential.creator'; import { TIME_GENERATOR, TimeGenerator } from '../../../../time.generator'; import { HttpService } from '@nestjs/axios'; import * as crypto from 'crypto'; import { TelegramCredential } from '../../../../../domain/credentials/telegram.credential'; import { TelegramCallback } from './callback/telegram.callback'; +import { VerifiedEthereumEip712Signature2021 } from '../../../../../domain/entities/eip712'; -export class TelegramSubjectResolver extends AbstractSubjectResolver { +export class TelegramSubjectResolver extends AbstractSubjectResolver< + TelegramCallback, + TelegramCredential +> { constructor( @Inject(ENVIRONMENT_GETTER) readonly environmentGetter: IEnvironmentGetter, @@ -19,14 +29,12 @@ export class TelegramSubjectResolver extends AbstractSubjectResolver { + async callbackSuccessful( + params: TelegramCallback + ): Promise { const { hash, ...telegramData } = params; const dataCheckString = Object.keys(telegramData) @@ -34,7 +42,10 @@ export class TelegramSubjectResolver extends AbstractSubjectResolver `${key}=${telegramData[key]}`) .join('\n'); - const secretKey = crypto.createHash('sha256').update(process.env.TELEGRAM_BOT_TOKEN!).digest(); + const secretKey = crypto + .createHash('sha256') + .update(process.env.TELEGRAM_BOT_TOKEN!) + .digest(); const hmac = crypto.createHmac('sha256', secretKey).update(dataCheckString); const calculatedHash = hmac.digest('hex'); @@ -44,11 +55,10 @@ export class TelegramSubjectResolver extends AbstractSubjectResolver { - +export class TwitterSubjectResolver extends AbstractSubjectResolver< + TwitterCallback, + TwitterCredential +> { // needs to be changed - codeVerifier = "1234567890123456789012345678901234567890123456789012345678901234"; + codeVerifier = + '1234567890123456789012345678901234567890123456789012345678901234'; - twitterAuthUrl = "https://twitter.com/i/oauth2/authorize"; - twitterTokenUrl = "https://api.twitter.com/2/oauth2/token"; - twitterUserUrl = "https://api.twitter.com/2/users/me"; + twitterAuthUrl = 'https://twitter.com/i/oauth2/authorize'; + twitterTokenUrl = 'https://api.twitter.com/2/oauth2/token'; + twitterUserUrl = 'https://api.twitter.com/2/users/me'; constructor( @Inject(ENVIRONMENT_GETTER) @@ -29,43 +38,44 @@ export class TwitterSubjectResolver extends AbstractSubjectResolver { - + async callbackSuccessful( + params: TwitterCallback + ): Promise { const response = await this.httpService.axiosRef.post( this.twitterTokenUrl, new URLSearchParams({ - grant_type: "authorization_code", + grant_type: 'authorization_code', code: params.code, redirect_uri: this.getCallbackUrl(), client_id: this.environmentGetter.getTwitterClientId(), @@ -73,10 +83,10 @@ export class TwitterSubjectResolver extends AbstractSubjectResolver( - this.twitterUserUrl, + this.twitterUserUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); const verifiedCredential = await this.generateCredentialSubject({ username: userResponse.data.data.username, }); - console.log(verifiedCredential); + return verifiedCredential; } } diff --git a/apps/vc-api/src/core/applications/environment/environment.getter.ts b/apps/vc-api/src/core/applications/environment/environment.getter.ts index 36b3dd3..25a10f8 100644 --- a/apps/vc-api/src/core/applications/environment/environment.getter.ts +++ b/apps/vc-api/src/core/applications/environment/environment.getter.ts @@ -1,6 +1,6 @@ import {IEnvironmentGetter} from "./ienvironment.getter"; import {ConfigService} from "@nestjs/config"; -import { Environment, EnvironmentType } from '../../domain/entities/environment'; +import { ChainId, Environment, EnvironmentType } from '../../domain/entities/environment'; import {Injectable} from "@nestjs/common"; @Injectable() @@ -18,6 +18,14 @@ export class EnvironmentGetter implements IEnvironmentGetter { return this.configService.get('ENVIRONMENT'); } + getChainId(): ChainId { + return parseInt(this.configService.get('CHAIN_ID')) as ChainId; + } + + getEnsDomain(): string { + return this.configService.get('ENS_DOMAIN'); + } + getInfuraProjectId(): string { return this.configService.get('INFURA_PROJECT_ID'); } diff --git a/apps/vc-api/src/core/applications/environment/ienvironment.getter.ts b/apps/vc-api/src/core/applications/environment/ienvironment.getter.ts index 210278b..3a6201f 100644 --- a/apps/vc-api/src/core/applications/environment/ienvironment.getter.ts +++ b/apps/vc-api/src/core/applications/environment/ienvironment.getter.ts @@ -1,10 +1,12 @@ -import {EnvironmentType} from "../../domain/entities/environment"; +import { ChainId, EnvironmentType } from '../../domain/entities/environment'; export const ENVIRONMENT_GETTER = 'ENVIRONMENT_GETTER' export interface IEnvironmentGetter { getPk(): string; getEnv(): EnvironmentType; + getEnsDomain(): string; + getChainId(): ChainId getApiDomain(): string; getInfuraProjectId(): string; getGithubClientId(): string; diff --git a/apps/vc-api/src/core/applications/key-management/ikey-management.fetcher.ts b/apps/vc-api/src/core/applications/key-management/ikey-management.fetcher.ts index 7e7470b..36b6f29 100644 --- a/apps/vc-api/src/core/applications/key-management/ikey-management.fetcher.ts +++ b/apps/vc-api/src/core/applications/key-management/ikey-management.fetcher.ts @@ -3,5 +3,5 @@ import {SigningWallet} from "../../domain/entities/signingWallet"; export const KEY_MANAGEMENT_FETCHER = 'KEY_MANAGEMENT_FETCHER'; export interface IKeyManagementFetcher { - fetchKeyManagement(): SigningWallet; + fetchKey(): SigningWallet; } diff --git a/apps/vc-api/src/core/domain/entities/eip712.ts b/apps/vc-api/src/core/domain/entities/eip712.ts index 1de6a1f..4e1c1ba 100644 --- a/apps/vc-api/src/core/domain/entities/eip712.ts +++ b/apps/vc-api/src/core/domain/entities/eip712.ts @@ -4,6 +4,7 @@ export type PrimitiveValue = string | number | boolean | null; export type CredentialSubjectValue = Record; +/* eslint-disable @typescript-eslint/ban-types */ export class EthereumEip712Signature2021 { credentialSubject: CredentialSubject & T; issuanceDate: Date; diff --git a/apps/vc-api/src/core/domain/entities/environment.ts b/apps/vc-api/src/core/domain/entities/environment.ts index fef5db6..a697e26 100644 --- a/apps/vc-api/src/core/domain/entities/environment.ts +++ b/apps/vc-api/src/core/domain/entities/environment.ts @@ -6,11 +6,19 @@ export enum EnvironmentType { production = 'production', } +export const SupportedChainIds = [1,11155111] as const; + +export type ChainId = typeof SupportedChainIds[number]; + export class Environment { SIGNING_PRIVATE_KEY!: string; ENVIRONMENT!: EnvironmentType; + ENS_DOMAIN!: string; + + CHAIN_ID!: ChainId; + INFURA_PROJECT_ID!: string; API_DOMAIN!: string; diff --git a/apps/vc-api/src/external/credentails/credential.agent.initiator.ts b/apps/vc-api/src/external/credentails/credential.agent.initiator.ts index e6ca1f3..489a931 100644 --- a/apps/vc-api/src/external/credentails/credential.agent.initiator.ts +++ b/apps/vc-api/src/external/credentails/credential.agent.initiator.ts @@ -14,13 +14,13 @@ export class CredentialAgentInitiator { this.agent = agent; } - async createAgentWithIdentifier(publicKey: string, privateKey: string): Promise<{ + async createAgentWithIdentifier(ensDomain: string, publicKey: string, privateKey: string): Promise<{ agent: Agent, identifier: Identifier }> { const identifier = await this.agent.didManagerImport({ - did: 'did:ethr:' + publicKey, - provider: 'did:ethr', + did: 'did:ens:' + ensDomain + '#' + publicKey, + provider: 'did:ens', keys: [{ privateKeyHex: privateKey, type: 'Secp256k1', diff --git a/apps/vc-api/src/external/credentails/credential.agent.ts b/apps/vc-api/src/external/credentails/credential.agent.ts index c42db5f..c893809 100644 --- a/apps/vc-api/src/external/credentails/credential.agent.ts +++ b/apps/vc-api/src/external/credentails/credential.agent.ts @@ -29,13 +29,16 @@ export class CredentialAgent implements ICredentialCreator, ICredentialVerifier, async onModuleInit(){ - const { agent, identifier } = await this.agentInitiator.createAgentWithIdentifier(this.keyManagementFetcher.fetchKeyManagement().publicKey, this.keyManagementFetcher.fetchKeyManagement().privateKey) + const { agent, identifier } = await this.agentInitiator.createAgentWithIdentifier( + this.environmentGetter.getEnsDomain(), + this.keyManagementFetcher.fetchKey().publicKey, + this.keyManagementFetcher.fetchKey().privateKey + ) this.agent = agent this.identifier = identifier } async createCredential(credential: EthereumEip712Signature2021): Promise { - console.debug('Creating credential', credential) const verifiedCredential = await this.agent.createVerifiableCredentialEIP712( this.credentialAgentMapper.mapEthereumEip712Signature2021ToVeramoICreateVerifiableCredentialEIP712Args(this.identifier.did,credential) ) diff --git a/apps/vc-api/src/external/key-management/key-management.fetcher.ts b/apps/vc-api/src/external/key-management/key-management.fetcher.ts index fa68ec2..9b9e0e7 100644 --- a/apps/vc-api/src/external/key-management/key-management.fetcher.ts +++ b/apps/vc-api/src/external/key-management/key-management.fetcher.ts @@ -17,7 +17,7 @@ export class KeyManagementFetcher implements IKeyManagementFetcher { } - fetchKeyManagement(): SigningWallet { + fetchKey(): SigningWallet { return { publicKey: this.wallet.address, privateKey: this.wallet.privateKey diff --git a/apps/vc-api/src/main.ts b/apps/vc-api/src/main.ts index 46a6b6e..4d1b2e2 100644 --- a/apps/vc-api/src/main.ts +++ b/apps/vc-api/src/main.ts @@ -4,19 +4,27 @@ */ import { NestFactory } from '@nestjs/core'; -import { HttpException, Logger, ValidationPipe, ValidationError, VersioningType } from '@nestjs/common'; +import { + HttpException, + Logger, + ValidationPipe, + ValidationError, + VersioningType, +} from '@nestjs/common'; import { VCManagementModule } from './vc-management.module'; import { NestExpressApplication } from '@nestjs/platform-express'; +import * as process from 'node:process'; async function bootstrap() { const app = await NestFactory.create( VCManagementModule, - { - bufferLogs: true, - rawBody: true, - } + { + bufferLogs: true, + rawBody: true, + } ); - const globalPrefix = ''; + + const globalPrefix = 'verifications'; app.useBodyParser('json'); @@ -44,12 +52,24 @@ async function bootstrap() { }, }) ); - app.setGlobalPrefix(globalPrefix); + + const version = '1'; + + app + .enableVersioning({ + type: VersioningType.URI, + defaultVersion: version, + }) + .setGlobalPrefix(globalPrefix); + const port = process.env.PORT || 3000; await app.listen(port); - Logger.log( - `🚀 Application is running on: http://localhost:${port}/${globalPrefix}` - ); + + if (process.env.NODE_ENV === 'development') { + Logger.log( + `🚀 Application is running on: http://localhost:${port}/${globalPrefix}/v${version}` + ); + } } bootstrap(); diff --git a/apps/vc-api/src/vc-management.module.ts b/apps/vc-api/src/vc-management.module.ts index 2041241..dea8cd9 100644 --- a/apps/vc-api/src/vc-management.module.ts +++ b/apps/vc-api/src/vc-management.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import {ConfigModule} from "@nestjs/config"; import {validate} from "./config/env.validation"; -import { AuthReadController } from './api/auth/auth.read.controller'; +import { AuthController } from './api/auth/auth.controller'; import { CREDENTIAL_CREATOR_FACADE } from './core/applications/credentials/facade/icredential.facade'; import { CredentialCreatorFacade } from './core/applications/credentials/facade/credential.facade'; import { SUBJECT_RESOLVER } from './core/applications/credentials/facade/subjects-resolvers/isubject.resolver'; @@ -35,6 +35,8 @@ import { import { TwitterSubjectResolver } from './core/applications/credentials/facade/subjects-resolvers/subjects/twitter.subject.resolver'; +import { AuthControllerMapper } from './api/auth/mapper/auth.controller.mapper'; +import { AUTH_CONTROLLER_MAPPER } from './api/auth/mapper/iauth.controller.mapper'; const dynamicImport = async (packageName: string) => new Function(`return import('${packageName}')`)(); @@ -47,9 +49,13 @@ const dynamicImport = async (packageName: string) => HttpModule ], controllers: [ - AuthReadController + AuthController ], providers: [ + { + useClass: AuthControllerMapper, + provide: AUTH_CONTROLLER_MAPPER + }, { useClass: CredentialCreatorFacade, provide: CREDENTIAL_CREATOR_FACADE @@ -127,12 +133,16 @@ const dynamicImport = async (packageName: string) => }), new DIDResolverPlugin({ resolver: new Resolver({ - ...ethrDidResolver({ infuraProjectId }), - ...webDidResolver(), + ...ethrDidResolver({ + networks: [ + { name: 'sepolia', rpcUrl: 'https://sepolia.infura.io/v3/' + infuraProjectId }, + { rpcUrl: 'https://mainnet.infura.io/v3/' + infuraProjectId } + ] + }), ...ensDidResolver({ networks: [ { name: 'sepolia', rpcUrl: 'https://sepolia.infura.io/v3/' + infuraProjectId }, - { name: 'mainnet', rpcUrl: 'https://mainnet.infura.io/v3/' + infuraProjectId } + { rpcUrl: 'https://mainnet.infura.io/v3/' + infuraProjectId } ] }) }) diff --git a/k8s-staging/staging-secrets.yaml b/k8s-staging/staging-secrets.yaml new file mode 100644 index 0000000..ce85bfd --- /dev/null +++ b/k8s-staging/staging-secrets.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: secrets-manager-access-sa + namespace: verifications-staging-namespace + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::905418196669:role/justaname-staging-cluster-eksClusterRole +--- +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: aws-secrets + namespace: verifications-staging-namespace + annotations: + last-modified: "2024-04-04" +spec: + provider: aws + secretObjects: + - secretName: foosecret-verifications + type: Opaque + data: + - objectName: infura_project_id + key: INFURA_PROJECT_ID + - objectName: signing_private_key + key: SIGNING_PRIVATE_KEY + - objectName: environment + key: ENVIRONMENT + - objectName: api_domain + key: API_DOMAIN + - objectName: github_client_id + key: GITHUB_CLIENT_ID + - objectName: github_client_secret + key: GITHUB_CLIENT_SECRET + - objectName: twitter_client_id + key: TWITTER_CLIENT_ID + - objectName: twitter_client_secret + key: TWITTER_CLIENT_SECRET + - objectName: telegram_bot_token + key: TELEGRAM_BOT_TOKEN + - objectName: telegram_bot_username + key: TELEGRAM_BOT_USERNAME + - objectName: discord_client_id + key: DISCORD_CLIENT_ID + - objectName: discord_client_secret + key: DISCORD_CLIENT_SECRET + - objectName: ens_domain + key: ENS_DOMAIN + - objectName: chain_id + key: CHAIN_ID + + + parameters: + region: eu-central-1 + objects: | + - objectName: "justaname-staging-verifications-env-var" + objectType: "secretsmanager" + jmesPath: + - path: INFURA_PROJECT_ID + objectAlias: infura_project_id + - path: SIGNING_PRIVATE_KEY + objectAlias: signing_private_key + - path: ENVIRONMENT + objectAlias: environment + - path: API_DOMAIN + objectAlias: api_domain + - path: GITHUB_CLIENT_ID + objectAlias: github_client_id + - path: GITHUB_CLIENT_SECRET + objectAlias: github_client_secret + - path: TWITTER_CLIENT_ID + objectAlias: twitter_client_id + - path: TWITTER_CLIENT_SECRET + objectAlias: twitter_client_secret + - path: TELEGRAM_BOT_TOKEN + objectAlias: telegram_bot_token + - path: TELEGRAM_BOT_USERNAME + objectAlias: telegram_bot_username + - path: DISCORD_CLIENT_ID + objectAlias: discord_client_id + - path: DISCORD_CLIENT_SECRET + objectAlias: discord_client_secret + - path: ENS_DOMAIN + objectAlias: ens_domain + - path: CHAIN_ID + objectAlias: chain_id + diff --git a/k8s-staging/vc-api-deployment.yaml b/k8s-staging/vc-api-deployment.yaml new file mode 100644 index 0000000..b365ab4 --- /dev/null +++ b/k8s-staging/vc-api-deployment.yaml @@ -0,0 +1,118 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vc-api-deployment + labels: + app: analytics + namespace: verifications-staging-namespace +spec: + selector: + matchLabels: + app: vc-api + replicas: 1 + template: + metadata: + labels: + app: vc-api + spec: + serviceAccountName: secrets-manager-access-sa + volumes: + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: "aws-secrets" + containers: + - name: analytics + image: 905418196669.dkr.ecr.eu-central-1.amazonaws.com/justaname/vc-api:latest + ports: + - containerPort: 3009 + volumeMounts: + - mountPath: "/mnt/secrets-store" + name: secrets-store-inline + readOnly: true + env: + - name: INFURA_PROJECT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: INFURA_PROJECT_ID + - name: SIGNING_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: SIGNING_PRIVATE_KEY + - name: ENVIRONMENT + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: ENVIRONMENT + - name: API_DOMAIN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: API_DOMAIN + - name: GITHUB_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: GITHUB_CLIENT_ID + - name: GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: GITHUB_CLIENT_SECRET + - name: TWITTER_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TWITTER_CLIENT_ID + - name: TWITTER_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TWITTER_CLIENT_SECRET + - name: TELEGRAM_BOT_TOKEN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TELEGRAM_BOT_TOKEN + - name: TELEGRAM_BOT_USERNAME + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TELEGRAM_BOT_USERNAME + - name: DISCORD_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: DISCORD_CLIENT_ID + - name: DISCORD_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: DISCORD_CLIENT_SECRET + - name: ENS_DOMAIN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: ENS_DOMAIN + - name: CHAIN_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: CHAIN_ID +--- +apiVersion: v1 +kind: Service +metadata: + name: vc-api-service + namespace: verifications-staging-namespace +spec: + selector: + app: vc-api + ports: + - port: 3009 + targetPort: 3009 + type: ClusterIP diff --git a/k8s/secrets.yaml b/k8s/secrets.yaml new file mode 100644 index 0000000..b8b1653 --- /dev/null +++ b/k8s/secrets.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: secrets-manager-access-sa + namespace: verifications-namespace + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::905418196669:role/justaname-cluster-eksClusterRole +--- +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: aws-secrets + namespace: verifications-namespace + annotations: + last-modified: "2024-04-04" +spec: + provider: aws + secretObjects: + - secretName: foosecret-verifications + type: Opaque + data: + - objectName: infura_project_id + key: INFURA_PROJECT_ID + - objectName: signing_private_key + key: SIGNING_PRIVATE_KEY + - objectName: environment + key: ENVIRONMENT + - objectName: api_domain + key: API_DOMAIN + - objectName: github_client_id + key: GITHUB_CLIENT_ID + - objectName: github_client_secret + key: GITHUB_CLIENT_SECRET + - objectName: twitter_client_id + key: TWITTER_CLIENT_ID + - objectName: twitter_client_secret + key: TWITTER_CLIENT_SECRET + - objectName: telegram_bot_token + key: TELEGRAM_BOT_TOKEN + - objectName: telegram_bot_username + key: TELEGRAM_BOT_USERNAME + - objectName: discord_client_id + key: DISCORD_CLIENT_ID + - objectName: discord_client_secret + key: DISCORD_CLIENT_SECRET + - objectName: ens_domain + key: ENS_DOMAIN + - objectName: chain_id + key: CHAIN_ID + + + parameters: + region: eu-central-1 + objects: | + - objectName: "justaname-verifications-env-var" + objectType: "secretsmanager" + jmesPath: + - path: INFURA_PROJECT_ID + objectAlias: infura_project_id + - path: SIGNING_PRIVATE_KEY + objectAlias: signing_private_key + - path: ENVIRONMENT + objectAlias: environment + - path: API_DOMAIN + objectAlias: api_domain + - path: GITHUB_CLIENT_ID + objectAlias: github_client_id + - path: GITHUB_CLIENT_SECRET + objectAlias: github_client_secret + - path: TWITTER_CLIENT_ID + objectAlias: twitter_client_id + - path: TWITTER_CLIENT_SECRET + objectAlias: twitter_client_secret + - path: TELEGRAM_BOT_TOKEN + objectAlias: telegram_bot_token + - path: TELEGRAM_BOT_USERNAME + objectAlias: telegram_bot_username + - path: DISCORD_CLIENT_ID + objectAlias: discord_client_id + - path: DISCORD_CLIENT_SECRET + objectAlias: discord_client_secret + - path: ENS_DOMAIN + objectAlias: ens_domain + - path: CHAIN_ID + objectAlias: chain_id + diff --git a/k8s/vc-api-deployment.yaml b/k8s/vc-api-deployment.yaml new file mode 100644 index 0000000..cc4c369 --- /dev/null +++ b/k8s/vc-api-deployment.yaml @@ -0,0 +1,118 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vc-api-deployment + labels: + app: analytics + namespace: verifications-namespace +spec: + selector: + matchLabels: + app: vc-api + replicas: 1 + template: + metadata: + labels: + app: vc-api + spec: + serviceAccountName: secrets-manager-access-sa + volumes: + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: "aws-secrets" + containers: + - name: analytics + image: 905418196669.dkr.ecr.eu-central-1.amazonaws.com/justaname-production/vc-api:latest + ports: + - containerPort: 3009 + volumeMounts: + - mountPath: "/mnt/secrets-store" + name: secrets-store-inline + readOnly: true + env: + - name: INFURA_PROJECT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: INFURA_PROJECT_ID + - name: SIGNING_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: SIGNING_PRIVATE_KEY + - name: ENVIRONMENT + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: ENVIRONMENT + - name: API_DOMAIN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: API_DOMAIN + - name: GITHUB_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: GITHUB_CLIENT_ID + - name: GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: GITHUB_CLIENT_SECRET + - name: TWITTER_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TWITTER_CLIENT_ID + - name: TWITTER_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TWITTER_CLIENT_SECRET + - name: TELEGRAM_BOT_TOKEN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TELEGRAM_BOT_TOKEN + - name: TELEGRAM_BOT_USERNAME + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: TELEGRAM_BOT_USERNAME + - name: DISCORD_CLIENT_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: DISCORD_CLIENT_ID + - name: DISCORD_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: DISCORD_CLIENT_SECRET + - name: ENS_DOMAIN + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: ENS_DOMAIN + - name: CHAIN_ID + valueFrom: + secretKeyRef: + name: foosecret-verifications + key: CHAIN_ID +--- +apiVersion: v1 +kind: Service +metadata: + name: vc-api-service + namespace: verifications-namespace +spec: + selector: + app: vc-api + ports: + - port: 3009 + targetPort: 3009 + type: ClusterIP diff --git a/package.json b/package.json index a10cdc2..eef42e1 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.2", "@nestjs/platform-express": "^10.0.2", + "@nx-tools/container-metadata": "^6.0.2", + "@nestjs/swagger": "^7.4.0", "@veramo/core": "^6.0.0", "@veramo/credential-eip712": "^6.0.0", "@veramo/credential-ld": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index d2f3443..6d27579 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,7 +9,25 @@ dependencies: "@actions/io" "^1.0.1" -"@actions/io@^1.0.1": +"@actions/github@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.0.tgz#65883433f9d81521b782a64cc1fd45eef2191ea7" + integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g== + dependencies: + "@actions/http-client" "^2.2.0" + "@octokit/core" "^5.0.1" + "@octokit/plugin-paginate-rest" "^9.0.0" + "@octokit/plugin-rest-endpoint-methods" "^10.0.0" + +"@actions/http-client@^2.2.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + +"@actions/io@^1.0.1", "@actions/io@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71" integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q== @@ -1047,6 +1065,14 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== +"@babel/runtime-corejs3@^7.12.1": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.25.6.tgz#5e3facf42775cc95bcde95746e940061931286e4" + integrity sha512-Gz0Nrobx8szge6kQQ5Z5MX9L3ObqNwCQY1PSwSNzreFL7aHGxv8Fp2j3ETV6/wWdbiV+mW6OSm8oQhg3Tcsniw== + dependencies: + core-js-pure "^3.30.2" + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.22.6", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2" @@ -1904,6 +1930,11 @@ semver "^7.5.4" uuid "^9.0.1" +"@microsoft/tsdoc@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz#f29a55df17cb6e87cfbabce33ff6a14a9f85076d" + integrity sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA== + "@module-federation/bridge-react-webpack-plugin@0.6.3": version "0.6.3" resolved "https://registry.yarnpkg.com/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.6.3.tgz#a212c35d5157fc0df1598a7a8e999b45c90ebb64" @@ -2067,6 +2098,11 @@ path-to-regexp "3.2.0" tslib "2.6.3" +"@nestjs/mapped-types@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz#485d6b44e19779c98d04e52bd1d2bcc7001df0ea" + integrity sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg== + "@nestjs/platform-express@^10.0.2": version "10.4.1" resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.4.1.tgz#65d3b5a08c79b938a50464887408f78ce5d5932c" @@ -2099,6 +2135,18 @@ jsonc-parser "3.2.0" pluralize "8.0.0" +"@nestjs/swagger@^7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-7.4.0.tgz#e61dbefdfc1d4011327a256896953c74e511c850" + integrity sha512-dCiwKkRxcR7dZs5jtrGspBAe/nqJd1AYzOBTzw9iCdbq3BGrLpwokelk6lFZPe4twpTsPQqzNKBwKzVbI6AR/g== + dependencies: + "@microsoft/tsdoc" "^0.15.0" + "@nestjs/mapped-types" "2.0.5" + js-yaml "4.1.0" + lodash "4.17.21" + path-to-regexp "3.2.0" + swagger-ui-dist "5.17.14" + "@nestjs/testing@^10.0.2": version "10.4.1" resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.4.1.tgz#146c0161ab98524ea9fafe4ca5316229d1e44387" @@ -2260,6 +2308,30 @@ consola "^2.15.0" node-fetch "^2.6.1" +"@nx-tools/ci-context@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@nx-tools/ci-context/-/ci-context-6.0.1.tgz#fc28f2a3c4c3e019ac56cd3b1b3f3b8b95a74d17" + integrity sha512-+nZqVr6rZvSpqqbf9cZkiwvpixPx7EjJWAbIixueclakyYU8okZ20wVy30wd4wOmcnOcb4VxCdSAY4AqsUgseg== + dependencies: + "@actions/github" "^6.0.0" + "@nx-tools/core" "6.0.1" + "@octokit/openapi-types" "^22.0.0" + ci-info "^4.0.0" + properties-file "^3.5.4" + +"@nx-tools/container-metadata@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@nx-tools/container-metadata/-/container-metadata-6.0.2.tgz#4b8b574bb6b3ef222858ce7f93dc09b2f041dbd9" + integrity sha512-jAad8dEPaRX6SKvvTo6uIcPphD2ZEOuERtx0jb1YTxU7J7+1SF9wv+Em+7Xx3G08QJfnrNV8ZVoVaHWqFs7gqw== + dependencies: + "@nx-tools/ci-context" "6.0.1" + "@nx-tools/core" "6.0.1" + "@renovate/pep440" "1.0.0" + csv-parse "^5.5.5" + handlebars "^4.7.8" + moment-timezone "^0.5.45" + semver "^7.6.0" + "@nx-tools/core@5.3.1": version "5.3.1" resolved "https://registry.yarnpkg.com/@nx-tools/core/-/core-5.3.1.tgz#61039d6c1d01bf646580de2cdb6cd69d4cdca6eb" @@ -2269,6 +2341,17 @@ chalk "^4.1.2" ci-info "^3.8.0" +"@nx-tools/core@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@nx-tools/core/-/core-6.0.1.tgz#d1417bc43417659fad4a166f1b16e6fc749e4be9" + integrity sha512-Uj0H5XWmOj60rTuRGe4JO6z0nO9sDZ76xrZJKkityWQl7KYjAM8KYIK4jbIgq9GdumK1pTAqv8GnzWNe15Uixw== + dependencies: + "@actions/exec" "^1.1.1" + "@actions/io" "^1.1.3" + chalk "^4.1.2" + ci-info "^4.0.0" + csv-parse "^5.5.5" + "@nx-tools/nx-container@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@nx-tools/nx-container/-/nx-container-5.3.1.tgz#bad9f5731992fbf71032ce23e6c179a80a54bfe5" @@ -2570,6 +2653,98 @@ tslib "^2.3.0" yargs-parser "21.1.1" +"@octokit/auth-token@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7" + integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== + +"@octokit/core@^5.0.1": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.2.0.tgz#ddbeaefc6b44a39834e1bb2e58a49a117672a7ea" + integrity sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg== + dependencies: + "@octokit/auth-token" "^4.0.0" + "@octokit/graphql" "^7.1.0" + "@octokit/request" "^8.3.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^9.0.1": + version "9.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.5.tgz#e6c0ee684e307614c02fc6ac12274c50da465c44" + integrity sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw== + dependencies: + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.1.0.tgz#9bc1c5de92f026648131f04101cab949eeffe4e0" + integrity sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ== + dependencies: + "@octokit/request" "^8.3.0" + "@octokit/types" "^13.0.0" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-20.0.0.tgz#9ec2daa0090eeb865ee147636e0c00f73790c6e5" + integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA== + +"@octokit/openapi-types@^22.0.0", "@octokit/openapi-types@^22.2.0": + version "22.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-22.2.0.tgz#75aa7dcd440821d99def6a60b5f014207ae4968e" + integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg== + +"@octokit/plugin-paginate-rest@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz#2e2a2f0f52c9a4b1da1a3aa17dabe3c459b9e401" + integrity sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw== + dependencies: + "@octokit/types" "^12.6.0" + +"@octokit/plugin-rest-endpoint-methods@^10.0.0": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz#41ba478a558b9f554793075b2e20cd2ef973be17" + integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg== + dependencies: + "@octokit/types" "^12.6.0" + +"@octokit/request-error@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.0.tgz#ee4138538d08c81a60be3f320cd71063064a3b30" + integrity sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q== + dependencies: + "@octokit/types" "^13.1.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^8.3.0", "@octokit/request@^8.3.1": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.0.tgz#7f4b7b1daa3d1f48c0977ad8fffa2c18adef8974" + integrity sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw== + dependencies: + "@octokit/endpoint" "^9.0.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + +"@octokit/types@^12.6.0": + version "12.6.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.6.0.tgz#8100fb9eeedfe083aae66473bd97b15b62aedcb2" + integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw== + dependencies: + "@octokit/openapi-types" "^20.0.0" + +"@octokit/types@^13.0.0", "@octokit/types@^13.1.0": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.5.0.tgz#4796e56b7b267ebc7c921dcec262b3d5bfb18883" + integrity sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ== + dependencies: + "@octokit/openapi-types" "^22.2.0" + "@peculiar/asn1-schema@^2.3.8": version "2.3.13" resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz#ec8509cdcbc0da3abe73fd7e690556b57a61b8f4" @@ -2609,6 +2784,13 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@renovate/pep440@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@renovate/pep440/-/pep440-1.0.0.tgz#9e05cac649b6a3d027cba7f2939b085de78f39ea" + integrity sha512-k3pZVxGEGpU7rpH507/9vxfFjuxX7qx4MSj9Fk+6zBsf/uZmAy8x97dNtZacbge7gP9TazbW1d7SEb5vsOmKlw== + dependencies: + xregexp "4.4.1" + "@rollup/plugin-babel@^6.0.4": version "6.0.4" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz#bd698e351fa9aa9619fcae780aea2a603d98e4c4" @@ -4535,6 +4717,11 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +before-after-hook@^2.2.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== + big-integer@^1.6.48: version "1.6.52" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" @@ -4838,6 +5025,11 @@ ci-info@^3.2.0, ci-info@^3.8.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + cipher-base@^1.0.1, cipher-base@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -5154,6 +5346,11 @@ core-js-compat@^3.37.1, core-js-compat@^3.38.0: dependencies: browserslist "^4.23.3" +core-js-pure@^3.30.2: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.38.1.tgz#e8534062a54b7221344884ba9b52474be495ada3" + integrity sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ== + core-util-is@^1.0.3, core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -5483,7 +5680,7 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -csv-parse@^5.4.0: +csv-parse@^5.4.0, csv-parse@^5.5.5: version "5.5.6" resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.6.tgz#0d726d58a60416361358eec291a9f93abe0b6b1a" integrity sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A== @@ -5608,6 +5805,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + destroy@1.2.0, destroy@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -6826,7 +7028,7 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.7.7: +handlebars@^4.7.7, handlebars@^4.7.8: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== @@ -7883,6 +8085,13 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +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.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -7891,13 +8100,6 @@ js-yaml@^3.10.0, 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" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -8558,7 +8760,14 @@ mkdirp@^2.1.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== -moment@^2.30.1: +moment-timezone@^0.5.45: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@^2.29.4, moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== @@ -9714,6 +9923,11 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +properties-file@^3.5.4: + version "3.5.7" + resolved "https://registry.yarnpkg.com/properties-file/-/properties-file-3.5.7.tgz#8d3ee6234e4948da7939580bec838dcf52cac4ae" + integrity sha512-f47I5uaVJJnE3KilDi9vfXT6Bc/6rrRgsJ8L55CUcalnsurxG+jwkGzAAqvyXDixK+ejzvO80Q8OngwDWErk3Q== + protons-runtime@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/protons-runtime/-/protons-runtime-5.5.0.tgz#ea06d9ef843aad77ea5de3e1ebafa81b58c24570" @@ -10789,6 +11003,11 @@ svgo@^3.2.0: csso "^5.0.5" picocolors "^1.0.0" +swagger-ui-dist@5.17.14: + version "5.17.14" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz#e2c222e5bf9e15ccf80ec4bc08b4aaac09792fd6" + integrity sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw== + tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -10994,6 +11213,11 @@ tsscmp@1.0.6: resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" @@ -11123,7 +11347,7 @@ undici-types@~6.19.2: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -undici@^5.21.2: +undici@^5.21.2, undici@^5.25.4: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== @@ -11160,6 +11384,11 @@ union@~0.5.0: dependencies: qs "^6.4.0" +universal-user-agent@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" + integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -11545,6 +11774,13 @@ ws@^8.18.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +xregexp@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.4.1.tgz#c84a88fa79e9ab18ca543959712094492185fe65" + integrity sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag== + dependencies: + "@babel/runtime-corejs3" "^7.12.1" + xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"