Skip to content

Commit

Permalink
refactor: qrcode (#84)
Browse files Browse the repository at this point in the history
* refactor: dtos validation

* refactor: qrcode
  • Loading branch information
KostaD02 authored Oct 11, 2023
1 parent 105c77d commit b1cff28
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/consts/api.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export const API_CONFIG = {
MIN_LASTNAME_LENGTH: 2,
MIN_PASSWORD_LENGTH: 8,
MAX_PASSWORD_LENGTH: 30,
MIN_QRCODE_TEXT_LENGTH: 1,
};
4 changes: 4 additions & 0 deletions src/enums/exceptions.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export enum CartExpectionKeys {
export enum QRCodeExpectionKeys {
TextShouldNotBeEmpty = 'errors.text_should_not_be_empty',
ImageUrlShouldNotBeEmpty = 'errors.image_url_should_not_be_empty',
TextShouldHaveValue = 'errors.text_should_have_value',
InvalidImageURL = 'errors.invalid_image_url',
InvalidImage = 'errors.invalid_image',
NotConvertable = 'errors.not_convertable_text_to_qr',
}

export enum QuoteExpectionKeys {
Expand Down
7 changes: 6 additions & 1 deletion src/modules/qrcode/dtos/generate-qr-image.dto.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { IsString } from 'class-validator';
import { IsString, IsUrl, MinLength } from 'class-validator';
import { API_CONFIG } from 'src/consts';
import { QRCodeExpectionKeys } from 'src/enums';

export class GenerateQrCodeWithImageDto {
@IsString({ message: QRCodeExpectionKeys.TextShouldNotBeEmpty })
@MinLength(API_CONFIG.MIN_QRCODE_TEXT_LENGTH, {
message: QRCodeExpectionKeys.TextShouldHaveValue,
})
text: string;

@IsString({ message: QRCodeExpectionKeys.ImageUrlShouldNotBeEmpty })
@IsUrl({}, { message: QRCodeExpectionKeys.InvalidImageURL })
imageURL: string;
}
6 changes: 5 additions & 1 deletion src/modules/qrcode/dtos/generate-qr.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { IsString } from 'class-validator';
import { IsString, MinLength } from 'class-validator';
import { API_CONFIG } from 'src/consts';
import { QRCodeExpectionKeys } from 'src/enums';

export class GenerateQrCodeDto {
@IsString({ message: QRCodeExpectionKeys.TextShouldNotBeEmpty })
@MinLength(API_CONFIG.MIN_QRCODE_TEXT_LENGTH, {
message: QRCodeExpectionKeys.TextShouldHaveValue,
})
text: string;
}
2 changes: 1 addition & 1 deletion src/modules/qrcode/qrcode.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class QrCodeController {
generateDefaultQrCode() {
return this.qrCodeService.generateQRCodeWithImage(
'https://everrest.educata.dev',
this.qrCodeService.basePath,
'https://github.com/educata/everrest/blob/main/docs/public/logo.png?raw=true',
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/modules/qrcode/qrcode.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';
import { QrCodeController } from './qrcode.controller';
import { QrCodeService } from './qrcode.service';
import { ExceptionService } from 'src/shared';

@Module({
imports: [],
providers: [QrCodeService],
providers: [QrCodeService, ExceptionService],
controllers: [QrCodeController],
})
export class QrCodeModule {}
60 changes: 49 additions & 11 deletions src/modules/qrcode/qrcode.service.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
import { ExceptionService } from 'src/shared';
import { Injectable } from '@nestjs/common';
import { createCanvas, loadImage } from 'canvas';
import * as QRCode from 'qrcode';
import { ExceptionStatusKeys, QRCodeExpectionKeys } from 'src/enums';

@Injectable()
export class QrCodeService {
readonly basePath = 'assets/images/educata-bg-white.png';
constructor(private exceptionService: ExceptionService) {}

generateQrCode(text: string) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
QRCode.toDataURL(text, (err, url) => {
if (err) {
reject(err);
this.exceptionService.throwError(
ExceptionStatusKeys.BadRequest,
err.message,
QRCodeExpectionKeys.NotConvertable,
);
}
resolve({ result: url });
resolve({
text,
type: 'png',
format: 'base64',
errorCorrectionLevel: 'M',
result: url,
});
});
});
}

async generateQRCodeWithImage(
value: string,
imageSrc: string,
): Promise<string> {
const qrCode = QRCode.create(value, { errorCorrectionLevel: 'H' });
async generateQRCodeWithImage(value: string, imageSrc: string) {
await this.checkIfImageIsValid(imageSrc).catch(() => {
this.exceptionService.throwError(
ExceptionStatusKeys.BadRequest,
'Invalid image',
QRCodeExpectionKeys.InvalidImage,
);
});
const qrCode = QRCode.create(value, { errorCorrectionLevel: 'M' });
const canvas = createCanvas(
qrCode.modules.size * 8,
qrCode.modules.size * 8,
Expand All @@ -30,7 +46,7 @@ export class QrCodeService {
QRCode.toCanvas(
canvas,
value,
{ errorCorrectionLevel: 'H', margin: 0 },
{ errorCorrectionLevel: 'M', margin: 0 },
(error) => error ?? '',
);

Expand Down Expand Up @@ -59,6 +75,28 @@ export class QrCodeService {
imageHeight,
);

return canvas.toDataURL();
return {
text: value,
type: 'png',
format: 'base64',
errorCorrectionLevel: 'M',
result: canvas.toDataURL(),
};
}

checkIfImageIsValid(url: string) {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => {
if (response.ok) {
const currentContentType = response.headers.get('Content-Type');
if (currentContentType.indexOf('image') > -1) {
return resolve(url);
}
}
reject(false);
})
.catch(reject);
});
}
}

0 comments on commit b1cff28

Please sign in to comment.