diff --git a/Dockerfiles/Dockerfile.user b/Dockerfiles/Dockerfile.user index 5f6f4bd73..4b59f0a25 100644 --- a/Dockerfiles/Dockerfile.user +++ b/Dockerfiles/Dockerfile.user @@ -1,6 +1,20 @@ # Stage 1: Build the application -FROM node:18-alpine as build +FROM node:18-slim as build RUN npm install -g pnpm + +# We don't need the standalone Chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true + +# Install Google Chrome Stable and fonts +# Note: this installs the necessary libs to make the browser work with Puppeteer. +RUN apt-get update && apt-get install gnupg wget -y && \ + wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ + sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \ + apt-get update && \ + apt-get install google-chrome-stable -y --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + +# RUN apk update && apk list --all-versions chromium # Set the working directory WORKDIR /app @@ -18,7 +32,19 @@ RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate RUN pnpm run build user # Stage 2: Create the final image -FROM node:18-alpine +FROM node:18-slim + +# We don't need the standalone Chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true + +# Install Google Chrome Stable and fonts +# Note: this installs the necessary libs to make the browser work with Puppeteer. +RUN apt-get update && apt-get install gnupg wget -y && \ + wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ + sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \ + apt-get update && \ + apt-get install google-chrome-stable -y --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* # Set the working directory WORKDIR /app @@ -38,4 +64,4 @@ CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx pri # docker build -t user -f Dockerfiles/Dockerfile.user . # docker run -d --env-file .env --name user docker.io/library/user -# docker logs -f user +# docker logs -f user \ No newline at end of file diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index dc6adb010..3b0bce14c 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -268,7 +268,7 @@ export class AgentServiceService { return agentSpinUpResponse.then(async (agentDetails) => { if (agentDetails) { const controllerEndpoints = JSON.parse(agentDetails); - const agentEndPoint = `${process.env.API_GATEWAY_PROTOCOL}://${controllerEndpoints.CONTROLLER_ENDPOINT}`; + const agentEndPoint = `${process.env.AGENT_PROTOCOL}://${controllerEndpoints.CONTROLLER_ENDPOINT}`; if (agentEndPoint && agentSpinupDto.clientSocketId) { const socket = io(`${process.env.SOCKET_HOST}`, { @@ -305,7 +305,9 @@ export class AgentServiceService { if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-started', { clientId: agentSpinupDto.clientSocketId }); } - await this._createLegacyConnectionInvitation(orgData.id, user, agentPayload.walletName); + + this.logger.log(`orgData.name ::: ${orgData.name}`); + await this._createLegacyConnectionInvitation(orgData.id, user, orgData.name); if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: agentSpinupDto.clientSocketId }); } @@ -551,6 +553,9 @@ export class AgentServiceService { ledgerId: payload.ledgerId }; + const getOrgAgent = await this.agentServiceRepository.getOrgDetails(payload.orgId); + this.logger.log(`getOrgAgent::: ${JSON.stringify(getOrgAgent)}`); + if (payload.clientSocketId) { socket.emit('agent-spinup-process-completed', { clientId: payload.clientSocketId }); } @@ -561,7 +566,7 @@ export class AgentServiceService { socket.emit('invitation-url-creation-started', { clientId: payload.clientSocketId }); } - await this._createLegacyConnectionInvitation(payload.orgId, user, storeOrgAgentData.walletName); + await this._createLegacyConnectionInvitation(payload.orgId, user, getOrgAgent.name); if (payload.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: payload.clientSocketId }); diff --git a/apps/api-gateway/src/app.module.ts b/apps/api-gateway/src/app.module.ts index 792999579..9eee2c533 100644 --- a/apps/api-gateway/src/app.module.ts +++ b/apps/api-gateway/src/app.module.ts @@ -20,6 +20,9 @@ import { commonNatsOptions } from 'libs/service/nats.options'; import { UserModule } from './user/user.module'; import { ConnectionModule } from './connection/connection.module'; import { EcosystemModule } from './ecosystem/ecosystem.module'; +import { BullModule } from '@nestjs/bull'; +import { CacheModule } from '@nestjs/cache-manager'; +import * as redisStore from 'cache-manager-redis-store'; @Module({ imports: [ @@ -42,7 +45,14 @@ import { EcosystemModule } from './ecosystem/ecosystem.module'; UserModule, ConnectionModule, IssuanceModule, - EcosystemModule + EcosystemModule, + CacheModule.register({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }), + BullModule.forRoot({ + redis: { + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT) + } + }) ], controllers: [AppController], providers: [AppService] diff --git a/apps/api-gateway/src/authz/socket.gateway.ts b/apps/api-gateway/src/authz/socket.gateway.ts index a8a9bc44b..bfb623de8 100644 --- a/apps/api-gateway/src/authz/socket.gateway.ts +++ b/apps/api-gateway/src/authz/socket.gateway.ts @@ -96,10 +96,18 @@ export class SocketGateway implements OnGatewayConnection { } @SubscribeMessage('error-in-wallet-creation-process') - async handleErrorResponse(payload: ISocketInterface): Promise { + async handleErrorResponse(client:string, payload: ISocketInterface): Promise { this.logger.log(`error-in-wallet-creation-process ${payload.clientId}`); this.server .to(payload.clientId) .emit('error-in-wallet-creation-process', payload.error); } + + @SubscribeMessage('bulk-issuance-process-completed') + async handleBulkIssuance(client:string, payload: ISocketInterface): Promise { + this.logger.log(`bulk-issuance-process-completed ${payload.clientId}`); + this.server + .to(payload.clientId) + .emit('bulk-issuance-process-completed', payload.error); + } } diff --git a/apps/api-gateway/src/enum.ts b/apps/api-gateway/src/enum.ts index d3f8d2110..c6b6edca8 100644 --- a/apps/api-gateway/src/enum.ts +++ b/apps/api-gateway/src/enum.ts @@ -124,5 +124,6 @@ export enum FileUploadType { export enum FileUploadStatus { started = 'PROCESS_STARTED', completed = 'PROCESS_COMPLETED', - interrupted= 'PROCESS INTERRUPTED' + interrupted= 'PROCESS_INTERRUPTED', + retry= 'PROCESS_REINITIATED' } diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 148f65f57..e439b1a78 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -148,6 +148,35 @@ export class OutOfBandCredentialDto { export class PreviewFileDetails { + @ApiProperty({ required: false }) + @IsOptional() + @Type(() => String) + search = ''; + + @ApiProperty({ required: false, default: 10 }) + @IsOptional() + @Type(() => Number) + @Transform(({ value }) => toNumber(value)) + pageSize = 10; + + @ApiProperty({ required: false }) + @IsOptional() + @Type(() => String) + sortValue = ''; + + @ApiProperty({ required: false }) + @IsOptional() + @Type(() => String) + sortBy = ''; + + @ApiProperty({ required: false, default: 1 }) + @IsOptional() + @Type(() => Number) + @Transform(({ value }) => toNumber(value)) + pageNumber = 1; +} + +export class FileParameter { @ApiProperty({ required: false, default: 1 }) @IsOptional() @Type(() => Number) @@ -175,4 +204,12 @@ export class PreviewFileDetails { @Type(() => String) sortValue = ''; +} + +export class ClientDetails { + @ApiProperty({ required: false, example: '68y647ayAv79879' }) + @IsOptional() + @Type(() => String) + clientId = ''; + } \ No newline at end of file diff --git a/apps/api-gateway/src/issuance/interfaces/index.ts b/apps/api-gateway/src/issuance/interfaces/index.ts index b6c8b0307..87aa925dd 100644 --- a/apps/api-gateway/src/issuance/interfaces/index.ts +++ b/apps/api-gateway/src/issuance/interfaces/index.ts @@ -59,11 +59,11 @@ export class IUserOrg { export interface FileExportResponse { response: unknown; fileContent: string; - fileName : string + fileName: string } export interface RequestPayload { credDefId: string; - filePath: string; + fileKey: string; fileName: string; - } \ No newline at end of file +} diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 75259fa0d..974af99dd 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -13,7 +13,10 @@ import { Query, Get, Param, - UseFilters + UseFilters, + Header, + UploadedFile, + UseInterceptors } from '@nestjs/common'; import { ApiTags, @@ -23,7 +26,9 @@ import { ApiForbiddenResponse, ApiUnauthorizedResponse, ApiQuery, - ApiExcludeEndpoint + ApiExcludeEndpoint, + ApiConsumes, + ApiBody } from '@nestjs/swagger'; import { AuthGuard } from '@nestjs/passport'; import { ApiResponseDto } from '../dtos/apiResponse.dto'; @@ -33,7 +38,7 @@ import { CommonService } from '@credebl/common/common.service'; import { Response } from 'express'; import IResponseType from '@credebl/common/interfaces/response.interface'; import { IssuanceService } from './issuance.service'; -import { IssuanceDto, IssueCredentialDto, OutOfBandCredentialDto } from './dtos/issuance.dto'; +import { ClientDetails, FileParameter, IssuanceDto, IssueCredentialDto, OutOfBandCredentialDto, PreviewFileDetails } from './dtos/issuance.dto'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { User } from '../authz/decorators/user.decorator'; import { ResponseMessages } from '@credebl/common/response-messages'; @@ -43,6 +48,11 @@ import { OrgRoles } from 'libs/org-roles/enums'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { ImageServiceService } from '@credebl/image-service'; +import { FileExportResponse, RequestPayload } from './interfaces'; +import { AwsService } from '@credebl/aws'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { v4 as uuidv4 } from 'uuid'; +import { RpcException } from '@nestjs/microservices'; @Controller() @UseFilters(CustomExceptionFilter) @ApiTags('credentials') @@ -53,6 +63,7 @@ export class IssuanceController { constructor( private readonly issueCredentialService: IssuanceService, private readonly imageServiceService: ImageServiceService, + private readonly awsService: AwsService, private readonly commonService: CommonService ) { } @@ -141,6 +152,353 @@ export class IssuanceController { return res.status(HttpStatus.OK).json(finalResponse); } + @Get('/orgs/:orgId/:credentialDefinitionId/download') + @ApiUnauthorizedResponse({ status: 401, description: 'Unauthorized', type: UnauthorizedErrorDto }) + @ApiForbiddenResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorDto }) + @Header('Content-Disposition', 'attachment; filename="schema.csv"') + @Header('Content-Type', 'application/csv') + @ApiOperation({ + summary: 'Download csv template for bulk-issuance', + description: 'Download csv template for bulk-issuance' + }) + async downloadBulkIssuanceCSVTemplate( + @Param('credentialDefinitionId') credentialDefinitionId: string, + @Param('orgId') orgId: number, + @Res() res: Response + ): Promise { + try { + const exportedData: FileExportResponse = await this.issueCredentialService.exportSchemaToCSV(credentialDefinitionId); + return res.header('Content-Disposition', `attachment; filename="${exportedData.fileName}.csv"`).status(200).send(exportedData.fileContent); + } catch (error) { + } + + } + + @Post('/orgs/:orgId/bulk/upload') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiOperation({ + summary: 'Upload file for bulk issuance', + description: 'Upload file for bulk issuance.' + }) + @ApiResponse({ status: 201, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiConsumes('multipart/form-data') + @ApiBody({ + schema: { + type: 'object', + nullable: false, + required: ['file'], + properties: { + file: { + // 👈 this property + type: 'string', + format: 'binary' + } + } + }, + required: true + }) + @UseInterceptors(FileInterceptor('file')) + async importAndPreviewDataForIssuance( + @Query('credDefId') credentialDefinitionId: string, + @UploadedFile() file: Express.Multer.File, + @Param('orgId') orgId: number, + @Res() res: Response + ): Promise { + try { + if (file) { + const fileKey: string = uuidv4(); + try { + + await this.awsService.uploadCsvFile(fileKey, file?.buffer); + + } catch (error) { + + throw new RpcException(error.response ? error.response : error); + + } + const reqPayload: RequestPayload = { + credDefId: credentialDefinitionId, + fileKey, + fileName: file?.filename || file?.originalname + }; + this.logger.log(`reqPayload::::::${JSON.stringify(reqPayload)}`); + const importCsvDetails = await this.issueCredentialService.importCsv( + reqPayload + ); + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.issuance.success.importCSV, + data: importCsvDetails.response + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } + + + @Get('/orgs/:orgId/:requestId/preview') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiOperation({ + summary: 'file-preview', + description: 'file-preview' + }) + + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'sortBy', + type: String, + required: false + }) + @ApiQuery({ + name: 'sortValue', + type: Number, + required: false + }) + async previewFileDataForIssuance( + @Param('requestId') requestId: string, + @Param('orgId') orgId: number, + @Query() previewFileDetails: PreviewFileDetails, + @Res() res: Response + ): Promise { + const perviewCSVDetails = await this.issueCredentialService.previewCSVDetails( + requestId, + orgId, + previewFileDetails + ); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.issuance.success.previewCSV, + data: perviewCSVDetails + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Post('/orgs/:orgId/:requestId/bulk') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiOperation({ + summary: 'bulk issue credential', + description: 'bulk issue credential' + }) + async issueBulkCredentials(@Param('requestId') requestId: string, @Param('orgId') orgId: number, @Res() res: Response, @Body() clientDetails: ClientDetails): Promise { + const bulkIssunaceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails.clientId); + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.issuance.success.bulkIssuance, + data: bulkIssunaceDetails.response + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + @Get('/orgs/:orgId/bulk/files') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiOperation({ + summary: 'Get the file list for bulk operation', + description: 'Get all the file list for organization for bulk operation' + }) + + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'sortBy', + type: String, + required: false + }) + @ApiQuery({ + name: 'sortValue', + type: Number, + required: false + }) + async issuedFileDetails( + @Param('orgId') orgId: number, + @Query() fileParameter: FileParameter, + @Res() res: Response + ): Promise { + const issuedFileDetails = await this.issueCredentialService.issuedFileDetails( + orgId, + fileParameter + ); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.issuance.success.previewCSV, + data: issuedFileDetails.response + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Get('/orgs/:orgId/:fileId/bulk/file-data') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiOperation({ + summary: 'Get the file data', + description: 'Get the file data by file id' + }) + + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'sortBy', + type: String, + required: false + }) + @ApiQuery({ + name: 'sortValue', + type: Number, + required: false + }) + async getFileDetailsByFileId( + @Param('orgId') orgId: number, + @Param('fileId') fileId: string, + @Query() fileParameter: FileParameter, + @Res() res: Response + ): Promise { + const issuedFileDetails = await this.issueCredentialService.getFileDetailsByFileId( + orgId, + fileId, + fileParameter + ); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.issuance.success.previewCSV, + data: issuedFileDetails.response + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Post('/orgs/:orgId/:fileId/retry/bulk') + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + type: UnauthorizedErrorDto + }) + @ApiForbiddenResponse({ + status: 403, + description: 'Forbidden', + type: ForbiddenErrorDto + }) + @ApiOperation({ + summary: 'Retry bulk issue credential', + description: 'Retry bulk issue credential' + }) + async retryBulkCredentials(@Param('fileId') fileId: string, @Param('orgId') orgId: number, @Res() res: Response, @Body() clientDetails: ClientDetails): Promise { + const bulkIssunaceDetails = await this.issueCredentialService.retryBulkCredential(fileId, orgId, clientDetails.clientId); + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.issuance.success.bulkIssuance, + data: bulkIssunaceDetails.response + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + /** * Description: Issuer send credential to create offer * @param user @@ -247,5 +605,4 @@ export class IssuanceController { return res.status(HttpStatus.CREATED).json(finalResponse); } - } diff --git a/apps/api-gateway/src/issuance/issuance.module.ts b/apps/api-gateway/src/issuance/issuance.module.ts index bde5e1d4d..15d182e93 100644 --- a/apps/api-gateway/src/issuance/issuance.module.ts +++ b/apps/api-gateway/src/issuance/issuance.module.ts @@ -5,6 +5,7 @@ import { IssuanceService } from './issuance.service'; import { CommonService } from '@credebl/common'; import { HttpModule } from '@nestjs/axios'; import { ImageServiceService } from '@credebl/image-service'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -20,6 +21,6 @@ import { ImageServiceService } from '@credebl/image-service'; ]) ], controllers: [IssuanceController], - providers: [IssuanceService, ImageServiceService, CommonService] + providers: [IssuanceService, ImageServiceService, CommonService, AwsService] }) export class IssuanceModule { } diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 84b87cfaf..e74fc7ed8 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -3,7 +3,8 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; -import { IssuanceDto, IssueCredentialDto, OutOfBandCredentialDto } from './dtos/issuance.dto'; +import { FileParameter, IssuanceDto, IssueCredentialDto, OutOfBandCredentialDto, PreviewFileDetails } from './dtos/issuance.dto'; +import { FileExportResponse, RequestPayload } from './interfaces'; @Injectable() export class IssuanceService extends BaseService { @@ -58,4 +59,61 @@ export class IssuanceService extends BaseService { return this.sendNats(this.issuanceProxy, 'out-of-band-credential-offer', payload); } + async exportSchemaToCSV(credentialDefinitionId: string + ): Promise { + const payload = { credentialDefinitionId }; + return (await this.sendNats(this.issuanceProxy, 'export-schema-to-csv-by-credDefId', payload)).response; + } + + async importCsv(importFileDetails: RequestPayload + ): Promise<{ response: object }> { + const payload = { importFileDetails }; + return this.sendNats(this.issuanceProxy, 'import-and-preview-data-for-issuance', payload); + } + + async previewCSVDetails(requestId: string, + orgId: number, + previewFileDetails: PreviewFileDetails + ): Promise { + const payload = { + requestId, + orgId, + previewFileDetails + }; + return this.sendNats(this.issuanceProxy, 'preview-csv-details', payload); + } + + async issuedFileDetails( + orgId: number, + fileParameter: FileParameter + ): Promise<{ response: object }> { + const payload = { + orgId, + fileParameter + }; + return this.sendNats(this.issuanceProxy, 'issued-file-details', payload); + } + + async getFileDetailsByFileId( + orgId: number, + fileId: string, + fileParameter: FileParameter + ): Promise<{ response: object }> { + const payload = { + orgId, + fileId, + fileParameter + }; + return this.sendNats(this.issuanceProxy, 'issued-file-data', payload); + } + + async issueBulkCredential(requestId: string, orgId: number, clientId: string): Promise<{ response: object }> { + const payload = { requestId, orgId, clientId }; + return this.sendNats(this.issuanceProxy, 'issue-bulk-credentials', payload); + } + + async retryBulkCredential(fileId: string, orgId: number, clientId: string): Promise<{ response: object }> { + const payload = { fileId, orgId, clientId }; + return this.sendNats(this.issuanceProxy, 'retry-bulk-credentials', payload); + } } diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index a9ec07d56..86a012a15 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -18,7 +18,9 @@ async function bootstrap(): Promise { expressApp.set('x-powered-by', false); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb' })); - app.use(helmet()); + app.use(helmet({ + xssFilter:true + })); app.useGlobalPipes(new ValidationPipe()); const options = new DocumentBuilder() .setTitle(`${process.env.PLATFORM_NAME}`) @@ -43,10 +45,12 @@ async function bootstrap(): Promise { app.use(express.static('uploadedFiles/holder-profile')); app.use(express.static('uploadedFiles/org-logo')); app.use(express.static('uploadedFiles/tenant-logo')); + app.use(express.static('uploadedFiles/exports')); app.use(express.static('resources')); app.use(express.static('genesis-file')); app.use(express.static('invoice-pdf')); app.use(express.static('uploadedFiles/bulk-verification-templates')); + app.use(express.static('uploadedFiles/import')); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); await app.listen(process.env.API_GATEWAY_PORT, `${process.env.API_GATEWAY_HOST}`); diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 3746ca933..09a9f1571 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -1,13 +1,9 @@ import { ApiBearerAuth, ApiForbiddenResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; import { CommonService } from '@credebl/common'; -import { Controller, Get, Put, Param, UseGuards, UseFilters } from '@nestjs/common'; +import { Controller, Get, Put, Param, UseGuards, UseFilters, Post, Body, Res, HttpStatus, Query } from '@nestjs/common'; import { OrganizationService } from './organization.service'; -import { Post } from '@nestjs/common'; -import { Body } from '@nestjs/common'; -import { Res } from '@nestjs/common'; import { CreateOrganizationDto } from './dtos/create-organization-dto'; import IResponseType from '@credebl/common/interfaces/response.interface'; -import { HttpStatus } from '@nestjs/common'; import { Response } from 'express'; import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; @@ -21,7 +17,6 @@ import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRoles } from 'libs/org-roles/enums'; import { UpdateUserRolesDto } from './dtos/update-user-roles.dto'; -import { Query } from '@nestjs/common'; import { GetAllOrganizationsDto } from './dtos/get-all-organizations.dto'; import { GetAllSentInvitationsDto } from './dtos/get-all-sent-invitations.dto'; import { UpdateOrganizationDto } from './dtos/update-organization-dto'; diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index 4dcfce609..52801680e 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -142,4 +142,6 @@ export class OrganizationService extends BaseService { return this.sendNats(this.serviceProxy, 'fetch-organization-profile', payload); } + + } diff --git a/apps/api-gateway/src/user/dto/add-user.dto.ts b/apps/api-gateway/src/user/dto/add-user.dto.ts index e0dea93df..4007d8629 100644 --- a/apps/api-gateway/src/user/dto/add-user.dto.ts +++ b/apps/api-gateway/src/user/dto/add-user.dto.ts @@ -6,19 +6,19 @@ import { IsBoolean, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-vali export class AddUserDetails { @ApiProperty({ example: 'awqx@getnada.com' }) - @IsEmail() - @IsNotEmpty({ message: 'Please provide valid email' }) - @IsString({ message: 'email should be string' }) + @IsEmail({}, { message: 'Please provide a valid email' }) + @IsNotEmpty({ message: 'Email is required' }) + @IsString({ message: 'Email should be a string' }) email: string; @ApiProperty({ example: 'Alen' }) - @IsNotEmpty({ message: 'Please provide valid email' }) - @IsString({ message: 'firstName should be string' }) + @IsNotEmpty({ message: 'First name is required' }) + @IsString({ message: 'First name should be a string' }) firstName: string; @ApiProperty({ example: 'Harvey' }) - @IsNotEmpty({ message: 'Please provide valid email' }) - @IsString({ message: 'lastName should be string' }) + @IsNotEmpty({ message: 'Last name is required' }) + @IsString({ message: 'Last name should be a string' }) lastName: string; @ApiProperty() diff --git a/apps/api-gateway/src/user/dto/share-certificate.dto.ts b/apps/api-gateway/src/user/dto/share-certificate.dto.ts new file mode 100644 index 000000000..8b4b29e3a --- /dev/null +++ b/apps/api-gateway/src/user/dto/share-certificate.dto.ts @@ -0,0 +1,30 @@ +import { IsArray, IsNotEmpty, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +interface Attribute { + name: string; + value: string; +} +export class CreateUserCertificateDto { + @ApiProperty() + @IsNotEmpty({ message: 'Please provide valid schemaId' }) + @IsString({ message: 'credentialId should be string' }) + credentialId: string; + + @ApiProperty({ example: 'SchemaId' }) + @IsNotEmpty({ message: 'Please provide valid schemaId' }) + @IsString({ message: 'schemaId should be string' }) + schemaId: string; + + @ApiProperty({ + example: [ + { + name: 'name', + value: 'value' + } + ] + }) + @IsArray({ message: 'attributes must be a valid array' }) + @IsNotEmpty({ message: 'please provide valid attributes' }) + attributes: Attribute[]; +} diff --git a/apps/api-gateway/src/user/user.controller.ts b/apps/api-gateway/src/user/user.controller.ts index df5476a9b..764580a9e 100644 --- a/apps/api-gateway/src/user/user.controller.ts +++ b/apps/api-gateway/src/user/user.controller.ts @@ -1,4 +1,17 @@ -import { Controller, Post, Put, Body, Param, UseFilters, Res, HttpStatus, BadRequestException, Get, Query, UseGuards } from '@nestjs/common'; +import { + Controller, + Post, + Put, + Body, + Param, + UseFilters, + Res, + HttpStatus, + BadRequestException, + Get, + Query, + UseGuards +} from '@nestjs/common'; import { UserService } from './user.service'; import { ApiBearerAuth, @@ -33,6 +46,8 @@ import { UpdatePlatformSettingsDto } from './dto/update-platform-settings.dto'; import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { OrgRoles } from 'libs/org-roles/enums'; +import { CreateUserCertificateDto } from './dto/share-certificate.dto'; +import { AwsService } from '@credebl/aws/aws.service'; @UseFilters(CustomExceptionFilter) @Controller('users') @@ -40,13 +55,17 @@ import { OrgRoles } from 'libs/org-roles/enums'; @ApiUnauthorizedResponse({ status: 401, description: 'Unauthorized', type: UnauthorizedErrorDto }) @ApiForbiddenResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorDto }) export class UserController { - constructor(private readonly userService: UserService, private readonly commonService: CommonService) { } + constructor( + private readonly userService: UserService, + private readonly commonService: CommonService, + private readonly awsService: AwsService + ) {} /** - * - * @param user - * @param orgId - * @param res + * + * @param user + * @param orgId + * @param res * @returns Users list of organization */ @Get('/public-profiles') @@ -67,8 +86,11 @@ export class UserController { type: String, required: false }) - async get(@User() user: IUserRequestInterface, @Query() getAllUsersDto: GetAllUsersDto, @Res() res: Response): Promise { - + async get( + @User() user: IUserRequestInterface, + @Query() getAllUsersDto: GetAllUsersDto, + @Res() res: Response + ): Promise { const users = await this.userService.get(getAllUsersDto); const finalResponse: IResponseType = { statusCode: HttpStatus.OK, @@ -99,7 +121,6 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } @Get('/profile') @@ -110,7 +131,6 @@ export class UserController { @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() async getProfile(@User() reqUser: user, @Res() res: Response): Promise { - const userData = await this.userService.getProfile(reqUser.id); const finalResponse: IResponseType = { @@ -120,11 +140,13 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } @Get('/platform-settings') - @ApiOperation({ summary: 'Get all platform and ecosystem settings', description: 'Get all platform and ecosystem settings' }) + @ApiOperation({ + summary: 'Get all platform and ecosystem settings', + description: 'Get all platform and ecosystem settings' + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.PLATFORM_ADMIN) @ApiBearerAuth() @@ -138,7 +160,6 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } @Get('/activity') @@ -149,8 +170,11 @@ export class UserController { @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() @ApiQuery({ name: 'limit', required: true }) - async getUserActivities(@Query('limit') limit: number, @Res() res: Response, @User() reqUser: user): Promise { - + async getUserActivities( + @Query('limit') limit: number, + @Res() res: Response, + @User() reqUser: user + ): Promise { const userDetails = await this.userService.getUserActivities(reqUser.id, limit); const finalResponse: IResponseType = { @@ -162,7 +186,6 @@ export class UserController { return res.status(HttpStatus.OK).json(finalResponse); } - @Get('/org-invitations') @ApiOperation({ summary: 'organization invitations', @@ -190,13 +213,20 @@ export class UserController { type: String, required: false }) - async invitations(@Query() getAllInvitationsDto: GetAllInvitationsDto, @User() reqUser: user, @Res() res: Response): Promise { - + async invitations( + @Query() getAllInvitationsDto: GetAllInvitationsDto, + @User() reqUser: user, + @Res() res: Response + ): Promise { if (!Object.values(Invitation).includes(getAllInvitationsDto.status)) { throw new BadRequestException(ResponseMessages.user.error.invalidInvitationStatus); } - const invitations = await this.userService.invitations(reqUser.id, getAllInvitationsDto.status, getAllInvitationsDto); + const invitations = await this.userService.invitations( + reqUser.id, + getAllInvitationsDto.status, + getAllInvitationsDto + ); const finalResponse: IResponseType = { statusCode: HttpStatus.OK, @@ -205,15 +235,14 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } /** - * - * @param email - * @param res - * @returns User email check - */ + * + * @param email + * @param res + * @returns User email check + */ @Get('/:email') @ApiOperation({ summary: 'Check user exist', description: 'check user existence' }) async checkUserExist(@Param() emailParam: EmailValidator, @Res() res: Response): Promise { @@ -226,14 +255,27 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); + } + + @Get('/user-credentials/:credentialId') + @ApiOperation({ summary: 'Get user credentials by Id', description: 'Get user credentials by Id' }) + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + async getUserCredentialsById (@Param('credentialId') credentialId: string, @Res() res: Response): Promise { + const getUserCrdentialsById = await this.userService.getUserCredentialsById(credentialId); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.user.success.userCredentials, + data: getUserCrdentialsById.response + }; + return res.status(HttpStatus.OK).json(finalResponse); } /** - * - * @param acceptRejectInvitation - * @param reqUser - * @param res + * + * @param acceptRejectInvitation + * @param reqUser + * @param res * @returns Organization invitation status */ @Post('/org-invitations/:invitationId') @@ -243,7 +285,12 @@ export class UserController { }) @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() - async acceptRejectInvitaion(@Body() acceptRejectInvitation: AcceptRejectInvitationDto, @Param('invitationId') invitationId: string, @User() reqUser: user, @Res() res: Response): Promise { + async acceptRejectInvitaion( + @Body() acceptRejectInvitation: AcceptRejectInvitationDto, + @Param('invitationId') invitationId: string, + @User() reqUser: user, + @Res() res: Response + ): Promise { acceptRejectInvitation.invitationId = parseInt(invitationId); const invitationRes = await this.userService.acceptRejectInvitaion(acceptRejectInvitation, reqUser.id); @@ -251,9 +298,31 @@ export class UserController { statusCode: HttpStatus.CREATED, message: invitationRes.response }; - return res.status(HttpStatus.CREATED).json(finalResponse); + } + @Post('/certificate') + @ApiOperation({ + summary: 'Share user certificate', + description: 'Share user certificate' + }) + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + async shareUserCertificate( + @Body() shareUserCredentials: CreateUserCertificateDto, + @Res() res: Response + ): Promise { + const schemaIdParts = shareUserCredentials.schemaId.split(':'); + // eslint-disable-next-line prefer-destructuring + const title = schemaIdParts[2]; + + const imageBuffer = await this.userService.shareUserCertificate(shareUserCredentials); + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: 'Certificate url generated successfully', + label: title, + data: imageBuffer.response + }; + return res.status(HttpStatus.CREATED).json(finalResponse); } @Put('/') @@ -264,8 +333,11 @@ export class UserController { @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt')) - async updateUserProfile(@Body() updateUserProfileDto: UpdateUserProfileDto, @User() reqUser: user, @Res() res: Response): Promise { - + async updateUserProfile( + @Body() updateUserProfileDto: UpdateUserProfileDto, + @User() reqUser: user, + @Res() res: Response + ): Promise { const userId = reqUser.id; updateUserProfileDto.id = userId; await this.userService.updateUserProfile(updateUserProfileDto); @@ -275,14 +347,17 @@ export class UserController { message: ResponseMessages.user.success.update }; return res.status(HttpStatus.OK).json(finalResponse); - } @Put('/password/:email') @ApiOperation({ summary: 'Store user password details', description: 'Store user password details' }) @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() - async addPasskey(@Body() userInfo: AddPasskeyDetails, @Param('email') email: string, @Res() res: Response): Promise { + async addPasskey( + @Body() userInfo: AddPasskeyDetails, + @Param('email') email: string, + @Res() res: Response + ): Promise { const userDetails = await this.userService.addPasskey(email, userInfo); const finalResponse = { statusCode: HttpStatus.OK, @@ -291,15 +366,20 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } - + @Put('/platform-settings') - @ApiOperation({ summary: 'Update platform and ecosystem settings', description: 'Update platform and ecosystem settings' }) + @ApiOperation({ + summary: 'Update platform and ecosystem settings', + description: 'Update platform and ecosystem settings' + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.PLATFORM_ADMIN) @ApiBearerAuth() - async updatePlatformSettings(@Body() platformSettings: UpdatePlatformSettingsDto, @Res() res: Response): Promise { + async updatePlatformSettings( + @Body() platformSettings: UpdatePlatformSettingsDto, + @Res() res: Response + ): Promise { const result = await this.userService.updatePlatformSettings(platformSettings); const finalResponse = { @@ -308,7 +388,5 @@ export class UserController { }; return res.status(HttpStatus.OK).json(finalResponse); - } - -} \ No newline at end of file +} diff --git a/apps/api-gateway/src/user/user.module.ts b/apps/api-gateway/src/user/user.module.ts index 7d494c009..af71d20af 100644 --- a/apps/api-gateway/src/user/user.module.ts +++ b/apps/api-gateway/src/user/user.module.ts @@ -5,6 +5,7 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { UserService } from './user.service'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -21,6 +22,6 @@ import { UserService } from './user.service'; ]) ], controllers: [UserController], - providers: [UserService, CommonService] + providers: [UserService, CommonService, AwsService] }) export class UserModule {} diff --git a/apps/api-gateway/src/user/user.service.ts b/apps/api-gateway/src/user/user.service.ts index 4409f0f8f..4d45bd159 100644 --- a/apps/api-gateway/src/user/user.service.ts +++ b/apps/api-gateway/src/user/user.service.ts @@ -8,6 +8,7 @@ import { GetAllUsersDto } from './dto/get-all-users.dto'; import { UpdateUserProfileDto } from './dto/update-user-profile.dto'; import { AddPasskeyDetails } from './dto/add-user.dto'; import { UpdatePlatformSettingsDto } from './dto/update-platform-settings.dto'; +import { CreateUserCertificateDto } from './dto/share-certificate.dto'; @Injectable() export class UserService extends BaseService { @@ -25,6 +26,12 @@ export class UserService extends BaseService { return this.sendNats(this.serviceProxy, 'get-user-public-profile', payload); } + + async getUserCredentialsById(credentialId: string): Promise<{ response: object }> { + const payload = { credentialId }; + return this.sendNats(this.serviceProxy, 'get-user-credentials-by-id', payload); + } + async updateUserProfile(updateUserProfileDto: UpdateUserProfileDto): Promise<{ response: object }> { const payload = { updateUserProfileDto }; return this.sendNats(this.serviceProxy, 'update-user-profile', payload); @@ -50,6 +57,13 @@ export class UserService extends BaseService { return this.sendNats(this.serviceProxy, 'accept-reject-invitations', payload); } + async shareUserCertificate( + shareUserCredentials: CreateUserCertificateDto + ): Promise<{ response: Buffer }> { + const payload = { shareUserCredentials}; + return this.sendNats(this.serviceProxy, 'share-user-certificate', payload); + } + async get( getAllUsersDto: GetAllUsersDto ): Promise<{ response: object }> { diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 9ea1d37c1..d1f3a04d1 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -71,23 +71,23 @@ export interface SchemaDetails { } export interface ImportFileDetails { credDefId: string; - filePath: string; + fileKey: string; fileName: string; } export interface PreviewRequest { - pageNumber: number, - search: string, - pageSize: number, - sortBy: string, - sortValue: string + pageNumber?: number, + search?: string, + pageSize?: number, + sortBy?: string, + sortValue?: string } export interface FileUpload { name?: string; upload_type?: string; status?: string; - orgId?: number | string; + orgId?: string; createDateTime?: Date; lastChangedDateTime?: Date; } @@ -100,4 +100,5 @@ export interface FileUploadData { createDateTime: Date; error?: string; detailError?: string; + jobId: string; } \ No newline at end of file diff --git a/apps/issuance/src/issuance.controller.ts b/apps/issuance/src/issuance.controller.ts index d3949bb0c..89a9b997e 100644 --- a/apps/issuance/src/issuance.controller.ts +++ b/apps/issuance/src/issuance.controller.ts @@ -1,6 +1,6 @@ import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; -import { IIssuance, IIssuanceWebhookInterface, IIssueCredentials, IIssueCredentialsDefinitions, OutOfBandCredentialOffer } from '../interfaces/issuance.interfaces'; +import { IIssuance, IIssuanceWebhookInterface, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, OutOfBandCredentialOffer, PreviewRequest } from '../interfaces/issuance.interfaces'; import { IssuanceService } from './issuance.service'; @Controller() @@ -32,6 +32,7 @@ export class IssuanceController { return this.issuanceService.getIssueCredentialsbyCredentialRecordId(user, credentialRecordId, orgId); } + @MessagePattern({ cmd: 'webhook-get-issue-credential' }) async getIssueCredentialWebhook(payload: IIssuanceWebhookInterface): Promise { const { createDateTime, connectionId, threadId, protocolVersion, credentialAttributes, orgId } = payload; @@ -44,4 +45,52 @@ export class IssuanceController { return this.issuanceService.outOfBandCredentialOffer(outOfBandCredentialDto); } + @MessagePattern({ cmd: 'export-schema-to-csv-by-credDefId' }) + async exportSchemaToCSV(payload: { + credentialDefinitionId: string + }): Promise { + return this.issuanceService.exportSchemaToCSV(payload.credentialDefinitionId); + } + + @MessagePattern({ cmd: 'import-and-preview-data-for-issuance' }) + async importCSV(payload: { + importFileDetails: ImportFileDetails + }): Promise { + this.logger.log(`payload.importFileDetails----${payload.importFileDetails}`); + return this.issuanceService.importAndPreviewDataForIssuance(payload.importFileDetails); + } + + @MessagePattern({ cmd: 'preview-csv-details' }) + async previewCSVDetails(payload: { requestId: string, previewFileDetails: PreviewRequest }): Promise { + return this.issuanceService.previewFileDataForIssuance( + payload.requestId, + payload.previewFileDetails + ); + } + + @MessagePattern({ cmd: 'issued-file-details' }) + async issuedFiles(payload: {orgId:string, fileParameter:PreviewRequest}): Promise { + return this.issuanceService.issuedFileDetails( + payload.orgId, + payload.fileParameter + ); + } + @MessagePattern({ cmd: 'issued-file-data' }) + async getFileDetailsByFileId(payload: {fileId:string, fileParameter:PreviewRequest}): Promise { + return this.issuanceService.getFileDetailsByFileId( + payload.fileId, + payload.fileParameter + ); + } + + + @MessagePattern({ cmd: 'issue-bulk-credentials' }) + async issueBulkCredentials(payload: { requestId: string, orgId: number, clientId: string }): Promise { + return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientId); + } + + @MessagePattern({ cmd: 'retry-bulk-credentials' }) + async retryeBulkCredentials(payload: { fileId: string, orgId: number, clientId: string }): Promise { + return this.issuanceService.retryBulkCredential(payload.fileId, payload.orgId, payload.clientId); + } } diff --git a/apps/issuance/src/issuance.module.ts b/apps/issuance/src/issuance.module.ts index 1bb961269..42c8857d7 100644 --- a/apps/issuance/src/issuance.module.ts +++ b/apps/issuance/src/issuance.module.ts @@ -8,7 +8,8 @@ import { IssuanceRepository } from './issuance.repository'; import { IssuanceService } from './issuance.service'; import { OutOfBandIssuance } from '../templates/out-of-band-issuance.template'; import { EmailDto } from '@credebl/common/dtos/email.dto'; - +import { BulkIssuanceProcessor } from './issuance.processor'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -25,6 +26,6 @@ import { EmailDto } from '@credebl/common/dtos/email.dto'; CommonModule ], controllers: [IssuanceController], - providers: [IssuanceService, IssuanceRepository, PrismaService, Logger, OutOfBandIssuance, EmailDto] + providers: [IssuanceService, IssuanceRepository, PrismaService, Logger, OutOfBandIssuance, EmailDto, BulkIssuanceProcessor, AwsService] }) export class IssuanceModule { } diff --git a/apps/issuance/src/issuance.processor.ts b/apps/issuance/src/issuance.processor.ts new file mode 100644 index 000000000..93c7487cf --- /dev/null +++ b/apps/issuance/src/issuance.processor.ts @@ -0,0 +1,26 @@ +import { OnQueueActive, Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; +import { IssuanceService } from './issuance.service'; +import { Logger } from '@nestjs/common'; + +@Processor('bulk-issuance') +export class BulkIssuanceProcessor { + private readonly logger = new Logger('IssueCredentialService'); + constructor(private readonly issuanceService: IssuanceService) {} + + @OnQueueActive() + onActive(job: Job): void { + this.logger.log( + `Processing job ${job.id} of type ${job.name} with data ${JSON.stringify(job.data)}...` + ); + } + + @Process('issue-credential') + async issueCredential(job: Job):Promise { + this.logger.log( + `Processing job ${job.id} of type ${job.name} with data ${JSON.stringify(job.data)}...` + ); + + await this.issuanceService.processIssuanceData(job.data); + } +} diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index a181e92bc..1edcabbd4 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -4,7 +4,8 @@ import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { agent_invitations, credentials, file_data, file_upload, org_agents, organisation, platform_config, shortening_url } from '@prisma/client'; import { ResponseMessages } from '@credebl/common/response-messages'; -import { FileUpload, FileUploadData, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadStatus } from 'apps/api-gateway/src/enum'; @Injectable() export class IssuanceRepository { @@ -198,13 +199,13 @@ export class IssuanceRepository { } } - async saveFileUploadDetails(fileUploadPayload: FileUpload): Promise { + async saveFileUploadDetails(fileUploadPayload): Promise { try { - const { name, orgId, status, upload_type } = fileUploadPayload; + const { name, status, upload_type, orgId } = fileUploadPayload; return this.prisma.file_upload.create({ data: { name, - orgId: `${orgId}`, + orgId: String(orgId), status, upload_type } @@ -216,18 +217,15 @@ export class IssuanceRepository { } } - async updateFileUploadDetails(fileId: string, fileUploadPayload: FileUpload): Promise { + async updateFileUploadDetails(fileId: string, fileUploadPayload): Promise { try { - const { name, orgId, status, upload_type } = fileUploadPayload; + const { status } = fileUploadPayload; return this.prisma.file_upload.update({ where: { id: fileId }, data: { - name, - orgId: `${orgId}`, - status, - upload_type + status } }); @@ -237,16 +235,176 @@ export class IssuanceRepository { } } - async saveFileUploadData(fileUploadData: FileUploadData): Promise { + async countErrorsForFile(fileUploadId: string): Promise { + try { + const errorCount = await this.prisma.file_data.count({ + where: { + fileUploadId, + isError: true + } + }); + + return errorCount; + } catch (error) { + this.logger.error(`[countErrorsForFile] - error: ${JSON.stringify(error)}`); + throw error; + } + } + async getAllFileDetails(orgId: string, getAllfileDetails: PreviewRequest): Promise<{ + fileCount: number; + fileList: { + id: string; + name: string; + status: string; + upload_type: string; + orgId: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + deletedAt: Date; + failedRecords: number; + totalRecords: number; + }[]; + }> { + try { + const fileList = await this.prisma.file_upload.findMany({ + where: { + orgId: String(orgId), + OR: [ + { name: { contains: getAllfileDetails?.search, mode: 'insensitive' } }, + { status: { contains: getAllfileDetails?.search, mode: 'insensitive' } }, + { upload_type: { contains: getAllfileDetails?.search, mode: 'insensitive' } } + ] + }, + take: Number(getAllfileDetails?.pageSize), + skip: (getAllfileDetails?.pageNumber - 1) * getAllfileDetails?.pageSize, + orderBy: { + createDateTime: 'desc' + } + }); + + const fileListWithDetails = await Promise.all( + fileList.map(async (file) => { + const failedRecords = await this.countErrorsForFile(file.id); + const totalRecords = await this.prisma.file_data.count({ + where: { + fileUploadId: file.id + } + }); + const successfulRecords = totalRecords - failedRecords; + return { ...file, failedRecords, totalRecords, successfulRecords }; + }) + ); + + const fileCount = await this.prisma.file_upload.count({ + where: { + orgId: String(orgId) + } + }); + + return { fileCount, fileList: fileListWithDetails }; + } catch (error) { + this.logger.error(`[getFileUploadDetails] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + async getFileDetailsByFileId(fileId: unknown, getAllfileDetails: PreviewRequest): Promise<{ + fileCount: number + fileDataList: { + id: string; + referenceId: string; + isError: boolean; + error: string; + detailError: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + deletedAt: Date; + fileUploadId: string; + }[] + }> { + try { + const fileDataList = await this.prisma.file_data.findMany({ + where: { + fileUploadId: fileId, + OR: [ + { error: { contains: getAllfileDetails?.search, mode: 'insensitive' } }, + { referenceId: { contains: getAllfileDetails?.search, mode: 'insensitive' } }, + { detailError: { contains: getAllfileDetails?.search, mode: 'insensitive' } } + ] + }, + take: Number(getAllfileDetails?.pageSize), + skip: (getAllfileDetails?.pageNumber - 1) * getAllfileDetails?.pageSize, + orderBy: { + createDateTime: 'desc' + } + }); + const fileCount = await this.prisma.file_data.count({ + where: { + fileUploadId: fileId + } + }); + return { fileCount, fileDataList }; + } catch (error) { + this.logger.error(`[getFileDetailsByFileId] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + + async updateFileUploadData(fileUploadData: FileUploadData): Promise { + try { + const { jobId, fileUpload, isError, referenceId, error, detailError } = fileUploadData; + if (jobId) { + return this.prisma.file_data.update({ + where: { id: jobId }, + data: { + detailError, + error, + isError, + referenceId, + fileUploadId: fileUpload + } + }); + } else { + throw error; + } + } catch (error) { + this.logger.error(`[saveFileUploadData] - error: ${JSON.stringify(error)}`); + throw error; + } + } + async deleteFileDataByJobId(jobId: string): Promise { + try { + if (jobId) { + return this.prisma.file_data.update({ + where: { id: jobId }, + data: { + credential_data: null + } + }); + } + } catch (error) { + this.logger.error(`[saveFileUploadData] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars + async saveFileDetails(fileData) { try { - const { fileUpload, isError, referenceId, error, detailError } = fileUploadData; + const { credential_data, schemaId, credDefId, status, isError, fileUploadId } = fileData; return this.prisma.file_data.create({ data: { - detailError, - error, - isError, - referenceId, - fileUploadId: fileUpload + credential_data, + schemaId, + credDefId, + status, + fileUploadId, + isError } }); @@ -255,4 +413,62 @@ export class IssuanceRepository { throw error; } } + + async getFileDetails(fileId: string): Promise { + try { + return this.prisma.file_data.findMany({ + where: { + fileUploadId: fileId + } + }); + } catch (error) { + this.logger.error(`[getFileDetails] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + async getFailedCredentials(fileId: string): Promise { + try { + return this.prisma.file_data.findMany({ + where: { + fileUploadId: fileId, + isError: true + } + }); + } catch (error) { + this.logger.error(`[getFileDetails] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + async updateFileUploadStatus(fileId: string): Promise { + try { + return this.prisma.file_upload.update({ + where: { + id: fileId + }, + data: { + status: FileUploadStatus.retry + } + }); + + } catch (error) { + this.logger.error(`[updateFileUploadStatus] - error: ${JSON.stringify(error)}`); + throw error; + } + } + + async getFileDetailsById(fileId: string): Promise { + try { + return this.prisma.file_upload.findUnique({ + where: { + id: fileId + } + }); + + } catch (error) { + this.logger.error(`[updateFileUploadStatus] - error: ${JSON.stringify(error)}`); + throw error; + } + } } \ No newline at end of file diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 35e653596..2979ab721 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -1,20 +1,33 @@ +/* eslint-disable no-useless-catch */ /* eslint-disable camelcase */ import { CommonService } from '@credebl/common'; -import { HttpException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { BadRequestException, HttpException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { IssuanceRepository } from './issuance.repository'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { CommonConstants } from '@credebl/common/common.constant'; import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; -import { ICredentialAttributesInterface, OutOfBandCredentialOfferPayload } from '../interfaces/issuance.interfaces'; +import { FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; import { OutOfBandIssuance } from '../templates/out-of-band-issuance.template'; import { EmailDto } from '@credebl/common/dtos/email.dto'; import { sendEmail } from '@credebl/common/send-grid-helper-file'; - +import { join } from 'path'; +import { parse } from 'json2csv'; +import { checkIfFileOrDirectoryExists, createFile } from '../../api-gateway/src/helper-files/file-operation.helper'; +import { parse as paParse } from 'papaparse'; +import { v4 as uuidv4 } from 'uuid'; +import { Cache } from 'cache-manager'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { orderValues, paginator } from '@credebl/common/common.utils'; +import { InjectQueue } from '@nestjs/bull'; +import { Queue } from 'bull'; +import { FileUploadStatus, FileUploadType } from 'apps/api-gateway/src/enum'; +import { AwsService } from '@credebl/aws'; +import { io } from 'socket.io-client'; @Injectable() export class IssuanceService { @@ -23,8 +36,11 @@ export class IssuanceService { @Inject('NATS_CLIENT') private readonly issuanceServiceProxy: ClientProxy, private readonly commonService: CommonService, private readonly issuanceRepository: IssuanceRepository, + @Inject(CACHE_MANAGER) private cacheManager: Cache, private readonly outOfBandIssuance: OutOfBandIssuance, - private readonly emailData: EmailDto + private readonly emailData: EmailDto, + private readonly awsService: AwsService, + @InjectQueue('bulk-issuance') private bulkIssuanceQueue: Queue ) { } @@ -277,7 +293,8 @@ export class IssuanceService { } }, autoAcceptCredential: 'always', - comment + comment, + label: organizationDetails?.name }; @@ -360,7 +377,7 @@ export class IssuanceService { return allSuccessful; } catch (error) { this.logger.error(`[outOfBoundCredentialOffer] - error in create out-of-band credentials: ${JSON.stringify(error)}`); - throw new RpcException(error); + throw new RpcException(error.response ? error.response : error); } } @@ -447,4 +464,498 @@ export class IssuanceService { } } -} + async exportSchemaToCSV(credentialDefinitionId: string): Promise { + try { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); + + const jsonData = []; + const attributesArray = JSON.parse(schemaResponse.attributes); + + // Extract the 'attributeName' values from the objects and store them in an array + const attributeNameArray = attributesArray.map(attribute => attribute.attributeName); + attributeNameArray.unshift('email'); + + const [csvData, csvFields] = [jsonData, attributeNameArray]; + + if (!csvData || !csvFields) { + // eslint-disable-next-line prefer-promise-reject-errors + return Promise.reject('Unable to transform schema data for CSV.'); + } + + const csv = parse(csvFields, { fields: csvFields }); + + const filePath = join(process.cwd(), `uploadedFiles/exports`); + + const timestamp = Math.floor(Date.now() / 1000); + const fileName = `${schemaResponse.tag}-${timestamp}.csv`; + + await createFile(filePath, fileName, csv); + this.logger.log(`File created - ${fileName}`); + const fullFilePath = join(process.cwd(), `uploadedFiles/exports/${fileName}`); + + if (!checkIfFileOrDirectoryExists(fullFilePath)) { + throw new NotFoundException(ResponseMessages.bulkIssuance.error.PathNotFound); + } + + // https required to download csv from frontend side + const filePathToDownload = `${process.env.API_GATEWAY_PROTOCOL_SECURE}://${process.env.UPLOAD_LOGO_HOST}/${fileName}`; + return { + fileContent: filePathToDownload, + fileName + }; + } catch (error) { + throw new Error('An error occurred during CSV export.'); + } + } + + + async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails): Promise { + this.logger.log(`START importAndPreviewDataForIssuance----${JSON.stringify(importFileDetails)}`); + try { + + const credDefResponse = + await this.issuanceRepository.getCredentialDefinitionDetails(importFileDetails.credDefId); + + this.logger.log(`credDefResponse----${JSON.stringify(credDefResponse)}`); + + this.logger.log(`csvFile::::::${JSON.stringify(importFileDetails.fileKey)}`); + + const getFileDetails = await this.awsService.getFile(importFileDetails.fileKey); + const csvData: string = getFileDetails.Body.toString(); + + const parsedData = paParse(csvData, { + header: true, + skipEmptyLines: true, + transformheader: (header) => header.toLowerCase().replace('#', '').trim(), + complete: (results) => results.data + }); + + this.logger.log(`parsedData----${JSON.stringify(parsedData)}`); + + if (0 >= parsedData.data.length) { + throw new BadRequestException(`File data is empty`); + } + + if (0 >= parsedData.meta.fields.length) { + throw new BadRequestException(`File header is empty`); + } + + const fileData: string[] = parsedData.data.map(Object.values); + const fileHeader: string[] = parsedData.meta.fields; + + const attributesArray = JSON.parse(credDefResponse.attributes); + + // Extract the 'attributeName' values from the objects and store them in an array + const attributeNameArray = attributesArray.map(attribute => attribute.attributeName); + + if (0 >= attributeNameArray.length) { + throw new BadRequestException( + `Attributes are empty for credential definition ${importFileDetails.credDefId}` + ); + } + + await this.validateFileHeaders(fileHeader, attributeNameArray); + await this.validateFileData(fileData); + + const resData = { + schemaLedgerId: credDefResponse.schemaLedgerId, + credentialDefinitionId: importFileDetails.credDefId, + fileData: parsedData, + fileName: importFileDetails.fileName + }; + const newCacheKey = uuidv4(); + + await this.cacheManager.set(newCacheKey, JSON.stringify(resData), 3600); + + return newCacheKey; + + } catch (error) { + this.logger.error(`error in validating credentials : ${error}`); + throw new RpcException(error.response ? error.response : error); + } finally { + // await this.awsService.deleteFile(importFileDetails.fileKey); + // this.logger.error(`Deleted uploaded file after processing.`); + } + } + + async previewFileDataForIssuance( + requestId: string, + previewRequest: PreviewRequest + ): Promise { + try { + if ('' !== requestId.trim()) { + const cachedData = await this.cacheManager.get(requestId); + if (!cachedData) { + throw new NotFoundException(ResponseMessages.issuance.error.emptyFileData); + } + if (cachedData === undefined || null) { + throw new BadRequestException(ResponseMessages.issuance.error.previewCachedData); + } + const parsedData = JSON.parse(cachedData as string).fileData.data; + parsedData.sort(orderValues(previewRequest.sortBy, previewRequest.sortValue)); + const finalData = paginator(parsedData, previewRequest.pageNumber, previewRequest.pageSize); + + return finalData; + } else { + throw new BadRequestException(ResponseMessages.issuance.error.previewFile); + } + } catch (error) { + this.logger.error(`error in previewFileDataForIssuance : ${error}`); + throw new RpcException(error.response); + } + } + + async getFileDetailsByFileId( + fileId: string, + getAllfileDetails: PreviewRequest + ): Promise { + try { + + const fileData = await this.issuanceRepository.getFileDetailsByFileId(fileId, getAllfileDetails); + + const fileResponse = { + totalItems: fileData.fileCount, + hasNextPage: getAllfileDetails.pageSize * getAllfileDetails.pageNumber < fileData.fileCount, + hasPreviousPage: 1 < getAllfileDetails.pageNumber, + nextPage: getAllfileDetails.pageNumber + 1, + previousPage: getAllfileDetails.pageNumber - 1, + lastPage: Math.ceil(fileData.fileCount / getAllfileDetails.pageSize), + data: fileData.fileDataList + }; + + if (0 !== fileData.fileCount) { + return fileResponse; + } else { + throw new NotFoundException(ResponseMessages.issuance.error.fileNotFound); + } + + } catch (error) { + this.logger.error(`error in issuedFileDetails : ${error}`); + throw new RpcException(error.response); + } + } + + async issuedFileDetails( + orgId: string, + getAllfileDetails: PreviewRequest + ): Promise { + try { + + const fileDetails = await this.issuanceRepository.getAllFileDetails(orgId, getAllfileDetails); + const fileResponse = { + totalItems: fileDetails.fileCount, + hasNextPage: getAllfileDetails.pageSize * getAllfileDetails.pageNumber < fileDetails.fileCount, + hasPreviousPage: 1 < getAllfileDetails.pageNumber, + nextPage: getAllfileDetails.pageNumber + 1, + previousPage: getAllfileDetails.pageNumber - 1, + lastPage: Math.ceil(fileDetails.fileCount / getAllfileDetails.pageSize), + data: fileDetails.fileList + }; + + if (0 !== fileDetails.fileCount) { + return fileResponse; + } else { + throw new NotFoundException(ResponseMessages.issuance.error.notFound); + } + + } catch (error) { + this.logger.error(`error in issuedFileDetails : ${error}`); + throw new RpcException(error.response); + } + } + + async issueBulkCredential(requestId: string, orgId: number, clientId: string): Promise { + const fileUpload: { + lastChangedDateTime: Date; + name?: string; + upload_type: string; + status: string; + orgId: string | number; + createDateTime: Date; + } = { + upload_type: '', + status: '', + orgId: '', + createDateTime: undefined, + lastChangedDateTime: undefined + }; + let respFileUpload; + if ('' === requestId.trim()) { + throw new BadRequestException( + `Param 'requestId' is missing from the request.` + ); + } + + this.logger.log(`requestId----${JSON.stringify(requestId)}`); + try { + const cachedData = await this.cacheManager.get(requestId); + this.logger.log(`cachedData----${JSON.stringify(cachedData)}`); + if (!cachedData) { + throw new BadRequestException(ResponseMessages.issuance.error.cacheTimeOut); + } + + const parsedData = JSON.parse(cachedData as string).fileData.data; + const parsedPrimeDetails = JSON.parse(cachedData as string); + fileUpload.upload_type = FileUploadType.Issuance; + fileUpload.status = FileUploadStatus.started; + fileUpload.orgId = String(orgId); + fileUpload.createDateTime = new Date(); + + if (parsedPrimeDetails && parsedPrimeDetails.fileName) { + fileUpload.name = parsedPrimeDetails.fileName; + } + + respFileUpload = await this.issuanceRepository.saveFileUploadDetails(fileUpload); + + const saveFileDetailsPromises = parsedData.map(async (element) => { + const credentialPayload = { + credential_data: element, + schemaId: parsedPrimeDetails.schemaLedgerId, + credDefId: parsedPrimeDetails.credentialDefinitionId, + state: false, + isError: false, + fileUploadId: respFileUpload.id + }; + return this.issuanceRepository.saveFileDetails(credentialPayload); + }); + + // Wait for all saveFileDetails operations to complete + await Promise.all(saveFileDetailsPromises); + + // Now fetch the file details + const respFile = await this.issuanceRepository.getFileDetails(respFileUpload.id); + if (!respFile) { + throw new BadRequestException(ResponseMessages.issuance.error.fileData); + } + for (const element of respFile) { + try { + this.logger.log(`element----${JSON.stringify(element)}`); + const payload = { + data: element.credential_data, + fileUploadId: element.fileUploadId, + clientId, + cacheId: requestId, + credentialDefinitionId: element.credDefId, + schemaLedgerId: element.schemaId, + isRetry: false, + orgId, + id: element.id, + isLastData: respFile.indexOf(element) === respFile.length - 1 + }; + + this.processIssuanceData(payload); + } catch (error) { + this.logger.error(`Error processing issuance data: ${error}`); + } + } + + return 'Process initiated for bulk issuance'; + } catch (error) { + fileUpload.status = FileUploadStatus.interrupted; + this.logger.error(`error in issueBulkCredential : ${error}`); + throw new RpcException(error.response); + } finally { + if (respFileUpload !== undefined && respFileUpload.id !== undefined) { + fileUpload.lastChangedDateTime = new Date(); + await this.issuanceRepository.updateFileUploadDetails(respFileUpload.id, fileUpload); + } + } + } + + async retryBulkCredential(fileId: string, orgId: number, clientId: string): Promise { + let respFile; + let respFileUpload; + + try { + + const fileDetails = await this.issuanceRepository.getFileDetailsById(fileId); + if (!fileDetails) { + throw new BadRequestException(ResponseMessages.issuance.error.retry); + } + + respFileUpload = await this.issuanceRepository.updateFileUploadStatus(fileId); + respFile = await this.issuanceRepository.getFailedCredentials(fileId); + + if (0 === respFile.length) { + const errorMessage = ResponseMessages.bulkIssuance.error.fileDetailsNotFound; + throw new BadRequestException(`${errorMessage}`); + } + + for (const element of respFile) { + try { + this.logger.log(`element----${JSON.stringify(element)}`); + const payload = { + data: element.credential_data, + fileUploadId: element.fileUploadId, + clientId, + credentialDefinitionId: element.credDefId, + schemaLedgerId: element.schemaId, + orgId, + id: element.id, + isRetry: true, + isLastData: respFile.indexOf(element) === respFile.length - 1 + }; + + await this.processIssuanceData(payload); + } catch (error) { + // Handle errors if needed + this.logger.error(`Error processing issuance data: ${error}`); + } + } + + return 'Process reinitiated for bulk issuance'; + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } finally { + // Update file upload details in the database + if (respFileUpload && respFileUpload.id) { + const fileUpload = { + status: FileUploadStatus.interrupted, + lastChangedDateTime: new Date() + }; + + await this.issuanceRepository.updateFileUploadDetails(respFileUpload.id, fileUpload); + } + } + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type + async processIssuanceData(jobDetails) { + const socket = await io(`${process.env.SOCKET_HOST}`, { + reconnection: true, + reconnectionDelay: 5000, + reconnectionAttempts: Infinity, + autoConnect: true, + transports: ['websocket'] + }); + + const fileUploadData: FileUploadData = { + fileUpload: '', + fileRow: '', + isError: false, + referenceId: '', + createDateTime: undefined, + error: '', + detailError: '', + jobId: '' + }; + this.logger.log(`jobDetails----${JSON.stringify(jobDetails)}`); + + fileUploadData.fileUpload = jobDetails.fileUploadId; + fileUploadData.fileRow = JSON.stringify(jobDetails); + fileUploadData.isError = false; + fileUploadData.createDateTime = new Date(); + fileUploadData.referenceId = jobDetails.data.email; + fileUploadData.jobId = jobDetails.id; + try { + + const oobIssuancepayload = { + credentialDefinitionId: jobDetails.credentialDefinitionId, + orgId: jobDetails.orgId, + attributes: [], + emailId: jobDetails.data.email + }; + + for (const key in jobDetails.data) { + if (jobDetails.data.hasOwnProperty(key) && 'email' !== key) { + const value = jobDetails.data[key]; + oobIssuancepayload.attributes.push({ name: key, value }); + } + } + + const oobCredentials = await this.outOfBandCredentialOffer( + oobIssuancepayload + ); + if (oobCredentials) { + await this.issuanceRepository.deleteFileDataByJobId(jobDetails.id); + } + } catch (error) { + this.logger.error( + `error in issuanceBulkCredential for data ${JSON.stringify(jobDetails)} : ${JSON.stringify(error)}` + ); + fileUploadData.isError = true; + fileUploadData.error = JSON.stringify(error.error) ? JSON.stringify(error.error) : JSON.stringify(error); + fileUploadData.detailError = `${JSON.stringify(error)}`; + } + await this.issuanceRepository.updateFileUploadData(fileUploadData); + + try { + if (jobDetails.isLastData) { + if (!jobDetails.isRetry) { + this.cacheManager.del(jobDetails.cacheId); + await this.issuanceRepository.updateFileUploadDetails(jobDetails.fileUploadId, { + status: FileUploadStatus.completed, + lastChangedDateTime: new Date() + }); + } else { + await this.issuanceRepository.updateFileUploadDetails(jobDetails.fileUploadId, { + status: FileUploadStatus.completed, + lastChangedDateTime: new Date() + }); + } + + this.logger.log(`jobDetails.clientId----${JSON.stringify(jobDetails.clientId)}`); + + socket.emit('bulk-issuance-process-completed', { clientId: jobDetails.clientId }); + } + } catch (error) { + this.logger.error(`Error completing bulk issuance process: ${error}`); + throw error; + } + + } + + async validateFileHeaders( + fileHeader: string[], + schemaAttributes: string[] + ): Promise { + try { + const fileSchemaHeader: string[] = fileHeader.slice(); + + if ('email' === fileHeader[0]) { + fileSchemaHeader.splice(0, 1); + } else { + throw new BadRequestException(ResponseMessages.bulkIssuance.error.emailColumn + ); + } + + if (schemaAttributes.length !== fileSchemaHeader.length) { + throw new BadRequestException(ResponseMessages.bulkIssuance.error.attributeNumber + ); + } + + const mismatchedAttributes = fileSchemaHeader.filter(value => !schemaAttributes.includes(value)); + + if (0 < mismatchedAttributes.length) { + throw new BadRequestException(ResponseMessages.bulkIssuance.error.mismatchedAttributes); + } + } catch (error) { + throw error; + + } + } + + async validateFileData(fileData: string[]): Promise { + let rowIndex: number = 0; + let columnIndex: number = 0; + const isNullish = Object.values(fileData).some((value) => { + columnIndex = 0; + rowIndex++; + const isFalsyForColumnValue = Object.values(value).some((colvalue) => { + columnIndex++; + if (null === colvalue || '' == colvalue) { + return true; + } + return false; + }); + return isFalsyForColumnValue; + }); + this.logger.log(`isNullish: ${isNullish}`); + if (isNullish) { + throw new BadRequestException( + `Empty data found at row ${rowIndex} and column ${columnIndex}` + ); + } + } + +} \ No newline at end of file diff --git a/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts b/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts index 7710d9f95..0db669d4f 100644 --- a/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts +++ b/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts @@ -168,7 +168,7 @@ export class CredentialDefinitionRepository { } } - + async getAllCredDefsByOrgIdForBulk(payload: BulkCredDefSchema): Promise { try { const { credDefSortBy, sortValue, orgId } = payload; @@ -190,7 +190,7 @@ export class CredentialDefinitionRepository { }); const schemaLedgerIdArray = credentialDefinitions.map((credDef) => credDef.schemaLedgerId); - + const schemas = await this.prisma.schema.findMany({ where: { schemaLedgerId: { @@ -201,10 +201,11 @@ export class CredentialDefinitionRepository { name: true, version: true, schemaLedgerId: true, - orgId: true + orgId: true, + attributes: true } }); - + // Match Credential Definitions with Schemas and map to CredDefSchema const matchingSchemas = credentialDefinitions.map((credDef) => { @@ -213,12 +214,16 @@ export class CredentialDefinitionRepository { if (matchingSchema) { return { credentialDefinitionId: credDef.credentialDefinitionId, - schemaCredDefName: `${matchingSchema.name}:${matchingSchema.version}-${credDef.tag}` + schemaCredDefName: `${matchingSchema.name}:${matchingSchema.version}-${credDef.tag}`, + schemaName: matchingSchema.name, + schemaVersion: matchingSchema.version, + schemaAttributes: matchingSchema.attributes, + credentialDefinition: credDef.tag }; } return null; }); - + // Filter out null values (missing schemas) and return the result return matchingSchemas.filter((schema) => null !== schema) as CredDefSchema[]; } catch (error) { @@ -226,6 +231,6 @@ export class CredentialDefinitionRepository { throw error; } } - - + + } \ No newline at end of file diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 0ec7ca182..4d0d6f968 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -375,6 +375,7 @@ export class OrganizationRepository { pageSize: number ): Promise { try { + const sortByName = 'asc'; const result = await this.prisma.$transaction([ this.prisma.organisation.findMany({ where: { @@ -394,7 +395,8 @@ export class OrganizationRepository { take: pageSize, skip: (pageNumber - 1) * pageSize, orderBy: { - createDateTime: 'desc' + name: sortByName + } }), this.prisma.organisation.count({ diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index 7ad363215..fe0be11b7 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -62,3 +62,14 @@ export interface PlatformSettingsI { enableEcosystem: boolean; multiEcosystemSupport: boolean; } + +export interface ShareUserCertificateI { + schemaId: string; + credentialId: string; + attributes: Attribute[]; +} + +export interface Attribute { + [key: string]: string; + label: string + } \ No newline at end of file diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index b8b35b53e..feefc6c61 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -3,6 +3,7 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { PlatformSettingsI, + ShareUserCertificateI, UpdateUserProfile, UserEmailVerificationDto, UserI, @@ -12,7 +13,7 @@ import { import { InternalServerErrorException } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase -import { user } from '@prisma/client'; +import { schema, user } from '@prisma/client'; interface UserQueryOptions { id?: number; // Use the appropriate type based on your data model @@ -102,6 +103,19 @@ export class UserRepository { return this.findUser(queryOptions); } + /** + * + * @param id + * @returns User profile data + */ + async getUserCredentialsById(credentialId: string): Promise { + return this.prisma.user_credentials.findUnique({ + where: { + credentialId + } + }); + } + /** * * @param id @@ -438,6 +452,34 @@ export class UserRepository { return { totalPages, users }; } + async getAttributesBySchemaId(shareUserCertificate: ShareUserCertificateI): Promise { + try { + const getAttributes = await this.prisma.schema.findFirst({ + where: { + schemaLedgerId: shareUserCertificate.schemaId + } + }); + return getAttributes; + } catch (error) { + this.logger.error(`checkSchemaExist:${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + + async saveCertificateImageUrl(imageUrl: string, credentialId: string): Promise { + try { + const saveImageUrl = await this.prisma.user_credentials.create({ + data: { + imageUrl, + credentialId + } + }); + return saveImageUrl; + } catch (error) { + throw new Error(`Error saving certificate image URL: ${error.message}`); + } + } + async checkUniqueUserExist(email: string): Promise { try { return this.prisma.user.findUnique({ @@ -491,7 +533,7 @@ export class UserRepository { } } - /** + /** * * @Body updatePlatformSettings * @returns Update platform settings @@ -519,21 +561,20 @@ export class UserRepository { } } -/** + /** * * @Body updatePlatformSettings * @returns Update ecosystem settings */ - async updateEcosystemSettings(eosystemKeys: string[], ecosystemObj: object): Promise { + async updateEcosystemSettings(eosystemKeys: string[], ecosystemObj: object): Promise { try { for (const key of eosystemKeys) { - const ecosystemKey = await this.prisma.ecosystem_config.findFirst({ where: { key } }); - + await this.prisma.ecosystem_config.update({ where: { id: ecosystemKey.id @@ -545,7 +586,6 @@ export class UserRepository { } return true; - } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); throw new InternalServerErrorException(error); @@ -571,5 +611,4 @@ export class UserRepository { throw new InternalServerErrorException(error); } } - } diff --git a/apps/user/src/fido/fido.module.ts b/apps/user/src/fido/fido.module.ts index a5106d814..14077a4b3 100644 --- a/apps/user/src/fido/fido.module.ts +++ b/apps/user/src/fido/fido.module.ts @@ -19,6 +19,7 @@ import { UserOrgRolesRepository } from 'libs/user-org-roles/repositories'; import { UserOrgRolesService } from '@credebl/user-org-roles'; import { UserRepository } from '../../repositories/user.repository'; import { UserService } from '../user.service'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -36,6 +37,7 @@ import { UserService } from '../user.service'; ], controllers: [FidoController], providers: [ + AwsService, UserService, PrismaService, FidoService, diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 5d3c3b3bb..f4087c1bd 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -1,4 +1,4 @@ -import { AddPasskeyDetails, PlatformSettingsI, UpdateUserProfile, UserEmailVerificationDto, UserI, userInfo } from '../interfaces/user.interface'; +import { AddPasskeyDetails, PlatformSettingsI, ShareUserCertificateI, UpdateUserProfile, UserEmailVerificationDto, UserI, userInfo } from '../interfaces/user.interface'; import { AcceptRejectInvitationDto } from '../dtos/accept-reject-invitation.dto'; import { Controller } from '@nestjs/common'; @@ -61,6 +61,12 @@ export class UserController { return this.userService.findUserByEmail(payload); } + + @MessagePattern({ cmd: 'get-user-credentials-by-id' }) + async getUserCredentialsById(payload: { credentialId }): Promise { + return this.userService.getUserCredentialsById(payload); + } + @MessagePattern({ cmd: 'get-org-invitations' }) async invitations(payload: { id; status; pageNumber; pageSize; search; }): Promise { return this.userService.invitations(payload); @@ -79,6 +85,18 @@ export class UserController { return this.userService.acceptRejectInvitations(payload.acceptRejectInvitation, payload.userId); } + /** + * + * @param payload + * @returns Share user certificate + */ + @MessagePattern({ cmd: 'share-user-certificate' }) + async shareUserCertificate(payload: { + shareUserCredentials: ShareUserCertificateI; + }): Promise { + return this.userService.shareUserCertificate(payload.shareUserCredentials); + } + /** * * @param payload diff --git a/apps/user/src/user.module.ts b/apps/user/src/user.module.ts index 865dc6e04..9ce90e213 100644 --- a/apps/user/src/user.module.ts +++ b/apps/user/src/user.module.ts @@ -17,6 +17,7 @@ import { UserOrgRolesService } from '@credebl/user-org-roles'; import { UserRepository } from '../repositories/user.repository'; import { UserService } from './user.service'; import { UserDevicesRepository } from '../repositories/user-device.repository'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -29,12 +30,14 @@ import { UserDevicesRepository } from '../repositories/user-device.repository'; } } ]), + CommonModule, FidoModule, OrgRolesModule ], controllers: [UserController], providers: [ + AwsService, UserService, UserRepository, PrismaService, diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index a8e25762f..6385ae49c 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { BadRequestException, ConflictException, @@ -27,8 +28,10 @@ import { sendEmail } from '@credebl/common/send-grid-helper-file'; import { user } from '@prisma/client'; import { AddPasskeyDetails, + Attribute, InvitationsI, PlatformSettingsI, + ShareUserCertificateI, UpdateUserProfile, UserEmailVerificationDto, UserI, @@ -39,7 +42,15 @@ import { UserActivityService } from '@credebl/user-activity'; import { SupabaseService } from '@credebl/supabase'; import { UserDevicesRepository } from '../repositories/user-device.repository'; import { v4 as uuidv4 } from 'uuid'; -import { EcosystemConfigSettings } from '@credebl/enum/enum'; +import { EcosystemConfigSettings, UserCertificateId } from '@credebl/enum/enum'; +import { WinnerTemplate } from '../templates/winner-template'; +import { ParticipantTemplate } from '../templates/participant-template'; +import { ArbiterTemplate } from '../templates/arbiter-template'; +import validator from 'validator'; +import { DISALLOWED_EMAIL_DOMAIN } from '@credebl/common/common.constant'; +import { AwsService } from '@credebl/aws'; +import puppeteer from 'puppeteer'; +import { WorldRecordTemplate } from '../templates/world-record-template'; @Injectable() export class UserService { @@ -52,6 +63,7 @@ export class UserService { private readonly userOrgRoleService: UserOrgRolesService, private readonly userActivityService: UserActivityService, private readonly userRepository: UserRepository, + private readonly awsService: AwsService, private readonly userDevicesRepository: UserDevicesRepository, private readonly logger: Logger, @Inject('NATS_CLIENT') private readonly userServiceProxy: ClientProxy @@ -64,6 +76,16 @@ export class UserService { */ async sendVerificationMail(userEmailVerificationDto: UserEmailVerificationDto): Promise { try { + const { email } = userEmailVerificationDto; + + if ('PROD' === process.env.PLATFORM_PROFILE_MODE) { + // eslint-disable-next-line prefer-destructuring + const domain = email.split('@')[1]; + + if (DISALLOWED_EMAIL_DOMAIN.includes(domain)) { + throw new BadRequestException(ResponseMessages.user.error.InvalidEmailDomain); + } + } const userDetails = await this.userRepository.checkUserExist(userEmailVerificationDto.email); if (userDetails && userDetails.isEmailVerified) { @@ -213,7 +235,7 @@ export class UserService { const resUser = await this.userRepository.addUserPassword(email, userInfo.password); const userDetails = await this.userRepository.getUserDetails(email); const decryptedPassword = await this.commonService.decryptPassword(userDetails.password); - + if (!resUser) { throw new NotFoundException(ResponseMessages.user.error.invalidEmail); } @@ -222,7 +244,7 @@ export class UserService { password: decryptedPassword }); } else { - const decryptedPassword = await this.commonService.decryptPassword(userInfo.password); + const decryptedPassword = await this.commonService.decryptPassword(userInfo.password); supaUser = await this.supabaseService.getClient().auth.signUp({ email, @@ -275,6 +297,12 @@ export class UserService { } } + private validateEmail(email: string): void { + if (!validator.isEmail(email)) { + throw new UnauthorizedException(ResponseMessages.user.error.invalidEmail); + } + } + /** * * @param loginUserDto @@ -282,7 +310,9 @@ export class UserService { */ async login(loginUserDto: LoginUserDto): Promise { const { email, password, isPasskey } = loginUserDto; + try { + this.validateEmail(email); const userData = await this.userRepository.checkUserExist(email); if (!userData) { throw new NotFoundException(ResponseMessages.user.error.notFound); @@ -312,14 +342,13 @@ export class UserService { async generateToken(email: string, password: string): Promise { try { - const supaInstance = await this.supabaseService.getClient(); this.logger.error(`supaInstance::`, supaInstance); - + const { data, error } = await supaInstance.auth.signInWithPassword({ email, password - }); + }); this.logger.error(`Supa Login Error::`, JSON.stringify(error)); @@ -337,21 +366,16 @@ export class UserService { async getProfile(payload: { id }): Promise { try { const userData = await this.userRepository.getUserById(payload.id); - const ecosystemSettingsList = await this.prisma.ecosystem_config.findMany( - { - where:{ - OR: [ - { key: EcosystemConfigSettings.ENABLE_ECOSYSTEM }, - { key: EcosystemConfigSettings.MULTI_ECOSYSTEM } - ] - } + const ecosystemSettingsList = await this.prisma.ecosystem_config.findMany({ + where: { + OR: [{ key: EcosystemConfigSettings.ENABLE_ECOSYSTEM }, { key: EcosystemConfigSettings.MULTI_ECOSYSTEM }] } - ); + }); for (const setting of ecosystemSettingsList) { userData[setting.key] = 'true' === setting.value; } - + return userData; } catch (error) { this.logger.error(`get user: ${JSON.stringify(error)}`); @@ -374,6 +398,19 @@ export class UserService { } } + async getUserCredentialsById(payload: { credentialId }): Promise { + try { + const userCredentials = await this.userRepository.getUserCredentialsById(payload.credentialId); + if (!userCredentials) { + throw new NotFoundException(ResponseMessages.user.error.credentialNotFound); + } + return userCredentials; + } catch (error) { + this.logger.error(`get user: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + async updateUserProfile(updateUserProfileDto: UpdateUserProfile): Promise { try { return this.userRepository.updateUserProfile(updateUserProfileDto); @@ -505,6 +542,97 @@ export class UserService { } } + /** + * + * @returns + */ + async shareUserCertificate(shareUserCertificate: ShareUserCertificateI): Promise { + const getAttributes = await this.userRepository.getAttributesBySchemaId(shareUserCertificate); + if (!getAttributes) { + throw new NotFoundException(ResponseMessages.schema.error.invalidSchemaId); + } + + const attributeArray = []; + let attributeJson = {}; + const attributePromises = shareUserCertificate.attributes.map(async (iterator: Attribute) => { + attributeJson = { + [iterator.name]: iterator.value + }; + attributeArray.push(attributeJson); + }); + await Promise.all(attributePromises); + let template; + + switch (shareUserCertificate.schemaId.split(':')[2]) { + case UserCertificateId.WINNER: + // eslint-disable-next-line no-case-declarations + const userWinnerTemplate = new WinnerTemplate(); + template = await userWinnerTemplate.getWinnerTemplate(attributeArray); + break; + case UserCertificateId.PARTICIPANT: + // eslint-disable-next-line no-case-declarations + const userParticipantTemplate = new ParticipantTemplate(); + template = await userParticipantTemplate.getParticipantTemplate(attributeArray); + break; + case UserCertificateId.ARBITER: + // eslint-disable-next-line no-case-declarations + const userArbiterTemplate = new ArbiterTemplate(); + template = await userArbiterTemplate.getArbiterTemplate(attributeArray); + break; + case UserCertificateId.WORLD_RECORD: + // eslint-disable-next-line no-case-declarations + const userWorldRecordTemplate = new WorldRecordTemplate(); + template = await userWorldRecordTemplate.getWorldRecordTemplate(attributeArray); + break; + default: + throw new NotFoundException('error in get attributes'); + } + + const imageBuffer = + await this.convertHtmlToImage(template, shareUserCertificate.credentialId); + const verifyCode = uuidv4(); + + const imageUrl = await this.awsService.uploadUserCertificate( + imageBuffer, + 'png', + verifyCode, + 'certificates', + 'base64' + ); + const existCredentialId = await this.userRepository.getUserCredentialsById(shareUserCertificate.credentialId); + + if (existCredentialId) { + return `${process.env.FRONT_END_URL}/certificates/${shareUserCertificate.credentialId}`; + } + + const saveCredentialData = await this.saveCertificateUrl(imageUrl, shareUserCertificate.credentialId); + + if (!saveCredentialData) { + throw new BadRequestException(ResponseMessages.schema.error.notStoredCredential); + } + + return `${process.env.FRONT_END_URL}/certificates/${shareUserCertificate.credentialId}`; + + } + + async saveCertificateUrl(imageUrl: string, credentialId: string): Promise { + return this.userRepository.saveCertificateImageUrl(imageUrl, credentialId); + } + + async convertHtmlToImage(template: string, credentialId: string): Promise { + const browser = await puppeteer.launch({ + executablePath: '/usr/bin/google-chrome', + args: ['--no-sandbox', '--disable-setuid-sandbox'], + headless: true + }); + const page = await browser.newPage(); + await page.setViewport({ width: 800, height: 1020, deviceScaleFactor: 6}); + await page.setContent(template); + const screenshot = await page.screenshot(); + await browser.close(); + return screenshot; + } + /** * * @param acceptRejectInvitation @@ -635,7 +763,7 @@ export class UserService { async updatePlatformSettings(platformSettings: PlatformSettingsI): Promise { try { const platformConfigSettings = await this.userRepository.updatePlatformSettings(platformSettings); - + if (!platformConfigSettings) { throw new BadRequestException(ResponseMessages.user.error.notUpdatePlatformSettings); } @@ -655,7 +783,7 @@ export class UserService { if (0 === eosystemKeys.length) { return ResponseMessages.user.success.platformEcosystemettings; } - + const ecosystemSettings = await this.userRepository.updateEcosystemSettings(eosystemKeys, ecosystemobj); if (!ecosystemSettings) { @@ -663,7 +791,6 @@ export class UserService { } return ResponseMessages.user.success.platformEcosystemettings; - } catch (error) { this.logger.error(`update platform settings: ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -671,29 +798,27 @@ export class UserService { } async getPlatformEcosystemSettings(): Promise { - try { + try { + const platformSettings = {}; + const platformConfigSettings = await this.userRepository.getPlatformSettings(); - const platformSettings = {}; - const platformConfigSettings = await this.userRepository.getPlatformSettings(); - - if (!platformConfigSettings) { - throw new BadRequestException(ResponseMessages.user.error.platformSetttingsNotFound); - } - - const ecosystemConfigSettings = await this.userRepository.getEcosystemSettings(); - - if (!ecosystemConfigSettings) { - throw new BadRequestException(ResponseMessages.user.error.ecosystemSetttingsNotFound); - } + if (!platformConfigSettings) { + throw new BadRequestException(ResponseMessages.user.error.platformSetttingsNotFound); + } - platformSettings['platform_config'] = platformConfigSettings; - platformSettings['ecosystem_config'] = ecosystemConfigSettings; + const ecosystemConfigSettings = await this.userRepository.getEcosystemSettings(); - return platformSettings; - - } catch (error) { - this.logger.error(`update platform settings: ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); + if (!ecosystemConfigSettings) { + throw new BadRequestException(ResponseMessages.user.error.ecosystemSetttingsNotFound); } + + platformSettings['platform_config'] = platformConfigSettings; + platformSettings['ecosystem_config'] = ecosystemConfigSettings; + + return platformSettings; + } catch (error) { + this.logger.error(`update platform settings: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } } -} \ No newline at end of file +} diff --git a/apps/user/templates/arbiter-template.ts b/apps/user/templates/arbiter-template.ts new file mode 100644 index 000000000..d2bfd4c73 --- /dev/null +++ b/apps/user/templates/arbiter-template.ts @@ -0,0 +1,88 @@ +import { Attribute } from '../interfaces/user.interface'; + +export class ArbiterTemplate { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getArbiterTemplate(attributes: Attribute[]): Promise { + try { + const [name, country, issuedBy] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'full_name')?.full_name ?? ''; + const country = this.findAttributeByName(attributes, 'country')?.country ?? ''; + const issuedBy = this.findAttributeByName(attributes, 'issued_by')?.issued_by ?? ''; + return [name, country, issuedBy]; + }); + return ` + + + + + + + + + + +
+ background +
+
+

CERTIFICATE

+

OF RECOGNITION

+
+ +

IS PROUDLY PRESENTED TO

+

${name}

+ + +

has served as an Arbiter at the + ${issuedBy} World Memory Championship 2023. +

+

+

+

Your dedication, professionalism, and impartiality as an Arbiter

+

have significantly contributed to the fair and smooth conduct

+

of the championship. Your commitment to upholding the

+

highest standards of integrity and sportsmanship has

+

played a crucial role in maintaining the credibility of the competition.

+ +

+
Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, ${country}
+
+
+ + + `; + } catch {} + } +} diff --git a/apps/user/templates/participant-template.ts b/apps/user/templates/participant-template.ts new file mode 100644 index 000000000..b040fbb70 --- /dev/null +++ b/apps/user/templates/participant-template.ts @@ -0,0 +1,84 @@ +import { Attribute } from "../interfaces/user.interface"; + +export class ParticipantTemplate { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getParticipantTemplate(attributes: Attribute[]): Promise { + + try { + const [name, country, issuedBy] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'full_name')?.full_name ?? ''; + const country = this.findAttributeByName(attributes, 'country')?.country ?? ''; + const issuedBy = this.findAttributeByName(attributes, 'issued_by')?.issued_by ?? ''; + return [name, country, issuedBy]; + }); + + return ` + + + + + + + + + + +
+ background +
+
+

