import fs from "fs"; import axios from "axios"; /* |-------------------------------------------------------------------------- | Routes |-------------------------------------------------------------------------- | | This file is dedicated for defining HTTP routes. A single file is enough | for majority of projects, however you can define routes in different | files and just make sure to import them inside this file. For example | | Define routes in following two files | ├── start/routes/cart.ts | ├── start/routes/customer.ts | | and then import them inside `start/routes.ts` as follows | | import './routes/cart' | import './routes/customer' | */ import Route from "@ioc:Adonis/Core/Route"; import { runtimeCheckLogs } from "App/utils/runtimeCheckLogs"; import Env from "@ioc:Adonis/Core/Env"; import { sendMessToZulip } from "App/utils/sendMessToZulip"; import moment from "moment"; import Product from "App/Models/Product"; import { sendDeviceInfora } from "App/utils/sendDeviceInfor"; import InfoDevice from "App/Models/InfoDevice"; import LogReport from "App/Models/LogReport"; import Cache from "@ioc:Kaperskyguru/Adonis-Cache"; import { checkDateWiki } from "App/utils/helper"; const util = require("util"); const exec = util.promisify(require("child_process").exec); const { DocumentProcessorServiceClient } = require("@google-cloud/documentai").v1; const fs = require("fs"); const path = require("path"); runtimeCheckLogs(Env.get("FOLDER_LOGS")); Route.post("/api/getIndexSerialNumber", "ErpsController.getIndexSerialNumber") .middleware("checkToken") .middleware("writeLog"); // .middleware("writeLog"); Route.post("/api/getParagraph", "ErpsController.getParagraph") .middleware("checkToken") .middleware("writeLog"); //Users Route.post("/api/account/createUser", "UsersController.create").middleware( "writeLog" ); Route.post("/api/account/checkLogin", "UsersController.checkLogin").middleware( "writeLog" ); //Log Route.get("/api/log/showLog/:name?", "LogsController.showLog").middleware( "writeLog" ); Route.get("/api/getAllLogDetect", "LogsController.getAllLogDetect"); //Key-Value Route.post("/api/getKeyValue", "ValuesController.getKeyValue"); Route.post("/api/deleteValue", "ValuesController.destroy").middleware( "writeLog" ); Route.post("/api/editValue", "ValuesController.edit").middleware("writeLog"); Route.post("/api/addValue", "ValuesController.create").middleware("writeLog"); Route.post("/api/backupProduct", async ({ request, response }) => { try { const from = request.all().from || moment(Date.now()).format("YYYYMMDD"); const to = request.all().to || moment(Date.now()).format("YYYYMMDD"); const res = await axios.post( "https://logs.danielvu.com/api/getIndexSerialNumber", { from: from, to: to }, { headers: { Authorization: request.headers().authorization?.replace(/"/g, ""), }, } ); res.data.map((obj, index) => { res.data[index] = { PID: res.data[index].PID, SN: res.data[index].SN, VID: res.data[index].VID, line: res.data[index].line.join(","), file: res.data[index].fileName, warehouse: res.data[index].warehouse, }; }); await Product.createMany(res.data); // console.log(addProduct) response.status(200).send("Add " + res.data.length + " success!"); await sendMessToZulip( "stream", "networkToolBot", "Log service", "Backup product " + from + " to " + to + " success with " + res.data.length + " products" ); } catch (error) { response.status(500).send(error); await sendMessToZulip( "stream", "networkToolBot", "Log service", "Backup product fail. Please check!" ); } }).middleware("writeLog"); Route.post("/api/sendMailInforDevice", async () => { try { sendDeviceInfora(); } catch (error) { console.log(error); } }) .middleware("checkToken") .middleware("writeLog"); Route.post( "/api/private-log/getFileOnFolder", async ({ request, response }) => { try { let result = []; let path = request.all().folerPath; return new Promise((resolve, reject) => { fs.readdir(path, (err, entries) => { if (err) { reject(err); return; } entries.forEach((entry) => { const entryPath = path + "/" + entry; fs.stat(entryPath, (statErr, stats) => { if (statErr) { reject(statErr); return; } const type = stats.isFile() ? "file" : "directory"; result.push({ name: entryPath, type: type }); if (result.length === entries.length) { resolve(result); } }); }); }); }); } catch (error) { console.log(error); } } ); Route.post("/api/private-log/readFile", async ({ request, response }) => { try { let result = []; let path = request.all().filePath; return await fs.readFileSync(path, "utf8"); } catch (error) { console.log(error); } }); Route.post("/api/extension/addressDetect", async ({ request, response }) => { // TODO(developer): Uncomment these variables before running the sample. const projectId = "532287737140"; const location = "us"; // Format is 'us' or 'eu' const processorId = "64ad0cc100561909"; // Create processor before running sample // const filePath = "https://int.ipsupply.com.au/upload/temp/packagepurchaseorder_17162360547876.jpeg"; const mimeType = "image/jpeg"; // Refer to https://cloud.google.com/document-ai/docs/file-types for supported file types const fieldMask = ["entities"]; // Optional. The fields to return in the Document object. const processorVersionId = "8aa31873669ac16f"; // Optional. Processor version to use const bucketName = "get-address"; // Replace with your GCS bucket name // Set up authentication process.env.GOOGLE_APPLICATION_CREDENTIALS = "strategic-block-424302-v3-54b10fc7e085.json"; async function downloadFile(url, outputLocationPath) { const writer = fs.createWriteStream(outputLocationPath); const response = await axios({ url, method: "GET", responseType: "stream", }); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); }); } async function processDocument(filePath) { const client = new DocumentProcessorServiceClient(); let name; if (processorVersionId) { // The full resource name of the processor version, e.g.: // `projects/${projectId}/locations/${location}/processors/${processorId}/processorVersions/${processorVersionId}` name = client.processorVersionPath( projectId, location, processorId, processorVersionId ); } else { // The full resource name of the processor, e.g.: // `projects/${projectId}/locations/${location}/processors/${processorId}` name = client.processorPath(projectId, location, processorId); } // Download the file from URL let imageContent; const tempFilePath = path.join(__dirname, "images/temp.jpeg"); if (filePath.includes("http")) { console.log("Download file"); await downloadFile(filePath, tempFilePath); imageContent = fs.readFileSync(tempFilePath); } else { imageContent = fs.readFileSync(filePath); } // Read the file into memory // imageContent = fs.readFileSync(filePath); // Load binary data const rawDocument = { content: imageContent, mimeType: mimeType, }; // Optional: Additional configurations for processing. const processOptions = { individualPageSelector: { pages: [1], }, }; // Configure the process request const request = { name: name, rawDocument: rawDocument, fieldMask: fieldMask, // Join fieldMask array into a comma-separated string processOptions: processOptions, }; const [result] = await client.processDocument(request); // For a full list of `Document` object attributes, reference this page: // https://cloud.google.com/document-ai/docs/reference/rest/v1/Document const document = result.document; // Read the text recognition output from the processor console.log("The document contains the following text:"); return extractAndPrintData(document.entities); } // Function to extract and print data function extractAndPrintData(response) { let result = ""; response.forEach((item) => { const type = item.type; const mentionText = item.mentionText; result += `${type} : ${mentionText}\n\n`; }); return result; } const { filePath } = request.all();; console.log(filePath) try { await processDocument(filePath).then((result) => { console.log(result) response.status(200).send(JSON.stringify(result)); }).catch((e) => console.log(e)); } catch (error) { response.status(500).send(`Error: ${error.message}`); } }); Route.post("/api/find-value", async ({ request, response }) => { try { const { value } = request.all(); console.log(value); const { stdout, stderr } = await exec(`grep -nrE "${value}" /home/logs`, { maxBuffer: 1024 * 500, }); // response.status(200).send("sdjkghs"); response.status(200).send(JSON.stringify(stdout)); } catch (error) { console.error(error); response.status(500).send(`Error: ${error.message}`); } }); Route.post("/api/test", async () => { try { const logs = await Cache.get("logs"); if (logs) { return { type: "cache", data: logs }; } else { let data = await LogReport.all(); Cache.set("logs", data, 120); return { type: "no cache", data: data }; } } catch (error) { console.log(error); } }); Route.post('/api/wiki/page/insert', async ({ request, response }) => { try { const { title, data: dataPayload, pid, vid, sn, lineNumber, license } = request.all() if (!title || !dataPayload) { return response.status(422).send({ error: `'title' & 'text' is required` }) } let text = "" if (typeof dataPayload === "string") text = dataPayload.replace(/\\n/g, '\n') else if (Array.isArray(dataPayload)) { const dataPlatform = dataPayload?.find(el => el.command?.trim() === "show platform") const DPELP = dataPlatform && !dataPlatform?.output?.includes("Incomplete") ? true : false text = `
Line ${lineNumber ?? ""}:
PID: ${pid ?? ""}
VID: ${vid ?? ""}
SN:  ${sn ?? ""}
Tested mode: ${DPELP ? "DPELP" : "DPEL"}
License:  ${license ?? ""}
` } // ============ LOGIN LẤY COOKIE ============ const loginTokenRes = await fetch( 'https://jv.ipsupply.com.au/wiki/api.php?action=query&meta=tokens&type=login&format=json' ) const loginToken = (await loginTokenRes.json())?.query?.tokens?.logintoken if (!loginToken) return response.status(500).send('Không lấy được loginToken') // gửi login const bodyLogin = new URLSearchParams() bodyLogin.append('username', 'Ips') bodyLogin.append('password', 'Work1234%') bodyLogin.append('logintoken', loginToken) bodyLogin.append('format', 'json') bodyLogin.append("loginreturnurl", "https://jv.ipsupply.com.au/wiki") const loginRes = await fetch( 'https://jv.ipsupply.com.au/wiki/api.php?action=clientlogin', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', cookie: loginTokenRes.headers.get('set-cookie') ?? '' }, body: bodyLogin, } ) const loginCookies = loginRes.headers.raw()['set-cookie']?.join('; ') ?? '' const loginData = await loginRes.json() if (loginData?.clientlogin?.status !== 'PASS') { return response.status(500).send(loginData) } // ============ LẤY CSRF TOKEN ============ const csrfRes = await fetch( 'https://jv.ipsupply.com.au/wiki/api.php?action=query&meta=tokens&type=csrf&format=json', { headers: { cookie: loginCookies }, } ) const csrfToken = (await csrfRes.json())?.query?.tokens?.csrftoken if (!csrfToken) return response.status(500).send('Không lấy được CSRF token') // ============ LẤY PAGE CŨ ============ const pageRes = await fetch( `https://jv.ipsupply.com.au/wiki/api.php?action=query&prop=revisions&rvprop=content&format=json&titles=${encodeURIComponent(title)}`, { headers: { cookie: loginCookies }, } ) const pageJson = await pageRes.json() const pageId = Object.keys(pageJson.query.pages)[0] const oldContent = pageJson.query.pages[pageId]?.revisions?.[0]?.['*'] ?? '' // nối nội dung mới vào const now = new Date(); const lineDate = `${String(now.getDate()).padStart(2, "0")}/${String(now.getMonth() + 1).padStart(2, "0")}/${now.getFullYear()}`; const scopeDate = `-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
ngay ${lineDate}
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

