diff --git a/package-lock.json b/package-lock.json index e4afc853..c8c77e5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@elastic/elasticsearch": "^8.14.0", "@nestjs/platform-express": "^10.3.9", "@tsoa/runtime": "^6.0.0", + "amqplib": "^0.10.4", "axios": "^1.7.2", "cors": "^2.8.5", "csv-parser": "^3.0.0", @@ -32,6 +33,7 @@ "xlsx": "^0.20.2" }, "devDependencies": { + "@types/amqplib": "^0.10.5", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.11.5", @@ -43,6 +45,45 @@ "typescript": "^5.3.3" } }, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -392,6 +433,15 @@ "yarn": ">=1.9.4" } }, + "node_modules/@types/amqplib": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.5.tgz", + "integrity": "sha512-/cSykxROY7BWwDoi4Y4/jLAuZTshZxd8Ey1QYa/VaXriMotBDoou7V/twJiOSHzU6t1Kp1AHAUXGCgqq+6DNeg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -568,6 +618,41 @@ "node": ">=0.10.0" } }, + "node_modules/amqplib": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.4.tgz", + "integrity": "sha512-DMZ4eCEjAVdX1II2TfIUpJhfKAuoCeDIo/YyETbfAqehHTXxxs7WOOd+N1Xxr4cKhx12y23zk8/os98FxlZHrw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/amqplib/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/amqplib/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/amqplib/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "node_modules/ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -860,6 +945,11 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3508,6 +3598,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3653,6 +3748,11 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -4967,6 +5067,15 @@ "node": ">= 0.8" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index e0606931..6a8cbda2 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@types/amqplib": "^0.10.5", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.11.5", @@ -30,6 +31,7 @@ "@elastic/elasticsearch": "^8.14.0", "@nestjs/platform-express": "^10.3.9", "@tsoa/runtime": "^6.0.0", + "amqplib": "^0.10.4", "axios": "^1.7.2", "cors": "^2.8.5", "csv-parser": "^3.0.0", diff --git a/src/app.ts b/src/app.ts index 40c284ac..d4906096 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,11 +5,14 @@ import express from "express"; import swaggerUi from "swagger-ui-express"; import swaggerDocument from "./swagger.json"; import * as cron from "node-cron"; +import { init as rabbitmqInit } from './services/rabbitmq'; import error from "./middlewares/error"; import { AppDataSource } from "./database/data-source"; import { RegisterRoutes } from "./routes"; import { OrganizationController } from "./controllers/OrganizationController"; import logMiddleware from "./middlewares/logs"; +import { run } from "node:test"; +import { CommandController } from "./controllers/CommandController"; async function main() { await AppDataSource.initialize(); @@ -43,6 +46,17 @@ async function main() { } }); + const cronTime_command = "0 2 * * * *"; + // const cronTime_command = "*/10 * * * * *"; + cron.schedule(cronTime_command, async () => { + try { + const commandController = new CommandController(); + await commandController.cronjobCommand(); + } catch (error) { + console.error("Error executing function from controller:", error); + } + }); + // app.listen(APP_PORT, APP_HOST, () => console.log(`Listening on: http://localhost:${APP_PORT}`)); app.listen( APP_PORT, @@ -52,6 +66,16 @@ async function main() { console.log(`[APP] Swagger on: http://localhost:${APP_PORT}/api-docs`) ), ); + async function runMessageQueue() { + try { + await rabbitmqInit(); + } catch (e) { + console.log(e); + setTimeout(runMessageQueue, 1000); + } + } + + runMessageQueue() } main(); diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 82870af9..85b2c98e 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -19,7 +19,7 @@ import HttpSuccess from "../interfaces/http-success"; import HttpStatusCode from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; import { Command } from "../entities/Command"; -import { Brackets, LessThan, MoreThan, Double, In, Not } from "typeorm"; +import { Brackets, LessThan, MoreThan, Double, In, Not ,Between } from "typeorm"; import { CommandType } from "../entities/CommandType"; import { CommandSend } from "../entities/CommandSend"; import { Profile, CreateProfileAllFields } from "../entities/Profile"; @@ -47,6 +47,7 @@ import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { ProfileDiscipline } from "../entities/ProfileDiscipline"; import { ProfileDisciplineHistory } from "../entities/ProfileDisciplineHistory"; import { PosMasterAct } from "../entities/PosMasterAct"; +import { sendToQueue } from "../services/rabbitmq"; import { PosLevel } from "../entities/PosLevel"; import { PosType } from "../entities/PosType"; import { addUserRoles, createUser, getRoles } from "../keycloak"; @@ -1038,34 +1039,86 @@ export class CommandController extends Controller { ) ) { command.status = "WAITING"; + command.lastUpdateUserId = request.user.sub; + command.lastUpdateFullName = request.user.name; + command.lastUpdatedAt = new Date(); + await this.commandRepository.save(command); } else { const path = this.commandTypePath(command.commandType.code); if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); - - await new CallAPI() - .PostData(request, path + "/excecute", { - refIds: command.commandRecives - .filter((x) => x.refId != null) - .map((x) => ({ - refId: x.refId, - commandAffectDate: command.commandAffectDate, - commandNo: command.commandNo, - commandYear: command.commandYear, - templateDoc: command.positionDetail, - amount: x.amount, - positionSalaryAmount: x.positionSalaryAmount, - mouthSalaryAmount: x.mouthSalaryAmount, - })), - }) - .then(async (res) => { - command.status = "REPORTED"; - }) - .catch((e) => {}); + const msg = { + data: { + id: command.id, + status: "REPORTED", + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + lastUpdatedAt: new Date(), + }, + user: request.user, + token: request.headers["authorization"], + }; + sendToQueue(msg); } - command.lastUpdateUserId = request.user.sub; - command.lastUpdateFullName = request.user.name; - command.lastUpdatedAt = new Date(); - await this.commandRepository.save(command); + return new HttpSuccess(); + } + + async cronjobCommand(@Request() request?: RequestWithUser) { + console.log(request); + const today = new Date(); + today.setHours(7, 0, 0, 0); //+7 เพื่อให้ตรง local time (อาจจะต้องใช้ moment) + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); + + const command = await this.commandRepository.find({ + relations: ["commandType", "commandRecives"], + where:{ + commandExcecuteDate: Between(today, tomorrow), + status: "WAITING" + } + }); + + const data = { + client_id: "gettoken", + client_secret: process.env.AUTH_ACCOUNT_SECRET, + grant_type: "password", + requested_token_type: "urn:ietf:params:oauth:token-type:refresh_token", + username: process.env.USERNAME_, + password: process.env.PASSWORD_, + }; + let _data: any = null; + await Promise.all([ + await new CallAPI() + .PostDataKeycloak("/realms/bma-ehr/protocol/openid-connect/token", data) + .then(async (x) => { + _data = x; + }) + .catch(async (x) => { + throw new HttpError(HttpStatus.UNAUTHORIZED, "ชื่อผู้ใช้งานหรือรหัสผ่านไม่ถูกต้อง"); + }), + ]); + if (_data == null) { + return new HttpError(HttpStatus.UNAUTHORIZED, "ชื่อผู้ใช้งานหรือรหัสผ่านไม่ถูกต้อง"); + } + + command.forEach(async (x) => { + const path = this.commandTypePath(x.commandType.code); + if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); + const msg = { + data: { + id: x.id, + status: "REPORTED", + lastUpdateUserId: "system", + lastUpdateFullName: "system", + // lastUpdateUserId: _data.user.sub, + // lastUpdateFullName: _data.user.name, + lastUpdatedAt: new Date(), + }, + user: _data.user, + token: _data.access_token, + }; + sendToQueue(msg); + }) + return new HttpSuccess(); } @@ -2704,6 +2757,7 @@ export class CommandController extends Controller { ) { return new HttpSuccess(); } + commandTypePath(commandCode: string) { switch (commandCode) { case "C-PM-01": diff --git a/src/interfaces/call-api.ts b/src/interfaces/call-api.ts index 24169649..9e62e712 100644 --- a/src/interfaces/call-api.ts +++ b/src/interfaces/call-api.ts @@ -41,7 +41,7 @@ class CallAPI { } } //Post - public async PostData(request: any, @Path() path: any, sendData: any) { + public async PostData(request: any, @Path() path: any, sendData: any, log = true) { const token = "Bearer " + request.headers.authorization.replace("Bearer ", ""); const url = process.env.API_URL + path; try { @@ -52,7 +52,7 @@ class CallAPI { api_key: process.env.API_KEY, }, }); - addLogSequence(request, { + if (log) addLogSequence(request, { action: "request", status: "success", description: "connected", @@ -65,7 +65,7 @@ class CallAPI { }); return response.data.result; } catch (error) { - addLogSequence(request, { + if (log) addLogSequence(request, { action: "request", status: "error", description: "unconnected", @@ -79,6 +79,7 @@ class CallAPI { throw error; } } + //Post public async PostDataKeycloak(@Path() path: any, sendData: any) { // const token = request.headers.authorization; diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 099a237c..d3f9ded0 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -209,3 +209,93 @@ export function addLogSequence(req: RequestWithUser, data: LogSequence) { export function editLogSequence(req: RequestWithUser, index: number, data: LogSequence) { req.app.locals.logData.sequence[index] = data; } + +export function commandTypePath(commandCode: string): string | null { + switch (commandCode) { + case "C-PM-01": + return "/placement/recruit/report"; + case "C-PM-02": + return "/placement/candidate/report"; + case "C-PM-03": + return "/placement/appoint/report"; + case "C-PM-04": + return "/placement/move/report"; + case "C-PM-05": + return "/placement/appointment/appoint/report"; + case "C-PM-06": + return "/placement/appointment/slip/report"; + case "C-PM-07": + return "/placement/appointment/move/report"; + case "C-PM-08": + return "/retirement/other/appoint/report"; + case "C-PM-09": + return "/retirement/other/out/report"; + case "C-PM-10": + return "/xxxxxx"; + case "C-PM-11": + return "/probation/report/command11/officer/report"; + case "C-PM-12": + return "/probation/report/command12/officer/report"; + case "C-PM-13": + return "/placement/transfer/command/report"; + case "C-PM-14": + return "/placement/Receive/command/report"; + case "C-PM-15": + return "/placement/officer/command/report"; + case "C-PM-16": + return "/placement/repatriation/command/report"; + case "C-PM-17": + return "/retirement/resign/command/report"; + case "C-PM-18": + return "/retirement/out/command/report"; + case "C-PM-19": + return "/discipline/result/command19/report"; + case "C-PM-20": + return "/discipline/result/command20/report"; + case "C-PM-21": + return "/org/command/command21/employee/report"; + case "C-PM-22": + return "/placement/appointment/employee-appoint/report"; + case "C-PM-23": + return "/retirement/resign/employee/report"; + case "C-PM-24": + return "/placement/appointment/employee-move/report"; + case "C-PM-25": + return "/discipline/result/command25/report"; + case "C-PM-26": + return "/discipline/result/command26/report"; + case "C-PM-27": + return "/discipline/result/command27/report"; + case "C-PM-28": + return "/discipline/result/command28/report"; + case "C-PM-29": + return "/discipline/result/command29/report"; + case "C-PM-30": + return "/discipline/result/command30/report"; + case "C-PM-31": + return "/discipline/result/command31/report"; + case "C-PM-32": + return "/discipline/result/command32/report"; + case "C-PM-33": + return "/salary/report/command/officer/report"; + case "C-PM-34": + return "/salary/report/command/officer/report"; + case "C-PM-35": + return "/salary/report/command/officer/report"; + case "C-PM-36": + return "/salary/report/command/employee/report"; + case "C-PM-37": + return "/salary/report/command/employee/report"; + case "C-PM-38": + return "/org/command/command38/officer/report"; + case "C-PM-39": + return "/placement/slip/report"; + case "C-PM-40": + return "/org/command/command40/officer/report"; + case "C-PM-41": + return "/retirement/resign/leave-cancel/report"; + default: + return null; + } +} + diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts new file mode 100644 index 00000000..eb482fd6 --- /dev/null +++ b/src/services/rabbitmq.ts @@ -0,0 +1,126 @@ +import amqp from "amqplib"; +import { AppDataSource } from "../database/data-source"; +import { Command } from "../entities/Command"; +import { commandTypePath } from "../interfaces/utils"; +import CallAPI from "../interfaces/call-api"; +import HttpError from "../interfaces/http-error"; +import HttpStatusCode from "../interfaces/http-status"; +import { RequestWithUser } from "../middlewares/user"; + +export let sendToQueue: (payload: any) => void; + +export async function init() { //----> (1) Producer + if (!process.env.AMQ_URL || !process.env.AMQ_QUEUE) return; + + const { AMQ_URL: url, AMQ_QUEUE: queue } = process.env; //----> (1.2) get url and queue from .env + + const connection = await amqp.connect(url); //----> (1.3) set up url with amqp protocol + + const channel = await connection.createChannel(); //----> (1.4) create Channel + + channel.assertQueue(queue, { durable: true }); //----> (1.5) assert queue and set durable (if "true" save to disk on RabbitMQ) + channel.prefetch(1); + + sendToQueue = (payload: any, persistent = true) => { //----> (2) sendQueue To RabbitMQ and set persistent (if "true" redo the failed queue when server run again) + channel.sendToQueue(queue, Buffer.from(JSON.stringify(payload)), { + persistent, + }); + }; + + console.log("[AMQ] Listening for message..."); + + createConsumer(queue, channel, handler); //----> (3) Process Consumer + // createConsumer(queue1, channel, handler1); + // createConsumer(queue2, channel, handler2); +} + +function createConsumer( //----> consumer + queue: string, + channel: amqp.Channel, + handler: (msg: amqp.ConsumeMessage) => Promise | boolean, +) { + channel.consume( + queue, + async (msg) => { + if (!msg) return; + + if (await handler(msg)) return channel.ack(msg); + + return await new Promise((resolve) => setTimeout(() => resolve(channel.nack(msg)), 3000)); + }, + { noAck: false }, + ); +} + +async function handler(msg: amqp.ConsumeMessage): Promise { //----> condition before process consumer + const repo = AppDataSource.getRepository(Command); + const { data, token, user } = JSON.parse(msg.content.toString()); + + const { id, status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt } = data; + + const command = await repo.findOne({ + where: { id: id }, + relations: ["commandType", "commandRecives"], + }); + + if (!command) return true; + + const path = commandTypePath(command.commandType.code); + if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); + + return await new CallAPI() + .PostData( + { + headers: { authorization: token }, //time bomb + }, + path + "/excecute", + { + refIds: command.commandRecives + .filter((x) => x.refId != null) + .map((x) => ({ + refId: x.refId, + commandAffectDate: command.commandAffectDate, + commandNo: command.commandNo, + commandYear: command.commandYear, + templateDoc: command.positionDetail, + amount: x.amount, + positionSalaryAmount: x.positionSalaryAmount, + mouthSalaryAmount: x.mouthSalaryAmount, + })), + }, + false, + ) + .then(async (res) => { + console.log("[AMQ] Excecute Command Success"); + Object.assign(command, { status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt }); + const result = await repo.save(command).catch((e) => console.log(e)); + if (result) return true; + return false; + }) + .catch((e) => { + console.error(e); + return false; + }); +} + +// async function handler(msg: amqp.ConsumeMessage): Promise { //----> condition before process consumer +// const repo = AppDataSource.getRepository(Command); + +// const { id, status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt } = JSON.parse( +// msg.content.toString(), +// ); + +// const record = await repo.findOne({ +// where: { id }, +// }); + +// if (!record) return true; + +// Object.assign(record, { status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt }); + +// const result = await repo.save(record).catch((e) => console.log(e)); + +// if (result) return true; + +// return false; +// }