CERTIFICATE

+

OF ACHIEVEMENT

+
+ +

IS PROUDLY PRESENTED TO

+

${name}

+ + for successfully participating in the + ${issuedBy} World Memory Championship 2023. +

+

+

We acknowledge your dedication, hard work, and

+

exceptional memory skills demonstrated during the competition.

+

+
Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, ${country}
+
+
+ + + `; + } catch (error) {} + } +} diff --git a/apps/user/templates/winner-template.ts b/apps/user/templates/winner-template.ts new file mode 100644 index 000000000..150fe5a05 --- /dev/null +++ b/apps/user/templates/winner-template.ts @@ -0,0 +1,89 @@ +import { Attribute } from '../interfaces/user.interface'; + +export class WinnerTemplate { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getWinnerTemplate(attributes: Attribute[]): Promise { + try { + const [name, country, position, discipline, issuedBy, category] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'full_name')?.full_name ?? ''; + const country = this.findAttributeByName(attributes, 'country')?.country ?? ''; + const position = this.findAttributeByName(attributes, 'position')?.position ?? ''; + const discipline = this.findAttributeByName(attributes, 'discipline')?.discipline ?? ''; + const issuedBy = this.findAttributeByName(attributes, 'issued_by')?.issued_by ?? ''; + const category = this.findAttributeByName(attributes, 'category')?.category ?? ''; + const date = this.findAttributeByName(attributes, 'issued_date')?.issued_date ?? ''; + return [name, country, position, discipline, issuedBy, category, date]; + }); + return ` + + + + + + + + + + +
+ background +
+
+

