From 07ca695ad9454366cb059139e9581e3f0638ea1e Mon Sep 17 00:00:00 2001 From: Call Vin Date: Thu, 11 Jul 2024 20:57:56 +0700 Subject: [PATCH] add: Get QR Code --- package.json | 1 + pnpm-lock.yaml | 103 ++++++++++++++++++ {src/public => public}/css/DELETE_ME | 0 {src/public => public}/img/DELETE_ME | 0 {src/public => public}/js/DELETE_ME | 0 {src => public}/views/DELETE_ME | 0 src/controllers/qrController.ts | 22 +++- .../migration.sql | 10 ++ src/database/schema.prisma | 9 ++ src/main/routes/publicRoute.ts | 1 + src/main/server/app.ts | 4 +- src/modules/whatsapp.ts | 5 +- src/services/qrService.ts | 22 ++++ tsconfig.json | 2 +- 14 files changed, 170 insertions(+), 9 deletions(-) rename {src/public => public}/css/DELETE_ME (100%) rename {src/public => public}/img/DELETE_ME (100%) rename {src/public => public}/js/DELETE_ME (100%) rename {src => public}/views/DELETE_ME (100%) rename src/database/migrations/{20240630152217_init => 20240711135107_init}/migration.sql (85%) create mode 100644 src/services/qrService.ts diff --git a/package.json b/package.json index be1d88b..6db4473 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "link-preview-js": "^3.0.5", "pino": "^9.2.0", "prisma": "^5.16.1", + "qrcode": "^1.5.3", "qrcode-terminal": "^0.12.0", "typescript": "^5.5.3", "uuid": "^10.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 778681d..d43985e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: prisma: specifier: ^5.16.1 version: 5.16.1 + qrcode: + specifier: ^1.5.3 + version: 1.5.3 qrcode-terminal: specifier: ^0.12.0 version: 0.12.0 @@ -1550,6 +1553,9 @@ packages: cjs-module-lexer@1.3.1: resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1679,6 +1685,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dedent@1.5.3: resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: @@ -1721,6 +1731,9 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1765,6 +1778,9 @@ packages: enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -2700,6 +2716,10 @@ packages: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2764,6 +2784,11 @@ packages: resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==} hasBin: true + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} @@ -2847,6 +2872,9 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -2912,6 +2940,9 @@ packages: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3218,6 +3249,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3235,6 +3269,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3258,6 +3296,9 @@ packages: utf-8-validate: optional: true + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3265,10 +3306,18 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -5055,6 +5104,12 @@ snapshots: cjs-module-lexer@1.3.1: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5192,6 +5247,8 @@ snapshots: dependencies: ms: 2.1.2 + decamelize@1.2.0: {} + dedent@1.5.3: {} deep-is@0.1.4: {} @@ -5219,6 +5276,8 @@ snapshots: diff-sequences@29.6.3: {} + dijkstrajs@1.0.3: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -5264,6 +5323,8 @@ snapshots: enabled@2.0.0: {} + encode-utf8@1.0.3: {} + encodeurl@1.0.2: {} end-of-stream@1.4.4: @@ -6413,6 +6474,8 @@ snapshots: dependencies: queue-lit: 1.5.2 + pngjs@5.0.0: {} + prelude-ls@1.2.1: {} prettier@3.3.2: {} @@ -6488,6 +6551,13 @@ snapshots: qrcode-terminal@0.12.0: {} + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + qs@6.11.0: dependencies: side-channel: 1.0.6 @@ -6568,6 +6638,8 @@ snapshots: require-directory@2.1.1: {} + require-main-filename@2.0.0: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -6635,6 +6707,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6925,6 +6999,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-module@2.0.1: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -6951,6 +7027,12 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -6966,12 +7048,33 @@ snapshots: ws@8.18.0: {} + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/src/public/css/DELETE_ME b/public/css/DELETE_ME similarity index 100% rename from src/public/css/DELETE_ME rename to public/css/DELETE_ME diff --git a/src/public/img/DELETE_ME b/public/img/DELETE_ME similarity index 100% rename from src/public/img/DELETE_ME rename to public/img/DELETE_ME diff --git a/src/public/js/DELETE_ME b/public/js/DELETE_ME similarity index 100% rename from src/public/js/DELETE_ME rename to public/js/DELETE_ME diff --git a/src/views/DELETE_ME b/public/views/DELETE_ME similarity index 100% rename from src/views/DELETE_ME rename to public/views/DELETE_ME diff --git a/src/controllers/qrController.ts b/src/controllers/qrController.ts index d650c5d..2cc889b 100644 --- a/src/controllers/qrController.ts +++ b/src/controllers/qrController.ts @@ -1,3 +1,5 @@ +import QRCode from "qrcode"; +import { QRService } from "../services/qrService"; import { isWAConnected } from "../modules/whatsapp"; import type { NextFunction, Request, Response } from "express"; @@ -5,9 +7,11 @@ export class QRController { static async getQR(req: Request, res: Response, next: NextFunction) { try { if (!isWAConnected()) { - res.status(200).json({ - qr_code: "result", - image_url: "result", + await QRService.getQR().then((qr: string) => { + res.status(200).json({ + qr_code: qr, + image_url: `${req.protocol}://${req.get("host")}${req.originalUrl}/show?qrcode=${qr}`, + }); }); } else { res.status(200).json({ @@ -18,4 +22,16 @@ export class QRController { next(e); } } + + static async showQR(req: Request, res: Response, next: NextFunction) { + try { + const qrcode = req.query.qrcode || ""; + const qrCodeImage = await QRCode.toDataURL(qrcode); + res.send(` + QR Code + `); + } catch (e) { + next(e); + } + } } diff --git a/src/database/migrations/20240630152217_init/migration.sql b/src/database/migrations/20240711135107_init/migration.sql similarity index 85% rename from src/database/migrations/20240630152217_init/migration.sql rename to src/database/migrations/20240711135107_init/migration.sql index 49a27e4..6cf47d1 100644 --- a/src/database/migrations/20240630152217_init/migration.sql +++ b/src/database/migrations/20240711135107_init/migration.sql @@ -13,6 +13,16 @@ CREATE TABLE `users` ( PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +-- CreateTable +CREATE TABLE `qr_code` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `qr` VARCHAR(500) NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `last_update` DATETIME(3) NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + -- CreateTable CREATE TABLE `messages` ( `id` VARCHAR(191) NOT NULL, diff --git a/src/database/schema.prisma b/src/database/schema.prisma index a000d65..02fe0ed 100644 --- a/src/database/schema.prisma +++ b/src/database/schema.prisma @@ -21,6 +21,15 @@ model User { @@map("users") } +model qrCode { + id Int @id @default(autoincrement()) + qr String @db.VarChar(500) + created_at DateTime @default(now()) + last_update DateTime? @updatedAt + + @@map("qr_code") +} + model Message { id String @unique text String diff --git a/src/main/routes/publicRoute.ts b/src/main/routes/publicRoute.ts index 139504b..b276faf 100644 --- a/src/main/routes/publicRoute.ts +++ b/src/main/routes/publicRoute.ts @@ -11,6 +11,7 @@ publicRouter.get("/", getVersion); publicRouter.post("/users", UserController.register); publicRouter.get("/qr", QRController.getQR); +publicRouter.get("/qr/show", QRController.showQR); // Messages publicRouter.post("/messages", MessageController.sendMessage); diff --git a/src/main/server/app.ts b/src/main/server/app.ts index d3f42f9..c405d68 100644 --- a/src/main/server/app.ts +++ b/src/main/server/app.ts @@ -26,8 +26,6 @@ app.use("/", publicRouter); app.use(ErrorMiddleware); app.disable("x-powered-by"); -// app.use(express.static(path.join(__dirname, "..", "..", "public"))); -app.set("view engine", "ejs"); -// app.set("views", path.join(__dirname, "..", "..", "public")); +// app.use(express.static(path.join(__dirname, "../../../public"))); export { app as web }; diff --git a/src/modules/whatsapp.ts b/src/modules/whatsapp.ts index b34dc4e..4b4ffe8 100644 --- a/src/modules/whatsapp.ts +++ b/src/modules/whatsapp.ts @@ -8,6 +8,7 @@ import { } from "@whiskeysockets/baileys"; import { Boom } from "@hapi/boom"; import { logger } from "../config/logger"; +import { QRService } from "../services/qrService"; import { MessageService } from "../services/messageService"; import type { WAMessagesUpdate } from "../@types/baileys/WAMessages"; @@ -21,7 +22,7 @@ export async function connectToWhatsApp(): Promise { waSock = makeWASocket({ generateHighQualityLinkPreview: false, linkPreviewImageThumbnailWidth: 0, - printQRInTerminal: true, + printQRInTerminal: false, syncFullHistory: false, auth: state, }); @@ -31,7 +32,7 @@ export async function connectToWhatsApp(): Promise { "connection.update", async (update: Partial) => { const { connection, lastDisconnect, qr } = update; - logger.info(`QR => ${qr}`); + qr != undefined && QRService.updateQR(qr); if (connection === "close") { const reason = new Boom(lastDisconnect?.error).output.statusCode; switch (reason) { diff --git a/src/services/qrService.ts b/src/services/qrService.ts new file mode 100644 index 0000000..63b4204 --- /dev/null +++ b/src/services/qrService.ts @@ -0,0 +1,22 @@ +import { prismaClient } from "../config/database"; + +export class QRService { + static async getQR(): Promise { + const qrData = await prismaClient.qrCode.findUnique({ + where: { + id: 1, + }, + select: { qr: true }, + }); + console.warn(JSON.stringify(qrData)); + return qrData.qr ?? ""; + } + + static async updateQR(qr: string) { + await prismaClient.qrCode.upsert({ + where: { id: 1 }, + update: { id: 1, qr: qr }, + create: { id: 1, qr: qr }, + }); + } +} diff --git a/tsconfig.json b/tsconfig.json index f3ac7e3..499f8dd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["./src/**/*", "docs/messages.ts"], + "include": ["src/**/*"], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */