import axios from "axios"; import exifr from "exifr"; import i18n from "i18next"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries"; import { axiosAuthInterceptorId } from "../../utils/CleanAxios"; import client from "../../utils/GraphQLClient"; //Context: currentUserEmail, bodyshop, jobid, invoiceid //Required to prevent headers from getting set and rejected from Cloudinary. var cleanAxios = axios.create(); cleanAxios.interceptors.request.eject(axiosAuthInterceptorId); export const handleUpload = (ev, context, notification) => { logImEXEvent("document_upload", { filetype: ev.file?.type }); const { onError, onSuccess, onProgress } = ev; const { bodyshop, jobId } = context; const fileName = ev.file?.name || ev.filename; let extension = fileName.split(".").pop(); let key = `${bodyshop.id}/${jobId}/${replaceAccents(fileName).replace(/[^A-Z0-9]+/gi, "_")}-${new Date().getTime()}.${extension}`; uploadToS3(key, extension, ev.file.type, ev.file, onError, onSuccess, onProgress, context, notification).catch( (error) => { console.error("Error uploading file to S3", error); notification.error({ message: i18n.t("documents.errors.insert", { message: error.message }) }); } ); }; //Handles only 1 file at a time. export const uploadToS3 = async ( key, extension, fileType, file, onError, onSuccess, onProgress, context, notification ) => { const { bodyshop, jobId, billId, uploaded_by, callback } = context; //Get the signed url allowing us to PUT to S3. const signedURLResponse = await axios.post("/media/imgproxy/sign", { filenames: [key], bodyshopid: bodyshop.id, jobid: jobId }); if (signedURLResponse.status !== 200) { if (onError) onError(signedURLResponse.statusText); notification.error({ message: i18n.t("documents.errors.getpresignurl", { message: signedURLResponse.statusText }) }); return; } //Key should be same as we provided to maintain backwards compatibility. const { presignedUrl: preSignedUploadUrlToS3, key: s3Key } = signedURLResponse.data.signedUrls[0]; const options = { onUploadProgress: (e) => { if (onProgress) onProgress({ percent: (e.loaded / e.total) * 100 }); } }; try { const s3UploadResponse = await cleanAxios.put(preSignedUploadUrlToS3, file, options); //Insert the document with the matching key. let takenat; if (fileType.includes("image")) { try { const exif = await exifr.parse(file); takenat = exif && exif.DateTimeOriginal; } catch (error) { console.log("Unable to parse image file for EXIF Data", error.message); } } const documentInsert = await client.mutate({ mutation: INSERT_NEW_DOCUMENT, variables: { docInput: [ { ...(jobId ? { jobid: jobId } : {}), ...(billId ? { billid: billId } : {}), uploaded_by: uploaded_by, key: s3Key, type: fileType, extension: s3UploadResponse.data.format || extension, bodyshopid: bodyshop.id, size: s3UploadResponse.data.bytes || file.size, //Leftover from Cloudinary. We don't do any optimization on upload, so it will always be file.size. takenat } ] } }); if (!documentInsert.errors) { if (onSuccess) onSuccess({ uid: documentInsert.data.insert_documents.returning[0].id, name: documentInsert.data.insert_documents.returning[0].name, status: "done", key: documentInsert.data.insert_documents.returning[0].key }); notification.success({ key: "docuploadsuccess", message: i18n.t("documents.successes.insert") }); if (callback) { callback(); } } else { if (onError) onError(JSON.stringify(documentInsert.errors)); notification.error({ message: i18n.t("documents.errors.insert", { message: JSON.stringify(documentInsert.errors) }) }); return; } } catch (error) { console.log("Error uploading file to S3", error.message, error.stack); notification.error({ message: i18n.t("documents.errors.insert", { message: error.message }) }); if (onError) onError(JSON.stringify(error.message)); } }; function replaceAccents(str) { // Verifies if the String has accents and replace them if (str.search(/[\xC0-\xFF]/g) > -1) { str = str .replace(/[\xC0-\xC5]/g, "A") .replace(/[\xC6]/g, "AE") .replace(/[\xC7]/g, "C") .replace(/[\xC8-\xCB]/g, "E") .replace(/[\xCC-\xCF]/g, "I") .replace(/[\xD0]/g, "D") .replace(/[\xD1]/g, "N") .replace(/[\xD2-\xD6\xD8]/g, "O") .replace(/[\xD9-\xDC]/g, "U") .replace(/[\xDD]/g, "Y") .replace(/[\xDE]/g, "P") .replace(/[\xE0-\xE5]/g, "a") .replace(/[\xE6]/g, "ae") .replace(/[\xE7]/g, "c") .replace(/[\xE8-\xEB]/g, "e") .replace(/[\xEC-\xEF]/g, "i") .replace(/[\xF1]/g, "n") .replace(/[\xF2-\xF6\xF8]/g, "o") .replace(/[\xF9-\xFC]/g, "u") .replace(/[\xFE]/g, "p") .replace(/[\xFD\xFF]/g, "y"); } return str; }