CERTIFICATE

+

OF EXCELLENCE

+
+ +

IS PROUDLY PRESENTED TO

+

${name}

+ +

has secured ${position} position for ${discipline}

+

in ${category} category at the

+

+ ${issuedBy} World Memory Championship 2023. +

+

+

+

We acknowledge your dedication, hard work, and

+

exceptional memory skills demonstrated during the competition.

+

+
Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, ${country}
+
+
+ + + + `; + } catch {} + } +} diff --git a/apps/user/templates/world-record-template.ts b/apps/user/templates/world-record-template.ts new file mode 100644 index 000000000..1436b2348 --- /dev/null +++ b/apps/user/templates/world-record-template.ts @@ -0,0 +1,86 @@ +import { Attribute } from '../interfaces/user.interface'; + +export class WorldRecordTemplate { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getWorldRecordTemplate(attributes: Attribute[]): Promise { + try { + const [name, country, discipline, issuedBy] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'full_name')?.full_name ?? ''; + const country = this.findAttributeByName(attributes, 'country')?.country ?? ''; + const discipline = this.findAttributeByName(attributes, 'discipline')?.discipline ?? ''; + const issuedBy = this.findAttributeByName(attributes, 'issued_by')?.issued_by ?? ''; + return [name, country, discipline, issuedBy]; + }); + return ` + + + + + + + + + + +
+ background +
+
+