` const newText = `${scopeDate}${text}\n\n${checkDateWiki(oldContent) ? oldContent.replace(scopeDate, "") : oldContent}` // ============ EDIT PAGE ============ const formData = new FormData() formData.append('text', newText) formData.append('token', csrfToken) const editRes = await fetch( `https://jv.ipsupply.com.au/wiki/api.php?action=edit&format=json&title=${encodeURIComponent(title)}`, { method: 'POST', headers: { cookie: loginCookies }, body: formData, } ) const editResult = await editRes.json() return response.status(200).send(editResult) } catch (err) { console.error(err) return response.status(500).send(err) } }) Route.post("/api/po-email/packing-slip", async ({ request, response }) => { try { const { data } = request.all() const PDFDocument = require('pdfkit') const getStream = require('stream-buffers') function generatePackingSlip(inputData) { function loadFont(name) { return path.join(__dirname, 'fonts', name + ".ttf") } const doc = new PDFDocument({ size: 'A4', margins: { top: 50, bottom: 50, left: 50, right: 50 } }) const stream = new getStream.WritableStreamBuffer() // Layout parameters const leftMargin = 50 const pageWidth = 500 const rightColumnX = 420 let y = doc.y doc.pipe(stream) // ===== LOGO (LEFT COLUMN) ===== const logoPath = '/home/Log_service/start/images/IP-Supply-Logo.png' try { doc.image(logoPath, leftMargin, y, { width: 180 }) } catch (err) { console.warn('⚠️ Logo not found:', logoPath) } // ===== HEADER (RIGHT COLUMN) ===== doc.font(loadFont('calibri-bold')).fontSize(18).fillColor('#396190') .text('IP Supply Pty Ltd', rightColumnX, y, { width: 195, align: 'left' }) doc.fillColor('black').font(loadFont('calibri-regular')).fontSize(10) doc.text('Unit 8, 4A Bachell Ave', rightColumnX, doc.y, { width: 195, align: 'left' }) doc.text('Lidcombe NSW 2141', rightColumnX, doc.y, { width: 195, align: 'left' }) doc.text('Australia', rightColumnX, doc.y, { width: 195, align: 'left' }) doc.moveDown(1.5) // ===== CONTACT INFO (TWO COLUMNS) ===== const contactY = doc.y // Left column - ABN, ACN doc.font(loadFont('calibri-bold')).fontSize(10) doc.text('ABN: ', leftMargin, contactY, { continued: true }) doc.font(loadFont('calibri-regular')).text('66 145 174 358') doc.font(loadFont('calibri-bold')) doc.text('ACN: ', leftMargin, doc.y, { continued: true }) doc.font(loadFont('calibri-regular')).text('145 174 358') // Right column - Phone, Mobile, Email doc.font(loadFont('calibri-bold')) doc.text('Ph: ', rightColumnX, contactY, { continued: true }) doc.font(loadFont('calibri-regular')).text('02 8061 6886') doc.font(loadFont('calibri-bold')) doc.text('M: ', rightColumnX, doc.y, { continued: true }) doc.font(loadFont('calibri-regular')).text('04 8881 6886') doc.font(loadFont('calibri-bold')) doc.text('E: ', rightColumnX, doc.y, { continued: true }) doc.font(loadFont('calibri-regular')).text('sales@ipsupply.com.au') doc.moveDown(2) // ===== "NOT CONFIRM TO" RIBBON (if needed) ===== // if (inputData.confirmStatus === "NOT CONFIRM TO") { // doc.save() // doc.fontSize(10).fillColor('white').font(loadFont('calibri-bold') // const ribbonText = 'NOT CONFIRM TO' // const ribbonWidth = doc.widthOfString(ribbonText) + 20 // doc.rotate(45, { origin: [pageWidth + leftMargin - 60, 60] }) // doc.rect(pageWidth + leftMargin - 80, 50, ribbonWidth, 20) // .fillAndStroke('#d80000', '#a00000') // doc.fillColor('white') // .text(ribbonText, pageWidth + leftMargin - 70, 55) // doc.restore() // } // ===== TITLE ===== doc.fontSize(25).font(loadFont('calibri-bold')).fillColor('#396190') .text('PACKING SLIP', leftMargin, doc.y, { width: pageWidth, align: 'center' }) doc.fillColor('black') doc.moveDown(0.5) // ===== SHIP TO & PO (TWO COLUMNS) ===== const detailsY = doc.y // Left column - Ship To doc.fontSize(13).font(loadFont('calibri-bold')) .text('Ship To', leftMargin, detailsY) doc.fontSize(11).font(loadFont('calibri-regular')) if (inputData.shipTo.name) { doc.text(inputData.shipTo.name, leftMargin, doc.y, { width: 280 }) } if (inputData.shipTo.address) { doc.text(inputData.shipTo.address, leftMargin, doc.y, { width: 280 }) } if (inputData.shipTo.attn) { doc.text(`Attn: ${inputData.shipTo.attn}`, leftMargin, doc.y, { width: 280 }) } if (inputData.shipTo.phone) { doc.text(`Phone: ${inputData.shipTo.phone}`, leftMargin, doc.y, { width: 280 }) } // Right column - PO doc.font(loadFont('calibri-bold')).fontSize(11) .text('PO: ', rightColumnX, detailsY, { continued: true }) doc.font(loadFont('calibri-regular')) .text(inputData.po || '', { width: 195 }) // Nối tất cả value thành một chuỗi, ngăn cách bằng xuống dòng const joined = Object.values(inputData.shipTo).join('\n'); // Tách theo \n và đếm số dòng (bỏ qua dòng rỗng) const lines = joined.split('\n').filter(line => line.trim() !== ''); const lineCount = lines.length; doc.moveDown(lineCount + 1) // ===== TABLE HEADER ===== const tableY = doc.y const qtyColX = leftMargin const descColX = leftMargin + 50 // Header background doc.rect(leftMargin, tableY, pageWidth, 20) .fillAndStroke('#396290', '#396290') // Header text doc.fillColor('white').font(loadFont('calibri-bold')).fontSize(11) doc.text('Qty', qtyColX + 10, tableY + 5, { width: 40, align: 'center' }) doc.text('Description', descColX + 5, tableY + 5, { width: pageWidth - 60 }) doc.fillColor('black') // doc.moveDown(0.5) // ===== TABLE ROWS ===== inputData.items.forEach((item) => { const rowStartY = doc.y // Calculate description height let descText = '' // if (item.name) descText += item.name + ' ' if (item.sku) descText += item.sku if (item.SN && item.SN.join(', ') !== "") { descText += `\nSN: ${item.SN.join(', ')}` } // Measure text height const textHeight = doc.heightOfString(descText.trim(), { width: pageWidth - 60, lineBreak: true }) const rowHeight = Math.max(25, textHeight + 10) // Draw cell borders doc.strokeColor('#396290').lineWidth(0.5) // Qty cell doc.rect(qtyColX, rowStartY, 50, rowHeight).stroke() doc.fontSize(11).font(loadFont('calibri-regular')) .text(item.qty.toString(), qtyColX, rowStartY + 8, { width: 50, align: 'center' }) // Description cell doc.rect(descColX, rowStartY, pageWidth - 50, rowHeight).stroke() doc.text(descText.trim(), descColX + 5, rowStartY + 5, { width: pageWidth - 60, lineBreak: true }) doc.y = rowStartY + rowHeight }) // ===== FOOTER ===== doc.moveDown(1) doc.fontSize(10).font(loadFont('calibri-bold')) .text('Thank you for your purchase.', leftMargin, doc.y, { width: pageWidth }) doc.end() return stream } const pdfStream = generatePackingSlip(data) await new Promise(resolve => pdfStream.on('finish', resolve)) const buffer = pdfStream.getContents() const fileName = `${data.po || 'PackingSlip'}_PackingSlip.pdf` const filePath = `/tmp/${fileName}` fs.writeFileSync(filePath, buffer) response.header('Content-Type', 'application/pdf') response.header('Content-Disposition', `attachment; filename="${fileName}"`) return response.download(filePath) } catch (err) { console.error(err) return response.status(500).json({ status: 'error', message: err.message, }) } })