// Notes: At the moment we take in RO Number, and ShopID. This is not very good considering the RO number can often be null, need // to ask if it is possible that we just send the Job ID itself, this way we don't need to really care about the bodyshop, and we // don't risk getting a null const axios = require("axios"); const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3"); const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); const { GET_JOB_BY_RO_NUMBER_AND_SHOP_ID, INSERT_NEW_DOCUMENT } = require("../../graphql-client/queries"); const { InstanceRegion } = require("../../utils/instanceMgr"); const moment = require("moment/moment"); const client = require("../../graphql-client/graphql-client").client; const S3_BUCKET = process.env?.IMGPROXY_DESTINATION_BUCKET; /** * @description VSSTA integration route * @type {string[]} */ const requiredParams = [ "shop_id", "ro_nbr", "pdf_download_link", "company_api_key", "scan_type", "scan_time", "technician", "year", "make", "model" ]; const vsstaIntegrationRoute = async (req, res) => { const { logger } = req; if (!S3_BUCKET) { logger.log("vssta-integration-missing-bucket", "error", "api", "vssta"); return res.status(500).json({ error: "Improper configuration" }); } try { const missingParams = requiredParams.filter((param) => !req.body[param]); if (missingParams.length > 0) { logger.log(`vssta-integration-missing-param`, "error", "api", "vssta", { params: missingParams }); return res.status(400).json({ error: "Missing required parameters", missingParams }); } // technician, year, make, model, is also available. const { shop_id, ro_nbr, pdf_download_link, scan_type, scan_time, company_api_key } = req.body; // 1. Get the job record by ro_number and shop_id const jobResult = await client.request(GET_JOB_BY_RO_NUMBER_AND_SHOP_ID, { roNumber: ro_nbr, shopId: shop_id }); if (!jobResult.jobs || jobResult.jobs.length === 0) { logger.log(`vssta-integration-missing-ro`, "error", "api", "vssta"); return res.status(404).json({ error: "Job not found" }); } const job = jobResult.jobs[0]; // 2. Download the base64-encoded PDF string from the provided link const pdfResponse = await axios.get(pdf_download_link, { responseType: "text", // Expect base64 string headers: { "auth-token": company_api_key } }); // 3. Decode the base64 string to a PDF buffer const base64String = pdfResponse.data.replace(/^data:application\/pdf;base64,/, ""); const pdfBuffer = Buffer.from(base64String, "base64"); // 4. Generate key for S3 const timestamp = moment(scan_time).tz(job.bodyshop.timezone).format("YYYYMMDD-HHmmss"); const fileName = `${timestamp}_VSSTA_${scan_type}`; const s3Key = `${job.shopid}/${job.id}/${fileName.replace(/[^A-Z0-9]+/gi, "_")}.pdf`; // 5. Generate presigned URL for S3 upload const s3Client = new S3Client({ region: InstanceRegion() }); const putCommand = new PutObjectCommand({ Bucket: S3_BUCKET, Key: s3Key, ContentType: "application/pdf", StorageClass: "INTELLIGENT_TIERING" }); const presignedUrl = await getSignedUrl(s3Client, putCommand, { expiresIn: 360 }); // 6. Upload the decoded PDF to S3 await axios.put(presignedUrl, pdfBuffer, { headers: { "Content-Type": "application/pdf" } }); // 7. Create document record in database const documentMeta = { jobid: job.id, uploaded_by: "VSSTA Integration", name: fileName, key: s3Key, type: "application/pdf", extension: "pdf", bodyshopid: job.shopid, size: pdfBuffer.length, takenat: scan_time }; const documentInsert = await client.request(INSERT_NEW_DOCUMENT, { docInput: [documentMeta] }); if (!documentInsert.insert_documents?.returning?.length) { logger.log(`vssta-integration-failed-to-create-document-record`, "error", "api", "vssta", { params: missingParams }); return res.status(500).json({ error: "Failed to create document record" }); } return res.status(200).json({ message: "VSSTA integration successful", documentId: documentInsert.insert_documents.returning[0].id }); } catch (error) { logger.log(`vssta-integration-general`, "error", "api", "vssta", { error: error?.message, stack: error?.stack }); return res.status(500).json({ error: error.message }); } }; module.exports = vsstaIntegrationRoute;