import express from "express" export const xlsxTemplateRoute = express.Router() import { mimeToExtension, templateOption } from "./report-template" import { ExcelTemplate } from "xlsx-template-next" import fs from "fs" import { LibreOfficeFileConverter } from "libreoffice-file-converter" const TEMPLATE_FOLDER_NAME = "templates/xlsx" /** * xlsxTemplate Uses xlsx-template-next to convert input data and template to output buffer. * You have to handle exception throw by function * template keep in folder templates * @param {Buffer|String} t template in buffer format or path to file * @param {templateOption} tdata Template Information in JSON format * @param {String} outputMediaType output extension * @param {Number} tab tab page of spread sheet , default = 1 * @return {Promise} output buffer after apply template. */ export async function xlsxTemplateX( t: Buffer | String, tdata: templateOption, outputMediaType: string = "xlsx", tab: number = 1 ): Promise { try { const templateBuff = Buffer.isBuffer(t) ? t : await fs.promises.readFile(String(t)) const template = new ExcelTemplate() await template.load(templateBuff) await template.process(tab, tdata.data) const buffer = (await template.build({ type: "uint8array" })) as Uint8Array if (outputMediaType === "xlsx") return buffer const libreOfficeFileConverter = new LibreOfficeFileConverter({ childProcessOptions: { timeout: 60 * 1000, }, }) //const lbuffer = await libreOfficeFileConverter.convertBuffer(Buffer.from(buffer as Uint8Array), outputMediaType) const lbuffer = await libreOfficeFileConverter.convert({ buffer: Buffer.from(buffer), format: outputMediaType, input: "buffer", output: "buffer", }) return lbuffer } catch (e) { throw e } } /** javascript-obfuscator:disable * @swagger * /api/v1/report-template/xlsx: * get: * summary: แสดงรายการ xlsx template * tags: [report-template] * responses: * 200: * description: array of template * content: * applicatin/json: * schema: * type: array * items: * type: string * example: ["hello"] * 500: * description: Server error */ xlsxTemplateRoute.get("/", async function (req, res) { try { const fileList = await fs.promises.readdir(`./${TEMPLATE_FOLDER_NAME}`) const templateList = fileList.map((f) => f.split(".xlsx")[0]) res.send(templateList) } catch (ex) { res.statusCode = 500 res.statusMessage = "Internal Server Error during get xlsx template list" res.end(res.statusMessage) console.error("Error during get template list: ", ex) } }) /** javascript-obfuscator:disable * @swagger * /api/v1/report-template/xlsx: * post: * summary: สร้างเอกสารจาก xlsx template แล้วส่งกลับมาเป็น xlsx pdf odt , ค่า template เป็นชื่อของ template ที่ใช้งาน, reportName เป็นชื่อไฟล์ที่ต้องการ * tags: [report-template] * parameters: * - name: folder * in: query * description: ชื่อโฟลเดอร์ * required: false * schema: * type: string * example: test * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/templateOption' * example: * template: hello * reportName: xlsx-report * data: {"docNo": "๑๒๓๔๕","me": "กระผม","prefix": "นาย","name": "สรวิชญ์","surname": "พลสิทธิ์","position": "Chief Technology Officer","org": {"type": "บริษัท","name": "เฟรปเป้ที","url": "https://frappet.com"},"employees": [{"id":1,"name": "ภาวิชญ์","surname": "พลสิทธิ์","score":80},{"id":2,"name": "วิชญาภา","surname": "พลสิทธิ์","score":50},{"id":3,"name": "ฐิตาภา","surname": "พลสิทธิ์","score":90},{"id":4,"name": "สรวิชญ์ พลสิทธิ์","surname": "พลสิทธิ์","score":99}]} * responses: * 201: * description: เอกสารถูกสร้างขึ้น created. * content: * application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: * schema: * type: string * format: binary * application/pdf: * schema: * type: string * format: binary * application/vnd.ms-excel: * schema: * type: string * format: binary * application/vnd.oasis.opendocument.spreadsheet: * schema: * type: string * format: binary * image/png: * schema: * type: string * format: binary * image/jpeg: * schema: * type: string * format: binary * 500: * description: Server error * */ xlsxTemplateRoute.post("/", async function (req, res) { try { if (!req.headers["content-type"] || !req.headers["accept"]) throw new Error("Require header content-type, accept") let inputType = mimeToExtension(req.headers["content-type"]) let outputMediaType = mimeToExtension(req.headers["accept"]) let template = null // Save the converted file to disk if (req.query["folder"]) { template = await fs.promises.readFile( `./${TEMPLATE_FOLDER_NAME}/${req.query["folder"]}/${req.body.template}.xlsx` ) } else { template = await fs.promises.readFile( `./${TEMPLATE_FOLDER_NAME}/${req.body.template}.xlsx` ) } let buffer = await xlsxTemplateX(template, req.body, outputMediaType) res.statusCode = 201 res.setHeader("Content-Type", req.headers["accept"]) res.setHeader( "Content-Disposition", `attachment;filename=${req.body.reportName}.${outputMediaType}` ) res.setHeader("Content-Length", buffer.length) res.end(buffer) } catch (ex) { if (ex instanceof SyntaxError) { res.statusCode = 400 res.statusMessage = ex.message res.end(res.statusMessage) console.error("report-template/xlsx: ", ex) } else { res.statusCode = 500 res.statusMessage = "Internal Server Error during POST report-template/xlsx" res.end(res.statusMessage) console.error("report-template/xlsx: ", ex) } } }) /** javascript-obfuscator:disable * @swagger * /api/v1/report-template/xlsx/upload: * post: * summary: อัพไฟล์ xlsx * tags: [report-template] * parameters: * - name: report_name * in: query * description: ชื่อไฟล์ * required: true * schema: * type: string * example: report * - name: folder * in: query * description: ชื่อโฟลเดอร์ * required: false * schema: * type: string * example: test * requestBody: * required: true * content: * application/octet-stream: * schema: * type: string * format: binary * responses: * 201: * description: file was converted. * content: * application/octet-stream: * schema: * type: string * format: binary * 400: * description: Invalid format * 500: * description: Server error * */ xlsxTemplateRoute.post("/upload", async function (req, res) { try { if ( !req.headers["content-type"] || !req.headers["accept"] || !req.query["report_name"] || req.headers["content-type"] !== "application/octet-stream" ) { res.statusCode = 400 res.statusMessage = "Require header: content-type(application/octet-stream) accept" res.end(res.statusMessage) console.log( req.headers["content-type"], req.headers["accept"], req.query["report_name"] ) return } // Determine the output media type and report name from headers let outputMediaType = mimeToExtension(req.headers["accept"]) let reportName = req.query["report_name"] console.log("convert output: " + outputMediaType) // Save the converted file to disk if (req.query["folder"]) { // Ensure the template folder exists await fs.promises.mkdir(`TEMPLATE_FOLDER_NAME/${req.query["folder"]}`, { recursive: true, }) await fs.promises.writeFile( `./${TEMPLATE_FOLDER_NAME}/${req.query["folder"]}/${reportName}.xlsx`, req.body ) } else { // Ensure the template folder exists await fs.promises.mkdir(TEMPLATE_FOLDER_NAME, { recursive: true }) await fs.promises.writeFile( `./${TEMPLATE_FOLDER_NAME}/${reportName}.xlsx`, req.body ) } // Send a response to the client res.statusCode = 201 res.json({ message: "File converted and saved successfully" }) } catch (ex) { res.statusCode = 500 res.statusMessage = "Internal Server Error" res.end(res.statusMessage) console.error(`Error during convert with soffice:`, ex) } }) /** javascript-obfuscator:disable * @swagger * /api/v1/report-template/xlsx/download: * post: * summary: โหลดไฟล์ xlsx * tags: [report-template] * parameters: * - name: folder * in: query * description: ชื่อโฟลเดอร์ * required: false * schema: * type: string * example: test * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/templateOption' * example: * template: xlsx-report * responses: * 201: * description: เอกสารถูกสร้างขึ้น * content: * application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: * schema: * type: string * format: binary * 500: * description: Server error * */ xlsxTemplateRoute.post("/download", async function (req, res) { try { if ( !req.headers["content-type"] || !req.headers["accept"] || !req.body.template ) throw new Error("Require header content-type, accept") let inputType = mimeToExtension(req.headers["content-type"]) let outputMediaType = mimeToExtension(req.headers["accept"]) console.log("content-type: ", inputType) console.log("accept: ", outputMediaType) let buffer = null if (req.query["folder"]) { buffer = await fs.promises.readFile( `./${TEMPLATE_FOLDER_NAME}/${req.query["folder"]}/${req.body.template}.xlsx` ) } else { buffer = await fs.promises.readFile( `./${TEMPLATE_FOLDER_NAME}/${req.body.template}.xlsx` ) } res.statusCode = 201 res.setHeader("Content-Type", req.headers["accept"]) res.setHeader( "Content-Disposition", `attachment;filename=${req.body.template}.${outputMediaType}` ) res.setHeader("Content-Length", buffer.length) res.end(buffer) } catch (ex) { res.statusCode = 500 res.statusMessage = "Internal Server Error during get xlsx template list" res.end(res.statusMessage) console.error("Error during apply template: ", ex) } })