From f46ab27cb5f0b371fd83edafb36848640134bac4 Mon Sep 17 00:00:00 2001 From: tipusinghaw <126460794+tipusinghaw@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:51:14 +0530 Subject: [PATCH] feat: create cloud wallet (#852) * feat: creat cloud wallet Signed-off-by: tipusinghaw * fix: added error handling logic in common file Signed-off-by: tipusinghaw * fix: changed nkey veriable Signed-off-by: tipusinghaw --------- Signed-off-by: tipusinghaw Signed-off-by: KulkarniShashank --- apps/api-gateway/src/app.module.ts | 4 +- .../cloud-wallet/cloud-wallet.controller.ts | 48 ++++++ .../src/cloud-wallet/cloud-wallet.module.ts | 24 +++ .../src/cloud-wallet/cloud-wallet.service.ts | 20 +++ .../src/cloud-wallet/dtos/cloudWallet.dto.ts | 20 +++ .../cloud-wallet/enums/connections.enum.ts | 4 + .../src/cloud-wallet.controller.ts | 16 ++ apps/cloud-wallet/src/cloud-wallet.module.ts | 27 ++++ .../src/cloud-wallet.repository.ts | 67 ++++++++ apps/cloud-wallet/src/cloud-wallet.service.ts | 82 ++++++++++ apps/cloud-wallet/src/main.ts | 23 +++ apps/cloud-wallet/test/jest-e2e.json | 9 ++ apps/cloud-wallet/tsconfig.app.json | 9 ++ libs/common/src/common.constant.ts | 3 +- libs/common/src/common.service.ts | 24 +++ .../src/interfaces/cloud-wallet.interface.ts | 32 ++++ libs/common/src/response-messages/index.ts | 10 ++ libs/enum/src/enum.ts | 5 + .../prisma/data/credebl-master-table.json | 9 ++ .../migration.sql | 50 ++++++ .../migration.sql | 3 + .../migration.sql | 2 + .../migration.sql | 2 + libs/prisma-service/prisma/schema.prisma | 151 ++++++++++++------ libs/prisma-service/prisma/seed.ts | 29 ++++ nest-cli.json | 9 ++ 26 files changed, 627 insertions(+), 55 deletions(-) create mode 100644 apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts create mode 100644 apps/api-gateway/src/cloud-wallet/cloud-wallet.module.ts create mode 100644 apps/api-gateway/src/cloud-wallet/cloud-wallet.service.ts create mode 100644 apps/api-gateway/src/cloud-wallet/dtos/cloudWallet.dto.ts create mode 100644 apps/api-gateway/src/cloud-wallet/enums/connections.enum.ts create mode 100644 apps/cloud-wallet/src/cloud-wallet.controller.ts create mode 100644 apps/cloud-wallet/src/cloud-wallet.module.ts create mode 100644 apps/cloud-wallet/src/cloud-wallet.repository.ts create mode 100644 apps/cloud-wallet/src/cloud-wallet.service.ts create mode 100644 apps/cloud-wallet/src/main.ts create mode 100644 apps/cloud-wallet/test/jest-e2e.json create mode 100644 apps/cloud-wallet/tsconfig.app.json create mode 100644 libs/common/src/interfaces/cloud-wallet.interface.ts create mode 100644 libs/prisma-service/prisma/migrations/20240717074318_cloud_wallet_tables/migration.sql create mode 100644 libs/prisma-service/prisma/migrations/20240717092122_added_agnet_url_and_apikey_in_cloud_wallet_table/migration.sql create mode 100644 libs/prisma-service/prisma/migrations/20240717102514_added_key_in_cloud_wallet_table/migration.sql create mode 100644 libs/prisma-service/prisma/migrations/20240717104549_added_connection_image_in_cloud_wallet_table/migration.sql diff --git a/apps/api-gateway/src/app.module.ts b/apps/api-gateway/src/app.module.ts index c7f3b00f9..905a4226b 100644 --- a/apps/api-gateway/src/app.module.ts +++ b/apps/api-gateway/src/app.module.ts @@ -27,6 +27,7 @@ import { WebhookModule } from './webhook/webhook.module'; import { UtilitiesModule } from './utilities/utilities.module'; import { NotificationModule } from './notification/notification.module'; import { CommonConstants } from '@credebl/common/common.constant'; +import { CloudWalletModule } from './cloud-wallet/cloud-wallet.module'; @Module({ imports: [ @@ -55,7 +56,8 @@ import { CommonConstants } from '@credebl/common/common.constant'; WebhookModule, NotificationModule, CacheModule.register({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }), - GeoLocationModule + GeoLocationModule, + CloudWalletModule ], controllers: [AppController], providers: [AppService] diff --git a/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts b/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts new file mode 100644 index 000000000..035abd755 --- /dev/null +++ b/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts @@ -0,0 +1,48 @@ +import { IResponse } from '@credebl/common/interfaces/response.interface'; +import { ResponseMessages } from '@credebl/common/response-messages'; +import { Controller, Post, Logger, Body, HttpStatus, Res, UseFilters } from '@nestjs/common'; +import { ApiBearerAuth, ApiForbiddenResponse, ApiOperation, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; +import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; +import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; +import { CloudWalletService } from './cloud-wallet.service'; +import { CreateCloudWalletDto } from './dtos/cloudWallet.dto'; +import { Response } from 'express'; +import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; +import { ApiResponseDto } from '../dtos/apiResponse.dto'; + +@UseFilters(CustomExceptionFilter) +@Controller() +@ApiTags('cloud-wallet') +@ApiBearerAuth() +@ApiUnauthorizedResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized', type: UnauthorizedErrorDto }) +@ApiForbiddenResponse({ status: HttpStatus.FORBIDDEN, description: 'Forbidden', type: ForbiddenErrorDto }) +export class CloudWalletController { + + private readonly logger = new Logger('cloud-wallet'); + constructor(private readonly cloudWalletService: CloudWalletService + ) { } + + /** + * Create cloud wallet + * @param cloudWalletDetails + * @param res + * @returns sucess message + */ + @Post('/create-wallet') + @ApiOperation({ summary: 'Create outbound out-of-band connection invitation', description: 'Create outbound out-of-band connection invitation' }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async createCloudWallet( + @Res() res: Response, + @Body() cloudWalletDetails: CreateCloudWalletDto + ): Promise { + + const cloudWalletData = await this.cloudWalletService.createCloudWallet(cloudWalletDetails); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.cloudWallet.success.create, + data: cloudWalletData + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } +} diff --git a/apps/api-gateway/src/cloud-wallet/cloud-wallet.module.ts b/apps/api-gateway/src/cloud-wallet/cloud-wallet.module.ts new file mode 100644 index 000000000..480b0bdf8 --- /dev/null +++ b/apps/api-gateway/src/cloud-wallet/cloud-wallet.module.ts @@ -0,0 +1,24 @@ +import { getNatsOptions } from '@credebl/common/nats.config'; +import { CloudWalletController } from './cloud-wallet.controller'; +import { CloudWalletService } from './cloud-wallet.service'; +import { Module } from '@nestjs/common'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { CommonConstants } from '@credebl/common/common.constant'; + +@Module({ + imports: [ + + ClientsModule.register([ + { + name: 'NATS_CLIENT', + transport: Transport.NATS, + options: getNatsOptions(CommonConstants.CLOUD_WALLET_SERVICE, process.env.API_GATEWAY_NKEY_SEED) + } + ]) + ], + controllers: [CloudWalletController], + providers: [CloudWalletService] +}) + +export class CloudWalletModule { +} \ No newline at end of file diff --git a/apps/api-gateway/src/cloud-wallet/cloud-wallet.service.ts b/apps/api-gateway/src/cloud-wallet/cloud-wallet.service.ts new file mode 100644 index 000000000..c43d785a7 --- /dev/null +++ b/apps/api-gateway/src/cloud-wallet/cloud-wallet.service.ts @@ -0,0 +1,20 @@ + +import { ICreateCloudWallet, IStoredWalletDetails } from '@credebl/common/interfaces/cloud-wallet.interface'; +import { Inject, Injectable} from '@nestjs/common'; +import { ClientProxy } from '@nestjs/microservices'; +import { BaseService } from 'libs/service/base.service'; + +@Injectable() +export class CloudWalletService extends BaseService { + constructor(@Inject('NATS_CLIENT') private readonly cloudWalletServiceProxy: ClientProxy) { + super('CloudWalletServiceProxy'); + } + + createCloudWallet( + cloudWalletDetails: ICreateCloudWallet + ): Promise { + return this.sendNatsMessage(this.cloudWalletServiceProxy, 'create-cloud-wallet', cloudWalletDetails); + } + + +} diff --git a/apps/api-gateway/src/cloud-wallet/dtos/cloudWallet.dto.ts b/apps/api-gateway/src/cloud-wallet/dtos/cloudWallet.dto.ts new file mode 100644 index 000000000..d5683cfb2 --- /dev/null +++ b/apps/api-gateway/src/cloud-wallet/dtos/cloudWallet.dto.ts @@ -0,0 +1,20 @@ +import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNotSQLInjection } from '@credebl/common/cast.helper'; + +export class CreateCloudWalletDto { + @ApiPropertyOptional() + @IsString({ message: 'label must be a string' }) + @IsNotEmpty({ message: 'please provide valid label' }) + @IsNotSQLInjection({ message: 'label is required.' }) + label: string; + + @ApiPropertyOptional() + @IsString({ message: 'Image URL must be a string' }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid image URL' }) + @IsNotSQLInjection({ message: 'Image URL is required.' }) + connectionImageUrl?: string; + +} diff --git a/apps/api-gateway/src/cloud-wallet/enums/connections.enum.ts b/apps/api-gateway/src/cloud-wallet/enums/connections.enum.ts new file mode 100644 index 000000000..a15929cf6 --- /dev/null +++ b/apps/api-gateway/src/cloud-wallet/enums/connections.enum.ts @@ -0,0 +1,4 @@ +export declare enum HandshakeProtocol { + Connections = "https://didcomm.org/connections/1.0", + DidExchange = "https://didcomm.org/didexchange/1.0" +} \ No newline at end of file diff --git a/apps/cloud-wallet/src/cloud-wallet.controller.ts b/apps/cloud-wallet/src/cloud-wallet.controller.ts new file mode 100644 index 000000000..3a3dd6b85 --- /dev/null +++ b/apps/cloud-wallet/src/cloud-wallet.controller.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Controller } from '@nestjs/common'; // Import the common service in the library +import { CloudWalletService } from './cloud-wallet.service'; // Import the common service in connection module +import { MessagePattern } from '@nestjs/microservices'; // Import the nestjs microservices package +import { ICreateCloudWallet, IStoredWalletDetails } from '@credebl/common/interfaces/cloud-wallet.interface'; + +@Controller() +export class CloudWalletController { + constructor(private readonly cloudWalletService: CloudWalletService) {} + + + @MessagePattern({ cmd: 'create-cloud-wallet' }) + async createConnectionInvitation(cloudWalletDetails: ICreateCloudWallet): Promise { + return this.cloudWalletService.createCloudWallet(cloudWalletDetails); + } +} \ No newline at end of file diff --git a/apps/cloud-wallet/src/cloud-wallet.module.ts b/apps/cloud-wallet/src/cloud-wallet.module.ts new file mode 100644 index 000000000..895aac9a2 --- /dev/null +++ b/apps/cloud-wallet/src/cloud-wallet.module.ts @@ -0,0 +1,27 @@ +import { Logger, Module } from '@nestjs/common'; +import { CloudWalletController } from './cloud-wallet.controller'; +import { CloudWalletService } from './cloud-wallet.service'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { CommonModule } from '@credebl/common'; +import { CacheModule } from '@nestjs/cache-manager'; +import { getNatsOptions } from '@credebl/common/nats.config'; +import { PrismaService } from '@credebl/prisma-service'; +import { CloudWalletRepository } from './cloud-wallet.repository'; + +@Module({ + imports: [ +ClientsModule.register([ + { + name: 'NATS_CLIENT', + transport: Transport.NATS, + options: getNatsOptions(process.env.CLOUD_WALLET_NKEY_SEED) + } + ]), + + CommonModule, + CacheModule.register() +], + controllers: [CloudWalletController], + providers: [CloudWalletService, CloudWalletRepository, PrismaService, Logger] +}) +export class CloudWalletModule {} diff --git a/apps/cloud-wallet/src/cloud-wallet.repository.ts b/apps/cloud-wallet/src/cloud-wallet.repository.ts new file mode 100644 index 000000000..d35040ce5 --- /dev/null +++ b/apps/cloud-wallet/src/cloud-wallet.repository.ts @@ -0,0 +1,67 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { PrismaService } from '@credebl/prisma-service'; +import { CloudWalletType } from '@credebl/enum/enum'; +// eslint-disable-next-line camelcase +import { cloud_wallet_user_info } from '@prisma/client'; +import { ICloudWalletDetails, IStoredWalletDetails } from '@credebl/common/interfaces/cloud-wallet.interface'; + + +@Injectable() +export class CloudWalletRepository { + constructor( + private readonly prisma: PrismaService, + private readonly logger: Logger + ) {} + + + // eslint-disable-next-line camelcase + async getCloudWalletDetails(type: CloudWalletType): Promise { + try { + const agentDetails = await this.prisma.cloud_wallet_user_info.findFirstOrThrow({ + where: { + type + } + }); + return agentDetails; + } catch (error) { + this.logger.error(`Error in getCloudWalletBaseAgentDetails: ${error.message}`); + throw error; + } + } + // eslint-disable-next-line camelcase + async storeCloudWalletDetails(cloudWalletDetails: ICloudWalletDetails): Promise { + try { + const {createdBy, label, lastChangedBy, tenantId, type, userId, agentApiKey, agentEndpoint, email, key, connectionImageUrl} = cloudWalletDetails; + + return await this.prisma.cloud_wallet_user_info.create({ + data: { + label, + tenantId, + email, + type, + createdBy, + lastChangedBy, + userId, + agentEndpoint, + agentApiKey, + key, + connectionImageUrl + }, + select: { + email: true, + connectionImageUrl: true, + createDateTime: true, + id: true, + tenantId: true, + label: true, + lastChangedDateTime: true + + } + }); + } catch (error) { + this.logger.error(`Error in storeCloudWalletDetails: ${error.message}`); + throw error; + } + } + +} diff --git a/apps/cloud-wallet/src/cloud-wallet.service.ts b/apps/cloud-wallet/src/cloud-wallet.service.ts new file mode 100644 index 000000000..93edc6a1b --- /dev/null +++ b/apps/cloud-wallet/src/cloud-wallet.service.ts @@ -0,0 +1,82 @@ +/* eslint-disable camelcase */ +import { CommonService } from '@credebl/common'; +import { BadRequestException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; +import { ClientProxy } from '@nestjs/microservices'; +import { Cache } from 'cache-manager'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { ICloudWalletDetails, ICreateCloudWallet, IStoredWalletDetails } from '@credebl/common/interfaces/cloud-wallet.interface'; +import { CloudWalletRepository } from './cloud-wallet.repository'; +import { ResponseMessages } from '@credebl/common/response-messages'; +import { CloudWalletType } from '@credebl/enum/enum'; +import { CommonConstants } from '@credebl/common/common.constant'; + +@Injectable() +export class CloudWalletService { + constructor( + private readonly commonService: CommonService, + @Inject('NATS_CLIENT') private readonly cloudWalletServiceProxy: ClientProxy, + private readonly cloudWalletRepository: CloudWalletRepository, + private readonly logger: Logger, + @Inject(CACHE_MANAGER) private cacheService: Cache + ) {} + + + /** + * Create clous wallet + * @param cloudWalletDetails + * @returns cloud wallet details + */ + async createCloudWallet(cloudWalletDetails: ICreateCloudWallet): Promise { + try { + // TODO - Add userId fetch logic + const {label, connectionImageUrl, email, userId} = cloudWalletDetails; + const agentPayload = { + config: { + label, + connectionImageUrl + } + }; + const baseWalletDetails = await this.cloudWalletRepository.getCloudWalletDetails(CloudWalletType.BASE_WALLET); + if (!baseWalletDetails) { + throw new NotFoundException(ResponseMessages.cloudWallet.error.baseWalletNotFound); + } + const {agentEndpoint, agentApiKey} = baseWalletDetails; + const decryptedApiKey = await this.commonService.decryptPassword(agentApiKey); + const url = `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`; + const createCloudWalletResponse = await this.commonService.httpPost(url, agentPayload, { headers: { authorization: decryptedApiKey } }); + if (!createCloudWalletResponse && !createCloudWalletResponse.id) { + throw new InternalServerErrorException(ResponseMessages.cloudWallet.error.createCloudWallet, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); + } + + const walletKey = await this.commonService.dataEncryption(createCloudWalletResponse.config.walletConfig.key); + + if (!walletKey) { + throw new BadRequestException(ResponseMessages.cloudWallet.error.encryptCloudWalletKey, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); + } + const cloudWalletResponse: ICloudWalletDetails = { + createdBy: userId, + label, + lastChangedBy: userId, + tenantId: createCloudWalletResponse.id, + type: CloudWalletType.SUB_WALLET, + userId, + agentApiKey, + agentEndpoint, + email, + key: walletKey, + connectionImageUrl + }; + const storeCloudWalletDetails = await this.cloudWalletRepository.storeCloudWalletDetails(cloudWalletResponse); + return storeCloudWalletDetails; + } catch (error) { + this.logger.error(`[createCloudWallet] - error in create cloud wallet: ${error}`); + await this.commonService.handleError(error); + } + } +} diff --git a/apps/cloud-wallet/src/main.ts b/apps/cloud-wallet/src/main.ts new file mode 100644 index 000000000..2168ad546 --- /dev/null +++ b/apps/cloud-wallet/src/main.ts @@ -0,0 +1,23 @@ +import { NestFactory } from '@nestjs/core'; +import { CloudWalletModule } from './cloud-wallet.module'; +import { HttpExceptionFilter } from 'libs/http-exception.filter'; +import { Logger } from '@nestjs/common'; +import { MicroserviceOptions, Transport } from '@nestjs/microservices'; +import { getNatsOptions } from '@credebl/common/nats.config'; +import { CommonConstants } from '@credebl/common/common.constant'; + +const logger = new Logger(); + +async function bootstrap(): Promise { + + const app = await NestFactory.createMicroservice(CloudWalletModule, { + transport: Transport.NATS, + options: getNatsOptions(CommonConstants.CLOUD_WALLET_SERVICE, process.env.CLOUD_WALLET_NKEY_SEED) + }); + + app.useGlobalFilters(new HttpExceptionFilter()); + + await app.listen(); + logger.log('Cloud-wallet Microservice is listening to NATS '); +} +bootstrap(); diff --git a/apps/cloud-wallet/test/jest-e2e.json b/apps/cloud-wallet/test/jest-e2e.json new file mode 100644 index 000000000..e9d912f3e --- /dev/null +++ b/apps/cloud-wallet/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/apps/cloud-wallet/tsconfig.app.json b/apps/cloud-wallet/tsconfig.app.json new file mode 100644 index 000000000..0788dd2e8 --- /dev/null +++ b/apps/cloud-wallet/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": false, + "outDir": "../../dist/apps/cloud-wallet" + }, + "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 c09c5fc59..714b6a768 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -355,7 +355,8 @@ VERIFICATION_SERVICE = 'verification', ECOSYSTEM_SERVICE = 'ecosystem', WEBHOOK_SERVICE = 'webhook', NOTIFICATION_SERVICE = 'notification', -GEO_LOCATION_SERVICE = 'geo-location' +GEO_LOCATION_SERVICE = 'geo-location', +CLOUD_WALLET_SERVICE = 'cloud-wallet' } export const postgresqlErrorCodes = []; diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index e6716eec2..b8412e727 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -18,6 +18,7 @@ import { CommonConstants } from './common.constant'; import { HttpService } from '@nestjs/axios/dist'; import { ResponseService } from '@credebl/response'; import * as dotenv from 'dotenv'; +import { RpcException } from '@nestjs/microservices'; dotenv.config(); @Injectable() @@ -392,4 +393,27 @@ export class CommonService { throw new BadRequestException('Invalid Credentials'); } } + dataEncryption(data: string) { + // eslint-disable-next-line no-useless-catch + try { + const encryptedToken = CryptoJS.AES.encrypt(JSON.stringify(data), process.env.CRYPTO_PRIVATE_KEY).toString(); + + return encryptedToken; + } catch (error) { + throw error; + } + } + + handleError(error): Promise { + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } } diff --git a/libs/common/src/interfaces/cloud-wallet.interface.ts b/libs/common/src/interfaces/cloud-wallet.interface.ts new file mode 100644 index 000000000..e7c4be400 --- /dev/null +++ b/libs/common/src/interfaces/cloud-wallet.interface.ts @@ -0,0 +1,32 @@ +import { CloudWalletType } from '@credebl/enum/enum'; + +export class ICreateCloudWallet { + label: string; + connectionImageUrl?: string; + email?: string; + userId?: string; + } + +export interface ICloudWalletDetails { + label: string; + tenantId: string; + email?: string; + type: CloudWalletType; + createdBy: string; + lastChangedBy: string; + userId: string; + agentEndpoint?: string; + agentApiKey?: string; + key?: string; + connectionImageUrl?: string; + } + +export interface IStoredWalletDetails { + email: string, + connectionImageUrl: string, + createDateTime: Date, + id: string, + tenantId: string, + label: string, + lastChangedDateTime: Date +} \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 4c3959581..83d718585 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -549,5 +549,15 @@ export const ResponseMessages = { stateNotFound: 'No states found for provided countryId.Please provide valid countryId', citiesNotFound: 'No cities found for provided stateId and countryId.Please provide valid stateId and countryId' } + }, + cloudWallet: { + success: { + create: 'Cloud wallet created successfully' + }, + error: { + baseWalletNotFound: 'Base wallet configuration not found', + createCloudWallet: 'Error while creating cloud wallet on agent', + encryptCloudWalletKey: 'Error while creating encrypting wallet key' + } } }; diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index bed79d4cb..8aff351d1 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -241,3 +241,8 @@ export enum ledgerLessDIDType { DID_KEY = 'did:key', DID_WEB = 'did:web' } + +export enum CloudWalletType { + BASE_WALLET = 'CLOUD_BASE_WALLET', + SUB_WALLET = 'CLOUD_SUB_WALLET' + } diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index 75e4935c6..89fd8f207 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -230,5 +230,14 @@ "did:web": "did:web" } } + ], + + "userRoleData": [ + { + "role": "HOLDER" + }, + { + "role": "DEFAULT_USER" + } ] } \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20240717074318_cloud_wallet_tables/migration.sql b/libs/prisma-service/prisma/migrations/20240717074318_cloud_wallet_tables/migration.sql new file mode 100644 index 000000000..97e5ad527 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240717074318_cloud_wallet_tables/migration.sql @@ -0,0 +1,50 @@ +-- CreateEnum +CREATE TYPE "UserRole" AS ENUM ('DEFAULT_USER', 'HOLDER'); + +-- CreateEnum +CREATE TYPE "CloudWalletType" AS ENUM ('CLOUD_BASE_WALLET', 'CLOUD_SUB_WALLET'); + +-- CreateTable +CREATE TABLE "user_role" ( + "id" UUID NOT NULL, + "role" "UserRole" NOT NULL, + + CONSTRAINT "user_role_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "user_role_mapping" ( + "id" UUID NOT NULL, + "userId" UUID NOT NULL, + "userRoleId" UUID NOT NULL, + + CONSTRAINT "user_role_mapping_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "cloud_wallet_user_info" ( + "id" UUID NOT NULL, + "label" TEXT NOT NULL, + "tenantId" TEXT NOT NULL, + "email" VARCHAR(500), + "type" "CloudWalletType" NOT NULL, + "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdBy" UUID NOT NULL, + "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastChangedBy" UUID NOT NULL, + "userId" UUID NOT NULL, + + CONSTRAINT "cloud_wallet_user_info_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "cloud_wallet_user_info_email_key" ON "cloud_wallet_user_info"("email"); + +-- AddForeignKey +ALTER TABLE "user_role_mapping" ADD CONSTRAINT "user_role_mapping_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "user_role_mapping" ADD CONSTRAINT "user_role_mapping_userRoleId_fkey" FOREIGN KEY ("userRoleId") REFERENCES "user_role"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "cloud_wallet_user_info" ADD CONSTRAINT "cloud_wallet_user_info_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/libs/prisma-service/prisma/migrations/20240717092122_added_agnet_url_and_apikey_in_cloud_wallet_table/migration.sql b/libs/prisma-service/prisma/migrations/20240717092122_added_agnet_url_and_apikey_in_cloud_wallet_table/migration.sql new file mode 100644 index 000000000..8c09c77ae --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240717092122_added_agnet_url_and_apikey_in_cloud_wallet_table/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "cloud_wallet_user_info" ADD COLUMN "agentApiKey" TEXT, +ADD COLUMN "agentEndpoint" TEXT; diff --git a/libs/prisma-service/prisma/migrations/20240717102514_added_key_in_cloud_wallet_table/migration.sql b/libs/prisma-service/prisma/migrations/20240717102514_added_key_in_cloud_wallet_table/migration.sql new file mode 100644 index 000000000..63748e5c6 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240717102514_added_key_in_cloud_wallet_table/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "cloud_wallet_user_info" ADD COLUMN "key" TEXT; diff --git a/libs/prisma-service/prisma/migrations/20240717104549_added_connection_image_in_cloud_wallet_table/migration.sql b/libs/prisma-service/prisma/migrations/20240717104549_added_connection_image_in_cloud_wallet_table/migration.sql new file mode 100644 index 000000000..f549f4261 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240717104549_added_connection_image_in_cloud_wallet_table/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "cloud_wallet_user_info" ADD COLUMN "connectionImageUrl" TEXT; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 457fd4c2e..5ca8bc44e 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -9,29 +9,31 @@ datasource db { } model user { - id String @id(map: "PK_cace4a159ff9f2512dd42373760") @default(uuid()) @db.Uuid - createDateTime DateTime @default(now()) @db.Timestamptz(6) - lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) - firstName String? @db.VarChar(500) - lastName String? @db.VarChar(500) - email String? @unique(map: "UQ_e12875dfb3b1d92d7d7c5377e22") @db.VarChar(500) - username String? @db.VarChar(500) - verificationCode String? @db.VarChar(500) - isEmailVerified Boolean @default(false) - supabaseUserId String? @db.VarChar(500) - keycloakUserId String? @db.VarChar(500) - clientId String? @db.VarChar(500) - clientSecret String? @db.VarChar(500) - profileImg String? - fidoUserId String? @db.VarChar(1000) - isFidoVerified Boolean @default(false) - publicProfile Boolean @default(false) - password String? @db.VarChar - orgInvitations org_invitations[] - user_activities user_activity[] - userDevices user_devices[] - userOrgRoles user_org_roles[] - token token[] + id String @id(map: "PK_cace4a159ff9f2512dd42373760") @default(uuid()) @db.Uuid + createDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + firstName String? @db.VarChar(500) + lastName String? @db.VarChar(500) + email String? @unique(map: "UQ_e12875dfb3b1d92d7d7c5377e22") @db.VarChar(500) + username String? @db.VarChar(500) + verificationCode String? @db.VarChar(500) + isEmailVerified Boolean @default(false) + supabaseUserId String? @db.VarChar(500) + keycloakUserId String? @db.VarChar(500) + clientId String? @db.VarChar(500) + clientSecret String? @db.VarChar(500) + profileImg String? + fidoUserId String? @db.VarChar(1000) + isFidoVerified Boolean @default(false) + publicProfile Boolean @default(false) + password String? @db.VarChar + orgInvitations org_invitations[] + user_activities user_activity[] + userDevices user_devices[] + userOrgRoles user_org_roles[] + token token[] + user_role_mapping user_role_mapping[] + cloud_wallet_user_info cloud_wallet_user_info[] } model token { @@ -106,12 +108,12 @@ model organisation { clientId String? @db.VarChar(500) clientSecret String? @db.VarChar(500) registrationNumber String? @db.VarChar(100) - countryId Int? - countries countries? @relation(fields:[countryId],references:[id]) - stateId Int? - states states? @relation(fields:[stateId],references:[id]) - cityId Int? - cities cities? @relation(fields:[cityId],references:[id]) + countryId Int? + countries countries? @relation(fields: [countryId], references: [id]) + stateId Int? + states states? @relation(fields: [stateId], references: [id]) + cityId Int? + cities cities? @relation(fields: [cityId], references: [id]) connections connections[] credentials credentials[] org_agents org_agents[] @@ -125,7 +127,6 @@ model organisation { file_upload file_upload[] } - model org_invitations { id String @id @default(uuid()) @db.Uuid createDateTime DateTime @default(now()) @db.Timestamptz(6) @@ -481,7 +482,7 @@ model file_upload { organisation organisation? @relation(fields: [orgId], references: [id]) orgId String? @db.Uuid credential_type String? - templateId String? @db.VarChar + templateId String? @db.VarChar } model file_data { @@ -547,37 +548,81 @@ enum RecordType { } model countries { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - states states[] - cities cities[] - organisation organisation[] + id Int @id @default(autoincrement()) @map("id") + name String @map("name") + states states[] + cities cities[] + organisation organisation[] + @@map("countries") } model states { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - countryId Int @map("country_id") - countryCode String @map("country_code") - countries countries @relation(fields: [countryId], references: [id], onDelete: Cascade) - cities cities[] - organisation organisation[] + id Int @id @default(autoincrement()) @map("id") + name String @map("name") + countryId Int @map("country_id") + countryCode String @map("country_code") + countries countries @relation(fields: [countryId], references: [id], onDelete: Cascade) + cities cities[] + organisation organisation[] @@map("states") } + model cities { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - stateId Int @map("state_id") - stateCode String @map("state_code") - countryId Int @map("country_id") - countryCode String @map("country_code") - state states @relation(fields: [stateId], references: [id], onDelete: Cascade) - country countries @relation(fields: [countryId], references: [id], onDelete: Cascade) - organisation organisation[] + id Int @id @default(autoincrement()) @map("id") + name String @map("name") + stateId Int @map("state_id") + stateCode String @map("state_code") + countryId Int @map("country_id") + countryCode String @map("country_code") + state states @relation(fields: [stateId], references: [id], onDelete: Cascade) + country countries @relation(fields: [countryId], references: [id], onDelete: Cascade) + organisation organisation[] @@index([stateId], name: "cities_stateId_idx") @@index([countryId], name: "cities_countryId_idx") @@map("cities") -} \ No newline at end of file +} + +model user_role { + id String @id @default(uuid()) @db.Uuid + role UserRole + user_role_mapping user_role_mapping[] +} + +enum UserRole { + DEFAULT_USER + HOLDER +} + +model user_role_mapping { + id String @id @default(uuid()) @db.Uuid + userId String @db.Uuid + user user @relation(fields: [userId], references: [id]) + userRoleId String @db.Uuid + user_role user_role @relation(fields: [userRoleId], references: [id]) +} + +model cloud_wallet_user_info { + id String @id @default(uuid()) @db.Uuid + label String + tenantId String + email String? @unique() @db.VarChar(500) + type CloudWalletType + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @db.Uuid + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @db.Uuid + userId String @db.Uuid + agentEndpoint String? + agentApiKey String? + key String? + connectionImageUrl String? + user user @relation(fields: [userId], references: [id]) +} + +enum CloudWalletType { + CLOUD_BASE_WALLET + CLOUD_SUB_WALLET +} diff --git a/libs/prisma-service/prisma/seed.ts b/libs/prisma-service/prisma/seed.ts index 239cbaab6..67fa762ba 100644 --- a/libs/prisma-service/prisma/seed.ts +++ b/libs/prisma-service/prisma/seed.ts @@ -345,6 +345,34 @@ const createLedgerConfig = async (): Promise => { } }; +const createUserRole = async (): Promise => { + try { + const { userRoleData } = JSON.parse(configData); + + const userRoleDetails = userRoleData.map(userRole => userRole.role); + const existUserRole = await prisma.user_role.findMany({ + where: { + role: { + in: userRoleDetails + } + } + }); + + if (0 === existUserRole.length) { + const userRole = await prisma.user_role.createMany({ + data: userRoleData + }); + + logger.log(userRole); + } else { + logger.log('Already seeding in user role'); + } + + + } catch (e) { + logger.error('An error occurred seeding user role:', e); + } +}; async function main(): Promise { @@ -359,6 +387,7 @@ async function main(): Promise { await createEcosystemRoles(); await createEcosystemConfig(); await createLedgerConfig(); + await createUserRole(); } diff --git a/nest-cli.json b/nest-cli.json index 778bef64b..c14b712ea 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -295,6 +295,15 @@ "compilerOptions": { "tsConfigPath": "apps/geo-location/tsconfig.app.json" } + }, + "cloud-wallet": { + "type": "application", + "root": "apps/cloud-wallet", + "entryFile": "main", + "sourceRoot": "apps/cloud-wallet/src", + "compilerOptions": { + "tsConfigPath": "apps/cloud-wallet/tsconfig.app.json" + } } } } \ No newline at end of file