diff --git a/apps/api-gateway/src/user/dto/share-certificate.dto.ts b/apps/api-gateway/src/user/dto/share-certificate.dto.ts index 802b849d9..ca82fd844 100644 --- a/apps/api-gateway/src/user/dto/share-certificate.dto.ts +++ b/apps/api-gateway/src/user/dto/share-certificate.dto.ts @@ -8,24 +8,19 @@ interface Attribute { value: string; } export class CreateCertificateDto { + @ApiProperty() - @IsNotEmpty({ message: 'Please provide valid schemaId' }) + @IsNotEmpty({ message: 'Please provide valid credentialId' }) @Transform(({ value }) => trim(value)) @IsString({ message: 'credentialId should be string' }) credentialId: string; - @ApiProperty({ example: 'SchemaId' }) + @ApiProperty({ example: 'schemaId' }) @IsNotEmpty({ message: 'Please provide valid schemaId' }) @Transform(({ value }) => trim(value)) @IsString({ message: 'schemaId should be string' }) schemaId: string; - @ApiProperty({ example: 'CredDefId' }) - @IsNotEmpty({ message: 'Please provide valid schemaId' }) - @Transform(({ value }) => trim(value)) - @IsString({ message: 'credDefId should be string' }) - credDefId?: string; - @ApiProperty({ example: [ { diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index 503370357..545362b06 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -80,7 +80,6 @@ export interface ISendVerificationEmail { export interface IShareUserCertificate { schemaId: string; - credDefId: string; credentialId: string; attributes: Attribute[]; invitationUrl?: string; diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index c4a3e589b..57b8e37a8 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -47,7 +47,7 @@ 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, UserCertificateId } from '@credebl/enum/enum'; +import { EcosystemConfigSettings, UserCertificateId, CertificateDetails} from '@credebl/enum/enum'; import { WinnerTemplate } from '../templates/winner-template'; import { ParticipantTemplate } from '../templates/participant-template'; import { ArbiterTemplate } from '../templates/arbiter-template'; @@ -60,6 +60,9 @@ import { IUsersActivity } from 'libs/user-activity/interface'; import { ISendVerificationEmail, ISignInUser, IVerifyUserEmail, IUserInvitations, IResetPasswordResponse } from '@credebl/common/interfaces/user.interface'; import { AddPasskeyDetailsDto } from 'apps/api-gateway/src/user/dto/add-user.dto'; import { URLUserResetPasswordTemplate } from '../templates/reset-password-template'; +import { EventPinnacle } from '../templates/event-pinnacle'; +import { EventCertificate } from '../templates/event-certificates'; +import * as QRCode from 'qrcode'; @Injectable() export class UserService { @@ -832,6 +835,7 @@ export class UserService { async shareUserCertificate(shareUserCertificate: IShareUserCertificate): Promise { + let template; const attributeArray = []; let attributeJson = {}; const attributePromises = shareUserCertificate.attributes.map(async (iterator: Attribute) => { @@ -841,8 +845,6 @@ export class UserService { attributeArray.push(attributeJson); }); await Promise.all(attributePromises); - let template; - switch (shareUserCertificate.schemaId.split(':')[2]) { case UserCertificateId.WINNER: // eslint-disable-next-line no-case-declarations @@ -864,22 +866,35 @@ export class UserService { const userWorldRecordTemplate = new WorldRecordTemplate(); template = await userWorldRecordTemplate.getWorldRecordTemplate(attributeArray); break; + case UserCertificateId.AYANWORKS_EVENT: + // eslint-disable-next-line no-case-declarations + const QRDetails = await this.getShorteningURL(shareUserCertificate, attributeArray); + + if (shareUserCertificate.attributes.some(item => item.value.toLocaleLowerCase().includes("pinnacle"))) { + const userPinnacleTemplate = new EventPinnacle(); + template = await userPinnacleTemplate.getPinnacleWinner(attributeArray, QRDetails); + } else { + const userCertificateTemplate = new EventCertificate(); + template = await userCertificateTemplate.getCertificateWinner(attributeArray, QRDetails); + } + break; default: throw new NotFoundException('error in get attributes'); } - const option: IPuppeteerOption = {height: 0, width: 1000}; + //Need to handle the option for all type of certificate + const option: IPuppeteerOption = {height: 974, width: 1606}; const imageBuffer = await this.convertHtmlToImage(template, shareUserCertificate.credentialId, option); - const verifyCode = uuidv4(); const imageUrl = await this.awsService.uploadUserCertificate( imageBuffer, 'svg', 'certificates', process.env.AWS_PUBLIC_BUCKET_NAME, - 'base64' + 'base64', + 'certificates' ); const existCredentialId = await this.userRepository.getUserCredentialsById(shareUserCertificate.credentialId); @@ -905,7 +920,7 @@ export class UserService { const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', args: ['--no-sandbox', '--disable-setuid-sandbox'], - protocolTimeout: 200000, + protocolTimeout: 800000, //initial - 200000 headless: true }); @@ -919,6 +934,22 @@ export class UserService { return screenshot; } + //Need to add interface + async getShorteningURL(shareUserCertificate, attributeArray): Promise { + const urlObject = { + schemaId: shareUserCertificate.schemaId, + credDefId: shareUserCertificate.credDefId, + attribute: attributeArray, + credentialId:shareUserCertificate.credentialId, + email:attributeArray.find((attr) => "email" in attr).email + }; + + const qrCodeOptions = { type: 'image/png' }; + const encodedData = Buffer.from(JSON.stringify(shareUserCertificate)).toString('base64'); + const qrCode = await QRCode.toDataURL(`https://credebl.id/c_v?${encodedData}`, qrCodeOptions); + + return qrCode; + } /** * * @param acceptRejectInvitation diff --git a/apps/user/templates/event-certificates.ts b/apps/user/templates/event-certificates.ts new file mode 100644 index 000000000..bed44507c --- /dev/null +++ b/apps/user/templates/event-certificates.ts @@ -0,0 +1,82 @@ +import { Attribute } from '../interfaces/user.interface'; + +export class EventCertificate { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getCertificateWinner(attributes: Attribute[], QRDetails): Promise { + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [name, description, category] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'name').name ?? ''; + const description = this.findAttributeByName(attributes, 'description').description ?? ''; + const category = this.findAttributeByName(attributes, 'category').category ?? ''; + return [name, description, category]; + }); + return ` + + + + + + + + Document + + + +
+
+ +
+
+
+ +
+
+
+ +
+

Certificate of Appreciation

+

- ${category} -

+
+

this certificate is proudly presented to

+

${name}

+

${description}

+

~ 23rd March 2024 ~

+
+
+ QR Code +
+
+
+ +
+

Kirankalyan Kulkarni

+

CEO & Co-Founder

+
+
+
+ +
+

Ajay Jadhav

+

CTO & Co-Founder

+
+
+
+
+
+
+ + + `; + } catch { } + } +} \ No newline at end of file diff --git a/apps/user/templates/event-pinnacle.ts b/apps/user/templates/event-pinnacle.ts new file mode 100644 index 000000000..236478765 --- /dev/null +++ b/apps/user/templates/event-pinnacle.ts @@ -0,0 +1,80 @@ +import { Attribute } from '../interfaces/user.interface'; + +export class EventPinnacle { + findAttributeByName(attributes: Attribute[], name: string): Attribute { + return attributes.find((attr) => name in attr); + } + + async getPinnacleWinner(attributes: Attribute[], qrCode): Promise { + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [name, description] = await Promise.all(attributes).then((attributes) => { + const name = this.findAttributeByName(attributes, 'name').name ?? ''; + const description = this.findAttributeByName(attributes, 'description').description ?? ''; + return [name, description]; + }); + return ` + + + + + + + + Document + + + +
+
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+

Certificate of Appreciation

+

- Pinnacle Performer -

+
+

this certificate is proudly presented to

+

${name}

+

${description}

+

~ 23rd March 2024 ~

+
+
+
+ +
+

Kirankalyan Kulkarni

+

CEO & Co-Founder

+
+
+
+ +
+

Ajay Jadhav

+

CTO & Co-Founder

+
+
+
+
+
+
+ + + `; + } catch { } + } +} \ No newline at end of file diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index f9c7cb7e4..01e8b985a 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -42,14 +42,14 @@ export class AwsService { try { await putObjectAsync({ - Bucket: `${process.env.AWS_ORG_LOGO_BUCKET_NAME}`, - Key: `${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.png`, + Bucket: `${bucketName}`, + Key: `${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`, Body: fileBuffer, ContentEncoding: encoding, ContentType: `image/png` }); - const imageUrl = `https://${process.env.AWS_ORG_LOGO_BUCKET_NAME}.s3.${process.env.AWS_PUBLIC_REGION}.amazonaws.com/${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`; + const imageUrl = `https://${bucketName}.s3.${process.env.AWS_PUBLIC_REGION}.amazonaws.com/${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`; return imageUrl; } catch (error) { throw new HttpException(error, HttpStatus.SERVICE_UNAVAILABLE); diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 047256879..73fe53fb5 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -71,11 +71,13 @@ export enum AgentSpinUpStatus { COMPLETED = 2 } + export enum UserCertificateId { - WINNER = 'Winner', - PARTICIPANT = 'Participant', - ARBITER = 'Arbiter', - WORLD_RECORD = 'WorldRecord' + WINNER = 'Winner', + PARTICIPANT = 'Participant', + ARBITER = 'Arbiter', + WORLD_RECORD = 'WorldRecord', + AYANWORKS_EVENT ='Appreciation Certificate' } export enum NodeEnvironment { @@ -102,4 +104,8 @@ const transitionMap: { [key in Invitation]: Invitation[] } = { [Invitation.REJECTED]: [] }; +export enum CertificateDetails { + PINNACLE_CRED_DEF = 'PKDMuYSzJE22Jkh4B1EMiX:3:CL:826:Pinnacle Certificate' +} + export const transition = (currentStatus: Invitation, nextStatus: Invitation): boolean => (transitionMap[currentStatus].includes(nextStatus)); \ No newline at end of file