diff --git a/package-lock.json b/package-lock.json index 7f12beb..8fc828d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "class-validator": "^0.14.1", "dotenv": "^16.4.7", "mysql2": "^3.14.0", + "qrcode": "^1.5.4", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.21" @@ -36,6 +37,7 @@ "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.10.7", + "@types/qrcode": "^1.5.5", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", @@ -3282,6 +3284,16 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -4705,7 +4717,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5228,6 +5239,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -5390,6 +5410,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -8918,7 +8944,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8975,7 +9000,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9193,6 +9217,15 @@ "node": ">=4" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9326,6 +9359,148 @@ ], "license": "MIT" }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -9498,6 +9673,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -9858,6 +10039,12 @@ "node": ">= 18" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -11673,6 +11860,12 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -11687,7 +11880,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -11741,7 +11933,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11751,7 +11942,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" diff --git a/package.json b/package.json index 539948a..d392895 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "class-validator": "^0.14.1", "dotenv": "^16.4.7", "mysql2": "^3.14.0", + "qrcode": "^1.5.4", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.21" @@ -47,6 +48,7 @@ "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.10.7", + "@types/qrcode": "^1.5.5", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", diff --git a/src/qr/qr.controller.ts b/src/qr/qr.controller.ts index e09a48e..3ed6f45 100644 --- a/src/qr/qr.controller.ts +++ b/src/qr/qr.controller.ts @@ -1,36 +1,57 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Post, + Query, +} from '@nestjs/common'; import { QrService } from './qr.service'; import { Qr } from './qr.entity'; import { CreateQrDto } from './dto/create-qr.dto'; import { UpdateQrDto } from './dto/update.qr.dto'; +import { ApiOperation, ApiQuery } from '@nestjs/swagger'; @Controller('qr') export class QrController { + constructor(private qrService: QrService) {} - constructor(private qrService: QrService) {} + @Get('generate') + @ApiOperation({ summary: 'Genera un código QR a partir de un texto' }) + @ApiQuery({ + name: 'text', + required: true, + description: 'Texto a codificar en el QR', + }) + async generateQRCode(@Query('text') text: string): Promise { + return this.qrService.generateQRCode(text); + } - @Get() - getQrs(): Promise { - return this.qrService.getQrs(); - } + @Get() + getQrs(): Promise { + return this.qrService.getQrs(); + } - @Get(':id') - getQr(@Param('id', ParseIntPipe) id: number) { - return this.qrService.getQr(id); - } + @Get(':id') + getQr(@Param('id', ParseIntPipe) id: number) { + return this.qrService.getQr(id); + } - @Post() //en el body ValidationPipe - createQr(@Body() newQr: CreateQrDto) { - return this.qrService.createQr(newQr) - } + @Post() //en el body ValidationPipe + createQr(@Body() newQr: CreateQrDto) { + return this.qrService.createQr(newQr); + } - @Delete(':id') - deleteQr(@Param('id', ParseIntPipe) id:number) { - return this.qrService.deleteQr(id); - } + @Delete(':id') + deleteQr(@Param('id', ParseIntPipe) id: number) { + return this.qrService.deleteQr(id); + } - @Patch(':id') - updateQr(@Param('id', ParseIntPipe) id: number, @Body() qr: UpdateQrDto) { - return this.qrService.updateQr(id, qr) - } + @Patch(':id') + updateQr(@Param('id', ParseIntPipe) id: number, @Body() qr: UpdateQrDto) { + return this.qrService.updateQr(id, qr); + } } diff --git a/src/qr/qr.service.ts b/src/qr/qr.service.ts index 65af8d2..5a46450 100644 --- a/src/qr/qr.service.ts +++ b/src/qr/qr.service.ts @@ -4,70 +4,87 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CreateQrDto } from './dto/create-qr.dto'; import { UpdateQrDto } from './dto/update.qr.dto'; +import * as QRCode from 'qrcode'; @Injectable() export class QrService { + constructor(@InjectRepository(Qr) private qrRepository: Repository) {} - constructor( - @InjectRepository(Qr) private qrRepository: Repository - ) {} + async generateQRCode(text: string): Promise { + return await QRCode.toDataURL(text); + } - async createQr(qr: CreateQrDto) { - const qrFound = await this.qrRepository.findOne({ - where: { - id_qr: qr.id_participante_evento - } - }) + async generateBuffer(text: string): Promise { + return await QRCode.toBuffer(text); // Devuelve un buffer para adjuntar en email o stream + } - if (qrFound) { - return new HttpException('Qr already exists', HttpStatus.CONFLICT) - } + // Para enviar el QR por email + /* const qrBuffer = await this.qrService.generateBuffer('Texto a codificar'); +await transporter.sendMail({ + to: 'destinatario@example.com', + subject: 'Tu código QR', + html: '

Escanea el siguiente código:

', + attachments: [{ + filename: 'qrcode.png', + content: qrBuffer, + cid: 'qrcode', + }], +}); */ - return this.qrRepository.save(qr) + async createQr(qr: CreateQrDto) { + const qrFound = await this.qrRepository.findOne({ + where: { + id_qr: qr.id_participante_evento, + }, + }); + + if (qrFound) { + return new HttpException('Qr already exists', HttpStatus.CONFLICT); } - getQrs() { - return this.qrRepository.find({}) - } + return this.qrRepository.save(qr); + } - async getQr(id_qr) { - const qrFound = await this.qrRepository.findOne({ - where: { - id_qr - } - }) + getQrs() { + return this.qrRepository.find({}); + } - if (!qrFound) { - return new HttpException('User not found', HttpStatus.NOT_FOUND); - } + async getQr(id_qr) { + const qrFound = await this.qrRepository.findOne({ + where: { + id_qr, + }, + }); - return qrFound + if (!qrFound) { + return new HttpException('User not found', HttpStatus.NOT_FOUND); } - async deleteQr(id_qr: number) { - const result = await this.qrRepository.delete({ id_qr }) + return qrFound; + } - if (result.affected === 0) { - return new HttpException('User not found', HttpStatus.NOT_FOUND); - } + async deleteQr(id_qr: number) { + const result = await this.qrRepository.delete({ id_qr }); - return result + if (result.affected === 0) { + return new HttpException('User not found', HttpStatus.NOT_FOUND); } - async updateQr(id_qr: number, qr: UpdateQrDto) { - const qrFound = await this.qrRepository.findOne({ - where: { - id_qr - } - }) + return result; + } - if (!qrFound) { - return new HttpException('User not found', HttpStatus.NOT_FOUND) - } + async updateQr(id_qr: number, qr: UpdateQrDto) { + const qrFound = await this.qrRepository.findOne({ + where: { + id_qr, + }, + }); - const updateQr = Object.assign(qrFound, qr) - return this.qrRepository.save(updateQr) + if (!qrFound) { + return new HttpException('User not found', HttpStatus.NOT_FOUND); } - + const updateQr = Object.assign(qrFound, qr); + return this.qrRepository.save(updateQr); + } }