diff --git a/client/src/components/chat-media-selector/chat-media-selector.component.jsx b/client/src/components/chat-media-selector/chat-media-selector.component.jsx index 0526fde43..b7e7d64a3 100644 --- a/client/src/components/chat-media-selector/chat-media-selector.component.jsx +++ b/client/src/components/chat-media-selector/chat-media-selector.component.jsx @@ -1,7 +1,8 @@ import { PictureFilled } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { Badge, Popover } from "antd"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -9,6 +10,7 @@ import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import AlertComponent from "../alert/alert.component"; import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component"; +import JobsDocumentImgproxyGalleryExternal from "../jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component"; import JobDocumentsLocalGalleryExternal from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; @@ -23,6 +25,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector); export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, conversation }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, { fetchPolicy: "network-only", @@ -42,6 +51,10 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c setSelectedMedia([]); }, [setSelectedMedia, conversation]); + //Knowingly taking on the technical debt of poor implementation below. Done this way to avoid an edge case where no component may be displayed. + //Cloudinary will be removed once the migration is completed. + //If Imageproxy is on, rely only on the LMS selector + //If not on, use the old methods. const content = (
{loading && } @@ -49,17 +62,37 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c {selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
{t("messaging.labels.maxtenimages")}
) : null} - {!bodyshop.uselocalmediaserver && data && ( - - )} - {bodyshop.uselocalmediaserver && open && ( - + + {Imgproxy.treatment === "on" ? ( + <> + {!bodyshop.uselocalmediaserver && ( + + )} + {bodyshop.uselocalmediaserver && open && ( + + )} + + ) : ( + <> + {!bodyshop.uselocalmediaserver && data && ( + + )} + {bodyshop.uselocalmediaserver && open && ( + + )} + )}
); diff --git a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx new file mode 100644 index 000000000..f71760e3b --- /dev/null +++ b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx @@ -0,0 +1,122 @@ +import { UploadOutlined } from "@ant-design/icons"; +import { Progress, Result, Space, Upload } from "antd"; +import { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; +import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; +import formatBytes from "../../utils/formatbytes"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; +import LockWrapperComponent from "../lock-wrapper/lock-wrapper.component"; +import { handleUpload } from "./documents-upload-imgproxy.utility.js"; + +const mapStateToProps = createStructuredSelector({ + currentUser: selectCurrentUser, + bodyshop: selectBodyshop +}); + +export function DocumentsUploadImgproxyComponent({ + children, + currentUser, + bodyshop, + jobId, + tagsArray, + billId, + callbackAfterUpload, + totalSize, + ignoreSizeLimit = false +}) { + const { t } = useTranslation(); + const [fileList, setFileList] = useState([]); + const notification = useNotification(); + + const pct = useMemo(() => { + return parseInt((totalSize / ((bodyshop && bodyshop.jobsizelimit) || 1)) * 100); + }, [bodyshop, totalSize]); + + if (pct > 100 && !ignoreSizeLimit) + return ( + + ); + + const handleDone = (uid) => { + setTimeout(() => { + setFileList((fileList) => fileList.filter((x) => x.uid !== uid)); + }, 2000); + }; + const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" }); + + return ( + { + if (f.event && f.event.percent === 100) handleDone(f.file.uid); + setFileList(f.fileList); + }} + beforeUpload={(file, fileList) => { + if (ignoreSizeLimit) return true; + const newFiles = fileList.reduce((acc, val) => acc + val.size, 0); + const shouldStopUpload = (totalSize + newFiles) / ((bodyshop && bodyshop.jobsizelimit) || 1) >= 1; + + //Check to see if old files plus newly uploaded ones will be too much. + if (shouldStopUpload) { + notification.error({ + key: "cannotuploaddocuments", + message: t("documents.labels.upload_limitexceeded_title"), + description: t("documents.labels.upload_limitexceeded") + }); + return Upload.LIST_IGNORE; + } + return true; + }} + customRequest={(ev) => + handleUpload( + ev, + { + bodyshop: bodyshop, + uploaded_by: currentUser.email, + jobId: jobId, + billId: billId, + tagsArray: tagsArray, + callback: callbackAfterUpload + }, + notification + ) + } + accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx" + // showUploadList={false} + > + {children || ( + <> +

+ +

+

+ {t("documents.labels.dragtoupload")} +

+ {!ignoreSizeLimit && ( + + + + {t("documents.labels.usage", { + percent: pct, + used: formatBytes(totalSize), + total: formatBytes(bodyshop && bodyshop.jobsizelimit) + })} + + + )} + + )} +
+ ); +} + +export default connect(mapStateToProps, null)(DocumentsUploadImgproxyComponent); diff --git a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js new file mode 100644 index 000000000..7ecbd50bb --- /dev/null +++ b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js @@ -0,0 +1,172 @@ +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; +} diff --git a/client/src/components/email-documents/email-documents.component.jsx b/client/src/components/email-documents/email-documents.component.jsx index d3c6b20da..7e17d5565 100644 --- a/client/src/components/email-documents/email-documents.component.jsx +++ b/client/src/components/email-documents/email-documents.component.jsx @@ -10,6 +10,8 @@ import AlertComponent from "../alert/alert.component"; import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component"; import JobsDocumentsLocalGalleryExternalComponent from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import JobsDocumentImgproxyGalleryExternal from "../jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -23,6 +25,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(EmailDocumentsCompon export function EmailDocumentsComponent({ emailConfig, form, selectedMediaState, bodyshop }) { const { t } = useTranslation(); + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); const [selectedMedia, setSelectedMedia] = selectedMediaState; const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, { @@ -46,17 +55,37 @@ export function EmailDocumentsComponent({ emailConfig, form, selectedMediaState, 10485760 - new Blob([form.getFieldValue("html")]).size ? (
{t("general.errors.sizelimit")}
) : null} - {!bodyshop.uselocalmediaserver && data && ( - - )} - {bodyshop.uselocalmediaserver && ( - + + {Imgproxy.treatment === "on" ? ( + <> + {!bodyshop.uselocalmediaserver && data && ( + + )} + {bodyshop.uselocalmediaserver && ( + + )} + + ) : ( + <> + {!bodyshop.uselocalmediaserver && data && ( + + )} + {bodyshop.uselocalmediaserver && ( + + )} + )} ); diff --git a/client/src/components/jobs-documents-gallery/jobs-document-gallery.download.component.jsx b/client/src/components/jobs-documents-gallery/jobs-document-gallery.download.component.jsx index f85db7c8c..cbc89ab38 100644 --- a/client/src/components/jobs-documents-gallery/jobs-document-gallery.download.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-document-gallery.download.component.jsx @@ -19,7 +19,13 @@ const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsDownloadButton); - +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ export function JobsDocumentsDownloadButton({ bodyshop, galleryImages, identifier }) { const { t } = useTranslation(); const [download, setDownload] = useState(null); diff --git a/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx b/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx index 33f2e964a..51c889729 100644 --- a/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx @@ -17,7 +17,13 @@ const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsGalleryReassign); - +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages, callback }) { const { t } = useTranslation(); const [form] = Form.useForm(); diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx index 0dcc18855..58adaccad 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx @@ -1,29 +1,34 @@ import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons"; import { Button, Card, Col, Row, Space } from "antd"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; +import Lightbox from "react-image-lightbox"; +import "react-image-lightbox/style.css"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import DocumentsUploadComponent from "../documents-upload/documents-upload.component"; import { DetermineFileType } from "../documents-upload/documents-upload.utility"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; +import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility"; import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component"; import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component"; import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component"; import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component"; -import Lightbox from "react-image-lightbox"; -import "react-image-lightbox/style.css"; -import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; - -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; -import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop }); const mapDispatchToProps = (dispatch) => ({}); - +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ function JobsDocumentsComponent({ bodyshop, data, @@ -114,6 +119,7 @@ function JobsDocumentsComponent({ ); setgalleryImages(documents); }, [data, setgalleryImages, t]); + const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" }); const hasMobileAccess = HasFeatureAccess({ bodyshop, featureName: "mobile" }); return ( @@ -137,7 +143,6 @@ function JobsDocumentsComponent({ )} - ({}); +export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsContainer); +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ +export function JobsDocumentsContainer({ + jobId, + billId, + documentsList, + billsCallback, + refetchOverride, + ignoreSizeLimit, + bodyshop +}) { + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); -export default function JobsDocumentsContainer({ jobId, billId, documentsList, billsCallback }) { const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, { variables: { jobId: jobId }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", - skip: !!billId + skip: Imgproxy.treatment === "on" || !!billId }); if (loading) return ; if (error) return ; - return ( - - ); + if (Imgproxy.treatment === "on") { + return ( + + ); + } else { + return ( + + ); + } } diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx index 5cd1fcb06..4b92e4c99 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx @@ -5,8 +5,13 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; -//Context: currentUserEmail, bodyshop, jobid, invoiceid - +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ export default function JobsDocumentsDeleteButton({ galleryImages, deletionCallback }) { const { t } = useTranslation(); const notification = useNotification(); diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.external.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.external.component.jsx index 940598052..d4340bb98 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.external.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.external.component.jsx @@ -3,6 +3,13 @@ import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility"; +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ function JobsDocumentGalleryExternal({ data, diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.selectall.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.selectall.component.jsx index 122ce6236..157e04dca 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.selectall.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.selectall.component.jsx @@ -2,6 +2,14 @@ import { Button, Space } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Imgproxy code. This code will be removed once the imgproxy migration is completed. +################################################################################################ +*/ + export default function JobsDocumentsGallerySelectAllComponent({ galleryImages, setGalleryImages }) { const { t } = useTranslation(); diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx new file mode 100644 index 000000000..6c08936dc --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.download.component.jsx @@ -0,0 +1,87 @@ +import { Button, Space } from "antd"; +import axios from "axios"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { logImEXEvent } from "../../firebase/firebase.utils"; +import cleanAxios from "../../utils/CleanAxios"; +import formatBytes from "../../utils/formatbytes"; +//import yauzl from "yauzl"; + +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); + +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ + +export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyDownloadButton); + +export function JobsDocumentsImgproxyDownloadButton({ bodyshop, galleryImages, identifier }) { + const { t } = useTranslation(); + const [download, setDownload] = useState(null); + const [loading, setLoading] = useState(false); + + const imagesToDownload = [ + ...galleryImages.images.filter((image) => image.isSelected), + ...galleryImages.other.filter((image) => image.isSelected) + ]; + + function downloadProgress(progressEvent) { + setDownload((currentDownloadState) => { + return { + downloaded: progressEvent.loaded || 0, + speed: (progressEvent.loaded || 0) - ((currentDownloadState && currentDownloadState.downloaded) || 0) + }; + }); + } + function standardMediaDownload(bufferData) { + const a = document.createElement("a"); + const url = window.URL.createObjectURL(new Blob([bufferData])); + a.href = url; + a.download = `${identifier || "documents"}.zip`; + a.click(); + } + const handleDownload = async () => { + logImEXEvent("jobs_documents_download"); + setLoading(true); + const zipUrl = await axios({ + url: "/media/imgproxy/download", + method: "POST", + data: { documentids: imagesToDownload.map((_) => _.id) } + }); + + const theDownloadedZip = await cleanAxios({ + url: zipUrl.data.url, + method: "GET", + responseType: "arraybuffer", + onDownloadProgress: downloadProgress + }); + setLoading(false); + setDownload(null); + + standardMediaDownload(theDownloadedZip.data); + }; + + return ( + <> + + + ); +} diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.reassign.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.reassign.component.jsx new file mode 100644 index 000000000..2fcd55a9f --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-document-imgproxy-gallery.reassign.component.jsx @@ -0,0 +1,134 @@ +import { useApolloClient } from "@apollo/client"; +import { Button, Form, Popover, Space } from "antd"; +import axios from "axios"; +import { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; +import { GET_DOC_SIZE_BY_JOB } from "../../graphql/documents.queries.js"; +import { selectBodyshop } from "../../redux/user/user.selectors.js"; +import JobSearchSelect from "../job-search-select/job-search-select.component.jsx"; +import { isFunction } from "lodash"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyGalleryReassign); +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ +export function JobsDocumentsImgproxyGalleryReassign({ bodyshop, galleryImages, callback }) { + const { t } = useTranslation(); + const [form] = Form.useForm(); + const notification = useNotification(); + + const selectedImages = useMemo(() => { + return [ + ...galleryImages.images.filter((image) => image.isSelected), + ...galleryImages.other.filter((image) => image.isSelected) + ]; + }, [galleryImages]); + const client = useApolloClient(); + const [open, setOpen] = useState(false); + const [loading, setLoading] = useState(false); + + const handleFinish = async ({ jobid }) => { + setLoading(true); + + //Check to see if the space remaining on the new job is sufficient. If it isn't cancel this. + const newJobData = await client.query({ + query: GET_DOC_SIZE_BY_JOB, + variables: { jobId: jobid } + }); + + const transferedDocSizeTotal = selectedImages.reduce((acc, val) => acc + val.size, 0); + + const shouldPreventTransfer = + bodyshop.jobsizelimit - newJobData.data.documents_aggregate.aggregate.sum.size < transferedDocSizeTotal; + + if (shouldPreventTransfer) { + notification.error({ + key: "cannotuploaddocuments", + message: t("documents.labels.reassign_limitexceeded_title"), + description: t("documents.labels.reassign_limitexceeded") + }); + setLoading(false); + return; + } + + const res = await axios.post("/media/imgproxy/rename", { + tojobid: jobid, + documents: selectedImages.map((i) => { + //Need to check if the current key folder is null, or another job. + const currentKeys = i.key.split("/"); + currentKeys[1] = jobid; + currentKeys.join("/"); + return { + id: i.id, + from: i.key, + to: currentKeys.join("/"), + extension: i.extension, + type: i.type + }; + }) + }); + //Add in confirmation & errors. + if (isFunction(callback)) callback(); + + if (res.errors) { + notification.error({ + message: t("documents.errors.updating", { + message: JSON.stringify(res.errors) + }) + }); + } + if (!res.mutationResult?.errors) { + notification.success({ + message: t("documents.successes.updated") + }); + } + setOpen(false); + setLoading(false); + }; + + const popContent = ( +
+
+ + + +
+ + + + +
+ ); + + return ( + + + + ); +} diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.component.jsx new file mode 100644 index 000000000..a07ed0bf1 --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.component.jsx @@ -0,0 +1,266 @@ +import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons"; +import { Button, Card, Col, Row, Space } from "antd"; +import axios from "axios"; +import i18n from "i18next"; +import { isFunction } from "lodash"; +import { useCallback, useEffect, useState } from "react"; +import { Gallery } from "react-grid-gallery"; +import { useTranslation } from "react-i18next"; +import Lightbox from "react-image-lightbox"; +import "react-image-lightbox/style.css"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import DocumentsUploadImgproxyComponent from "../documents-upload-imgproxy/documents-upload-imgproxy.component"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; +import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; +import JobsDocumentsDownloadButton from "./jobs-document-imgproxy-gallery.download.component"; +import JobsDocumentsGalleryReassign from "./jobs-document-imgproxy-gallery.reassign.component"; +import JobsDocumentsDeleteButton from "./jobs-documents-imgproxy-gallery.delete.component"; +import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-imgproxy-gallery.selectall.component"; + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop +}); +const mapDispatchToProps = (dispatch) => ({}); +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ +function JobsDocumentsImgproxyComponent({ + bodyshop, + data, + jobId, + refetch, + billId, + billsCallback, + totalSize, + downloadIdentifier, + ignoreSizeLimit +}) { + const [galleryImages, setGalleryImages] = useState({ images: [], other: [] }); + const { t } = useTranslation(); + const [modalState, setModalState] = useState({ open: false, index: 0 }); + + const fetchThumbnails = useCallback(() => { + fetchImgproxyThumbnails({ setStateCallback: setGalleryImages, jobId }); + }, [jobId, setGalleryImages]); + + useEffect(() => { + if (data) { + fetchThumbnails(); + } + }, [data, fetchThumbnails]); + + const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" }); + const hasMobileAccess = HasFeatureAccess({ bodyshop, featureName: "mobile" }); + return ( +
+ + + + + + + + {!billId && ( + + )} + + + {!hasMediaAccess && ( + + + + + + )} + + + + + + {hasMediaAccess && !hasMobileAccess && ( + + + + + + )} + + + { + setModalState({ open: true, index: index }); + // window.open( + // item.fullsize, + // "_blank", + // "toolbar=0,location=0,menubar=0" + // ); + }} + onSelect={(index, image) => { + setGalleryImages({ + ...galleryImages, + images: galleryImages.images.map((g, idx) => + index === idx ? { ...g, isSelected: !g.isSelected } : g + ) + }); + }} + /> + + + + + { + return { + backgroundImage: , + height: "100%", + width: "100%", + cursor: "pointer" + }; + }} + onClick={(index) => { + window.open(galleryImages.other[index].source, "_blank", "toolbar=0,location=0,menubar=0"); + }} + onSelect={(index) => { + setGalleryImages({ + ...galleryImages, + other: galleryImages.other.map((g, idx) => (index === idx ? { ...g, isSelected: !g.isSelected } : g)) + }); + }} + /> + + + {modalState.open && ( + { + const newWindow = window.open( + `${window.location.protocol}//${window.location.host}/edit?documentId=${ + galleryImages.images[modalState.index].id + }`, + "_blank", + "noopener,noreferrer" + ); + if (newWindow) newWindow.opener = null; + }} + /> + ]} + mainSrc={galleryImages.images[modalState.index].fullsize} + nextSrc={galleryImages.images[(modalState.index + 1) % galleryImages.images.length].fullsize} + prevSrc={ + galleryImages.images[(modalState.index + galleryImages.images.length - 1) % galleryImages.images.length] + .fullsize + } + onCloseRequest={() => setModalState({ open: false, index: 0 })} + onMovePrevRequest={() => + setModalState({ + ...modalState, + index: (modalState.index + galleryImages.images.length - 1) % galleryImages.images.length + }) + } + onMoveNextRequest={() => + setModalState({ + ...modalState, + index: (modalState.index + 1) % galleryImages.images.length + }) + } + /> + )} + +
+ ); +} + +export default connect(mapStateToProps, mapDispatchToProps)(JobsDocumentsImgproxyComponent); + +export const fetchImgproxyThumbnails = async ({ setStateCallback, jobId, imagesOnly }) => { + const result = await axios.post("/media/imgproxy/thumbnails", { jobid: jobId }); + const documents = result.data.reduce( + (acc, value) => { + if (value.type.startsWith("image")) { + acc.images.push({ + src: value.thumbnailUrl, + fullsize: value.originalUrl, + height: 225, + width: 225, + isSelected: false, + key: value.key, + extension: value.extension, + id: value.id, + type: value.type, + size: value.size, + tags: [{ value: value.type, title: value.type }] + }); + } else { + const fileName = value.key.split("/").pop(); + acc.other.push({ + source: value.originalUrlViaProxyPath, + src: value.thumbnailUrl, + fullsize: value.presignedGetUrl, + tags: [ + { + value: fileName, + title: fileName + }, + + { value: value.type, title: value.type }, + ...(value.bill + ? [ + { + value: value.bill.vendor.name, + title: i18n.t("vendors.fields.name") + }, + { value: value.bill.date, title: i18n.t("bills.fields.date") }, + { + value: value.bill.invoice_number, + title: i18n.t("bills.fields.invoice_number") + } + ] + : []) + ], + height: 225, + width: 225, + isSelected: false, + extension: value.extension, + key: value.key, + id: value.id, + type: value.type, + size: value.size + }); + } + return acc; + }, + { images: [], other: [] } + ); + + setStateCallback(imagesOnly ? documents.images : documents); +}; diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.container.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.container.jsx new file mode 100644 index 000000000..9191b262c --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.container.jsx @@ -0,0 +1,37 @@ +import { useQuery } from "@apollo/client"; +import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries"; +import AlertComponent from "../alert/alert.component"; +import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import JobDocuments from "./jobs-documents-imgproxy-gallery.component"; + +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ + +export default function JobsDocumentsImgproxyContainer({ jobId, billId, documentsList, billsCallback }) { + const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, { + variables: { jobId: jobId }, + fetchPolicy: "network-only", + nextFetchPolicy: "network-only", + skip: !!billId + }); + + if (loading) return ; + if (error) return ; + + return ( + + ); +} diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.delete.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.delete.component.jsx new file mode 100644 index 000000000..4701bca67 --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.delete.component.jsx @@ -0,0 +1,75 @@ +import { QuestionCircleOutlined } from "@ant-design/icons"; +import { Button, Popconfirm } from "antd"; +import axios from "axios"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { logImEXEvent } from "../../firebase/firebase.utils.js"; +import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; +import { isFunction } from "lodash"; + +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ + +export default function JobsDocumentsImgproxyDeleteButton({ galleryImages, deletionCallback }) { + const { t } = useTranslation(); + const notification = useNotification(); + + const imagesToDelete = [ + ...galleryImages.images.filter((image) => image.isSelected), + ...galleryImages.other.filter((image) => image.isSelected) + ]; + const [loading, setLoading] = useState(false); + + const handleDelete = async () => { + logImEXEvent("job_documents_delete", { count: imagesToDelete.length }); + try { + setLoading(true); + const res = await axios.post("/media/imgproxy/delete", { + ids: imagesToDelete.map((d) => d.id) + }); + + if (res.data.error) { + notification.error({ + message: t("documents.errors.deleting", { + error: JSON.stringify(res.data.error.response.errors) + }) + }); + } else { + notification.success({ + key: "docdeletedsuccesfully", + message: t("documents.successes.delete") + }); + + if (isFunction(deletionCallback)) deletionCallback(); + } + } catch (error) { + notification.error({ + message: t("documents.errors.deleting", { + error: error.message + }) + }); + } + setLoading(false); + }; + + return ( + } + onConfirm={handleDelete} + title={t("documents.labels.confirmdelete")} + okText={t("general.actions.delete")} + okButtonProps={{ danger: true }} + cancelText={t("general.actions.cancel")} + > + + + ); +} diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx new file mode 100644 index 000000000..a970b01b7 --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.external.component.jsx @@ -0,0 +1,33 @@ +import { useEffect } from "react"; +import { Gallery } from "react-grid-gallery"; +import { fetchImgproxyThumbnails } from "./jobs-documents-imgproxy-gallery.component"; + +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ + +function JobsDocumentImgproxyGalleryExternal({ jobId, externalMediaState }) { + const [galleryImages, setgalleryImages] = externalMediaState; + + useEffect(() => { + if (jobId) fetchImgproxyThumbnails({ setStateCallback: setgalleryImages, jobId, imagesOnly: true }); + }, [jobId, setgalleryImages]); + + return ( +
+ { + setgalleryImages(galleryImages.map((g, idx) => (index === idx ? { ...g, isSelected: !g.isSelected } : g))); + }} + /> +
+ ); +} + +export default JobsDocumentImgproxyGalleryExternal; diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.selectall.component.jsx b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.selectall.component.jsx new file mode 100644 index 000000000..4d4b0b32e --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.selectall.component.jsx @@ -0,0 +1,63 @@ +import { Button, Space } from "antd"; +import { useTranslation } from "react-i18next"; + +/* +################################################################################################ + Developer Note: + Known Technical Debt Item + Modifications to this code requires complementary changes to the Cloudinary code. Cloudinary code will be removed upon completed migration. +################################################################################################ +*/ + +export default function JobsDocumentsImgproxyGallerySelectAllComponent({ galleryImages, setGalleryImages }) { + const { t } = useTranslation(); + + const handleSelectAll = () => { + setGalleryImages({ + ...galleryImages, + other: galleryImages.other.map((i) => { + return { ...i, isSelected: true }; + }), + images: galleryImages.images.map((i) => { + return { ...i, isSelected: true }; + }) + }); + }; + const handleSelectAllImages = () => { + setGalleryImages({ + ...galleryImages, + + images: galleryImages.images.map((i) => { + return { ...i, isSelected: true }; + }) + }); + }; + const handleSelectAllDocuments = () => { + setGalleryImages({ + ...galleryImages, + other: galleryImages.other.map((i) => { + return { ...i, isSelected: true }; + }) + }); + }; + const handleDeselectAll = () => { + setGalleryImages({ + ...galleryImages, + other: galleryImages.other.map((i) => { + return { ...i, isSelected: false }; + }), + images: galleryImages.images.map((i) => { + return { ...i, isSelected: false }; + }) + }); + }; + + return ( + + + + + + + ); +} diff --git a/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.styles.scss b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.styles.scss new file mode 100644 index 000000000..6df357ffc --- /dev/null +++ b/client/src/components/jobs-documents-imgproxy-gallery/jobs-documents-imgproxy-gallery.styles.scss @@ -0,0 +1,10 @@ +/* you can make up upload button and sample style by using stylesheets */ +.ant-upload-select-picture-card i { + font-size: 32px; + color: #999; +} + +.ant-upload-select-picture-card .ant-upload-text { + margin-top: 8px; + color: #666; +} diff --git a/client/src/components/parts-order-modal/parts-order-modal.component.jsx b/client/src/components/parts-order-modal/parts-order-modal.component.jsx index 4fc3a5789..c3ade9187 100644 --- a/client/src/components/parts-order-modal/parts-order-modal.component.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal.component.jsx @@ -1,16 +1,17 @@ import { DeleteFilled, DownOutlined, WarningFilled } from "@ant-design/icons"; import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { Checkbox, Divider, Dropdown, Form, Input, InputNumber, Radio, Select, Space, Tag } from "antd"; +import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import PartsOrderModalPriceChange from "./parts-order-modal-price-change.component"; +import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -32,7 +33,7 @@ export function PartsOrderModalComponent({ bodyshop, vendorList, sendTypeState, }); const { t } = useTranslation(); - const handleClick = ({ item }) => { + const handleClick = ({ item, key, keyPath }) => { form.setFieldsValue({ comments: item.props.value }); }; @@ -97,18 +98,17 @@ export function PartsOrderModalComponent({ bodyshop, vendorList, sendTypeState, )} - {!isReturn && ( - - - {t("parts_orders.labels.parts_order")} - {t("parts_orders.labels.sublet_order")} - - - )} + + + + {t("parts_orders.labels.parts_order")} + {t("parts_orders.labels.sublet_order")} + + {t("parts_orders.labels.inthisorder")} - {(fields, { remove, move }) => { + {(fields, { add, remove, move }) => { return (
{fields.map((field, index) => ( diff --git a/client/src/pages/temporary-docs/temporary-docs.component.jsx b/client/src/pages/temporary-docs/temporary-docs.component.jsx index 092011e65..26948d7a4 100644 --- a/client/src/pages/temporary-docs/temporary-docs.component.jsx +++ b/client/src/pages/temporary-docs/temporary-docs.component.jsx @@ -1,14 +1,15 @@ import { useQuery } from "@apollo/client"; import React from "react"; import AlertComponent from "../../components/alert/alert.component"; -import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs-documents-gallery.component"; +import JobsDocumentsContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container"; +import { selectBodyshop } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -19,10 +20,18 @@ const mapDispatchToProps = (dispatch) => ({ export default connect(mapStateToProps, mapDispatchToProps)(TemporaryDocsComponent); export function TemporaryDocsComponent({ bodyshop }) { + const { + treatments: { Imgproxy } + } = useSplitTreatments({ + attributes: {}, + names: ["Imgproxy"], + splitKey: bodyshop && bodyshop.imexshopid + }); + const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, { fetchPolicy: "network-only", nextFetchPolicy: "network-only", - skip: bodyshop.uselocalmediaserver + skip: Imgproxy.treatment === "on" }); if (loading) return ; @@ -32,12 +41,14 @@ export function TemporaryDocsComponent({ bodyshop }) { return ; } return ( - + <> + + ); } diff --git a/package-lock.json b/package-lock.json index 76296e97b..66794a554 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,12 @@ "@aws-sdk/client-secrets-manager": "^3.772.0", "@aws-sdk/client-ses": "^3.772.0", "@aws-sdk/credential-provider-node": "^3.772.0", + "@aws-sdk/lib-storage": "^3.743.0", + "@aws-sdk/s3-request-presigner": "^3.731.1", "@opensearch-project/opensearch": "^2.13.0", "@socket.io/admin-ui": "^0.5.1", "@socket.io/redis-adapter": "^8.3.0", + "archiver": "^7.0.1", "aws4": "^1.13.2", "axios": "^1.8.4", "bee-queue": "^1.7.1", @@ -285,24 +288,24 @@ } }, "node_modules/@aws-sdk/client-cloudwatch-logs": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.772.0.tgz", - "integrity": "sha512-lbslpGJ7Yn9xHDFOGFnZQjmwVJb2S7TzYHqCnbSVu+vHzYmAWanVu+y8zc0sqcPb4ltS+KGtGdHYlmUJq2EqKg==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.774.0.tgz", + "integrity": "sha512-P+qEZJMd4kZVbTs2YRvBHOYlRd0U7bgpbEhRoYRS6vOWymRinQaxj/KCaw1cmdBILj502CUieFGj6DEYAbiNzQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.772.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-node": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/eventstream-serde-browser": "^4.0.1", @@ -353,24 +356,24 @@ } }, "node_modules/@aws-sdk/client-elasticache": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-elasticache/-/client-elasticache-3.772.0.tgz", - "integrity": "sha512-wAkPFxHIJ4VYGXrjALPkN7exzTuXJbaWgLmVqsrgj4ZjmMY9M4l+mHH0IL85ZvIgJbNsV68aWyvckHkUVD1f4g==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-elasticache/-/client-elasticache-3.774.0.tgz", + "integrity": "sha512-uBWSLt6p5tSZCdoizuNyytGiZBFgtUAnXH7GnYH1tVzPeQoQb5GI5of5cDw9lFIIR6DWpYjA7JZ7EEmNvvD7rw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.772.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-node": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", @@ -404,32 +407,32 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.772.0.tgz", - "integrity": "sha512-HQXlQIyyLp47h1/Hdjr36yK8/gsAAFX2vRzgDJhSRaz0vWZlWX07AJdYfrxapLUXfVU6DbBu3rwi2UGqM7ixqQ==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.774.0.tgz", + "integrity": "sha512-HQ5Xi01r/Pv2IpzPTf5acrN0g/yJQalheDCYbBSA8VU31zoYiT/w5kLFWYJHQDB2hRozh2DB/VC/VDmntkWXxA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.772.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-node": "3.774.0", "@aws-sdk/middleware-bucket-endpoint": "3.734.0", "@aws-sdk/middleware-expect-continue": "3.734.0", - "@aws-sdk/middleware-flexible-checksums": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-flexible-checksums": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-location-constraint": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-sdk-s3": "3.758.0", + "@aws-sdk/middleware-sdk-s3": "3.774.0", "@aws-sdk/middleware-ssec": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/signature-v4-multi-region": "3.758.0", + "@aws-sdk/signature-v4-multi-region": "3.774.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@aws-sdk/xml-builder": "3.734.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", @@ -471,24 +474,24 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.772.0.tgz", - "integrity": "sha512-hY/MWUbjeHW9fQpRQbm6AXHDwL7++/cZYu+b7vxT4gH/6g4ivLiO/kgMI0F6Xeu3v6/fMumTUuNuLDfWfE7vpQ==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.774.0.tgz", + "integrity": "sha512-AoVpmzLY/PW/0H6hcc/Rn/uisDTFE7WX/oWZ8qU2gYMuH3z2VUAIhHQZq3mNpXH4ZlWPH+sDA8cG1tCc8dgVqA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.772.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-node": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", @@ -536,24 +539,24 @@ } }, "node_modules/@aws-sdk/client-ses": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.772.0.tgz", - "integrity": "sha512-/KSwaR/yI+RDXOKL2iAx2gpADCuKUn/ZmkflMUMPwgUFGrS7WK/3q5jra9qZJJOrSHPTkYP+/OEQp1mEWXF3rg==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.774.0.tgz", + "integrity": "sha512-EWZRm9nV7xFQYjFmk259gf1Mp40MuJ1KWHbY5y6EYt3dkuYFJnqAdBETB3tzbC0EmIkVSR1Uzxc72jRS8klvHg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.772.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-node": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", @@ -587,23 +590,23 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.772.0.tgz", - "integrity": "sha512-sDdxepi74+cL6gXJJ2yw3UNSI7GBvoGTwZqFyPoNAzcURvaYwo8dBr7G4jS9GDanjTlO3CGVAf2VMcpqEvmoEw==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.774.0.tgz", + "integrity": "sha512-bN+wd2gpTq+DNJ/fZdam/mX6K3TcVdZBIvxaVtg+imep6xAuRukdFhsoG0cDzk96+WHPCOhkyi+6lFljCof43Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", @@ -636,9 +639,9 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", - "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.774.0.tgz", + "integrity": "sha512-JDkAAlPyGWMX42L4Cv8mxybwHTOoFweNbNrOc5oQJhFxZAe1zkW4uLTEfr79vYhnXCFbThCyPpBotmo3U2vULA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.734.0", @@ -658,12 +661,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.758.0.tgz", - "integrity": "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.774.0.tgz", + "integrity": "sha512-FkSDBi9Ly0bmzyrMDeqQq1lGsFMrrd/bIB3c9VD4Llh0sPLxB/DU31+VTPTuQ0pBPz4sX5Vay6tLy43DStzcFQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", @@ -674,12 +677,12 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.758.0.tgz", - "integrity": "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.774.0.tgz", + "integrity": "sha512-iurWGQColf52HpHeHCQs/LnSjZ0Ufq3VtSQx/6QdZwIhmgbbqvGMAaBJg41SQjWhpqdufE96HzcaCJw/lnCefQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", @@ -695,18 +698,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.772.0.tgz", - "integrity": "sha512-T1Ec9Q25zl5c/eZUPHZsiq8vgBeWBjHM7WM5xtZszZRPqqhQGnmFlomz1r9rwhW8RFB5k8HRaD/SLKo6jtYl/A==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.774.0.tgz", + "integrity": "sha512-+AsJOX9pGsnGPAC8wQw7LAO8ZfXzjXTjJxSP1fvg04PX7OBk4zwhVaryH6pu5raan+9cVbfEO1Z7EEMdkweGQA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-env": "3.758.0", - "@aws-sdk/credential-provider-http": "3.758.0", - "@aws-sdk/credential-provider-process": "3.758.0", - "@aws-sdk/credential-provider-sso": "3.772.0", - "@aws-sdk/credential-provider-web-identity": "3.772.0", - "@aws-sdk/nested-clients": "3.772.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/credential-provider-env": "3.774.0", + "@aws-sdk/credential-provider-http": "3.774.0", + "@aws-sdk/credential-provider-process": "3.774.0", + "@aws-sdk/credential-provider-sso": "3.774.0", + "@aws-sdk/credential-provider-web-identity": "3.774.0", + "@aws-sdk/nested-clients": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", @@ -719,17 +722,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.772.0.tgz", - "integrity": "sha512-0IdVfjBO88Mtekq/KaScYSIEPIeR+ABRvBOWyj/c/qQ2KJyI0GRlSAzpANfxDLHVPn3yEHuZd9nRL6sOmOMI0A==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.774.0.tgz", + "integrity": "sha512-/t+TNhHNW6BNyf7Lgv6I0NUfFk6/dz4+6dUjopRxpDVJtp1YvNza0Zhl25ffRkqX4CKmuXyJYusDbbObcsncUA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.758.0", - "@aws-sdk/credential-provider-http": "3.758.0", - "@aws-sdk/credential-provider-ini": "3.772.0", - "@aws-sdk/credential-provider-process": "3.758.0", - "@aws-sdk/credential-provider-sso": "3.772.0", - "@aws-sdk/credential-provider-web-identity": "3.772.0", + "@aws-sdk/credential-provider-env": "3.774.0", + "@aws-sdk/credential-provider-http": "3.774.0", + "@aws-sdk/credential-provider-ini": "3.774.0", + "@aws-sdk/credential-provider-process": "3.774.0", + "@aws-sdk/credential-provider-sso": "3.774.0", + "@aws-sdk/credential-provider-web-identity": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", @@ -742,12 +745,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.758.0.tgz", - "integrity": "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.774.0.tgz", + "integrity": "sha512-lycBRY1NeWa46LefN258m1MRVUPQgvf6TPA6ZYajyq6/dCr6BPeuUoUAyrzePTPlxV/M25YXNiyORHjjwlK0ug==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", @@ -759,14 +762,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.772.0.tgz", - "integrity": "sha512-yR3Y5RAVPa4ogojcBOpZUx6XyRVAkynIJCjd0avdlxW1hhnzSr5/pzoiJ6u21UCbkxlJJTDZE3jfFe7tt+HA4w==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.774.0.tgz", + "integrity": "sha512-j7vbGCWF6dVpd9qiT0PQGzY4NKf8KUa86sSoosGGbtu0dV9T/Y0s/fvPZ0F8ZyuPIKUMJaBpIJYZ/ECZRfT2mg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.772.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/token-providers": "3.772.0", + "@aws-sdk/client-sso": "3.774.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/token-providers": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", @@ -778,13 +781,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.772.0.tgz", - "integrity": "sha512-yHAT5Y2y0fnecSuWRUn8NMunKfDqFYhnOpGq8UyCEcwz9aXzibU0hqRIEm51qpR81hqo0GMFDH0EOmegZ/iW5w==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.774.0.tgz", + "integrity": "sha512-kuE5Hdqm9xXdrYBWCU6l2aM3W3HBtZrIBgyf0y41LulJHwld1nvIySus/lILdzbipmUAv9FI07B8TF5y7p/aFA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/nested-clients": "3.772.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/nested-clients": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", @@ -794,6 +797,37 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.743.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.743.0.tgz", + "integrity": "sha512-Rf/5sljlEJRVtB5C4UjLCOIcK2ODZet9rQsRtsn0bIc2byURbpOdqIGvfEcKWPayoXCS4dC/5bdjhL1zhZ0TMg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/smithy-client": "^4.1.2", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.743.0" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { "version": "3.734.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.734.0.tgz", @@ -828,15 +862,15 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.758.0.tgz", - "integrity": "sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.774.0.tgz", + "integrity": "sha512-S0vs+U7sEZkRRnjf05KCbEHDduyxGgNPq+ZeaiyWbs5yTZ8wzSYrSzMAKcbCqAseNVYQbpGMXDh8tnzx6H/ihw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.0.1", @@ -852,9 +886,9 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", - "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.774.0.tgz", + "integrity": "sha512-7QHA0ZyEBVfyJqmIc0FW4MUtPdrWhDsHQudsvBCHFS+mqP5fhpU/o4e5RQ+0M7tQqDE65+8MrZRniRa+Txz3xA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.734.0", @@ -910,12 +944,12 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.758.0.tgz", - "integrity": "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.774.0.tgz", + "integrity": "sha512-iMEaOj+S8LZfg7fZaSfXQ8YDtEfOSBiQUllyxzaVhSYlM7IeNDbpBdCWnRi34VrI1J1AuryMdX/foU9JNSTLXg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.1.5", @@ -949,12 +983,12 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.758.0.tgz", - "integrity": "sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.774.0.tgz", + "integrity": "sha512-SVDeBV6DESgc9zex1Wk5XYbUqRI1tmJYQor47uKqD18r6UaCpvzVOBP4x8l/6hteAYxsWER6ZZmsjBQkenEuFQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", + "@aws-sdk/core": "3.774.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@smithy/core": "^3.1.5", @@ -967,23 +1001,23 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.772.0.tgz", - "integrity": "sha512-gNJbBxR5YlEumsCS9EWWEASXEnysL0aDnr9MNPX1ip/g1xOqRHmytgV/+t8RFZFTKg0OprbWTq5Ich3MqsEuCQ==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.774.0.tgz", + "integrity": "sha512-00+UiYvxiZaDFVzn87kPpiZ/GiEWNaTNzC82C+bIyXt1M9AnAR6PAnnvMErTFwyG+Un6n2ai/I81wvJ1ftFmeQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/core": "3.774.0", + "@aws-sdk/middleware-host-header": "3.774.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.772.0", - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/util-user-agent-node": "3.774.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", @@ -1032,13 +1066,109 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.758.0.tgz", - "integrity": "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==", + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.731.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.731.1.tgz", + "integrity": "sha512-GdG0pXkcTgBpenouB834FoCHyLaivV2rGQn7OEQBiT8SBaTxSackZ6tGlJQAlzZQkiQfE/NePUJU7DczJZZvrg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.758.0", + "@aws-sdk/signature-v4-multi-region": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-format-url": "3.731.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/core": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.731.0.tgz", + "integrity": "sha512-ithBN1VWASkvAIlozJmenqDvNnFddr/SZXAs58+jCnBHgy3tXLHABZGVNCjetZkHRqNdXEO1kirnoxaFeXMeDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.731.0.tgz", + "integrity": "sha512-J9aKyQaVoec5eWTSDfO4h2sKHNP0wTzN15LFcHnkD+e/d0rdmOi7BTkkbJrIaynma9WShIasmrtM3HNi9GiiTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.731.0.tgz", + "integrity": "sha512-1r/b4Os15dR+BCVRRLVQJMF7Krq6xX6IKHxN43kuvODYWz8Nv3XDlaSpeRpAzyJuzW/fTp4JgE+z0+gmJfdEeA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.731.0.tgz", + "integrity": "sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.774.0.tgz", + "integrity": "sha512-vQwATjZfl5vxfO+BDUH7nUnhfKoIJMnBmOxmUh4N10PWlz3WWwkT/YtH79nVpr+y1eM6GQUSGuNa4Reda6SaFA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", @@ -1050,12 +1180,12 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.772.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.772.0.tgz", - "integrity": "sha512-d1Waa1vyebuokcAWYlkZdtFlciIgob7B39vPRmtxMObbGumJKiOy/qCe2/FB/72h1Ej9Ih32lwvbxUjORQWN4g==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.774.0.tgz", + "integrity": "sha512-DDERwCduWFFXj7gx3qvnaB8GlnCUpQ8ZA03qI4QFokWu3EyHNK+hjp3nN5Dg81fI0Z82LRe30Q2uDsLBwNCZDg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "3.772.0", + "@aws-sdk/nested-clients": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", @@ -1106,6 +1236,34 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.731.0.tgz", + "integrity": "sha512-wZHObjnYmiz8wFlUQ4/5dHsT7k0at+GvZM02LgvshcRJLnFjYdrzjelMKuNynd/NNK3gLgTsFTGuIgPpz9r4rA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/querystring-builder": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.731.0.tgz", + "integrity": "sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.693.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", @@ -1131,12 +1289,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.758.0.tgz", - "integrity": "sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==", + "version": "3.774.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.774.0.tgz", + "integrity": "sha512-kFmnK4sf5Wco8mkzO2PszqDXEwtQ5H896tUxqWDQhk67NtOLsHYfg98ymOBWWudth2POaldiIx6KFXtg0DvLLQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/middleware-user-agent": "3.774.0", "@aws-sdk/types": "3.734.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", @@ -1183,14 +1341,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", - "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -1220,13 +1378,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", - "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.8" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1248,32 +1406,32 @@ } }, "node_modules/@babel/template": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", - "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", - "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.8", - "@babel/parser": "^7.26.8", - "@babel/template": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1292,9 +1450,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", - "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "license": "MIT", "dependencies": { @@ -2260,6 +2418,16 @@ "node": ">=14" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -2384,12 +2552,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", - "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.2.tgz", + "integrity": "sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2422,15 +2590,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", - "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.0.tgz", + "integrity": "sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", + "@smithy/util-middleware": "^4.0.2", "tslib": "^2.6.2" }, "engines": { @@ -2438,17 +2606,17 @@ } }, "node_modules/@smithy/core": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.5.tgz", - "integrity": "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.2.0.tgz", + "integrity": "sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.0.2", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -2457,15 +2625,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", - "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.2.tgz", + "integrity": "sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", "tslib": "^2.6.2" }, "engines": { @@ -2543,14 +2711,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", - "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.2.tgz", + "integrity": "sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, @@ -2656,18 +2824,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.6.tgz", - "integrity": "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.0.tgz", + "integrity": "sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-middleware": "^4.0.1", + "@smithy/core": "^3.2.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-middleware": "^4.0.2", "tslib": "^2.6.2" }, "engines": { @@ -2675,18 +2843,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.7.tgz", - "integrity": "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.0.tgz", + "integrity": "sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/service-error-classification": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/service-error-classification": "^4.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -2708,12 +2876,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", - "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.3.tgz", + "integrity": "sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2721,12 +2889,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", - "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.2.tgz", + "integrity": "sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2734,14 +2902,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", - "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.2.tgz", + "integrity": "sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2749,15 +2917,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.3.tgz", - "integrity": "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.4.tgz", + "integrity": "sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2765,12 +2933,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", - "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.2.tgz", + "integrity": "sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2778,12 +2946,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", - "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.0.tgz", + "integrity": "sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2791,12 +2959,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", - "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.2.tgz", + "integrity": "sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, @@ -2805,12 +2973,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", - "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.2.tgz", + "integrity": "sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2818,24 +2986,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", - "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.2.tgz", + "integrity": "sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0" + "@smithy/types": "^4.2.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", - "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.2.tgz", + "integrity": "sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2862,17 +3030,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.6.tgz", - "integrity": "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.0.tgz", + "integrity": "sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", + "@smithy/core": "^3.2.0", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2880,9 +3048,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", - "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2892,13 +3060,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", - "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.2.tgz", + "integrity": "sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/querystring-parser": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2969,14 +3137,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.7.tgz", - "integrity": "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.8.tgz", + "integrity": "sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -2985,17 +3153,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.7.tgz", - "integrity": "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.8.tgz", + "integrity": "sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.0.1", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "@smithy/config-resolver": "^4.1.0", + "@smithy/credential-provider-imds": "^4.0.2", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3029,12 +3197,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", - "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.2.tgz", + "integrity": "sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3042,13 +3210,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", - "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.2.tgz", + "integrity": "sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/service-error-classification": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3056,14 +3224,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.1.2.tgz", - "integrity": "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.0.tgz", + "integrity": "sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/types": "^4.2.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", @@ -3461,7 +3629,6 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", - "optional": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -3492,9 +3659,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3596,6 +3763,155 @@ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "license": "ISC" }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", @@ -3788,6 +4104,15 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -3849,23 +4174,36 @@ } }, "node_modules/axios-ntlm": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.3.tgz", - "integrity": "sha512-CS6WE8chZpEDKxv4IFwr5zcG7InMC6Ek0aj2n2tHauBh+8KiYVC4qMn3N2arjR5tnyILQuTGlI0mc83hgWxS4Q==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.4.tgz", + "integrity": "sha512-kpCRdzMfL8gi0Z0o96P3QPAK4XuC8iciGgxGXe+PeQ4oyjI2LZN8WSOKbu0Y9Jo3T/A7pB81n6jYVPIpglEuRA==", "license": "MIT", "dependencies": { - "axios": "^1.7.9", + "axios": "^1.8.4", "des.js": "^1.1.0", "dev-null": "^0.1.1", "js-md4": "^0.3.2" } }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "license": "Apache-2.0", + "optional": true + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -4071,6 +4409,39 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4451,6 +4822,38 @@ "node": ">=18" } }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -4642,6 +5045,47 @@ "node": ">=10.0.0" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/crisp-status-reporter": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crisp-status-reporter/-/crisp-status-reporter-1.2.2.tgz", @@ -5755,11 +6199,19 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", - "optional": true, "engines": { "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -6600,6 +7052,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/graphql": { "version": "16.10.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", @@ -6866,6 +7324,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -7041,11 +7519,12 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -7377,12 +7856,12 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -7754,6 +8233,48 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/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==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7977,9 +8498,9 @@ } }, "node_modules/luxon": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", - "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.0.tgz", + "integrity": "sha512-WE7p0p7W1xji9qxkLYsvcIxZyfP48GuFrWIBQZIsbjCyf65dG1rv4n83HcOyEyhvzxJCrUoObCRNFgRNIQ5KNA==", "license": "MIT", "engines": { "node": ">=12" @@ -8405,6 +8926,15 @@ "node": ">=6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/notepack.io": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", @@ -8914,6 +9444,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -9131,6 +9670,36 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/recursive-diff": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/recursive-diff/-/recursive-diff-1.0.9.tgz", @@ -10149,6 +10718,16 @@ "node": ">= 0.8" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, "node_modules/stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -10174,6 +10753,19 @@ "node": ">=10.0.0" } }, + "node_modules/streamx": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -10426,6 +11018,17 @@ "node": ">=10" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -10528,6 +11131,15 @@ "rimraf": "bin.js" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -11499,6 +12111,36 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } } } } diff --git a/package.json b/package.json index 4de2904c0..05a8eb327 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,12 @@ "@aws-sdk/client-secrets-manager": "^3.772.0", "@aws-sdk/client-ses": "^3.772.0", "@aws-sdk/credential-provider-node": "^3.772.0", + "@aws-sdk/lib-storage": "^3.743.0", + "@aws-sdk/s3-request-presigner": "^3.731.1", "@opensearch-project/opensearch": "^2.13.0", "@socket.io/admin-ui": "^0.5.1", "@socket.io/redis-adapter": "^8.3.0", + "archiver": "^7.0.1", "aws4": "^1.13.2", "axios": "^1.8.4", "bee-queue": "^1.7.1", diff --git a/server.js b/server.js index 9a9e65ceb..52f5c6e9e 100644 --- a/server.js +++ b/server.js @@ -370,7 +370,7 @@ const loadQueues = async ({ pubClient, logger, redisHelpers, ioRedis }) => { */ const main = async () => { const app = express(); - const port = process.env.PORT || 5000; + const port = process.env.PORT || 4000; const server = http.createServer(app); diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 3df3e810c..b0c72aa7d 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2771,3 +2771,61 @@ exports.GET_BODYSHOP_BY_ID = ` } } `; + +exports.GET_DOCUMENTS_BY_JOB = ` + query GET_DOCUMENTS_BY_JOB($jobId: uuid!) { + jobs_by_pk(id: $jobId) { + id + ro_number + } + documents_aggregate(where: { jobid: { _eq: $jobId } }) { + aggregate { + sum { + size + } + } + } + documents(order_by: { takenat: desc }, where: { jobid: { _eq: $jobId } }) { + id + name + key + type + size + takenat + extension + bill { + id + invoice_number + date + vendor { + id + name + } + } + } + }`; + +exports.QUERY_TEMPORARY_DOCS = ` query QUERY_TEMPORARY_DOCS { + documents(where: { jobid: { _is_null: true } }, order_by: { takenat: desc }) { + id + name + key + type + extension + size + takenat + } + }`; + +exports.GET_DOCUMENTS_BY_IDS = ` + query GET_DOCUMENTS_BY_IDS($documentIds: [uuid!]!) { + documents(where: {id: {_in: $documentIds}}, order_by: {takenat: desc}) { + id + name + key + type + extension + size + takenat + } +}`; diff --git a/server/media/imgproxy-media.js b/server/media/imgproxy-media.js new file mode 100644 index 000000000..fdb313984 --- /dev/null +++ b/server/media/imgproxy-media.js @@ -0,0 +1,348 @@ +const path = require("path"); +require("dotenv").config({ + path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) +}); +const logger = require("../utils/logger"); +const { + S3Client, + PutObjectCommand, + GetObjectCommand, + CopyObjectCommand, + DeleteObjectCommand +} = require("@aws-sdk/client-s3"); +const { Upload } = require("@aws-sdk/lib-storage"); + +const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); +const crypto = require("crypto"); +const { InstanceRegion } = require("../utils/instanceMgr"); +const { + GET_DOCUMENTS_BY_JOB, + QUERY_TEMPORARY_DOCS, + GET_DOCUMENTS_BY_IDS, + DELETE_MEDIA_DOCUMENTS +} = require("../graphql-client/queries"); +const archiver = require("archiver"); +const stream = require("node:stream"); + +const imgproxyBaseUrl = process.env.IMGPROXY_BASE_URL; // `https://u4gzpp5wm437dnm75qa42tvza40fguqr.lambda-url.ca-central-1.on.aws` //Direct Lambda function access to bypass CDN. +const imgproxyKey = process.env.IMGPROXY_KEY; +const imgproxySalt = process.env.IMGPROXY_SALT; +const imgproxyDestinationBucket = process.env.IMGPROXY_DESTINATION_BUCKET; + +//Generate a signed upload link for the S3 bucket. +//All uploads must be going to the same shop and jobid. +exports.generateSignedUploadUrls = async (req, res) => { + const { filenames, bodyshopid, jobid } = req.body; + try { + logger.log("imgproxy-upload-start", "DEBUG", req.user?.email, jobid, { filenames, bodyshopid, jobid }); + + const signedUrls = []; + for (const filename of filenames) { + const key = filename; + const client = new S3Client({ region: InstanceRegion() }); + const command = new PutObjectCommand({ + Bucket: imgproxyDestinationBucket, + Key: key, + StorageClass: "INTELLIGENT_TIERING" + }); + const presignedUrl = await getSignedUrl(client, command, { expiresIn: 360 }); + signedUrls.push({ filename, presignedUrl, key }); + } + + logger.log("imgproxy-upload-success", "DEBUG", req.user?.email, jobid, { signedUrls }); + res.json({ + success: true, + signedUrls + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message, + stack: error.stack + }); + logger.log("imgproxy-upload-error", "ERROR", req.user?.email, jobid, { + message: error.message, + stack: error.stack + }); + } +}; + +exports.getThumbnailUrls = async (req, res) => { + const { jobid, billid } = req.body; + + try { + logger.log("imgproxy-thumbnails", "DEBUG", req.user?.email, jobid, { billid, jobid }); + + //Delayed as the key structure may change slightly from what it is currently and will require evaluating mobile components. + const client = req.userGraphQLClient; + //If there's no jobid and no billid, we're in temporary documents. + const data = await (jobid + ? client.request(GET_DOCUMENTS_BY_JOB, { jobId: jobid }) + : client.request(QUERY_TEMPORARY_DOCS)); + + const thumbResizeParams = `rs:fill:250:250:1/g:ce`; + const s3client = new S3Client({ region: InstanceRegion() }); + const proxiedUrls = []; + + for (const document of data.documents) { + //Format to follow: + /////< base 64 URL encoded to image path> + + //When working with documents from Cloudinary, the URL does not include the extension. + let key; + if (/\.[^/.]+$/.test(document.key)) { + key = document.key; + } else { + key = `${document.key}.${document.extension.toLowerCase()}`; + } + // Build the S3 path to the object. + const fullS3Path = `s3://${imgproxyDestinationBucket}/${key}`; + const base64UrlEncodedKeyString = base64UrlEncode(fullS3Path); + //Thumbnail Generation Block + const thumbProxyPath = `${thumbResizeParams}/${base64UrlEncodedKeyString}`; + const thumbHmacSalt = createHmacSha256(`${imgproxySalt}/${thumbProxyPath}`); + + //Full Size URL block + + const fullSizeProxyPath = `${base64UrlEncodedKeyString}`; + const fullSizeHmacSalt = createHmacSha256(`${imgproxySalt}/${fullSizeProxyPath}`); + + const s3Props = {}; + if (!document.type.startsWith("image")) { + //If not a picture, we need to get a signed download link to the file using S3 (or cloudfront preferably) + const command = new GetObjectCommand({ + Bucket: imgproxyDestinationBucket, + Key: key + }); + const presignedGetUrl = await getSignedUrl(s3client, command, { expiresIn: 360 }); + s3Props.presignedGetUrl = presignedGetUrl; + + const originalProxyPath = `raw:1/${base64UrlEncodedKeyString}`; + const originalHmacSalt = createHmacSha256(`${imgproxySalt}/${originalProxyPath}`); + s3Props.originalUrlViaProxyPath = `${imgproxyBaseUrl}/${originalHmacSalt}/${originalProxyPath}`; + } + + proxiedUrls.push({ + originalUrl: `${imgproxyBaseUrl}/${fullSizeHmacSalt}/${fullSizeProxyPath}`, + thumbnailUrl: `${imgproxyBaseUrl}/${thumbHmacSalt}/${thumbProxyPath}`, + fullS3Path, + base64UrlEncodedKeyString, + thumbProxyPath, + ...s3Props, + ...document + }); + } + + res.json(proxiedUrls); + //Iterate over them, build the link based on the media type, and return the array. + } catch (error) { + logger.log("imgproxy-thumbnails-error", "ERROR", req.user?.email, jobid, { + jobid, + billid, + message: error.message, + stack: error.stack + }); + res.status(400).json({ message: error.message, stack: error.stack }); + } +}; + +exports.getBillFiles = async (req, res) => { + //Givena bill ID, get the documents associated to it. +}; + +exports.downloadFiles = async (req, res) => { + //Given a series of document IDs or keys, generate a file (or a link) to download all images in bulk + const { jobid, billid, documentids } = req.body; + try { + logger.log("imgproxy-download", "DEBUG", req.user?.email, jobid, { billid, jobid, documentids }); + + //Delayed as the key structure may change slightly from what it is currently and will require evaluating mobile components. + const client = req.userGraphQLClient; + //Query for the keys of the document IDs + const data = await client.request(GET_DOCUMENTS_BY_IDS, { documentIds: documentids }); + //Using the Keys, get all of the S3 links, zip them, and send back to the client. + const s3client = new S3Client({ region: InstanceRegion() }); + const archiveStream = archiver("zip"); + archiveStream.on("error", (error) => { + console.error("Archival encountered an error:", error); + throw new Error(error); + }); + const passthrough = new stream.PassThrough(); + + archiveStream.pipe(passthrough); + for (const key of data.documents.map((d) => d.key)) { + const response = await s3client.send(new GetObjectCommand({ Bucket: imgproxyDestinationBucket, Key: key })); + // :: `response.Body` is a Buffer + console.log(path.basename(key)); + archiveStream.append(response.Body, { name: path.basename(key) }); + } + + archiveStream.finalize(); + + const archiveKey = `archives/${jobid}/archive-${new Date().toISOString()}.zip`; + + const parallelUploads3 = new Upload({ + client: s3client, + queueSize: 4, // optional concurrency configuration + leavePartsOnError: false, // optional manually handle dropped parts + params: { Bucket: imgproxyDestinationBucket, Key: archiveKey, Body: passthrough } + }); + + parallelUploads3.on("httpUploadProgress", (progress) => { + console.log(progress); + }); + + const uploadResult = await parallelUploads3.done(); + //Generate the presigned URL to download it. + const presignedUrl = await getSignedUrl( + s3client, + new GetObjectCommand({ Bucket: imgproxyDestinationBucket, Key: archiveKey }), + { expiresIn: 360 } + ); + + res.json({ success: true, url: presignedUrl }); + //Iterate over them, build the link based on the media type, and return the array. + } catch (error) { + logger.log("imgproxy-thumbnails-error", "ERROR", req.user?.email, jobid, { + jobid, + billid, + message: error.message, + stack: error.stack + }); + res.status(400).json({ message: error.message, stack: error.stack }); + } +}; + +exports.deleteFiles = async (req, res) => { + //Mark a file for deletion in s3. Lifecycle deletion will actually delete the copy in the future. + //Mark as deleted from the documents section of the database. + const { ids } = req.body; + try { + logger.log("imgproxy-delete-files", "DEBUG", req.user.email, null, { ids }); + const client = req.userGraphQLClient; + + //Do this to make sure that they are only deleting things that they have access to + const data = await client.request(GET_DOCUMENTS_BY_IDS, { documentIds: ids }); + + const s3client = new S3Client({ region: InstanceRegion() }); + + const deleteTransactions = []; + data.documents.forEach((document) => { + deleteTransactions.push( + (async () => { + try { + // Delete the original object + const deleteResult = await s3client.send( + new DeleteObjectCommand({ + Bucket: imgproxyDestinationBucket, + Key: document.key + }) + ); + + return document; + } catch (error) { + return { document, error: error, bucket: imgproxyDestinationBucket }; + } + })() + ); + }); + + const result = await Promise.all(deleteTransactions); + const errors = result.filter((d) => d.error); + + //Delete only the succesful deletes. + const deleteMutationResult = await client.request(DELETE_MEDIA_DOCUMENTS, { + ids: result.filter((t) => !t.error).map((d) => d.id) + }); + + res.json({ errors, deleteMutationResult }); + } catch (error) { + logger.log("imgproxy-delete-files-error", "ERROR", req.user.email, null, { + ids, + message: error.message, + stack: error.stack + }); + res.status(400).json({ message: error.message, stack: error.stack }); + } +}; + +exports.moveFiles = async (req, res) => { + const { documents, tojobid } = req.body; + try { + logger.log("imgproxy-move-files", "DEBUG", req.user.email, null, { documents, tojobid }); + const s3client = new S3Client({ region: InstanceRegion() }); + + const moveTransactions = []; + documents.forEach((document) => { + moveTransactions.push( + (async () => { + try { + // Copy the object to the new key + const copyresult = await s3client.send( + new CopyObjectCommand({ + Bucket: imgproxyDestinationBucket, + CopySource: `${imgproxyDestinationBucket}/${document.from}`, + Key: document.to, + StorageClass: "INTELLIGENT_TIERING" + }) + ); + + // Delete the original object + const deleteResult = await s3client.send( + new DeleteObjectCommand({ + Bucket: imgproxyDestinationBucket, + Key: document.from + }) + ); + + return document; + } catch (error) { + return { id: document.id, from: document.from, error: error, bucket: imgproxyDestinationBucket }; + } + })() + ); + }); + + const result = await Promise.all(moveTransactions); + const errors = result.filter((d) => d.error); + + let mutations = ""; + result + .filter((d) => !d.error) + .forEach((d, idx) => { + //Create mutation text + mutations = + mutations + + ` + update_doc${idx}:update_documents_by_pk(pk_columns: { id: "${d.id}" }, _set: {key: "${d.to}", jobid: "${tojobid}"}){ + id + } + `; + }); + + const client = req.userGraphQLClient; + if (mutations !== "") { + const mutationResult = await client.request(`mutation { + ${mutations} + }`); + res.json({ errors, mutationResult }); + } else { + res.json({ errors: "No images were succesfully moved on remote server. " }); + } + } catch (error) { + logger.log("imgproxy-move-files-error", "ERROR", req.user.email, null, { + documents, + tojobid, + message: error.message, + stack: error.stack + }); + res.status(400).json({ message: error.message, stack: error.stack }); + } +}; + +function base64UrlEncode(str) { + return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} +function createHmacSha256(data) { + return crypto.createHmac("sha256", imgproxyKey).update(data).digest("base64url"); +} diff --git a/server/media/media.js b/server/media/media.js index 6706d3392..06b1c9bb8 100644 --- a/server/media/media.js +++ b/server/media/media.js @@ -11,12 +11,14 @@ require("dotenv").config({ var cloudinary = require("cloudinary").v2; cloudinary.config(process.env.CLOUDINARY_URL); -exports.createSignedUploadURL = (req, res) => { +const createSignedUploadURL = (req, res) => { logger.log("media-signed-upload", "DEBUG", req.user.email, null, null); res.send(cloudinary.utils.api_sign_request(req.body, process.env.CLOUDINARY_API_SECRET)); }; -exports.downloadFiles = (req, res) => { +exports.createSignedUploadURL = createSignedUploadURL; + +const downloadFiles = (req, res) => { const { ids } = req.body; logger.log("media-bulk-download", "DEBUG", req.user.email, ids, null); @@ -26,8 +28,9 @@ exports.downloadFiles = (req, res) => { }); res.send(url); }; +exports.downloadFiles = downloadFiles; -exports.deleteFiles = async (req, res) => { +const deleteFiles = async (req, res) => { const { ids } = req.body; const types = _.groupBy(ids, (x) => DetermineFileType(x.type)); @@ -88,7 +91,9 @@ exports.deleteFiles = async (req, res) => { } }; -exports.renameKeys = async (req, res) => { +exports.deleteFiles = deleteFiles; + +const renameKeys = async (req, res) => { const { documents, tojobid } = req.body; logger.log("media-bulk-rename", "DEBUG", req.user.email, null, documents); @@ -146,6 +151,7 @@ exports.renameKeys = async (req, res) => { res.json({ errors: "No images were succesfully moved on remote server. " }); } }; +exports.renameKeys = renameKeys; //Also needs to be updated in upload utility and mobile app. function DetermineFileType(filetype) { diff --git a/server/routes/mediaRoutes.js b/server/routes/mediaRoutes.js index 699579bb9..59ee836ec 100644 --- a/server/routes/mediaRoutes.js +++ b/server/routes/mediaRoutes.js @@ -1,13 +1,28 @@ const express = require("express"); const router = express.Router(); const { createSignedUploadURL, downloadFiles, renameKeys, deleteFiles } = require("../media/media"); +const { + generateSignedUploadUrls: createSignedUploadURLImgproxy, + getThumbnailUrls: getThumbnailUrlsImgproxy, + downloadFiles: downloadFilesImgproxy, + moveFiles: moveFilesImgproxy, + deleteFiles: deleteFilesImgproxy +} = require("../media/imgproxy-media"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); +const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware"); router.use(validateFirebaseIdTokenMiddleware); +router.use(withUserGraphQLClientMiddleware); router.post("/sign", createSignedUploadURL); router.post("/download", downloadFiles); router.post("/rename", renameKeys); router.post("/delete", deleteFiles); +router.post("/imgproxy/sign", createSignedUploadURLImgproxy); +router.post("/imgproxy/thumbnails", getThumbnailUrlsImgproxy); +router.post("/imgproxy/download", downloadFilesImgproxy); +router.post("/imgproxy/rename", moveFilesImgproxy); +router.post("/imgproxy/delete", deleteFilesImgproxy); + module.exports = router;