CERTIFICATE

+

OF WORLD RECORD

+
+ +

IS PROUDLY PRESENTED TO

+

${name}

+ + for successfully creating the world record in the + ${discipline} +

discipline during the + ${issuedBy} World Memory Championship 2023. +

+

+

+

We acknowledge your dedication, hard work, and

+

exceptional memory skills demonstrated during the competition.

+

+
Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, ${country}
+
+
+ + + `; + } catch {} + } +} diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 99ad821f7..26f725065 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -84,12 +84,13 @@ export interface ISendProofRequestPayload { connectionId?: string; proofFormats: IProofFormats; autoAcceptProof: string; + label?: string; } export interface IProofRequestPayload { url: string; apiKey: string; - proofRequestPayload: ISendProofRequestPayload + proofRequestPayload: ISendProofRequestPayload; } interface IWebhookPresentationProof { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 7d49ff2b2..e432d2dd4 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -344,6 +344,7 @@ export class VerificationService { proofRequestPayload: { protocolVersion, comment, + label: organizationDetails?.name, proofFormats: { indy: { name: 'Proof Request', diff --git a/libs/aws/src/aws.module.ts b/libs/aws/src/aws.module.ts new file mode 100644 index 000000000..1a2a90f39 --- /dev/null +++ b/libs/aws/src/aws.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { AwsService } from './aws.service'; + +@Module({ + providers: [AwsService], + exports: [AwsService] +}) +export class AwsModule {} diff --git a/libs/aws/src/aws.service.spec.ts b/libs/aws/src/aws.service.spec.ts new file mode 100644 index 000000000..f37dab349 --- /dev/null +++ b/libs/aws/src/aws.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AwsService } from './aws.service'; + +describe('AwsService', () => { + let service: AwsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AwsService] + }).compile(); + + service = module.get(AwsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts new file mode 100644 index 000000000..619e32ebb --- /dev/null +++ b/libs/aws/src/aws.service.ts @@ -0,0 +1,87 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { RpcException } from '@nestjs/microservices'; +import { S3 } from 'aws-sdk'; +import { promisify } from 'util'; + +@Injectable() +export class AwsService { + private s3: S3; + private s4: S3; + + constructor() { + this.s3 = new S3({ + accessKeyId: process.env.AWS_ACCESS_KEY, + secretAccessKey: process.env.AWS_SECRET_KEY, + region: process.env.AWS_REGION + }); + this.s4 = new S3({ + + accessKeyId: process.env.AWS_PUBLIC_ACCESS_KEY, + secretAccessKey: process.env.AWS_PUBLIC_SECRET_KEY, + region: process.env.AWS_PUBLIC_REGION + }); + } + + async uploadUserCertificate( + fileBuffer: Buffer, + ext: string, + verifyCode: string, + pathAWS: string = '', + encoding = 'base64', + filename: string = 'certificate' + ): Promise { + const timestamp = Date.now(); + const putObjectAsync = promisify(this.s4.putObject).bind(this.s4); + try { + await putObjectAsync({ + + Bucket: process.env.AWS_PUBLIC_BUCKET_NAME, + Key: `${pathAWS}/${encodeURIComponent(filename)}.${timestamp}.${ext}`, + Body: fileBuffer, + ContentEncoding: encoding, + ContentType: `image/png` + }); + return `https://${process.env.AWS_PUBLIC_BUCKET_NAME}.s3.${process.env.AWS_PUBLIC_REGION}.amazonaws.com/${pathAWS}/${encodeURIComponent(filename)}.${timestamp}.${ext}`; + } catch (err) { + throw new HttpException('An error occurred while uploading the image', HttpStatus.SERVICE_UNAVAILABLE); + } + } + + async uploadCsvFile(key: string, body: unknown): Promise { + const params: AWS.S3.PutObjectRequest = { + Bucket: process.env.AWS_BUCKET, + Key: key, + Body: 'string' === typeof body ? body : body.toString() + }; + + try { + await this.s3.upload(params).promise(); + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } + + async getFile(key: string): Promise { + const params: AWS.S3.GetObjectRequest = { + Bucket: process.env.AWS_BUCKET, + Key: key + }; + try { + return this.s3.getObject(params).promise(); + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } + + async deleteFile(key: string): Promise { + const params: AWS.S3.DeleteObjectRequest = { + Bucket: process.env.AWS_BUCKET, + Key: key + }; + try { + await this.s3.deleteObject(params).promise(); + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } +} diff --git a/libs/aws/src/index.ts b/libs/aws/src/index.ts new file mode 100644 index 000000000..182a99dc1 --- /dev/null +++ b/libs/aws/src/index.ts @@ -0,0 +1,2 @@ +export * from './aws.module'; +export * from './aws.service'; diff --git a/libs/aws/tsconfig.lib.json b/libs/aws/tsconfig.lib.json new file mode 100644 index 000000000..4abcb1f0b --- /dev/null +++ b/libs/aws/tsconfig.lib.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "../../dist/libs/aws" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 3ccac0c81..11e0fbe07 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -1,4 +1,3 @@ - export enum CommonConstants { // Error and Success Responses from POST and GET calls RESP_ERR_HTTP_INVALID_HEADER_VALUE = 'ERR_HTTP_INVALID_HEADER_VALUE', @@ -42,7 +41,6 @@ export enum CommonConstants { URL_LEDG_GET_TAA = '/ledger/taa', URL_LEDG_POST_TAA_ACCEPT = '/ledger/taa/accept', - // MESSAGING SERVICES URL_MSG_SEND_MESSAGE = '/connections/#/send-message', URL_MSG_TRUST_PING = '/connections/#/send-ping', @@ -110,7 +108,6 @@ export enum CommonConstants { URL_SEND_OUT_OF_BAND_CREATE_REQUEST = '/proofs/create-request-oob', URL_PROOF_FORM_DATA = '/proofs/#/form-data', - // server or agent URL_SERVER_STATUS = '/status', URL_AGENT_WRITE_DID = '/dids/write', @@ -130,7 +127,6 @@ export enum CommonConstants { ENTITY_ACTION_UPDATE = 'update', ENTITY_ACTION_DELETE = 'delete', - // EVENTS EVENT_AUDIT = 'audit_event', @@ -144,17 +140,16 @@ export enum CommonConstants { DOMAIN_EVENT_USER_ONBOARD = 'User Onboard', DOMAIN_EVENT_WALLET_CREATED = 'Wallet Created', - // (Platform) admin permissions + // (Platform) admin permissions PERMISSION_TENANT_MGMT = 'Tenant Management', PERMISSION_ROLE_MGMT = 'Role Management', PERMISSION_ORG_REPORTS = 'Organization Reports', PERMISSION_TENANT_REPORTS = 'Tenant Reports', - // Tenant permissions + // Tenant permissions PERMISSION_ORG_MGMT = 'Organization Management', PERMISSION_MODIFY_ORG = 'Modify Organizations', - // Roles And Permissions PERMISSION_PLATFORM_MANAGEMENT = 'Platform Management', PERMISSION_USER_MANAGEMENT = 'User Management', @@ -256,7 +251,6 @@ export enum CommonConstants { LOGIN_PASSWORDLESS = 'passwordless', LOGIN_PASSWORD = 'password', - //onBoarding Type ONBOARDING_TYPE_ADMIN = 0, ONBOARDING_TYPE_EXTERNAL = 1, @@ -265,14 +259,13 @@ export enum CommonConstants { // ecosystem config auto endorsement ECOSYSTEM_AUTO_ENDOSEMENT = 'autoEndorsement', - // Network + // Network TESTNET = 'testnet', STAGINGNET = 'stagingnet', BUILDERNET = 'buildernet', MAINNET = 'mainnet', LIVENET = 'livenet', - // Features Id SCHEMA_CREATION = 1, CREATE_CREDENTIAL_DEFINITION = 2, @@ -291,8 +284,9 @@ export enum CommonConstants { TRANSACTION_MULTITENANT_SCHEMA = '/multi-tenancy/schema/#', TRANSACTION_MULTITENANT_CRED_DEF = '/multi-tenancy/credential-definition/#', TRANSACTION_MULTITENANT_SIGN = '/multi-tenancy/transactions/endorse/#', - TRANSACTION_MULTITENANT_SUMBIT = '/multi-tenancy/transactions/write/#' + TRANSACTION_MULTITENANT_SUMBIT = '/multi-tenancy/transactions/write/#', + } export const postgresqlErrorCodes = []; @@ -336,7 +330,6 @@ postgresqlErrorCodes['22012'] = 'division_by_zero'; postgresqlErrorCodes['22005'] = 'error_in_assignment'; postgresqlErrorCodes['2200B'] = 'escape_character_conflict'; - postgresqlErrorCodes['22022'] = 'indicator_overflow'; postgresqlErrorCodes['22015'] = 'interval_field_overflow'; postgresqlErrorCodes['2201E'] = 'invalid_argument_for_logarithm'; @@ -350,3 +343,405 @@ postgresqlErrorCodes['22019'] = 'invalid_escape_character'; postgresqlErrorCodes['22P02'] = 'invalid_datatype'; postgresqlErrorCodes[''] = ''; + +export const DISALLOWED_EMAIL_DOMAIN = [ + '0x01.gq', + '0x01.tk', + '10mail.org', + '10mail.tk', + '33m.co', + '33mail.com', + '3dxtras.com', + '3utilities.com', + '567map.xyz', + '8191.at', + 'aa.am', + 'accountsite.me', + 'acmetoy.com', + 'acusupply.com', + 'adultvidlite.com', + 'aji.kr', + 'anonaddy.com', + 'anonaddy.me', + 'anonbox.net', + 'anyalias.com', + 'asanatest1.us', + 'azzawajalla.store', + 'bajetesik.store', + 'band-freier.de', + 'bandband1.com', + 'bangmadid.store', + 'batikbantul.com', + 'bccto.me', + 'bebekpenyet.buzz', + 'bei.kr', + 'bel.kr', + 'beo.kr', + 'bfo.kr', + 'bgsaddrmwn.me', + 'bho.kr', + 'biasaelho.space', + 'biz.st', + 'biz.tm', + 'bko.kr', + 'blacksong.pw', + 'blueauramassage.com', + 'bounceme.net', + 'bum.net', + 'buwosok.tech', + 'buzzndaraiangop2wae.buzz', + 'byui.me', + 'caboodle.buzz', + 'cad.edu.gr', + 'cempue.online', + 'chickenkiller.com', + 'choirul.host', + 'cid.kr', + 'ciran.xyz', + 'cko.kr', + 'cloudns.asia', + 'cloudns.cc', + 'cloudns.cx', + 'cloudns.nz', + 'com.com', + 'coms.hk', + 'comx.cf', + 'craigslist.org', + 'creo.site', + 'creo.tips', + 'creou.dev', + 'crowdpress.it', + 'cu.cc', + 'cua77.xyz', + 'd3vs.net', + 'dadosa.xyz', + 'danuarte.online', + 'darrels.site', + 'daseus.online', + 'dayatan.host', + 'dbo.kr', + 'ddns.net', + 'ddnsfree.com', + 'deail.com', + 'dedyn.io', + 'defaultdomain.ml', + 'discard-email.cf', + 'dko.kr', + 'dlink.cf', + 'dlink.gq', + 'dlyemail.com', + 'dmtc.dev', + 'dmtc.edu.pl', + 'dmtc.press', + 'dns-cloud.net', + 'dns.navy', + 'dnsabr.com', + 'dnses.ro', + 'doy.kr', + 'drope.ml', + 'dropmail.me', + 'dynu.net', + 'dzalaev-advokat.ru', + 'e4ward.com', + 'ediantenan.site', + 'edu.auction', + 'efo.kr', + 'eho.kr', + 'ely.kr', + 'email-temp.com', + 'emailfake.com', + 'emailfake.ml', + 'emailfreedom.ml', + 'emlhub.com', + 'emlpro.com', + 'emltmp.com', + 'emy.kr', + 'enu.kr', + 'eny.kr', + 'epizy.com', + 'escritossad.net', + 'ese.kr', + 'esy.es', + 'ewa.kr', + 'exi.kr', + 'ezyro.com', + 'fackme.gq', + 'fassagforpresident.ga', + 'firste.ml', + 'flu.cc', + 'foy.kr', + 'fr.nf', + 'freeml.net', + 'gadzooks.buzz', + 'gettrials.com', + 'giize.com', + 'gmail.gr.com', + 'gmeil.me', + 'gok.kr', + 'gotdns.ch', + 'gpa.lu', + 'grigio.cf', + 'guardmail.cf', + 'haddo.eu', + 'heliohost.org', + 'higogoya.com', + 'historial.store', + 'hitechinfo.com', + 'hix.kr', + 'hiz.kr', + 'hmail.us', + 'hopto.org', + 'hostingarif.me', + 'idn.vn', + 'iesco.info', + 'igg.biz', + 'ignorelist.com', + 'iki.kr', + 'ilovemyniggers.club', + 'imouto.pro', + 'info.tm', + 'infos.st', + 'irr.kr', + 'isgre.at', + 'it2-mail.tk', + 'jil.kr', + 'jindmail.club', + 'jto.kr', + 'junnuok.com', + 'justemail.ml', + 'kadokawa.top', + 'kantal.buzz', + 'keitin.site', + 'kentel.buzz', + 'kerl.cf', + 'kerl.gq', + 'kikwet.com', + 'kondomeus.site', + 'kozow.com', + 'kranjingan.store', + 'kranjingan.tech', + 'kranjingans.tech', + 'kro.kr', + 'lal.kr', + 'laste.ml', + 'lbe.kr', + 'legundi.site', + 'lei.kr', + 'likevip.net', + 'liopers.link', + 'lko.co.kr', + 'lko.kr', + 'll47.net', + 'lofteone.ru', + 'lom.kr', + 'longdz.site', + 'longmusic.com', + 'lostandalone.com', + 'loudcannabisapp.com', + 'loy.kr', + 'loyalherceghalom.ml', + 'luk2.com', + 'luksarcenter.ru', + 'luo.kr', + 'lyrics-lagu.me', + 'mail-temp.com', + 'mail0.ga', + 'mailinator.com', + 'mailr.eu', + 'marrone.cf', + 'mbe.kr', + 'mblimbingan.space', + 'mebelnovation.ru', + 'mefound.com', + 'mintemail.com', + 'mishmash.buzz', + 'mko.kr', + 'mlo.kr', + 'mooo.com', + 'motifasidiri.website', + 'mp-j.cf', + 'mp-j.ga', + 'mp-j.gq', + 'mp-j.ml', + 'mp-j.tk', + 'mr-meshkat.com', + 'mrossi.cf', + 'mrossi.gq', + 'mrossi.ml', + 'ms1.email', + 'msdc.co', + 'muabanwin.net', + 'museumplanet.com', + 'my.id', + 'my3mail.cf', + 'my3mail.ga', + 'my3mail.gq', + 'my3mail.ml', + 'my3mail.tk', + 'myddns.me', + 'myeslbookclub.com', + 'mymy.cf', + 'mysafe.ml', + 'mzon.store', + 'n-e.kr', + 'nafko.cf', + 'nctu.me', + 'netmail.tk', + 'netricity.nl', + 'new-mgmt.ga', + 'ngalasmoen.xyz', + 'ngguwokulon.online', + 'njambon.space', + 'nko.kr', + 'now.im', + 'npv.kr', + 'nuo.co.kr', + 'nuo.kr', + 'nut.cc', + 'o-r.kr', + 'oazis.site', + 'obo.kr', + 'ocry.com', + 'office.gy', + 'okezone.bid', + 'one.pl', + 'onlysext.com', + 'oovy.org', + 'oppoesrt.online', + 'orangotango.ml', + 'otherinbox.com', + 'ourhobby.com', + 'owa.kr', + 'owh.ooo', + 'oyu.kr', + 'p-e.kr', + 'pafnuty.com', + 'pandies.space', + 'paqeh.online', + 'pe.hu', + 'petinggiean.tech', + 'peyekkolipi.buzz', + 'poderosamulher.com', + 'poistaa.com', + 'porco.cf', + 'poy.kr', + 'prapto.host', + 'probatelawarizona.com', + 'ptcu.dev', + 'pubgm.website', + 'qbi.kr', + 'qc.to', + 'r-e.kr', + 'ragel.me', + 'rao.kr', + 'reilis.site', + 'rf.gd', + 'ringen.host', + 'rko.kr', + 'rosso.ml', + 'row.kr', + 'rr.nu', + 'rshagor.xyz', + 's-ly.me', + 'safe-mail.gq', + 'sagun.info', + 'samsueng.site', + 'saucent.online', + 'sborra.tk', + 'schwarzmail.ga', + 'seluang.com', + 'sempak.link', + 'sendaljepit.site', + 'sendangagung.online', + 'servegame.com', + 'shp7.cn', + 'siambretta.com', + 'skodaauto.cf', + 'soju.buzz', + 'solidplai.us', + 'somee.com', + 'spamtrap.ro', + 'spymail.one', + 'ssanphone.me', + 'standeight.com', + 'statuspage.ga', + 'steakbeef.site', + 'stonedogdigital.com', + 'stop-my-spam.pp.ua', + 'storeyee.com', + 'sumanan.site', + 'supere.ml', + 'svblog.com', + 'sytes.net', + 'tandy.co', + 'tangtingtung.tech', + 'teml.net', + 'tempembus.buzz', + 'tempremail.cf', + 'tempremail.tk', + 'tgwrzqr.top', + 'thepieter.com', + 'theworkpc.com', + 'thinktimessolve.info', + 'thumoi.com', + 'tko.co.kr', + 'tko.kr', + 'tmo.kr', + 'tmpeml.com', + 'toh.info', + 'toi.kr', + 'tomcrusenono.host', + 'topikurrohman.xyz', + 'tourbalitravel.com', + 'traveldesk.com', + 'tricakesi.store', + 'trillianpro.com', + 'twilightparadox.com', + 'tyrex.cf', + 'uha.kr', + 'uk.to', + 'uko.kr', + 'umy.kr', + 'unaux.com', + 'undo.it', + 'uny.kr', + 'uola.org', + 'upy.kr', + 'urbanban.com', + 'us.to', + 'usa.cc', + 'uu.gl', + 'uvy.kr', + 'uyu.kr', + 'vay.kr', + 'vba.kr', + 'veo.kr', + 'viola.gq', + 'vivoheroes.xyz', + 'vkbags.in', + 'vo.uk', + 'volvo-xc.tk', + 'vuforia.us', + 'wakultimbo.buzz', + 'web.id', + 'weprof.it', + 'werkuldino.buzz', + 'wil.kr', + 'wingkobabat.buzz', + 'x24hr.com', + 'xiaomie.store', + 'xo.uk', + 'xxi2.com', + 'yarien.eu', + 'yawahid.host', + 'ye.vc', + 'yertxenor.tk', + 'yomail.info', + 'yopmail.com', + 'yoqoyyum.space', + 'youdontcare.com', + 'zalvisual.us', + 'zapto.org', + 'ze.cx', + 'zeroe.ml' +]; diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index 72ff4a70a..be9faae23 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -325,21 +325,5 @@ export class CommonService { } catch (error) { throw new BadRequestException('Invalid Credentials'); } - } - - - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - // readFileDetails(filePath: string) { - // try { - // const csvFile = readFileSync(filePath); - - // this.logger.log(`csvFile----${JSON.stringify(csvFile)}`); - // const csvData: string = csvFile.toString(); - // return csvData; - // } catch (error) { - // throw new RpcException(error.response); - // } - - // } } diff --git a/libs/common/src/interfaces/response.interface.ts b/libs/common/src/interfaces/response.interface.ts index 7a7211741..52639580f 100644 --- a/libs/common/src/interfaces/response.interface.ts +++ b/libs/common/src/interfaces/response.interface.ts @@ -1,6 +1,7 @@ export default interface IResponseType { statusCode: number; message?: string; + label?: string; data?: unknown; error?: unknown; }; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 44797b26c..7a305fed1 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -15,6 +15,7 @@ export const ResponseMessages = { checkEmail: 'User email checked successfully.', sendVerificationCode: 'Verification code has been sent sucessfully to the mail. Please verify', userActivity: 'User activities fetched successfully', + userCredentials: 'User credentials fetched successfully', platformEcosystemettings: 'Platform and ecosystem settings updated', fetchPlatformSettings: 'Platform settings fetched' }, @@ -40,7 +41,9 @@ export const ResponseMessages = { adduser: 'Unable to add user details', verifyEmail: 'The verification link has already been sent to your email address. please verify', emailNotVerified: 'The verification link has already been sent to your email address. please verify', - userNotRegisterd: 'The user has not yet completed the registration process' + userNotRegisterd: 'The user has not yet completed the registration process', + InvalidEmailDomain :'Email from this domain is not allowed', + credentialNotFound: 'User credentials not found' } }, organisation: { @@ -105,7 +108,8 @@ export const ResponseMessages = { notCreated: 'Schema not created', notFound: 'Schema records not found', schemaIdNotFound: 'SchemaLedgerId not found', - credentialDefinitionNotFound: 'No credential definition exist' + credentialDefinitionNotFound: 'No credential definition exist', + notStoredCredential: 'User credential not stored' } }, credentialDefinition: { @@ -159,11 +163,12 @@ export const ResponseMessages = { }, issuance: { success: { - create: 'Issue-credential offer created successfully', - fetch: 'Issue-credential fetched successfully', + create: 'Credentials offer created successfully', + fetch: 'Credentials fetched successfully', importCSV: 'File imported sucessfully', previewCSV: 'File details fetched sucessfully', - bulkIssuance: 'Bulk-issunace process started' + bulkIssuance: 'Issuance process started. It will take some time', + notFound: 'Schema records not found' }, error: { exists: 'Credentials is already exist', @@ -178,7 +183,11 @@ export const ResponseMessages = { emailSend: 'Unable to send email to the user', previewFile: 'Error while fetching file details', previewCachedData: 'Error while fetching cached data', - cacheTimeOut: 'Timeout for reviewing data, re-upload your file and generate new request.' + emptyFileData: 'File details does not exit or removed', + cacheTimeOut: 'Timeout for reviewing data, re-upload your file and generate new request', + fileNotFound: 'File details not found', + fileData: 'File data does not exist for the specific file', + retry: 'Credentials do not exist for retry' } }, verification: { @@ -265,7 +274,11 @@ export const ResponseMessages = { create: 'Issuance process successfully' }, error: { - PathNotFound: 'Path to export data not found.' + PathNotFound: 'Path to export data not found.', + emailColumn: '1st column of the file should always be email.', + attributeNumber: 'Number of supplied values is different from the number of schema attributes.', + mismatchedAttributes: 'Schema attributes are mismatched in the file header.', + fileDetailsNotFound: 'File details not found.' } } }; \ No newline at end of file diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 6a9402c58..729805fbf 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -37,3 +37,10 @@ export enum OrgAgentType { DEDICATED = 1, SHARED = 2 } + +export enum UserCertificateId { + WINNER = 'Winner', + PARTICIPANT = 'Participant', + ARBITER = 'Arbiter', + WORLD_RECORD = 'WorldRecord' +} diff --git a/libs/image-service/src/image-service.service.ts b/libs/image-service/src/image-service.service.ts index c8633642d..61140b982 100644 --- a/libs/image-service/src/image-service.service.ts +++ b/libs/image-service/src/image-service.service.ts @@ -1,9 +1,9 @@ -import { Injectable, Logger, Res } from '@nestjs/common'; +import { Injectable, Logger} from '@nestjs/common'; @Injectable() export class ImageServiceService { - private readonly logger = new Logger("Base64ImageService") + private readonly logger = new Logger("Base64ImageService"); constructor( ) { } diff --git a/libs/prisma-service/prisma/migrations/20231109044921_user_credentials/migration.sql b/libs/prisma-service/prisma/migrations/20231109044921_user_credentials/migration.sql new file mode 100644 index 000000000..79c1d23f6 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231109044921_user_credentials/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "user_credentials" ( + "id" TEXT NOT NULL, + "imageUrl" TEXT, + "credentialId" TEXT, + "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdBy" TEXT NOT NULL DEFAULT '1', + "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastChangedBy" TEXT NOT NULL DEFAULT '1', + "deletedAt" TIMESTAMP(6), + + CONSTRAINT "user_credentials_pkey" PRIMARY KEY ("id") +); diff --git a/libs/prisma-service/prisma/migrations/20231116112146_status_file_data/migration.sql b/libs/prisma-service/prisma/migrations/20231116112146_status_file_data/migration.sql new file mode 100644 index 000000000..164da6ee3 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231116112146_status_file_data/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "file_data" ADD COLUMN "credDefId" TEXT, +ADD COLUMN "schemaId" TEXT, +ADD COLUMN "status" BOOLEAN NOT NULL DEFAULT false; diff --git a/libs/prisma-service/prisma/migrations/20231116114452_credentials_file_data/migration.sql b/libs/prisma-service/prisma/migrations/20231116114452_credentials_file_data/migration.sql new file mode 100644 index 000000000..48452a863 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231116114452_credentials_file_data/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "file_data" ADD COLUMN "credential_data" JSONB; diff --git a/libs/prisma-service/prisma/migrations/20231120132943_credential_id_unique/migration.sql b/libs/prisma-service/prisma/migrations/20231120132943_credential_id_unique/migration.sql new file mode 100644 index 000000000..535e03894 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231120132943_credential_id_unique/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - A unique constraint covering the columns `[credentialId]` on the table `user_credentials` will be added. If there are existing duplicate values, this will fail. + - Made the column `credentialId` on table `user_credentials` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "user_credentials" ALTER COLUMN "credentialId" SET NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "user_credentials_credentialId_key" ON "user_credentials"("credentialId"); diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index fcef6ebce..90daa09f3 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -49,6 +49,18 @@ model user_activity { user user @relation(fields: [userId], references: [id]) } +model user_credentials { + id String @id @default(uuid()) + imageUrl String? + credentialId String @unique + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @default("1") + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @default("1") + deletedAt DateTime? @db.Timestamp(6) +} + + model org_roles { id Int @id @default(autoincrement()) name String @unique @@ -442,4 +454,8 @@ model file_data { deletedAt DateTime? @db.Timestamp(6) fileUploadId String fileUpload file_upload @relation(fields: [fileUploadId], references: [id]) + schemaId String? + credDefId String? + status Boolean @default(false) + credential_data Json? } diff --git a/nest-cli.json b/nest-cli.json index 3bd64fc64..466b6d3a7 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -250,6 +250,15 @@ "compilerOptions": { "tsConfigPath": "libs/image-service/tsconfig.lib.json" } + }, + "aws": { + "type": "library", + "root": "libs/aws", + "entryFile": "index", + "sourceRoot": "libs/aws/src", + "compilerOptions": { + "tsConfigPath": "libs/aws/tsconfig.lib.json" + } } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e9a37cc22..970155b11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@types/pdfkit": "^0.12.6", "async-retry": "^1.3.3", "auth0-js": "^9.22.1", + "aws-sdk": "^2.1492.0", "bcrypt": "^5.1.0", "blob-stream": "^0.1.3", "body-parser": "^1.20.1", @@ -79,6 +80,7 @@ "typeorm": "^0.3.10", "unzipper": "^0.10.14", "uuid": "^9.0.0", + "validator": "^13.11.0", "web-push": "^3.6.4", "xml-js": "^1.6.11" }, @@ -3706,6 +3708,79 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sdk": { + "version": "2.1492.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1492.0.tgz", + "integrity": "sha512-3q17ruBkwb3pL87CHSbRlYiwx1LCq7D7hIjHgZ/5SPeKknkXgkHnD20SD2lC8Nj3xGbpIUhoKXcpDAGgIM5DBA==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/aws-sdk/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/aws-sdk/node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/aws-sdk/node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -7627,6 +7702,20 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -8535,6 +8624,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/jose": { "version": "4.14.4", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", @@ -10999,6 +11096,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13104,11 +13210,25 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, "node_modules/urlsafe-base64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", @@ -13650,6 +13770,26 @@ "xml-js": "bin/cli.js" } }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", @@ -16476,6 +16616,72 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, + "aws-sdk": { + "version": "2.1492.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1492.0.tgz", + "integrity": "sha512-3q17ruBkwb3pL87CHSbRlYiwx1LCq7D7hIjHgZ/5SPeKknkXgkHnD20SD2lC8Nj3xGbpIUhoKXcpDAGgIM5DBA==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -19367,6 +19573,14 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -20043,6 +20257,11 @@ } } }, + "jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + }, "jose": { "version": "4.14.4", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", @@ -21846,6 +22065,11 @@ "side-channel": "^1.0.4" } }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -23335,6 +23559,22 @@ "punycode": "^2.1.0" } }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + } + } + }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -23763,6 +24003,20 @@ "sax": "^1.2.4" } }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/package.json b/package.json index 88ff1e3fc..5da437495 100755 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@types/pdfkit": "^0.12.6", "async-retry": "^1.3.3", "auth0-js": "^9.22.1", + "aws-sdk": "^2.1492.0", "bcrypt": "^5.1.0", "blob-stream": "^0.1.3", "body-parser": "^1.20.1", @@ -64,6 +65,7 @@ "generate-password": "^1.7.0", "helmet": "^7.0.0", "html-pdf": "^3.0.1", + "html-to-image": "^1.11.11", "json2csv": "^5.0.7", "jsonwebtoken": "^9.0.1", "jwks-rsa": "^3.0.1", @@ -74,6 +76,7 @@ "nats": "^2.15.1", "nestjs-supabase-auth": "^1.0.9", "nestjs-typeorm-paginate": "^4.0.4", + "node-html-to-image": "^4.0.0", "node-qpdf2": "^2.0.0", "papaparse": "^5.4.1", "passport": "^0.6.0", @@ -82,6 +85,7 @@ "path": "^0.12.7", "pdfkit": "^0.13.0", "pg": "^8.11.2", + "puppeteer": "^21.5.0", "qrcode": "^1.5.3", "qs": "^6.11.2", "reflect-metadata": "^0.1.13", @@ -93,6 +97,7 @@ "typeorm": "^0.3.10", "unzipper": "^0.10.14", "uuid": "^9.0.0", + "validator": "^13.11.0", "web-push": "^3.6.4", "xml-js": "^1.6.11" }, @@ -170,7 +175,8 @@ "^@credebl/user-org-roles(|/.*)$": "/libs/user-org-roles/src/$1", "^y/user-activity(|/.*)$": "/libs/user-activity/src/$1", "^@app/supabase(|/.*)$": "/libs/supabase/src/$1", - "^@credebl/image-service(|/.*)$": "/libs/image-service/src/$1" + "^@credebl/image-service(|/.*)$": "/libs/image-service/src/$1", + "^@credebl/aws(|/.*)$": "/libs/aws/src/$1" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc723d681..5fe5087c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ dependencies: auth0-js: specifier: ^9.22.1 version: 9.22.1 + aws-sdk: + specifier: ^2.1492.0 + version: 2.1499.0 bcrypt: specifier: ^5.1.0 version: 5.1.0 @@ -119,6 +122,9 @@ dependencies: html-pdf: specifier: ^3.0.1 version: 3.0.1 + html-to-image: + specifier: ^1.11.11 + version: 1.11.11 json2csv: specifier: ^5.0.7 version: 5.0.7 @@ -149,6 +155,9 @@ dependencies: nestjs-typeorm-paginate: specifier: ^4.0.4 version: 4.0.4(@nestjs/common@10.2.8)(typeorm@0.3.10) + node-html-to-image: + specifier: ^4.0.0 + version: 4.0.0 node-qpdf2: specifier: ^2.0.0 version: 2.0.0 @@ -173,6 +182,9 @@ dependencies: pg: specifier: ^8.11.2 version: 8.11.2 + puppeteer: + specifier: ^21.5.0 + version: 21.5.0(typescript@5.1.6) qrcode: specifier: ^1.5.3 version: 1.5.3 @@ -206,6 +218,9 @@ dependencies: uuid: specifier: ^9.0.0 version: 9.0.0 + validator: + specifier: ^13.11.0 + version: 13.11.0 web-push: specifier: ^3.6.4 version: 3.6.4 @@ -400,7 +415,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.22.5 - dev: true /@babel/compat-data@7.22.9: resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} @@ -522,7 +536,6 @@ packages: /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.22.5: resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} @@ -547,7 +560,6 @@ packages: '@babel/helper-validator-identifier': 7.22.5 chalk: 2.4.2 js-tokens: 4.0.0 - dev: true /@babel/parser@7.22.7: resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} @@ -1592,6 +1604,38 @@ packages: resolution: {integrity: sha512-NV/4nVNWFZSJCCIA3HIFJbbDKO/NARc9ej0tX5S9k2EVbkrFJC4Xt9b0u4rNZWL4V+F5LAjvta8vzEUw0rw+HA==} requiresBuild: true + /@puppeteer/browsers@1.5.0: + resolution: {integrity: sha512-za318PweGINh5LnHSph7C4xhs0tmRjCD8EPpzcKlw4nzSPhnULj+LTG3+TGefZvW1ti5gjw2JkdQvQsivBeZlg==} + engines: {node: '>=16.3.0'} + hasBin: true + dependencies: + debug: 4.3.4 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.3.0 + tar-fs: 3.0.4 + unbzip2-stream: 1.4.3 + yargs: 17.7.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@puppeteer/browsers@1.8.0: + resolution: {integrity: sha512-TkRHIV6k2D8OlUe8RtG+5jgOF/H98Myx0M6AOafC8DdNVOFiBSFa5cpRDtpm8LXOa9sVwe0+e6Q3FC56X/DZfg==} + engines: {node: '>=16.3.0'} + hasBin: true + dependencies: + debug: 4.3.4 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.3.1 + tar-fs: 3.0.4 + unbzip2-stream: 1.4.3 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: false + /@sendgrid/client@7.7.0: resolution: {integrity: sha512-SxH+y8jeAQSnDavrTD0uGDXYIIkFylCo+eDofVmZLQ0f862nnqbC3Vd1ej6b7Le7lboyzQF6F7Fodv02rYspuA==} engines: {node: 6.* || 8.* || >=10.*} @@ -1701,7 +1745,11 @@ packages: /@swc/helpers@0.3.17: resolution: {integrity: sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 + dev: false + + /@tootallnate/quickjs-emscripten@0.23.0: + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} dev: false /@tsconfig/node10@1.0.9: @@ -1984,6 +2032,14 @@ packages: '@types/yargs-parser': 21.0.0 dev: true + /@types/yauzl@2.10.2: + resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==} + requiresBuild: true + dependencies: + '@types/node': 20.4.6 + dev: false + optional: true + /@typescript-eslint/eslint-plugin@6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.1.6): resolution: {integrity: sha512-iZVM/ALid9kO0+I81pnp1xmYiFyqibAHzrqX4q5YvvVEyJqY+e6rfTXSCsc2jUxGNqJqTfFSSij/NFkZBiBzLw==} engines: {node: ^16.0.0 || >=18.0.0} @@ -2416,7 +2472,6 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -2577,6 +2632,13 @@ packages: dev: false optional: true + /ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + dependencies: + tslib: 2.6.2 + dev: false + /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2610,6 +2672,22 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} + /aws-sdk@2.1499.0: + resolution: {integrity: sha512-kh89lcXx7lP83uVjzRPkOueRoM8gQlep86W9+l3qCTHSLiVJuc0MiPmqCLMPlOAZil+35roFkwWIP2FJ1WcdXg==} + engines: {node: '>= 10.0.0'} + dependencies: + buffer: 4.9.2 + events: 1.1.1 + ieee754: 1.1.13 + jmespath: 0.16.0 + querystring: 0.2.0 + sax: 1.2.1 + url: 0.10.3 + util: 0.12.5 + uuid: 8.0.0 + xml2js: 0.5.0 + dev: false + /aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} requiresBuild: true @@ -2640,6 +2718,10 @@ packages: - debug dev: false + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: false + /babel-jest@29.6.2(@babel/core@7.22.9): resolution: {integrity: sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2727,6 +2809,11 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + /basic-ftp@5.0.3: + resolution: {integrity: sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==} + engines: {node: '>=10.0.0'} + dev: false + /bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} requiresBuild: true @@ -2881,7 +2968,6 @@ packages: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} requiresBuild: true dev: false - optional: true /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -2900,12 +2986,19 @@ packages: engines: {node: '>=4'} dev: false + /buffer@4.9.2: + resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + isarray: 1.0.0 + dev: false + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -2967,7 +3060,6 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} @@ -3001,7 +3093,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -3049,6 +3140,25 @@ packages: engines: {node: '>=6.0'} dev: true + /chromium-bidi@0.4.20(devtools-protocol@0.0.1147663): + resolution: {integrity: sha512-ruHgVZFEv00mAQMz1tQjfjdG63jiPWrQPF6HLlX2ucqLqVTJoWngeBEKHaJ6n1swV/HSvgnBNbtTRIlcVyW3Fw==} + peerDependencies: + devtools-protocol: '*' + dependencies: + devtools-protocol: 0.0.1147663 + mitt: 3.0.1 + dev: false + + /chromium-bidi@0.4.33(devtools-protocol@0.0.1203626): + resolution: {integrity: sha512-IxoFM5WGQOIAd95qrSXzJUv4eXIrh+RvU3rwwqIiwYuvfE7U/Llj4fejbsJnjJMUYCuGtVQsY2gv7oGl4aTNSQ==} + peerDependencies: + devtools-protocol: '*' + dependencies: + devtools-protocol: 0.0.1203626 + mitt: 3.0.1 + urlpattern-polyfill: 9.0.0 + dev: false + /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -3066,7 +3176,7 @@ packages: dependencies: '@types/validator': 13.9.0 libphonenumber-js: 1.10.39 - validator: 13.9.0 + validator: 13.11.0 /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} @@ -3180,7 +3290,6 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - dev: true /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -3190,7 +3299,6 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -3321,6 +3429,32 @@ packages: yaml: 1.10.2 dev: true + /cosmiconfig@8.2.0: + resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: false + + /cosmiconfig@8.3.6(typescript@5.1.6): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.1.6 + dev: false + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -3345,6 +3479,14 @@ packages: - encoding dev: false + /cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + dependencies: + node-fetch: 2.6.12 + transitivePeerDependencies: + - encoding + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -3381,6 +3523,11 @@ packages: dev: false optional: true + /data-uri-to-buffer@6.0.1: + resolution: {integrity: sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==} + engines: {node: '>= 14'} + dev: false + /date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -3478,6 +3625,15 @@ packages: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + /degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + dev: false + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -3510,6 +3666,14 @@ packages: engines: {node: '>=8'} dev: true + /devtools-protocol@0.0.1147663: + resolution: {integrity: sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==} + dev: false + + /devtools-protocol@0.0.1203626: + resolution: {integrity: sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g==} + dev: false + /dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} dependencies: @@ -3625,7 +3789,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /engine.io-client@6.5.2: resolution: {integrity: sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==} @@ -3676,7 +3839,6 @@ packages: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 - dev: true /es-abstract@1.22.1: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} @@ -3804,7 +3966,6 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} @@ -3816,6 +3977,18 @@ packages: engines: {node: '>=10'} dev: true + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: false + /eslint-config-prettier@8.10.0(eslint@8.46.0): resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -4098,7 +4271,6 @@ packages: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - dev: true /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} @@ -4122,17 +4294,20 @@ packages: /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - dev: true /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - dev: true /etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + /events@1.1.1: + resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} + engines: {node: '>=0.4.x'} + dev: false + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -4273,6 +4448,20 @@ packages: dev: false optional: true + /extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + dependencies: + debug: 4.3.4 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.2 + transitivePeerDependencies: + - supports-color + dev: false + /extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} @@ -4287,6 +4476,10 @@ packages: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: false + /fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -4326,7 +4519,6 @@ packages: dependencies: pend: 1.2.0 dev: false - optional: true /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} @@ -4502,6 +4694,15 @@ packages: universalify: 2.0.0 dev: true + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: false + /fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -4605,7 +4806,6 @@ packages: engines: {node: '>=8'} dependencies: pump: 3.0.0 - dev: true /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -4620,6 +4820,18 @@ packages: get-intrinsic: 1.2.1 dev: true + /get-uri@6.0.2: + resolution: {integrity: sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==} + engines: {node: '>= 14'} + dependencies: + basic-ftp: 5.0.3 + data-uri-to-buffer: 6.0.1 + debug: 4.3.4 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} requiresBuild: true @@ -4709,6 +4921,19 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.17.4 + dev: false + /har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -4733,7 +4958,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -4811,6 +5035,10 @@ packages: - supports-color dev: false + /html-to-image@1.11.11: + resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + dev: false + /http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -4821,6 +5049,16 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 + /http-proxy-agent@7.0.0: + resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /http-signature@1.2.0: resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} @@ -4859,6 +5097,16 @@ packages: - supports-color dev: false + /https-proxy-agent@7.0.2: + resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -4897,6 +5145,10 @@ packages: url-join: 4.0.1 dev: false + /ieee754@1.1.13: + resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} + dev: false + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -4911,7 +5163,6 @@ packages: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - dev: true /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} @@ -5017,6 +5268,14 @@ packages: - supports-color dev: false + /ip@1.1.8: + resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} + dev: false + + /ip@2.0.0: + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: false + /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -5038,7 +5297,6 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -5094,6 +5352,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5694,6 +5959,11 @@ packages: - ts-node dev: true + /jmespath@0.16.0: + resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} + engines: {node: '>= 0.6.0'} + dev: false + /jose@4.14.4: resolution: {integrity: sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==} dev: false @@ -5704,7 +5974,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -5738,7 +6007,6 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -5799,6 +6067,12 @@ packages: dev: false optional: true + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -5945,7 +6219,6 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true /lint-staged@13.2.3: resolution: {integrity: sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==} @@ -6056,10 +6329,9 @@ packages: wrap-ansi: 6.2.0 dev: true - /lru-cache@10.0.0: - resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==} + /lru-cache@10.0.1: + resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} - dev: true /lru-cache@4.0.2: resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} @@ -6080,6 +6352,11 @@ packages: dependencies: yallist: 4.0.0 + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + /lru-memoizer@2.2.0: resolution: {integrity: sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==} dependencies: @@ -6242,6 +6519,14 @@ packages: yallist: 4.0.0 dev: false + /mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + dev: false + + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -6352,7 +6637,6 @@ packages: /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - dev: true /nestjs-supabase-auth@1.0.9: resolution: {integrity: sha512-1Aar5K2WuGggPV8q/xzJCIeAQz5wkPcvKGLPTUXwt1he1EKLg+OdWK2C0T7LzTgO4uX2WLakNWZBsUTZEOvt4Q==} @@ -6368,6 +6652,11 @@ packages: typeorm: 0.3.10(pg@8.11.2)(ts-node@10.9.1) dev: false + /netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: false @@ -6415,6 +6704,19 @@ packages: hasBin: true dev: false + /node-html-to-image@4.0.0: + resolution: {integrity: sha512-lB8fkRleAKG4afJ2Wr7qJzIA5+//ue9OEoz+BMxQsowriGKR8sf4j4lK/pIXKakYwf/3aZHoDUNgOXuJ4HOzYA==} + dependencies: + handlebars: 4.7.8 + puppeteer: 21.0.1 + puppeteer-cluster: 0.23.0(puppeteer@21.0.1) + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: false + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -6631,6 +6933,31 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + /pac-proxy-agent@7.0.1: + resolution: {integrity: sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==} + engines: {node: '>= 14'} + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.0 + debug: 4.3.4 + get-uri: 6.0.2 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + pac-resolver: 7.0.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /pac-resolver@7.0.0: + resolution: {integrity: sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==} + engines: {node: '>= 14'} + dependencies: + degenerator: 5.0.1 + ip: 1.1.8 + netmask: 2.0.2 + dev: false + /packet-reader@1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} dev: false @@ -6648,7 +6975,6 @@ packages: engines: {node: '>=6'} dependencies: callsites: 3.1.0 - dev: true /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} @@ -6658,7 +6984,6 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true /parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -6732,7 +7057,7 @@ packages: resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} engines: {node: '>=16 || 14 >=14.17'} dependencies: - lru-cache: 10.0.0 + lru-cache: 10.0.1 minipass: 7.0.2 dev: true @@ -6745,7 +7070,6 @@ packages: /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true /path@0.12.7: resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} @@ -6771,7 +7095,6 @@ packages: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} requiresBuild: true dev: false - optional: true /performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -6992,6 +7315,11 @@ packages: dev: false optional: true + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: false + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -7007,6 +7335,38 @@ packages: forwarded: 0.2.0 ipaddr.js: 1.9.1 + /proxy-agent@6.3.0: + resolution: {integrity: sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.1 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /proxy-agent@6.3.1: + resolution: {integrity: sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.1 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: false + /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: false @@ -7026,12 +7386,91 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true + + /punycode@1.3.2: + resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} + dev: false /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} + /puppeteer-cluster@0.23.0(puppeteer@21.0.1): + resolution: {integrity: sha512-108terIWDzPrQopmoYSPd5yDoy3FGJ2dNnoGMkGYPs6xtkdhgaECwpfZkzaRToMQPZibUOz0/dSSGgPEdXEhkQ==} + peerDependencies: + puppeteer: '>=1.5.0' + dependencies: + debug: 4.3.4 + puppeteer: 21.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /puppeteer-core@21.0.1: + resolution: {integrity: sha512-E8eWLGhaZZpa7dYe/58qGX7SLb4mTg42NP5M7B+ibPrncgNjTOQa9x1sFIlTn1chF/BmoZqOcMIvwuxcb/9XzQ==} + engines: {node: '>=16.3.0'} + dependencies: + '@puppeteer/browsers': 1.5.0 + chromium-bidi: 0.4.20(devtools-protocol@0.0.1147663) + cross-fetch: 4.0.0 + debug: 4.3.4 + devtools-protocol: 0.0.1147663 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: false + + /puppeteer-core@21.5.0: + resolution: {integrity: sha512-qG0RJ6qKgFz09UUZxDB9IcyTJGypQXMuE8WmEoHk7kgjutmRiOVv5RgsyUkY67AxDdBWx21bn1PHHRJnO/6b4A==} + engines: {node: '>=16.3.0'} + dependencies: + '@puppeteer/browsers': 1.8.0 + chromium-bidi: 0.4.33(devtools-protocol@0.0.1203626) + cross-fetch: 4.0.0 + debug: 4.3.4 + devtools-protocol: 0.0.1203626 + ws: 8.14.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: false + + /puppeteer@21.0.1: + resolution: {integrity: sha512-KTjmSdPZ6bMkq3EbAzAUhcB3gMDXvdwd6912rxG9hNtjwRJzHSA568vh6vIbO2WQeNmozRdt1LtiUMLSWfeMrg==} + engines: {node: '>=16.3.0'} + requiresBuild: true + dependencies: + '@puppeteer/browsers': 1.5.0 + cosmiconfig: 8.2.0 + puppeteer-core: 21.0.1 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: false + + /puppeteer@21.5.0(typescript@5.1.6): + resolution: {integrity: sha512-prvy9rdauyIaaEgefQRcw9zhQnYQbl8O1Gj5VJazKJ7kwNx703+Paw/1bwA+b96jj/S+r55hrmF5SfiEG5PUcg==} + engines: {node: '>=16.3.0'} + requiresBuild: true + dependencies: + '@puppeteer/browsers': 1.8.0 + cosmiconfig: 8.3.6(typescript@5.1.6) + puppeteer-core: 21.5.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + dev: false + /pure-rand@6.0.2: resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} dev: true @@ -7066,10 +7505,20 @@ packages: dev: false optional: true + /querystring@0.2.0: + resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} + engines: {node: '>=0.4.x'} + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: false + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -7232,7 +7681,6 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - dev: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -7356,6 +7804,10 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + /sax@1.2.1: + resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} + dev: false + /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} dev: false @@ -7503,6 +7955,11 @@ packages: is-fullwidth-code-point: 4.0.0 dev: true + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: false + /socket.io-adapter@2.5.2: resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==} dependencies: @@ -7550,6 +8007,25 @@ packages: - supports-color - utf-8-validate + /socks-proxy-agent@8.0.2: + resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + socks: 2.7.1 + transitivePeerDependencies: + - supports-color + dev: false + + /socks@2.7.1: + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + dependencies: + ip: 2.0.0 + smart-buffer: 4.2.0 + dev: false + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -7567,7 +8043,6 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: true /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} @@ -7627,6 +8102,13 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + /streamx@2.15.2: + resolution: {integrity: sha512-b62pAV/aeMjUoRN2C/9F0n+G8AfcJjNC0zw/ZmOHeFsIe4m4GzjVW9m6VHXVjk536NbdU9JRwKMJRfkc+zUFTg==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: false + /string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -7782,7 +8264,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -7830,6 +8311,22 @@ packages: engines: {node: '>=6'} dev: true + /tar-fs@3.0.4: + resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + dependencies: + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 3.1.6 + dev: false + + /tar-stream@3.1.6: + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.2 + dev: false + /tar@6.1.15: resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} @@ -7935,7 +8432,6 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true /tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -8321,6 +8817,14 @@ packages: engines: {node: '>=14.17'} hasBin: true + /uglify-js@3.17.4: + resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} + engines: {node: '>=0.8.0'} + hasBin: true + requiresBuild: true + dev: false + optional: true + /uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} @@ -8336,6 +8840,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + dependencies: + buffer: 5.7.1 + through: 2.3.8 + dev: false + /unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} dev: false @@ -8354,6 +8865,11 @@ packages: tiny-inflate: 1.0.3 dev: false + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: false + /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} @@ -8398,6 +8914,17 @@ packages: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} dev: false + /url@0.10.3: + resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + dev: false + + /urlpattern-polyfill@9.0.0: + resolution: {integrity: sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==} + dev: false + /urlsafe-base64@1.0.0: resolution: {integrity: sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA==} dev: false @@ -8419,6 +8946,16 @@ packages: inherits: 2.0.3 dev: false + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.11 + dev: false + /utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -8431,6 +8968,11 @@ packages: dev: false optional: true + /uuid@8.0.0: + resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} + hasBin: true + dev: false + /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -8453,8 +8995,8 @@ packages: convert-source-map: 1.9.0 dev: true - /validator@13.9.0: - resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==} + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} engines: {node: '>= 0.10'} /vary@1.1.2: @@ -8685,6 +9227,10 @@ packages: execa: 4.1.0 dev: true + /wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: false + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -8724,6 +9270,32 @@ packages: utf-8-validate: optional: true + /ws@8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + + /ws@8.14.2: + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /xml-js@1.6.11: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true @@ -8739,6 +9311,14 @@ packages: xmlbuilder: 11.0.1 dev: false + /xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + dependencies: + sax: 1.2.4 + xmlbuilder: 11.0.1 + dev: false + /xmlbuilder@11.0.1: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} @@ -8834,6 +9414,19 @@ packages: yargs-parser: 20.2.9 dev: false + /yargs@17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: false + /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -8853,7 +9446,6 @@ packages: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 dev: false - optional: true /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} diff --git a/tsconfig.json b/tsconfig.json index fb258944f..7926469d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -106,6 +106,12 @@ ], "@credebl/image-service/*": [ "libs/image-service/src/*" + ], + "@credebl/aws": [ + "libs/aws/src" + ], + "@credebl/aws/*": [ + "libs/aws/src/*" ] } },