diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..fcadb2cf9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/.platform/hooks/predeploy/00-install-fonts.sh b/.platform/hooks/predeploy/00-install-fonts.sh
new file mode 100644
index 000000000..6f740d0c7
--- /dev/null
+++ b/.platform/hooks/predeploy/00-install-fonts.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Install required packages
+dnf install -y fontconfig freetype
+
+# Move to the /tmp directory for temporary download and extraction
+cd /tmp
+
+# Download the Montserrat font zip file
+wget https://images.imex.online/fonts/montserrat.zip -O montserrat.zip
+
+# Unzip the downloaded font file
+unzip montserrat.zip -d montserrat
+
+# Move the font files to the system fonts directory
+mv montserrat/montserrat/*.ttf /usr/share/fonts
+
+# Rebuild the font cache
+fc-cache -fv
+
+# Clean up
+rm -rf /tmp/montserrat /tmp/montserrat.zip
+
+echo "Montserrat fonts installed and cached successfully."
diff --git a/.vscode/settings.json b/.vscode/settings.json
index dd6a1330b..74b58f715 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,5 +8,35 @@
"pattern": "**/IMEX.xml",
"systemId": "logs/IMEX.xsd"
}
+ ],
+ "cSpell.words": [
+ "antd",
+ "appointmentconfirmation",
+ "appt",
+ "autohouse",
+ "autohouseid",
+ "billlines",
+ "bodyshop",
+ "bodyshopid",
+ "bodyshops",
+ "CIECA",
+ "claimscorp",
+ "claimscorpid",
+ "Dinero",
+ "driveable",
+ "IMEX",
+ "imexshopid",
+ "jobid",
+ "joblines",
+ "Kaizen",
+ "labhrs",
+ "larhrs",
+ "mixdata",
+ "ownr",
+ "promanager",
+ "shopname",
+ "smartscheduling",
+ "timetickets",
+ "touchtime"
]
}
diff --git a/client/src/components/dashboard-components/job-lifecycle/job-lifecycle-dashboard.component.jsx b/client/src/components/dashboard-components/job-lifecycle/job-lifecycle-dashboard.component.jsx
index 38d939c89..5f52d62c6 100644
--- a/client/src/components/dashboard-components/job-lifecycle/job-lifecycle-dashboard.component.jsx
+++ b/client/src/components/dashboard-components/job-lifecycle/job-lifecycle-dashboard.component.jsx
@@ -1,10 +1,10 @@
import { Card, Table, Tag } from "antd";
-import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
-import { useTranslation } from "react-i18next";
-import React, { useEffect, useState } from "react";
-import dayjs from "../../../utils/day";
-import DashboardRefreshRequired from "../refresh-required.component";
import axios from "axios";
+import React, { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import dayjs from "../../../utils/day";
+import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
+import DashboardRefreshRequired from "../refresh-required.component";
const fortyFiveDaysAgo = () => dayjs().subtract(45, "day").toLocaleString();
@@ -46,6 +46,11 @@ export default function JobLifecycleDashboardComponent({ data, bodyshop, ...card
dataIndex: "humanReadable",
key: "humanReadable"
},
+ {
+ title: t("job_lifecycle.columns.average_human_readable"),
+ dataIndex: "averageHumanReadable",
+ key: "averageHumanReadable"
+ },
{
title: t("job_lifecycle.columns.status_count"),
key: "statusCount",
diff --git a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx
index c9bbc0dd2..649e02f9a 100644
--- a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx
+++ b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx
@@ -1,4 +1,4 @@
-import { DatePicker } from "antd";
+import { DatePicker, Space, TimePicker } from "antd";
import PropTypes from "prop-types";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -20,6 +20,7 @@ const DateTimePicker = ({
onlyFuture,
onlyToday,
isDateOnly = false,
+ isSeparatedTime = false,
bodyshop,
...restProps
}) => {
@@ -87,24 +88,56 @@ const DateTimePicker = ({
return (
-
+ {isSeparatedTime && (
+
+
+ {value && (
+ {
+ handleChange(value);
+ onBlur();
+ }}
+ placeholder={t("general.labels.time")}
+ {...restProps}
+ />
+ )}
+
+ )}
+ {!isSeparatedTime && (
+
+ )}
);
};
@@ -116,7 +149,8 @@ DateTimePicker.propTypes = {
id: PropTypes.string,
onlyFuture: PropTypes.bool,
onlyToday: PropTypes.bool,
- isDateOnly: PropTypes.bool
+ isDateOnly: PropTypes.bool,
+ isSeparatedTime: PropTypes.bool
};
export default connect(mapStateToProps, null)(DateTimePicker);
diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx
index 984455466..070798452 100644
--- a/client/src/components/job-detail-lines/job-lines.component.jsx
+++ b/client/src/components/job-detail-lines/job-lines.component.jsx
@@ -118,8 +118,7 @@ export function JobLinesComponent({
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {})
}
}),
- sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
- ellipsis: true
+ sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
},
{
title: t("joblines.fields.oem_partno"),
diff --git a/client/src/components/job-line-note-popup/job-line-note-popup.component.jsx b/client/src/components/job-line-note-popup/job-line-note-popup.component.jsx
index 11d943354..2adb3bf72 100644
--- a/client/src/components/job-line-note-popup/job-line-note-popup.component.jsx
+++ b/client/src/components/job-line-note-popup/job-line-note-popup.component.jsx
@@ -45,7 +45,8 @@ export default function JobLineNotePopup({ jobline, disabled }) {
if (editing)
return (
- : null}
value={note}
diff --git a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx
index e07c54606..668452b9a 100644
--- a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx
+++ b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx
@@ -1,10 +1,10 @@
+import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Form, Input, InputNumber, Modal, Select, Switch } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import InputCurrency from "../form-items-formatted/currency-form-item.component";
-import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
-import { useSplitTreatments } from "@splitsoftware/splitio-react";
+import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -61,7 +61,7 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
]}
name="line_desc"
>
-
+
diff --git a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx
index 1fee1a6b5..f36269c83 100644
--- a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx
+++ b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
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 FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
@@ -12,7 +13,6 @@ import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
import FormRow from "../layout-form-row/layout-form-row.component";
-import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -185,6 +185,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
+
+
+
diff --git a/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx b/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx
index ea942e5d2..abc01afbd 100644
--- a/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx
+++ b/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx
@@ -1,6 +1,5 @@
import { Button, Col, Form, Input, Row, Select, Space, Switch, Typography } from "antd";
import axios from "axios";
-import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -8,13 +7,14 @@ import { createStructuredSelector } from "reselect";
import { calculateScheduleLoad } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateFormatter } from "../../utils/DateFormatter";
+import dayjs from "../../utils/day";
+import InstanceRenderManager from "../../utils/instanceRenderMgr";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import EmailInput from "../form-items-formatted/email-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
import "./schedule-job-modal.scss";
-import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -84,7 +84,7 @@ export function ScheduleJobModalComponent({
}
]}
>
-
+
{
const socketRef = useRef(null);
const [clientId, setClientId] = useState(null);
@@ -31,6 +32,14 @@ const useSocket = (bodyshop) => {
socketRef.current = socketInstance;
const handleBodyshopMessage = (message) => {
+ if (!message || !message?.type) return;
+
+ switch (message.type) {
+ case "alert-update":
+ store.dispatch(addAlerts(message.payload));
+ break;
+ }
+
if (!import.meta.env.DEV) return;
console.log(`Received message for bodyshop ${bodyshop.id}:`, message);
};
@@ -39,22 +48,22 @@ const useSocket = (bodyshop) => {
console.log("Socket connected:", socketInstance.id);
socketInstance.emit("join-bodyshop-room", bodyshop.id);
setClientId(socketInstance.id);
- store.dispatch(setWssStatus("connected"))
+ store.dispatch(setWssStatus("connected"));
};
const handleReconnect = (attempt) => {
console.log(`Socket reconnected after ${attempt} attempts`);
- store.dispatch(setWssStatus("connected"))
+ store.dispatch(setWssStatus("connected"));
};
const handleConnectionError = (err) => {
console.error("Socket connection error:", err);
- store.dispatch(setWssStatus("error"))
+ store.dispatch(setWssStatus("error"));
};
const handleDisconnect = () => {
console.log("Socket disconnected");
- store.dispatch(setWssStatus("disconnected"))
+ store.dispatch(setWssStatus("disconnected"));
};
socketInstance.on("connect", handleConnect);
diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js
index a71d08d10..51b44a1b2 100644
--- a/client/src/graphql/jobs.queries.js
+++ b/client/src/graphql/jobs.queries.js
@@ -692,6 +692,7 @@ export const GET_JOB_BY_PK = gql`
tax_str_rt
tax_sub_rt
tax_tow_rt
+ tlos_ind
towin
towing_payable
unit_number
diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx
index 46d66016f..6dfd8af6e 100644
--- a/client/src/pages/manage/manage.page.component.jsx
+++ b/client/src/pages/manage/manage.page.component.jsx
@@ -1,4 +1,4 @@
-import { FloatButton, Layout, Spin } from "antd";
+import { FloatButton, Layout, notification, Spin } from "antd";
// import preval from "preval.macro";
import React, { lazy, Suspense, useContext, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -21,11 +21,12 @@ import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-st
import { requestForToken } from "../../firebase/firebase.utils";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
-
import UpdateAlert from "../../components/update-alert/update-alert.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
import "./manage.page.styles.scss";
import WssStatusDisplayComponent from "../../components/wss-status-display/wss-status-display.component.jsx";
+import { selectAlerts } from "../../redux/application/application.selectors.js";
+import { addAlerts } from "../../redux/application/application.actions.js";
const JobsPage = lazy(() => import("../jobs/jobs.page"));
@@ -104,16 +105,80 @@ const { Content, Footer } = Layout;
const mapStateToProps = createStructuredSelector({
conflict: selectInstanceConflict,
- bodyshop: selectBodyshop
+ bodyshop: selectBodyshop,
+ alerts: selectAlerts
});
-const mapDispatchToProps = (dispatch) => ({});
+const ALERT_FILE_URL = InstanceRenderManager({
+ imex: "https://images.imex.online/alerts/alerts-imex.json",
+ rome: "https://images.imex.online/alerts/alerts-rome.json"
+});
-export function Manage({ conflict, bodyshop }) {
+const mapDispatchToProps = (dispatch) => ({
+ setAlerts: (alerts) => dispatch(addAlerts(alerts))
+});
+
+export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
const { t } = useTranslation();
const [chatVisible] = useState(false);
const { socket, clientId } = useContext(SocketContext);
+ // State to track displayed alerts
+ const [displayedAlertIds, setDisplayedAlertIds] = useState([]);
+
+ // Fetch displayed alerts from localStorage on mount
+ useEffect(() => {
+ const displayedAlerts = JSON.parse(localStorage.getItem("displayedAlerts") || "[]");
+ setDisplayedAlertIds(displayedAlerts);
+ }, []);
+
+ // Fetch alerts from the JSON file and dispatch to Redux store
+ useEffect(() => {
+ const fetchAlerts = async () => {
+ try {
+ const response = await fetch(ALERT_FILE_URL);
+ const fetchedAlerts = await response.json();
+ setAlerts(fetchedAlerts);
+ } catch (error) {
+ console.error("Error fetching alerts:", error);
+ }
+ };
+
+ fetchAlerts();
+ }, []);
+
+ // Use useEffect to watch for new alerts
+ useEffect(() => {
+ if (alerts && Object.keys(alerts).length > 0) {
+ // Convert the alerts object into an array
+ const alertArray = Object.values(alerts);
+
+ // Filter out alerts that have already been dismissed
+ const newAlerts = alertArray.filter((alert) => !displayedAlertIds.includes(alert.id));
+
+ newAlerts.forEach((alert) => {
+ // Display the notification
+ notification.open({
+ key: "notification-alerts-" + alert.id,
+ message: alert.message,
+ description: alert.description,
+ type: alert.type || "info",
+ duration: 0,
+ placement: "bottomRight",
+ closable: true,
+ onClose: () => {
+ // When the notification is closed, update displayed alerts state and localStorage
+ setDisplayedAlertIds((prevIds) => {
+ const updatedIds = [...prevIds, alert.id];
+ localStorage.setItem("displayedAlerts", JSON.stringify(updatedIds));
+ return updatedIds;
+ });
+ }
+ });
+ });
+ }
+ }, [alerts, displayedAlertIds]);
+
useEffect(() => {
const widgetId = InstanceRenderManager({
imex: "IABVNO4scRKY11XBQkNr",
diff --git a/client/src/redux/application/application.actions.js b/client/src/redux/application/application.actions.js
index c8246022b..4d362c6d8 100644
--- a/client/src/redux/application/application.actions.js
+++ b/client/src/redux/application/application.actions.js
@@ -67,6 +67,12 @@ export const setUpdateAvailable = (isUpdateAvailable) => ({
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
payload: isUpdateAvailable
});
+
+export const addAlerts = (alerts) => ({
+ type: ApplicationActionTypes.ADD_ALERTS,
+ payload: alerts
+});
+
export const setWssStatus = (status) => ({
type: ApplicationActionTypes.SET_WSS_STATUS,
payload: status
diff --git a/client/src/redux/application/application.reducer.js b/client/src/redux/application/application.reducer.js
index 21878e52a..6d5421e27 100644
--- a/client/src/redux/application/application.reducer.js
+++ b/client/src/redux/application/application.reducer.js
@@ -15,7 +15,8 @@ const INITIAL_STATE = {
error: null
},
jobReadOnly: false,
- partnerVersion: null
+ partnerVersion: null,
+ alerts: {}
};
const applicationReducer = (state = INITIAL_STATE, action) => {
@@ -91,6 +92,18 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
case ApplicationActionTypes.SET_WSS_STATUS: {
return { ...state, wssStatus: action.payload };
}
+
+ case ApplicationActionTypes.ADD_ALERTS: {
+ const newAlertsMap = { ...state.alerts };
+
+ action.payload.alerts.forEach((alert) => {
+ newAlertsMap[alert.id] = alert;
+ });
+ return {
+ ...state,
+ alerts: newAlertsMap
+ };
+ }
default:
return state;
}
diff --git a/client/src/redux/application/application.selectors.js b/client/src/redux/application/application.selectors.js
index a4f434cfe..6b0d1c2c4 100644
--- a/client/src/redux/application/application.selectors.js
+++ b/client/src/redux/application/application.selectors.js
@@ -23,3 +23,4 @@ export const selectOnline = createSelector([selectApplication], (application) =>
export const selectProblemJobs = createSelector([selectApplication], (application) => application.problemJobs);
export const selectUpdateAvailable = createSelector([selectApplication], (application) => application.updateAvailable);
export const selectWssStatus = createSelector([selectApplication], (application) => application.wssStatus);
+export const selectAlerts = createSelector([selectApplication], (application) => application.alerts);
diff --git a/client/src/redux/application/application.types.js b/client/src/redux/application/application.types.js
index 1672cda0b..26c1b4c7d 100644
--- a/client/src/redux/application/application.types.js
+++ b/client/src/redux/application/application.types.js
@@ -13,6 +13,7 @@ const ApplicationActionTypes = {
INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL",
SET_PROBLEM_JOBS: "SET_PROBLEM_JOBS",
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE",
- SET_WSS_STATUS: "SET_WSS_STATUS"
+ SET_WSS_STATUS: "SET_WSS_STATUS",
+ ADD_ALERTS: "ADD_ALERTS"
};
export default ApplicationActionTypes;
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 855dcfffd..b743b209a 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -1257,6 +1257,7 @@
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
+ "time": "Select Time",
"total": "Total",
"totals": "Totals",
"tuesday": "Tuesday",
@@ -1341,6 +1342,8 @@
},
"job_lifecycle": {
"columns": {
+ "average_human_readable": "Average Human Readable",
+ "average_value": "Average Value",
"duration": "Duration",
"end": "End",
"human_readable": "Human Readable",
@@ -1874,6 +1877,7 @@
"tax_str_rt": "Storage Tax Rate",
"tax_sub_rt": "Sublet Tax Rate",
"tax_tow_rt": "Towing Tax Rate",
+ "tlos_ind": "Total Loss Indicator",
"towin": "Tow In",
"towing_payable": "Towing Payable",
"unitnumber": "Unit #",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index b46bd1116..4a5a39a9f 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -1257,6 +1257,7 @@
"sunday": "",
"text": "",
"thursday": "",
+ "time": "",
"total": "",
"totals": "",
"tuesday": "",
@@ -1341,6 +1342,8 @@
},
"job_lifecycle": {
"columns": {
+ "average_human_readable": "",
+ "average_value": "",
"duration": "",
"end": "",
"human_readable": "",
@@ -1874,6 +1877,7 @@
"tax_str_rt": "",
"tax_sub_rt": "",
"tax_tow_rt": "",
+ "tlos_ind": "",
"towin": "",
"towing_payable": "Remolque a pagar",
"unitnumber": "Unidad #",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 46f10e081..df418a823 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -1257,6 +1257,7 @@
"sunday": "",
"text": "",
"thursday": "",
+ "time": "",
"total": "",
"totals": "",
"tuesday": "",
@@ -1341,6 +1342,8 @@
},
"job_lifecycle": {
"columns": {
+ "average_human_readable": "",
+ "average_value": "",
"duration": "",
"end": "",
"human_readable": "",
@@ -1874,6 +1877,7 @@
"tax_str_rt": "",
"tax_sub_rt": "",
"tax_tow_rt": "",
+ "tlos_ind": "",
"towin": "",
"towing_payable": "Remorquage à payer",
"unitnumber": "Unité #",
diff --git a/docker-compose.yml b/docker-compose.yml
index 503d874b3..5c9cdc14b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -74,7 +74,7 @@ services:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- - SERVICES=ses,secretsmanager,cloudwatch,logs
+ - SERVICES=s3,ses,secretsmanager,cloudwatch,logs
- DEBUG=0
- AWS_ACCESS_KEY_ID=test
- AWS_SECRET_ACCESS_KEY=test
@@ -115,7 +115,8 @@ services:
aws --endpoint-url=http://localstack:4566 ses verify-domain-identity --domain imex.online --region ca-central-1
aws --endpoint-url=http://localstack:4566 ses verify-email-identity --email-address noreply@imex.online --region ca-central-1
aws --endpoint-url=http://localstack:4566 secretsmanager create-secret --name CHATTER_PRIVATE_KEY --secret-string file:///tmp/certs/id_rsa
- aws --endpoint-url=http://localstack:4566 logs create-log-group --log-group-name development --region ca-central-1
+ aws --endpoint-url=http://localstack:4566 logs create-log-group --log-group-name development --region ca-central-1
+ aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-large-log --create-bucket-configuration LocationConstraint=ca-central-1
"
# Node App: The Main IMEX API
node-app:
@@ -167,6 +168,28 @@ services:
# volumes:
# - redis-insight-data:/db
+# ##Optional Container for SFTP/SSH Server for testing
+# ssh-sftp-server:
+# image: atmoz/sftp:alpine # Using an image with SFTP support
+# container_name: ssh-sftp-server
+# hostname: ssh-sftp-server
+# networks:
+# - redis-cluster-net
+# ports:
+# - "2222:22" # Expose port 22 for SSH/SFTP (mapped to 2222 on the host)
+# volumes:
+# - ./certs/id_rsa.pub:/home/user/.ssh/keys/id_rsa.pub:ro # Mount the SSH public key
+# - ./upload:/home/user/upload # Mount a local directory for SFTP uploads
+# environment:
+# - SFTP_USERS=user:password:1000::upload
+# command: >
+# /bin/sh -c "
+# chmod -R 007 /home/user/upload &&
+# echo 'Match User user' >> /etc/ssh/sshd_config &&
+# sed -i -e 's#ForceCommand internal-sftp#ForceCommand internal-sftp -d /upload#' /etc/ssh/sshd_config &&
+# /usr/sbin/sshd -D
+# "
+
networks:
redis-cluster-net:
driver: bridge
diff --git a/hasura/migrations/1730516697278_idx_timetickets_date/down.sql b/hasura/migrations/1730516697278_idx_timetickets_date/down.sql
new file mode 100644
index 000000000..f309d3260
--- /dev/null
+++ b/hasura/migrations/1730516697278_idx_timetickets_date/down.sql
@@ -0,0 +1,3 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE INDEX idx_timetickets_date ON timetickets (date );
diff --git a/hasura/migrations/1730516697278_idx_timetickets_date/up.sql b/hasura/migrations/1730516697278_idx_timetickets_date/up.sql
new file mode 100644
index 000000000..604b60dc1
--- /dev/null
+++ b/hasura/migrations/1730516697278_idx_timetickets_date/up.sql
@@ -0,0 +1 @@
+CREATE INDEX idx_timetickets_date ON timetickets (date );
diff --git a/hasura/migrations/1730517308367_run_sql_migration/down.sql b/hasura/migrations/1730517308367_run_sql_migration/down.sql
new file mode 100644
index 000000000..8df0a9af1
--- /dev/null
+++ b/hasura/migrations/1730517308367_run_sql_migration/down.sql
@@ -0,0 +1,9 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE INDEX idx_jobs_ownr_fn ON jobs USING gin (ownr_fn gin_trgm_ops);
+-- CREATE INDEX idx_jobs_ownr_ln ON jobs USING gin (ownr_ln gin_trgm_ops);
+-- CREATE INDEX idx_jobs_ownr_co_nm ON jobs USING gin (ownr_co_nm gin_trgm_ops);
+-- CREATE INDEX idx_jobs_clm_no ON jobs USING gin (clm_no gin_trgm_ops);
+-- CREATE INDEX idx_jobs_v_make_desc ON jobs USING gin (v_make_desc gin_trgm_ops);
+-- CREATE INDEX idx_jobs_v_model_desc ON jobs USING gin (v_model_desc gin_trgm_ops);
+-- CREATE INDEX idx_jobs_plate_no ON jobs USING gin (plate_no gin_trgm_ops);
diff --git a/hasura/migrations/1730517308367_run_sql_migration/up.sql b/hasura/migrations/1730517308367_run_sql_migration/up.sql
new file mode 100644
index 000000000..9513eb7fb
--- /dev/null
+++ b/hasura/migrations/1730517308367_run_sql_migration/up.sql
@@ -0,0 +1,7 @@
+CREATE INDEX idx_jobs_ownr_fn ON jobs USING gin (ownr_fn gin_trgm_ops);
+CREATE INDEX idx_jobs_ownr_ln ON jobs USING gin (ownr_ln gin_trgm_ops);
+CREATE INDEX idx_jobs_ownr_co_nm ON jobs USING gin (ownr_co_nm gin_trgm_ops);
+CREATE INDEX idx_jobs_clm_no ON jobs USING gin (clm_no gin_trgm_ops);
+CREATE INDEX idx_jobs_v_make_desc ON jobs USING gin (v_make_desc gin_trgm_ops);
+CREATE INDEX idx_jobs_v_model_desc ON jobs USING gin (v_model_desc gin_trgm_ops);
+CREATE INDEX idx_jobs_plate_no ON jobs USING gin (plate_no gin_trgm_ops);
diff --git a/hasura/migrations/1730518121867_run_sql_migration/down.sql b/hasura/migrations/1730518121867_run_sql_migration/down.sql
new file mode 100644
index 000000000..fa6b92022
--- /dev/null
+++ b/hasura/migrations/1730518121867_run_sql_migration/down.sql
@@ -0,0 +1,3 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE INDEX idx_exportlog_createdat_desc ON exportlog (created_at desc);
diff --git a/hasura/migrations/1730518121867_run_sql_migration/up.sql b/hasura/migrations/1730518121867_run_sql_migration/up.sql
new file mode 100644
index 000000000..2450e76a7
--- /dev/null
+++ b/hasura/migrations/1730518121867_run_sql_migration/up.sql
@@ -0,0 +1 @@
+CREATE INDEX idx_exportlog_createdat_desc ON exportlog (created_at desc);
diff --git a/hasura/migrations/1730521661838_run_sql_migration/down.sql b/hasura/migrations/1730521661838_run_sql_migration/down.sql
new file mode 100644
index 000000000..87ca7ff08
--- /dev/null
+++ b/hasura/migrations/1730521661838_run_sql_migration/down.sql
@@ -0,0 +1,4 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE index idx_messages_unread_agg ON messages (read, isoutbound)
+-- WHERE read = false AND isoutbound = false;
diff --git a/hasura/migrations/1730521661838_run_sql_migration/up.sql b/hasura/migrations/1730521661838_run_sql_migration/up.sql
new file mode 100644
index 000000000..e3728cb7f
--- /dev/null
+++ b/hasura/migrations/1730521661838_run_sql_migration/up.sql
@@ -0,0 +1,2 @@
+CREATE index idx_messages_unread_agg ON messages (read, isoutbound)
+WHERE read = false AND isoutbound = false;
diff --git a/hasura/migrations/1731471670370_run_sql_migration/down.sql b/hasura/migrations/1731471670370_run_sql_migration/down.sql
new file mode 100644
index 000000000..923cf39ca
--- /dev/null
+++ b/hasura/migrations/1731471670370_run_sql_migration/down.sql
@@ -0,0 +1,10 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
+-- CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);
diff --git a/hasura/migrations/1731471670370_run_sql_migration/up.sql b/hasura/migrations/1731471670370_run_sql_migration/up.sql
new file mode 100644
index 000000000..4b5ab065f
--- /dev/null
+++ b/hasura/migrations/1731471670370_run_sql_migration/up.sql
@@ -0,0 +1,8 @@
+CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
+ CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);
diff --git a/package-lock.json b/package-lock.json
index 2f0edfad5..d95191db5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.679.0",
"@aws-sdk/client-elasticache": "^3.675.0",
+ "@aws-sdk/client-s3": "^3.689.0",
"@aws-sdk/client-secrets-manager": "^3.675.0",
"@aws-sdk/client-ses": "^3.675.0",
"@aws-sdk/credential-provider-node": "^3.675.0",
@@ -86,6 +87,69 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-crypto/crc32c": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz",
+ "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz",
+ "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@aws-crypto/sha256-browser": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
@@ -802,6 +866,566 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-sdk/client-s3": {
+ "version": "3.689.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.689.0.tgz",
+ "integrity": "sha512-qYD1GJEPeLM6H3x8BuAAMXZltvVce5vGiwtZc9uMkBBo3HyFnmPitIPTPfaD1q8LOn/7KFdkY4MJ4e8D3YpV9g==",
+ "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/client-sso-oidc": "3.687.0",
+ "@aws-sdk/client-sts": "3.687.0",
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/credential-provider-node": "3.687.0",
+ "@aws-sdk/middleware-bucket-endpoint": "3.686.0",
+ "@aws-sdk/middleware-expect-continue": "3.686.0",
+ "@aws-sdk/middleware-flexible-checksums": "3.689.0",
+ "@aws-sdk/middleware-host-header": "3.686.0",
+ "@aws-sdk/middleware-location-constraint": "3.686.0",
+ "@aws-sdk/middleware-logger": "3.686.0",
+ "@aws-sdk/middleware-recursion-detection": "3.686.0",
+ "@aws-sdk/middleware-sdk-s3": "3.687.0",
+ "@aws-sdk/middleware-ssec": "3.686.0",
+ "@aws-sdk/middleware-user-agent": "3.687.0",
+ "@aws-sdk/region-config-resolver": "3.686.0",
+ "@aws-sdk/signature-v4-multi-region": "3.687.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-endpoints": "3.686.0",
+ "@aws-sdk/util-user-agent-browser": "3.686.0",
+ "@aws-sdk/util-user-agent-node": "3.687.0",
+ "@aws-sdk/xml-builder": "3.686.0",
+ "@smithy/config-resolver": "^3.0.10",
+ "@smithy/core": "^2.5.1",
+ "@smithy/eventstream-serde-browser": "^3.0.11",
+ "@smithy/eventstream-serde-config-resolver": "^3.0.8",
+ "@smithy/eventstream-serde-node": "^3.0.10",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/hash-blob-browser": "^3.1.7",
+ "@smithy/hash-node": "^3.0.8",
+ "@smithy/hash-stream-node": "^3.1.7",
+ "@smithy/invalid-dependency": "^3.0.8",
+ "@smithy/md5-js": "^3.0.8",
+ "@smithy/middleware-content-length": "^3.0.10",
+ "@smithy/middleware-endpoint": "^3.2.1",
+ "@smithy/middleware-retry": "^3.0.25",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/middleware-stack": "^3.0.8",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
+ "@smithy/util-base64": "^3.0.0",
+ "@smithy/util-body-length-browser": "^3.0.0",
+ "@smithy/util-body-length-node": "^3.0.0",
+ "@smithy/util-defaults-mode-browser": "^3.0.25",
+ "@smithy/util-defaults-mode-node": "^3.0.25",
+ "@smithy/util-endpoints": "^2.1.4",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-retry": "^3.0.8",
+ "@smithy/util-stream": "^3.2.1",
+ "@smithy/util-utf8": "^3.0.0",
+ "@smithy/util-waiter": "^3.1.7",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.687.0.tgz",
+ "integrity": "sha512-dfj0y9fQyX4kFill/ZG0BqBTLQILKlL7+O5M4F9xlsh2WNuV2St6WtcOg14Y1j5UODPJiJs//pO+mD1lihT5Kw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/middleware-host-header": "3.686.0",
+ "@aws-sdk/middleware-logger": "3.686.0",
+ "@aws-sdk/middleware-recursion-detection": "3.686.0",
+ "@aws-sdk/middleware-user-agent": "3.687.0",
+ "@aws-sdk/region-config-resolver": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-endpoints": "3.686.0",
+ "@aws-sdk/util-user-agent-browser": "3.686.0",
+ "@aws-sdk/util-user-agent-node": "3.687.0",
+ "@smithy/config-resolver": "^3.0.10",
+ "@smithy/core": "^2.5.1",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/hash-node": "^3.0.8",
+ "@smithy/invalid-dependency": "^3.0.8",
+ "@smithy/middleware-content-length": "^3.0.10",
+ "@smithy/middleware-endpoint": "^3.2.1",
+ "@smithy/middleware-retry": "^3.0.25",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/middleware-stack": "^3.0.8",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
+ "@smithy/util-base64": "^3.0.0",
+ "@smithy/util-body-length-browser": "^3.0.0",
+ "@smithy/util-body-length-node": "^3.0.0",
+ "@smithy/util-defaults-mode-browser": "^3.0.25",
+ "@smithy/util-defaults-mode-node": "^3.0.25",
+ "@smithy/util-endpoints": "^2.1.4",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-retry": "^3.0.8",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.687.0.tgz",
+ "integrity": "sha512-Rdd8kLeTeh+L5ZuG4WQnWgYgdv7NorytKdZsGjiag1D8Wv3PcJvPqqWdgnI0Og717BSXVoaTYaN34FyqFYSx6Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/credential-provider-node": "3.687.0",
+ "@aws-sdk/middleware-host-header": "3.686.0",
+ "@aws-sdk/middleware-logger": "3.686.0",
+ "@aws-sdk/middleware-recursion-detection": "3.686.0",
+ "@aws-sdk/middleware-user-agent": "3.687.0",
+ "@aws-sdk/region-config-resolver": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-endpoints": "3.686.0",
+ "@aws-sdk/util-user-agent-browser": "3.686.0",
+ "@aws-sdk/util-user-agent-node": "3.687.0",
+ "@smithy/config-resolver": "^3.0.10",
+ "@smithy/core": "^2.5.1",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/hash-node": "^3.0.8",
+ "@smithy/invalid-dependency": "^3.0.8",
+ "@smithy/middleware-content-length": "^3.0.10",
+ "@smithy/middleware-endpoint": "^3.2.1",
+ "@smithy/middleware-retry": "^3.0.25",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/middleware-stack": "^3.0.8",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
+ "@smithy/util-base64": "^3.0.0",
+ "@smithy/util-body-length-browser": "^3.0.0",
+ "@smithy/util-body-length-node": "^3.0.0",
+ "@smithy/util-defaults-mode-browser": "^3.0.25",
+ "@smithy/util-defaults-mode-node": "^3.0.25",
+ "@smithy/util-endpoints": "^2.1.4",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-retry": "^3.0.8",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-sts": "^3.687.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.687.0.tgz",
+ "integrity": "sha512-SQjDH8O4XCTtouuCVYggB0cCCrIaTzUZIkgJUpOsIEJBLlTbNOb/BZqUShAQw2o9vxr2rCeOGjAQOYPysW/Pmg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/client-sso-oidc": "3.687.0",
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/credential-provider-node": "3.687.0",
+ "@aws-sdk/middleware-host-header": "3.686.0",
+ "@aws-sdk/middleware-logger": "3.686.0",
+ "@aws-sdk/middleware-recursion-detection": "3.686.0",
+ "@aws-sdk/middleware-user-agent": "3.687.0",
+ "@aws-sdk/region-config-resolver": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-endpoints": "3.686.0",
+ "@aws-sdk/util-user-agent-browser": "3.686.0",
+ "@aws-sdk/util-user-agent-node": "3.687.0",
+ "@smithy/config-resolver": "^3.0.10",
+ "@smithy/core": "^2.5.1",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/hash-node": "^3.0.8",
+ "@smithy/invalid-dependency": "^3.0.8",
+ "@smithy/middleware-content-length": "^3.0.10",
+ "@smithy/middleware-endpoint": "^3.2.1",
+ "@smithy/middleware-retry": "^3.0.25",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/middleware-stack": "^3.0.8",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
+ "@smithy/util-base64": "^3.0.0",
+ "@smithy/util-body-length-browser": "^3.0.0",
+ "@smithy/util-body-length-node": "^3.0.0",
+ "@smithy/util-defaults-mode-browser": "^3.0.25",
+ "@smithy/util-defaults-mode-node": "^3.0.25",
+ "@smithy/util-endpoints": "^2.1.4",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-retry": "^3.0.8",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.686.0.tgz",
+ "integrity": "sha512-Xt3DV4DnAT3v2WURwzTxWQK34Ew+iiLzoUoguvLaZrVMFOqMMrwVjP+sizqIaHp1j7rGmFcN5I8saXnsDLuQLA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/core": "^2.5.1",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/signature-v4": "^4.2.0",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "fast-xml-parser": "4.4.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.686.0.tgz",
+ "integrity": "sha512-osD7lPO8OREkgxPiTWmA1i6XEmOth1uW9HWWj/+A2YGCj1G/t2sHu931w4Qj9NWHYZtbTTXQYVRg+TErALV7nQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.686.0.tgz",
+ "integrity": "sha512-xyGAD/f3vR/wssUiZrNFWQWXZvI4zRm2wpHhoHA1cC2fbRMNFYtFn365yw6dU7l00ZLcdFB1H119AYIUZS7xbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-stream": "^3.2.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.687.0.tgz",
+ "integrity": "sha512-6d5ZJeZch+ZosJccksN0PuXv7OSnYEmanGCnbhUqmUSz9uaVX6knZZfHCZJRgNcfSqg9QC0zsFA/51W5HCUqSQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/credential-provider-env": "3.686.0",
+ "@aws-sdk/credential-provider-http": "3.686.0",
+ "@aws-sdk/credential-provider-process": "3.686.0",
+ "@aws-sdk/credential-provider-sso": "3.687.0",
+ "@aws-sdk/credential-provider-web-identity": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/credential-provider-imds": "^3.2.4",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/shared-ini-file-loader": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-sts": "^3.687.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.687.0.tgz",
+ "integrity": "sha512-Pqld8Nx11NYaBUrVk3bYiGGpLCxkz8iTONlpQWoVWFhSOzlO7zloNOaYbD2XgFjjqhjlKzE91drs/f41uGeCTA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "3.686.0",
+ "@aws-sdk/credential-provider-http": "3.686.0",
+ "@aws-sdk/credential-provider-ini": "3.687.0",
+ "@aws-sdk/credential-provider-process": "3.686.0",
+ "@aws-sdk/credential-provider-sso": "3.687.0",
+ "@aws-sdk/credential-provider-web-identity": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/credential-provider-imds": "^3.2.4",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/shared-ini-file-loader": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.686.0.tgz",
+ "integrity": "sha512-sXqaAgyzMOc+dm4CnzAR5Q6S9OWVHyZjLfW6IQkmGjqeQXmZl24c4E82+w64C+CTkJrFLzH1VNOYp1Hy5gE6Qw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/shared-ini-file-loader": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.687.0.tgz",
+ "integrity": "sha512-N1YCoE7DovIRF2ReyRrA4PZzF0WNi4ObPwdQQkVxhvSm7PwjbWxrfq7rpYB+6YB1Uq3QPzgVwUFONE36rdpxUQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/client-sso": "3.687.0",
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/token-providers": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/shared-ini-file-loader": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.686.0.tgz",
+ "integrity": "sha512-40UqCpPxyHCXDP7CGd9JIOZDgDZf+u1OyLaGBpjQJlz1HYuEsIWnnbTe29Yg3Ah/Zc3g4NBWcUdlGVotlnpnDg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-sts": "^3.686.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.686.0.tgz",
+ "integrity": "sha512-+Yc6rO02z+yhFbHmRZGvEw1vmzf/ifS9a4aBjJGeVVU+ZxaUvnk+IUZWrj4YQopUQ+bSujmMUzJLXSkbDq7yuw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.686.0.tgz",
+ "integrity": "sha512-cX43ODfA2+SPdX7VRxu6gXk4t4bdVJ9pkktbfnkE5t27OlwNfvSGGhnHrQL8xTOFeyQ+3T+oowf26gf1OI+vIg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.686.0.tgz",
+ "integrity": "sha512-jF9hQ162xLgp9zZ/3w5RUNhmwVnXDBlABEUX8jCgzaFpaa742qR/KKtjjZQ6jMbQnP+8fOCSXFAVNMU+s6v81w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.687.0.tgz",
+ "integrity": "sha512-nUgsKiEinyA50CaDXojAkOasAU3Apdg7Qox6IjNUC4ZjgOu7QWsCDB5N28AYMUt06cNYeYQdfMX1aEzG85a1Mg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-endpoints": "3.686.0",
+ "@smithy/core": "^2.5.1",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.686.0.tgz",
+ "integrity": "sha512-6zXD3bSD8tcsMAVVwO1gO7rI1uy2fCD3czgawuPGPopeLiPpo6/3FoUWCQzk2nvEhj7p9Z4BbjwZGSlRkVrXTw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-config-provider": "^3.0.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.686.0.tgz",
+ "integrity": "sha512-9oL4kTCSePFmyKPskibeiOXV6qavPZ63/kXM9Wh9V6dTSvBtLeNnMxqGvENGKJcTdIgtoqyqA6ET9u0PJ5IRIg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/shared-ini-file-loader": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-sso-oidc": "^3.686.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.686.0.tgz",
+ "integrity": "sha512-7msZE2oYl+6QYeeRBjlDgxQUhq/XRky3cXE0FqLFs2muLS7XSuQEXkpOXB3R782ygAP6JX0kmBxPTLurRTikZg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-endpoints": "^2.1.4",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.686.0.tgz",
+ "integrity": "sha512-YiQXeGYZegF1b7B2GOR61orhgv79qmI0z7+Agm3NXLO6hGfVV3kFUJbXnjtH1BgWo5hbZYW7HQ2omGb3dnb6Lg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/types": "^3.6.0",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.687.0.tgz",
+ "integrity": "sha512-idkP6ojSTZ4ek1pJ8wIN7r9U3KR5dn0IkJn3KQBXQ58LWjkRqLtft2vxzdsktWwhPKjjmIKl1S0kbvqLawf8XQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-user-agent": "3.687.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "aws-crt": ">=1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "aws-crt": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@smithy/fetch-http-handler": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz",
+ "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/querystring-builder": "^3.0.8",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-base64": "^3.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
"node_modules/@aws-sdk/client-secrets-manager": {
"version": "3.675.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.675.0.tgz",
@@ -1237,6 +1861,124 @@
"@aws-sdk/client-sts": "^3.667.0"
}
},
+ "node_modules/@aws-sdk/middleware-bucket-endpoint": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.686.0.tgz",
+ "integrity": "sha512-6qCoWI73/HDzQE745MHQUYz46cAQxHCgy1You8MZQX9vHAQwqBnkcsb2hGp7S6fnQY5bNsiZkMWVQ/LVd2MNjg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-arn-parser": "3.679.0",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-config-provider": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-expect-continue": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.686.0.tgz",
+ "integrity": "sha512-5yYqIbyhLhH29vn4sHiTj7sU6GttvLMk3XwCmBXjo2k2j3zHqFUwh9RyFGF9VY6Z392Drf/E/cl+qOGypwULpg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-flexible-checksums": {
+ "version": "3.689.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.689.0.tgz",
+ "integrity": "sha512-6VxMOf3mgmAgg6SMagwKj5pAe+putcx2F2odOAWviLcobFpdM/xK9vNry7p6kY+RDNmSlBvcji9wnU59fjV74Q==",
+ "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.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/is-array-buffer": "^3.0.0",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-stream": "^3.2.1",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.686.0.tgz",
+ "integrity": "sha512-Xt3DV4DnAT3v2WURwzTxWQK34Ew+iiLzoUoguvLaZrVMFOqMMrwVjP+sizqIaHp1j7rGmFcN5I8saXnsDLuQLA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/core": "^2.5.1",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/signature-v4": "^4.2.0",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "fast-xml-parser": "4.4.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@aws-sdk/middleware-host-header": {
"version": "3.667.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.667.0.tgz",
@@ -1252,6 +1994,33 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-sdk/middleware-location-constraint": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.686.0.tgz",
+ "integrity": "sha512-pCLeZzt5zUGY3NbW4J/5x3kaHyJEji4yqtoQcUlJmkoEInhSxJ0OE8sTxAfyL3nIOF4yr6L2xdaLCqYgQT8Aog==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@aws-sdk/middleware-logger": {
"version": "3.667.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.667.0.tgz",
@@ -1281,6 +2050,93 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-sdk/middleware-sdk-s3": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.687.0.tgz",
+ "integrity": "sha512-YGHYqiyRiNNucmvLrfx3QxIkjSDWR/+cc72bn0lPvqFUQBRHZgmYQLxVYrVZSmRzzkH2FQ1HsZcXhOafLbq4vQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.686.0",
+ "@aws-sdk/types": "3.686.0",
+ "@aws-sdk/util-arn-parser": "3.679.0",
+ "@smithy/core": "^2.5.1",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/signature-v4": "^4.2.0",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-config-provider": "^3.0.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-stream": "^3.2.1",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.686.0.tgz",
+ "integrity": "sha512-Xt3DV4DnAT3v2WURwzTxWQK34Ew+iiLzoUoguvLaZrVMFOqMMrwVjP+sizqIaHp1j7rGmFcN5I8saXnsDLuQLA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/core": "^2.5.1",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/property-provider": "^3.1.7",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/signature-v4": "^4.2.0",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "fast-xml-parser": "4.4.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-ssec": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.686.0.tgz",
+ "integrity": "sha512-zJXml/CpVHFUdlGQqja87vNQ3rPB5SlDbfdwxlj1KBbjnRRwpBtxxmOlWRShg8lnVV6aIMGv95QmpIFy4ayqnQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@aws-sdk/middleware-user-agent": {
"version": "3.669.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.669.0.tgz",
@@ -1316,6 +2172,36 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.687.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.687.0.tgz",
+ "integrity": "sha512-vdOQHCRHJPX9mT8BM6xOseazHD6NodvHl9cyF5UjNtLn+gERRJEItIA9hf0hlt62odGD8Fqp+rFRuqdmbNkcNw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "3.687.0",
+ "@aws-sdk/types": "3.686.0",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/signature-v4": "^4.2.0",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz",
+ "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@aws-sdk/token-providers": {
"version": "3.667.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.667.0.tgz",
@@ -1348,6 +2234,18 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@aws-sdk/util-arn-parser": {
+ "version": "3.679.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.679.0.tgz",
+ "integrity": "sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@aws-sdk/util-endpoints": {
"version": "3.667.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.667.0.tgz",
@@ -1410,6 +2308,19 @@
}
}
},
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.686.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.686.0.tgz",
+ "integrity": "sha512-k0z5b5dkYSuOHY0AOZ4iyjcGBeVL9lWsQNF4+c+1oK3OW4fRWl/bNa1soMRMpangsHPzgyn/QkzuDbl7qR4qrw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
@@ -2460,28 +3371,47 @@
}
},
"node_modules/@smithy/abort-controller": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz",
- "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==",
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.6.tgz",
+ "integrity": "sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.0.0"
}
},
- "node_modules/@smithy/config-resolver": {
- "version": "3.0.9",
- "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz",
- "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==",
+ "node_modules/@smithy/chunked-blob-reader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-4.0.0.tgz",
+ "integrity": "sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/types": "^3.5.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@smithy/chunked-blob-reader-native": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.1.tgz",
+ "integrity": "sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-base64": "^3.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@smithy/config-resolver": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.10.tgz",
+ "integrity": "sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/types": "^3.6.0",
"@smithy/util-config-provider": "^3.0.0",
- "@smithy/util-middleware": "^3.0.7",
+ "@smithy/util-middleware": "^3.0.8",
"tslib": "^2.6.2"
},
"engines": {
@@ -2489,19 +3419,17 @@
}
},
"node_modules/@smithy/core": {
- "version": "2.4.8",
- "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.8.tgz",
- "integrity": "sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==",
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.1.tgz",
+ "integrity": "sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/middleware-endpoint": "^3.1.4",
- "@smithy/middleware-retry": "^3.0.23",
- "@smithy/middleware-serde": "^3.0.7",
- "@smithy/protocol-http": "^4.1.4",
- "@smithy/smithy-client": "^3.4.0",
- "@smithy/types": "^3.5.0",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
"@smithy/util-body-length-browser": "^3.0.0",
- "@smithy/util-middleware": "^3.0.7",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-stream": "^3.2.1",
"@smithy/util-utf8": "^3.0.0",
"tslib": "^2.6.2"
},
@@ -2510,15 +3438,15 @@
}
},
"node_modules/@smithy/credential-provider-imds": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz",
- "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==",
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.5.tgz",
+ "integrity": "sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/property-provider": "^3.1.7",
- "@smithy/types": "^3.5.0",
- "@smithy/url-parser": "^3.0.7",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/property-provider": "^3.1.8",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
"tslib": "^2.6.2"
},
"engines": {
@@ -2605,13 +3533,25 @@
"tslib": "^2.6.2"
}
},
- "node_modules/@smithy/hash-node": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz",
- "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==",
+ "node_modules/@smithy/hash-blob-browser": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.7.tgz",
+ "integrity": "sha512-4yNlxVNJifPM5ThaA5HKnHkn7JhctFUHvcaz6YXxHlYOSIrzI6VKQPTN8Gs1iN5nqq9iFcwIR9THqchUCouIfg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/chunked-blob-reader": "^4.0.0",
+ "@smithy/chunked-blob-reader-native": "^3.0.1",
+ "@smithy/types": "^3.6.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@smithy/hash-node": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.8.tgz",
+ "integrity": "sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
"@smithy/util-buffer-from": "^3.0.0",
"@smithy/util-utf8": "^3.0.0",
"tslib": "^2.6.2"
@@ -2620,13 +3560,27 @@
"node": ">=16.0.0"
}
},
- "node_modules/@smithy/invalid-dependency": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz",
- "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==",
+ "node_modules/@smithy/hash-stream-node": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.7.tgz",
+ "integrity": "sha512-xMAsvJ3hLG63lsBVi1Hl6BBSfhd8/Qnp8fC06kjOpJvyyCEXdwHITa5Kvdsk6gaAXLhbZMhQMIGvgUbfnJDP6Q==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@smithy/invalid-dependency": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.8.tgz",
+ "integrity": "sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
}
},
@@ -2641,14 +3595,25 @@
"node": ">=16.0.0"
}
},
- "node_modules/@smithy/middleware-content-length": {
- "version": "3.0.9",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz",
- "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==",
+ "node_modules/@smithy/md5-js": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.8.tgz",
+ "integrity": "sha512-LwApfTK0OJ/tCyNUXqnWCKoE2b4rDSr4BJlDAVCkiWYeHESr+y+d5zlAanuLW6fnitVJRD/7d9/kN/ZM9Su4mA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^4.1.4",
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-utf8": "^3.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@smithy/middleware-content-length": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.10.tgz",
+ "integrity": "sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2656,17 +3621,18 @@
}
},
"node_modules/@smithy/middleware-endpoint": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz",
- "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.1.tgz",
+ "integrity": "sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/middleware-serde": "^3.0.7",
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/shared-ini-file-loader": "^3.1.8",
- "@smithy/types": "^3.5.0",
- "@smithy/url-parser": "^3.0.7",
- "@smithy/util-middleware": "^3.0.7",
+ "@smithy/core": "^2.5.1",
+ "@smithy/middleware-serde": "^3.0.8",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/shared-ini-file-loader": "^3.1.9",
+ "@smithy/types": "^3.6.0",
+ "@smithy/url-parser": "^3.0.8",
+ "@smithy/util-middleware": "^3.0.8",
"tslib": "^2.6.2"
},
"engines": {
@@ -2674,18 +3640,18 @@
}
},
"node_modules/@smithy/middleware-retry": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz",
- "integrity": "sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==",
+ "version": "3.0.25",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.25.tgz",
+ "integrity": "sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/protocol-http": "^4.1.4",
- "@smithy/service-error-classification": "^3.0.7",
- "@smithy/smithy-client": "^3.4.0",
- "@smithy/types": "^3.5.0",
- "@smithy/util-middleware": "^3.0.7",
- "@smithy/util-retry": "^3.0.7",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/service-error-classification": "^3.0.8",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-middleware": "^3.0.8",
+ "@smithy/util-retry": "^3.0.8",
"tslib": "^2.6.2",
"uuid": "^9.0.1"
},
@@ -2707,12 +3673,12 @@
}
},
"node_modules/@smithy/middleware-serde": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz",
- "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.8.tgz",
+ "integrity": "sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2720,12 +3686,12 @@
}
},
"node_modules/@smithy/middleware-stack": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz",
- "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.8.tgz",
+ "integrity": "sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2733,14 +3699,14 @@
}
},
"node_modules/@smithy/node-config-provider": {
- "version": "3.1.8",
- "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz",
- "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==",
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.9.tgz",
+ "integrity": "sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^3.1.7",
- "@smithy/shared-ini-file-loader": "^3.1.8",
- "@smithy/types": "^3.5.0",
+ "@smithy/property-provider": "^3.1.8",
+ "@smithy/shared-ini-file-loader": "^3.1.9",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2748,15 +3714,15 @@
}
},
"node_modules/@smithy/node-http-handler": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz",
- "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==",
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.5.tgz",
+ "integrity": "sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/abort-controller": "^3.1.5",
- "@smithy/protocol-http": "^4.1.4",
- "@smithy/querystring-builder": "^3.0.7",
- "@smithy/types": "^3.5.0",
+ "@smithy/abort-controller": "^3.1.6",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/querystring-builder": "^3.0.8",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2764,12 +3730,12 @@
}
},
"node_modules/@smithy/property-provider": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz",
- "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==",
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.8.tgz",
+ "integrity": "sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2790,12 +3756,12 @@
}
},
"node_modules/@smithy/querystring-builder": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz",
- "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz",
+ "integrity": "sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"@smithy/util-uri-escape": "^3.0.0",
"tslib": "^2.6.2"
},
@@ -2804,12 +3770,12 @@
}
},
"node_modules/@smithy/querystring-parser": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz",
- "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.8.tgz",
+ "integrity": "sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2817,24 +3783,24 @@
}
},
"node_modules/@smithy/service-error-classification": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz",
- "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.8.tgz",
+ "integrity": "sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0"
+ "@smithy/types": "^3.6.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@smithy/shared-ini-file-loader": {
- "version": "3.1.8",
- "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz",
- "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==",
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.9.tgz",
+ "integrity": "sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^3.5.0",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2861,16 +3827,17 @@
}
},
"node_modules/@smithy/smithy-client": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.0.tgz",
- "integrity": "sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.2.tgz",
+ "integrity": "sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/middleware-endpoint": "^3.1.4",
- "@smithy/middleware-stack": "^3.0.7",
- "@smithy/protocol-http": "^4.1.4",
- "@smithy/types": "^3.5.0",
- "@smithy/util-stream": "^3.1.9",
+ "@smithy/core": "^2.5.1",
+ "@smithy/middleware-endpoint": "^3.2.1",
+ "@smithy/middleware-stack": "^3.0.8",
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-stream": "^3.2.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -2890,13 +3857,13 @@
}
},
"node_modules/@smithy/url-parser": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz",
- "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.8.tgz",
+ "integrity": "sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/querystring-parser": "^3.0.7",
- "@smithy/types": "^3.5.0",
+ "@smithy/querystring-parser": "^3.0.8",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
}
},
@@ -2957,14 +3924,14 @@
}
},
"node_modules/@smithy/util-defaults-mode-browser": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.23.tgz",
- "integrity": "sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==",
+ "version": "3.0.25",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.25.tgz",
+ "integrity": "sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^3.1.7",
- "@smithy/smithy-client": "^3.4.0",
- "@smithy/types": "^3.5.0",
+ "@smithy/property-provider": "^3.1.8",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
},
@@ -2973,17 +3940,17 @@
}
},
"node_modules/@smithy/util-defaults-mode-node": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.23.tgz",
- "integrity": "sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==",
+ "version": "3.0.25",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.25.tgz",
+ "integrity": "sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/config-resolver": "^3.0.9",
- "@smithy/credential-provider-imds": "^3.2.4",
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/property-provider": "^3.1.7",
- "@smithy/smithy-client": "^3.4.0",
- "@smithy/types": "^3.5.0",
+ "@smithy/config-resolver": "^3.0.10",
+ "@smithy/credential-provider-imds": "^3.2.5",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/property-provider": "^3.1.8",
+ "@smithy/smithy-client": "^3.4.2",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2991,13 +3958,13 @@
}
},
"node_modules/@smithy/util-endpoints": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz",
- "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.4.tgz",
+ "integrity": "sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^3.1.8",
- "@smithy/types": "^3.5.0",
+ "@smithy/node-config-provider": "^3.1.9",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -3030,13 +3997,13 @@
}
},
"node_modules/@smithy/util-retry": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz",
- "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.8.tgz",
+ "integrity": "sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/service-error-classification": "^3.0.7",
- "@smithy/types": "^3.5.0",
+ "@smithy/service-error-classification": "^3.0.8",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -3044,14 +4011,14 @@
}
},
"node_modules/@smithy/util-stream": {
- "version": "3.1.9",
- "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz",
- "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.2.1.tgz",
+ "integrity": "sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/fetch-http-handler": "^3.2.9",
- "@smithy/node-http-handler": "^3.2.4",
- "@smithy/types": "^3.5.0",
+ "@smithy/fetch-http-handler": "^4.0.0",
+ "@smithy/node-http-handler": "^3.2.5",
+ "@smithy/types": "^3.6.0",
"@smithy/util-base64": "^3.0.0",
"@smithy/util-buffer-from": "^3.0.0",
"@smithy/util-hex-encoding": "^3.0.0",
@@ -3062,6 +4029,19 @@
"node": ">=16.0.0"
}
},
+ "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz",
+ "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^4.1.5",
+ "@smithy/querystring-builder": "^3.0.8",
+ "@smithy/types": "^3.6.0",
+ "@smithy/util-base64": "^3.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
"node_modules/@smithy/util-uri-escape": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz",
@@ -3087,13 +4067,13 @@
}
},
"node_modules/@smithy/util-waiter": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.6.tgz",
- "integrity": "sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.7.tgz",
+ "integrity": "sha512-d5yGlQtmN/z5eoTtIYgkvOw27US2Ous4VycnXatyoImIF9tzlcpnKqQ/V7qhvJmb2p6xZne1NopCLakdTnkBBQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/abort-controller": "^3.1.5",
- "@smithy/types": "^3.5.0",
+ "@smithy/abort-controller": "^3.1.6",
+ "@smithy/types": "^3.6.0",
"tslib": "^2.6.2"
},
"engines": {
diff --git a/package.json b/package.json
index 2a3c24ded..f51af45b5 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.679.0",
"@aws-sdk/client-elasticache": "^3.675.0",
+ "@aws-sdk/client-s3": "^3.689.0",
"@aws-sdk/client-secrets-manager": "^3.675.0",
"@aws-sdk/client-ses": "^3.675.0",
"@aws-sdk/credential-provider-node": "^3.675.0",
diff --git a/server.js b/server.js
index a92ab7241..f98ec67da 100644
--- a/server.js
+++ b/server.js
@@ -21,7 +21,7 @@ const { applyRedisHelpers } = require("./server/utils/redisHelpers");
const { applyIOHelpers } = require("./server/utils/ioHelpers");
const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents");
const { ElastiCacheClient, DescribeCacheClustersCommand } = require("@aws-sdk/client-elasticache");
-const { default: InstanceManager } = require("./server/utils/instanceMgr");
+const { InstanceRegion } = require("./server/utils/instanceMgr");
const CLUSTER_RETRY_BASE_DELAY = 100;
const CLUSTER_RETRY_MAX_DELAY = 5000;
@@ -114,10 +114,7 @@ const applyRoutes = ({ app }) => {
*/
const getRedisNodesFromAWS = async () => {
const client = new ElastiCacheClient({
- region: InstanceManager({
- imex: "ca-central-1",
- rome: "us-east-2"
- })
+ region: InstanceRegion()
});
const params = {
diff --git a/server/accounting/qbo/qbo-payables.js b/server/accounting/qbo/qbo-payables.js
index 196520de0..04b0c7e08 100644
--- a/server/accounting/qbo/qbo-payables.js
+++ b/server/accounting/qbo/qbo-payables.js
@@ -167,7 +167,7 @@ async function QueryVendorRecord(oauthClient, qbo_realmId, req, bill) {
async function InsertVendorRecord(oauthClient, qbo_realmId, req, bill) {
const Vendor = {
- DisplayName: bill.vendor.name
+ DisplayName: StandardizeName(bill.vendor.name)
};
try {
const result = await oauthClient.makeApiCall({
diff --git a/server/accounting/qbo/qbo-payments.js b/server/accounting/qbo/qbo-payments.js
index 6426bfa4f..99b4447cd 100644
--- a/server/accounting/qbo/qbo-payments.js
+++ b/server/accounting/qbo/qbo-payments.js
@@ -219,6 +219,7 @@ async function InsertPayment(oauthClient, qbo_realmId, req, payment, parentRef,
PaymentMethodRef: {
value: paymentMethods[payment.type]
},
+ PrivateNote: payment.memo.length > 4000 ? payment.memo.substring(0, 4000).trim() : payment.memo.trim(),
PaymentRefNum: payment.transactionid,
...(invoices && invoices.length === 1 && invoices[0]
? {
diff --git a/server/accounting/qbo/qbo.js b/server/accounting/qbo/qbo.js
index 74cc3c742..5c7e9d874 100644
--- a/server/accounting/qbo/qbo.js
+++ b/server/accounting/qbo/qbo.js
@@ -10,7 +10,7 @@ function urlBuilder(realmId, object, query = null) {
}
function StandardizeName(str) {
- return str.replace(new RegExp(/'/g), "\\'");
+ return str.replace(new RegExp(/'/g), "\\'").trim();
}
exports.urlBuilder = urlBuilder;
diff --git a/server/alerts/alertcheck.js b/server/alerts/alertcheck.js
new file mode 100644
index 000000000..e29ff1e6a
--- /dev/null
+++ b/server/alerts/alertcheck.js
@@ -0,0 +1,76 @@
+const axios = require("axios");
+const _ = require("lodash");
+const { default: InstanceMgr } = require("../utils/instanceMgr"); // For deep object comparison
+
+// Constants
+const ALERTS_REDIS_KEY = "alerts_data"; // The key under which we'll store alerts in Redis
+const GLOBAL_SOCKET_ID = "global"; // Use 'global' as a socketId to store global data
+
+const ALERT_FILE_URL = InstanceMgr({
+ imex: "https://images.imex.online/alerts/alerts-imex.json",
+ rome: "https://images.imex.online/alerts/alerts-rome.json"
+});
+
+const alertCheck = async (req, res) => {
+ // Access Redis helper functions
+ const { ioRedis, logger } = req;
+ const { getSessionData, setSessionData } = req.sessionUtils;
+
+ try {
+ // Get the JSON Alert file from the server
+ const response = await axios.get(ALERT_FILE_URL);
+ const currentAlerts = response.data;
+ // Retrieve stored alerts from Redis using a global socketId
+ const storedAlerts = await getSessionData(GLOBAL_SOCKET_ID, ALERTS_REDIS_KEY);
+ if (!storedAlerts) {
+ // Alerts not in Redis, store them
+ await setSessionData(GLOBAL_SOCKET_ID, ALERTS_REDIS_KEY, currentAlerts);
+ logger.logger.debug("Alerts added to Redis for the first time.");
+
+ // Emit to clients
+ if (ioRedis) {
+ ioRedis.emit("bodyshop-message", {
+ type: "alert-update",
+ payload: currentAlerts
+ });
+ logger.logger.debug("Alerts emitted to clients for the first time.");
+ } else {
+ logger.log("Socket.IO instance not found. (1)", "error");
+ }
+
+ return res.status(200).send("Alerts added to Redis and emitted to clients.");
+ } else {
+ // Alerts are in Redis, compare them
+ if (!_.isEqual(currentAlerts, storedAlerts)) {
+ // Alerts are different, update Redis and emit to clients
+ await setSessionData(GLOBAL_SOCKET_ID, ALERTS_REDIS_KEY, currentAlerts);
+ logger.logger.debug("Alerts updated in Redis.");
+
+ // Emit the new alerts to all connected clients
+ if (ioRedis) {
+ ioRedis.emit("bodyshop-message", {
+ type: "alert-update",
+ payload: currentAlerts
+ });
+ logger.logger.debug("Alerts emitted to clients after update.");
+ } else {
+ logger.log("Socket.IO instance not found. (2)", "error");
+ }
+
+ return res.status(200).send("Alerts updated in Redis and emitted to clients.");
+ } else {
+ return res.status(200).send("No changes in alerts.");
+ }
+ }
+ } catch (error) {
+ logger.log("Error in alertCheck:", "error", null, null, {
+ error: {
+ message: error.message,
+ stack: error.stack
+ }
+ });
+ return res.status(500).send("Internal server error.");
+ }
+};
+
+module.exports = { alertCheck };
diff --git a/server/data/autohouse.js b/server/data/autohouse.js
index 9b6494e67..7d8447f68 100644
--- a/server/data/autohouse.js
+++ b/server/data/autohouse.js
@@ -13,6 +13,7 @@ let Client = require("ssh2-sftp-client");
const client = require("../graphql-client/graphql-client").client;
const { sendServerEmail } = require("../email/sendemail");
+
const AHDineroFormat = "0.00";
const AhDateFormat = "MMDDYYYY";
@@ -26,170 +27,180 @@ const ftpSetup = {
password: process.env.AUTOHOUSE_PASSWORD,
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
algorithms: {
- serverHostKey: ["ssh-rsa", "ssh-dss"]
+ serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
}
};
+const allxmlsToUpload = [];
+const allErrors = [];
+
exports.default = async (req, res) => {
// Only process if in production environment.
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
-
- //Query for the List of Bodyshop Clients.
- logger.log("autohouse-start", "DEBUG", "api", null, null);
- const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
-
- const specificShopIds = req.body.bodyshopIds; // ['uuid]
- const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+ // Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
- const allxmlsToUpload = [];
- const allErrors = [];
+
+ // Send immediate response and continue processing.
+ res.status(202).json({
+ success: true,
+ message: "Processing request ...",
+ timestamp: new Date().toISOString()
+ });
+
try {
- for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
+ logger.log("autohouse-start", "DEBUG", "api", null, null);
+ const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS); //Query for the List of Bodyshop Clients.
+ const specificShopIds = req.body.bodyshopIds; // ['uuid];
+ const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+
+ const batchSize = 10;
+
+ const shopsToProcess =
+ specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
+ logger.log("autohouse-shopsToProcess-generated", "DEBUG", "api", null, null);
+
+ if (shopsToProcess.length === 0) {
+ logger.log("autohouse-shopsToProcess-empty", "DEBUG", "api", null, null);
+ return;
+ }
+ const batchPromises = [];
+ for (let i = 0; i < shopsToProcess.length; i += batchSize) {
+ const batch = shopsToProcess.slice(i, i + batchSize);
+ const batchPromise = (async () => {
+ await processBatch(batch, start, end);
+
+ if (skipUpload) {
+ for (const xmlObj of allxmlsToUpload) {
+ fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
+ }
+ } else {
+ await uploadViaSFTP(allxmlsToUpload);
+ }
+ })();
+ batchPromises.push(batchPromise);
+ }
+ await Promise.all(batchPromises);
+ await sendServerEmail({
+ subject: `Autohouse Report ${moment().format("MM-DD-YY")}`,
+ text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
+ allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
+ null,
+ 2
+ )}`
+ });
+
+ logger.log("autohouse-end", "DEBUG", "api", null, null);
+ } catch (error) {
+ logger.log("autohouse-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ }
+};
+
+async function processBatch(batch, start, end) {
+ for (const bodyshop of batch) {
+ const erroredJobs = [];
+ try {
logger.log("autohouse-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
- const erroredJobs = [];
- try {
- const { jobs, bodyshops_by_pk } = await client.request(queries.AUTOHOUSE_QUERY, {
- bodyshopid: bodyshop.id,
- start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
- ...(end && { end: moment(end).endOf("day") })
- });
- const autoHouseObject = {
- AutoHouseExport: {
- RepairOrder: jobs.map((j) =>
- CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
- erroredJobs.push({ job: job, error: error.toString() });
- })
- )
- }
- };
-
- if (erroredJobs.length > 0) {
- logger.log("autohouse-failed-jobs", "ERROR", "api", bodyshop.id, {
- count: erroredJobs.length,
- jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
- });
- }
-
- var ret = builder
- .create(
- {
- // version: "1.0",
- // encoding: "UTF-8",
- //keepNullNodes: true,
- },
- autoHouseObject
- )
- .end({ allowEmptyTags: true });
-
- allxmlsToUpload.push({
- count: autoHouseObject.AutoHouseExport.RepairOrder.length,
- xml: ret,
- filename: `IM_${bodyshop.autohouseid}_${moment().format("DDMMYYYY_HHMMss")}.xml`
- });
-
- logger.log("autohouse-end-shop-extract", "DEBUG", "api", bodyshop.id, {
- shopname: bodyshop.shopname
- });
- } catch (error) {
- //Error at the shop level.
- logger.log("autohouse-error-shop", "ERROR", "api", bodyshop.id, {
- ...error
- });
-
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- autuhouseid: bodyshop.autuhouseid,
- fatal: true,
- errors: [error.toString()]
- });
- } finally {
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- autohouseid: bodyshop.autohouseid,
- errors: erroredJobs.map((ej) => ({
- ro_number: ej.job?.ro_number,
- jobid: ej.job?.id,
- error: ej.error
- }))
- });
- }
- }
-
- if (skipUpload) {
- for (const xmlObj of allxmlsToUpload) {
- fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
- }
-
- res.json(allxmlsToUpload);
- sendServerEmail({
- subject: `Autohouse Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
+ const { jobs, bodyshops_by_pk } = await client.request(queries.AUTOHOUSE_QUERY, {
+ bodyshopid: bodyshop.id,
+ start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
+ ...(end && { end: moment(end).endOf("day") })
});
- return;
- }
- let sftp = new Client();
- sftp.on("error", (errors) =>
- logger.log("autohouse-sftp-error", "ERROR", "api", null, {
- ...errors
- })
- );
- try {
- //Connect to the FTP and upload all.
+ const autoHouseObject = {
+ AutoHouseExport: {
+ RepairOrder: jobs.map((j) =>
+ CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
+ erroredJobs.push({ job: job, error: error.toString() });
+ })
+ )
+ }
+ };
- await sftp.connect(ftpSetup);
-
- for (const xmlObj of allxmlsToUpload) {
- logger.log("autohouse-sftp-upload", "DEBUG", "api", null, {
- filename: xmlObj.filename
- });
-
- const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
- logger.log("autohouse-sftp-upload-result", "DEBUG", "api", null, {
- uploadResult
+ if (erroredJobs.length > 0) {
+ logger.log("autohouse-failed-jobs", "ERROR", "api", bodyshop.id, {
+ count: erroredJobs.length,
+ jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
- //***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
+ const ret = builder.create({}, autoHouseObject).end({ allowEmptyTags: true });
+
+ allxmlsToUpload.push({
+ count: autoHouseObject.AutoHouseExport.RepairOrder.length,
+ xml: ret,
+ filename: `IM_${bodyshop.autohouseid}_${moment().format("DDMMYYYY_HHMMss")}.xml`
+ });
+
+ logger.log("autohouse-end-shop-extract", "DEBUG", "api", bodyshop.id, {
+ shopname: bodyshop.shopname
+ });
} catch (error) {
- logger.log("autohouse-sftp-error", "ERROR", "api", null, {
- ...error
+ //Error at the shop level.
+ logger.log("autohouse-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
+
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ autohouseid: bodyshop.autohouseid,
+ fatal: true,
+ errors: [error.toString()]
});
} finally {
- sftp.end();
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ autohouseid: bodyshop.autohouseid,
+ errors: erroredJobs.map((ej) => ({
+ ro_number: ej.job?.ro_number,
+ jobid: ej.job?.id,
+ error: ej.error
+ }))
+ });
}
- sendServerEmail({
- subject: `Autohouse Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
- });
- res.sendStatus(200);
- } catch (error) {
- res.status(200).json(error);
}
-};
+}
+
+async function uploadViaSFTP(allxmlsToUpload) {
+ const sftp = new Client();
+ sftp.on("error", (errors) =>
+ logger.log("autohouse-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
+ );
+ try {
+ //Connect to the FTP and upload all.
+ await sftp.connect(ftpSetup);
+
+ for (const xmlObj of allxmlsToUpload) {
+ try {
+ xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
+ logger.log("autohouse-sftp-upload", "DEBUG", "api", null, {
+ filename: xmlObj.filename,
+ result: xmlObj.result
+ });
+ } catch (error) {
+ logger.log("autohouse-sftp-upload-error", "ERROR", "api", null, {
+ filename: xmlObj.filename,
+ error: error.message,
+ stack: error.stack
+ });
+ throw error;
+ }
+ }
+ } catch (error) {
+ logger.log("autohouse-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ throw error;
+ } finally {
+ sftp.end();
+ }
+}
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
@@ -287,8 +298,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
InsuranceCo: job.ins_co_nm || "",
CompanyName: job.ins_co_nm || "",
Address: job.ins_addr1 || "",
- City: job.ins_addr1 || "",
- State: job.ins_city || "",
+ City: job.ins_city || "",
+ State: job.ins_st || "",
Zip: job.ins_zip || "",
Phone: job.ins_ph1 || "",
Fax: job.ins_fax || "",
@@ -601,10 +612,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
- logger.log("autohouse-job-calculate-error", "ERROR", "api", null, {
- error
- });
-
+ logger.log("autohouse-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};
diff --git a/server/data/chatter.js b/server/data/chatter.js
index e610f9791..8890fafe9 100644
--- a/server/data/chatter.js
+++ b/server/data/chatter.js
@@ -22,135 +22,133 @@ const ftpSetup = {
serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
}
};
+
+const allcsvsToUpload = [];
+const allErrors = [];
+
exports.default = async (req, res) => {
// Only process if in production environment.
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
-
+ // Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
- //Query for the List of Bodyshop Clients.
- logger.log("chatter-start", "DEBUG", "api", null, null);
- const { bodyshops } = await client.request(queries.GET_CHATTER_SHOPS);
- const specificShopIds = req.body.bodyshopIds; // ['uuid]
- const { start, end, skipUpload } = req.body; //YYYY-MM-DD
- const allcsvsToUpload = [];
- const allErrors = [];
+ // Send immediate response and continue processing.
+ res.status(202).json({
+ success: true,
+ message: "Processing request ...",
+ timestamp: new Date().toISOString()
+ });
+
try {
- for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
- logger.log("chatter-start-shop-extract", "DEBUG", "api", bodyshop.id, {
- shopname: bodyshop.shopname
- });
- try {
- const { jobs, bodyshops_by_pk } = await client.request(queries.CHATTER_QUERY, {
- bodyshopid: bodyshop.id,
- start: start ? moment(start).startOf("day") : moment().subtract(1, "days").startOf("day"),
- ...(end && { end: moment(end).endOf("day") })
- });
+ logger.log("chatter-start", "DEBUG", "api", null, null);
+ const { bodyshops } = await client.request(queries.GET_CHATTER_SHOPS); //Query for the List of Bodyshop Clients.
+ const specificShopIds = req.body.bodyshopIds; // ['uuid];
+ const { start, end, skipUpload } = req.body; //YYYY-MM-DD
- const chatterObject = jobs.map((j) => {
- return {
- poc_trigger_code: bodyshops_by_pk.chatterid,
- firstname: j.ownr_co_nm ? null : j.ownr_fn,
- lastname: j.ownr_co_nm ? j.ownr_co_nm : j.ownr_ln,
- transaction_id: j.ro_number,
- email: j.ownr_ea,
- phone_number: j.ownr_ph1
- };
- });
+ const batchSize = 10;
- const ret = converter.json2csv(chatterObject, { emptyFieldValue: "" });
+ const shopsToProcess =
+ specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
+ logger.log("chatter-shopsToProcess-generated", "DEBUG", "api", null, null);
- allcsvsToUpload.push({
- count: chatterObject.length,
- csv: ret,
- filename: `${bodyshop.shopname}_solicitation_${moment().format("YYYYMMDD")}.csv`
- });
-
- logger.log("chatter-end-shop-extract", "DEBUG", "api", bodyshop.id, {
- shopname: bodyshop.shopname
- });
- } catch (error) {
- //Error at the shop level.
- logger.log("chatter-error-shop", "ERROR", "api", bodyshop.id, {
- ...error
- });
-
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- shopname: bodyshop.shopname,
- fatal: true,
- errors: [error.toString()]
- });
- } finally {
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- shopname: bodyshop.shopname
- });
- }
- }
-
- if (skipUpload) {
- for (const csvObj of allcsvsToUpload) {
- fs.writeFile(`./logs/${csvObj.filename}`, csvObj.csv);
- }
-
- sendServerEmail({
- subject: `Chatter Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allcsvsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
- });
- res.json(allcsvsToUpload);
+ if (shopsToProcess.length === 0) {
+ logger.log("chatter-shopsToProcess-empty", "DEBUG", "api", null, null);
return;
}
- const sftp = new Client();
- sftp.on("error", (errors) => logger.log("chatter-sftp-error", "ERROR", "api", null, { ...errors }));
- try {
- //Get the private key from AWS Secrets Manager.
- ftpSetup.privateKey = await getPrivateKey();
-
- //Connect to the FTP and upload all.
- await sftp.connect(ftpSetup);
-
- for (const csvObj of allcsvsToUpload) {
- logger.log("chatter-sftp-upload", "DEBUG", "api", null, { filename: csvObj.filename });
-
- const uploadResult = await sftp.put(Buffer.from(csvObj.xml), `/${csvObj.filename}`);
- logger.log("chatter-sftp-upload-result", "DEBUG", "api", null, { uploadResult });
- }
- } catch (error) {
- logger.log("chatter-sftp-error", "ERROR", "api", null, { ...error });
- } finally {
- sftp.end();
+ const batchPromises = [];
+ for (let i = 0; i < shopsToProcess.length; i += batchSize) {
+ const batch = shopsToProcess.slice(i, i + batchSize);
+ const batchPromise = (async () => {
+ await processBatch(batch, start, end);
+ if (skipUpload) {
+ for (const csvObj of allcsvsToUpload) {
+ await fs.promises.writeFile(`./logs/${csvObj.filename}`, csvObj.csv);
+ }
+ } else {
+ await uploadViaSFTP(allcsvsToUpload);
+ }
+ })();
+ batchPromises.push(batchPromise);
}
- sendServerEmail({
+ await Promise.all(batchPromises);
+ await sendServerEmail({
subject: `Chatter Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allcsvsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
+ text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
+ allcsvsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
null,
2
)}`
});
- res.sendStatus(200);
+
+ logger.log("chatter-end", "DEBUG", "api", null, null);
} catch (error) {
- res.status(200).json(error);
+ logger.log("chatter-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
}
};
+async function processBatch(batch, start, end) {
+ for (const bodyshop of batch) {
+ try {
+ logger.log("chatter-start-shop-extract", "DEBUG", "api", bodyshop.id, {
+ shopname: bodyshop.shopname
+ });
+
+ const { jobs, bodyshops_by_pk } = await client.request(queries.CHATTER_QUERY, {
+ bodyshopid: bodyshop.id,
+ start: start ? moment(start).startOf("day") : moment().subtract(1, "days").startOf("day"),
+ ...(end && { end: moment(end).endOf("day") })
+ });
+
+ const chatterObject = jobs.map((j) => {
+ return {
+ poc_trigger_code: bodyshops_by_pk.chatterid,
+ firstname: j.ownr_co_nm ? null : j.ownr_fn,
+ lastname: j.ownr_co_nm ? j.ownr_co_nm : j.ownr_ln,
+ transaction_id: j.ro_number,
+ email: j.ownr_ea,
+ phone_number: j.ownr_ph1
+ };
+ });
+
+ const ret = converter.json2csv(chatterObject, { emptyFieldValue: "" });
+
+ allcsvsToUpload.push({
+ count: chatterObject.length,
+ csv: ret,
+ filename: `${bodyshop.shopname}_solicitation_${moment().format("YYYYMMDD")}.csv`
+ });
+
+ logger.log("chatter-end-shop-extract", "DEBUG", "api", bodyshop.id, {
+ shopname: bodyshop.shopname
+ });
+ } catch (error) {
+ //Error at the shop level.
+ logger.log("chatter-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
+
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ shopname: bodyshop.shopname,
+ fatal: true,
+ errors: [error.toString()]
+ });
+ } finally {
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ shopname: bodyshop.shopname
+ });
+ }
+ }
+}
+
async function getPrivateKey() {
// Connect to AWS Secrets Manager
const client = new SecretsManagerClient({ region: "ca-central-1" });
@@ -160,10 +158,46 @@ async function getPrivateKey() {
try {
const { SecretString, SecretBinary } = await client.send(command);
if (SecretString || SecretBinary) logger.log("chatter-retrieved-private-key", "DEBUG", "api", null, null);
- const chatterPrivateKey = SecretString ? JSON.parse(SecretString) : JSON.parse(Buffer.from(SecretBinary, "base64").toString("ascii"));
- return chatterPrivateKey.private_key;
+ const chatterPrivateKey = SecretString ? SecretString : Buffer.from(SecretBinary, "base64").toString("ascii");
+ return chatterPrivateKey;
} catch (error) {
- logger.log("chatter-get-private-key", "ERROR", "api", null, error);
- throw err;
+ logger.log("chatter-get-private-key", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ throw error;
+ }
+}
+
+async function uploadViaSFTP(allcsvsToUpload) {
+ const sftp = new Client();
+ sftp.on("error", (errors) =>
+ logger.log("chatter-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
+ );
+ try {
+ //Get the private key from AWS Secrets Manager.
+ const privateKey = await getPrivateKey();
+
+ //Connect to the FTP and upload all.
+ await sftp.connect({ ...ftpSetup, privateKey });
+
+ for (const csvObj of allcsvsToUpload) {
+ try {
+ csvObj.result = await sftp.put(Buffer.from(csvObj.csv), `${csvObj.filename}`);
+ logger.log("chatter-sftp-upload", "DEBUG", "api", null, {
+ filename: csvObj.filename,
+ result: csvObj.result
+ });
+ } catch (error) {
+ logger.log("chatter-sftp-upload-error", "ERROR", "api", null, {
+ filename: csvObj.filename,
+ error: error.message,
+ stack: error.stack
+ });
+ throw error;
+ }
+ }
+ } catch (error) {
+ logger.log("chatter-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ throw error;
+ } finally {
+ sftp.end();
}
}
diff --git a/server/data/claimscorp.js b/server/data/claimscorp.js
index 6ebc63f81..cafa03df1 100644
--- a/server/data/claimscorp.js
+++ b/server/data/claimscorp.js
@@ -26,174 +26,184 @@ const ftpSetup = {
password: process.env.CLAIMSCORP_PASSWORD,
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
algorithms: {
- serverHostKey: ["ssh-rsa", "ssh-dss"]
+ serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
}
};
+const allxmlsToUpload = [];
+const allErrors = [];
+
exports.default = async (req, res) => {
// Only process if in production environment.
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
-
- //Query for the List of Bodyshop Clients.
- logger.log("claimscorp-start", "DEBUG", "api", null, null);
- const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS);
-
- const specificShopIds = req.body.bodyshopIds; // ['uuid]
- const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+ // Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
- const allxmlsToUpload = [];
- const allErrors = [];
+
+ // Send immediate response and continue processing.
+ res.status(202).json({
+ success: true,
+ message: "Processing request ...",
+ timestamp: new Date().toISOString()
+ });
+
try {
- for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
+ logger.log("claimscorp-start", "DEBUG", "api", null, null);
+ const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS); //Query for the List of Bodyshop Clients.
+ const specificShopIds = req.body.bodyshopIds; // ['uuid];
+ const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+
+ const batchSize = 10;
+
+ const shopsToProcess =
+ specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
+ logger.log("claimscorp-shopsToProcess-generated", "DEBUG", "api", null, null);
+
+ if (shopsToProcess.length === 0) {
+ logger.log("claimscorp-shopsToProcess-empty", "DEBUG", "api", null, null);
+ return;
+ }
+ const batchPromises = [];
+ for (let i = 0; i < shopsToProcess.length; i += batchSize) {
+ const batch = shopsToProcess.slice(i, i + batchSize);
+ const batchPromise = (async () => {
+ await processBatch(batch, start, end);
+
+ if (skipUpload) {
+ for (const xmlObj of allxmlsToUpload) {
+ fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
+ }
+ } else {
+ await uploadViaSFTP(allxmlsToUpload);
+ }
+ })();
+ batchPromises.push(batchPromise);
+ }
+ await Promise.all(batchPromises);
+ await sendServerEmail({
+ subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
+ text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
+ allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
+ null,
+ 2
+ )}`
+ });
+
+ logger.log("claimscorp-end", "DEBUG", "api", null, null);
+ } catch (error) {
+ logger.log("claimscorp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ }
+};
+
+async function processBatch(batch, start, end) {
+ for (const bodyshop of batch) {
+ const erroredJobs = [];
+ try {
logger.log("claimscorp-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
- const erroredJobs = [];
- try {
- const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
- bodyshopid: bodyshop.id,
- start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
- ...(end && { end: moment(end).endOf("day") })
- });
- const claimsCorpObject = {
- DataFeed: {
- ShopInfo: {
- ShopID: bodyshops_by_pk.claimscorpid,
- ShopName: bodyshops_by_pk.shopname,
- RO: jobs.map((j) =>
- CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
- erroredJobs.push({ job: job, error: error.toString() });
- })
- )
- }
- }
- };
-
- if (erroredJobs.length > 0) {
- logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
- count: erroredJobs.length,
- jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
- });
- }
-
- var ret = builder
- .create(
- {
- // version: "1.0",
- // encoding: "UTF-8",
- //keepNullNodes: true,
- },
- claimsCorpObject
- )
- .end({ allowEmptyTags: true });
-
- allxmlsToUpload.push({
- count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
- xml: ret,
- filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
- });
-
- logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
- shopname: bodyshop.shopname
- });
- } catch (error) {
- //Error at the shop level.
- logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, {
- ...error
- });
-
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- claimscorpid: bodyshop.claimscorpid,
- fatal: true,
- errors: [error.toString()]
- });
- } finally {
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- claimscorpid: bodyshop.claimscorpid,
- errors: erroredJobs.map((ej) => ({
- ro_number: ej.job?.ro_number,
- jobid: ej.job?.id,
- error: ej.error
- }))
- });
- }
- }
-
- if (skipUpload) {
- for (const xmlObj of allxmlsToUpload) {
- fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
- }
-
- res.json(allxmlsToUpload);
- sendServerEmail({
- subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
+ const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
+ bodyshopid: bodyshop.id,
+ start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
+ ...(end && { end: moment(end).endOf("day") })
});
- return;
- }
- let sftp = new Client();
- sftp.on("error", (errors) =>
- logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
- ...errors
- })
- );
- try {
- //Connect to the FTP and upload all.
+ const claimsCorpObject = {
+ DataFeed: {
+ ShopInfo: {
+ ShopID: bodyshops_by_pk.claimscorpid,
+ ShopName: bodyshops_by_pk.shopname,
+ RO: jobs.map((j) =>
+ CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
+ erroredJobs.push({ job: job, error: error.toString() });
+ })
+ )
+ }
+ }
+ };
- await sftp.connect(ftpSetup);
-
- for (const xmlObj of allxmlsToUpload) {
- logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
- filename: xmlObj.filename
- });
-
- const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
- logger.log("claimscorp-sftp-upload-result", "DEBUG", "api", null, {
- uploadResult
+ if (erroredJobs.length > 0) {
+ logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
+ count: erroredJobs.length,
+ jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
- //***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
+ const ret = builder.create({}, claimsCorpObject).end({ allowEmptyTags: true });
+
+ allxmlsToUpload.push({
+ count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
+ xml: ret,
+ filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
+ });
+
+ logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
+ shopname: bodyshop.shopname
+ });
} catch (error) {
- logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
- ...error
+ //Error at the shop level.
+ logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
+
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ claimscorpid: bodyshop.claimscorpid,
+ fatal: true,
+ errors: [error.toString()]
});
} finally {
- sftp.end();
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ claimscorpid: bodyshop.claimscorpid,
+ errors: erroredJobs.map((ej) => ({
+ ro_number: ej.job?.ro_number,
+ jobid: ej.job?.id,
+ error: ej.error
+ }))
+ });
}
- sendServerEmail({
- subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
- });
- res.sendStatus(200);
- } catch (error) {
- res.status(200).json(error);
}
-};
+}
+
+async function uploadViaSFTP(allxmlsToUpload) {
+ const sftp = new Client();
+ sftp.on("error", (errors) =>
+ logger.log("claimscorp-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
+ );
+ try {
+ //Connect to the FTP and upload all.
+ await sftp.connect(ftpSetup);
+
+ for (const xmlObj of allxmlsToUpload) {
+ try {
+ xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
+ logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
+ filename: xmlObj.filename,
+ result: xmlObj.result
+ });
+ } catch (error) {
+ logger.log("claimscorp-sftp-upload-error", "ERROR", "api", null, {
+ filename: xmlObj.filename,
+ error: error.message,
+ stack: error.stack
+ });
+ throw error;
+ }
+ }
+ } catch (error) {
+ logger.log("claimscorp-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ throw error;
+ } finally {
+ sftp.end();
+ }
+}
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
@@ -445,10 +455,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
- logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, {
- error
- });
-
+ logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};
diff --git a/server/data/kaizen.js b/server/data/kaizen.js
index c9794acff..b79fe4745 100644
--- a/server/data/kaizen.js
+++ b/server/data/kaizen.js
@@ -16,8 +16,7 @@ const { sendServerEmail } = require("../email/sendemail");
const DineroFormat = "0,0.00";
const DateFormat = "MM/DD/YYYY";
-const repairOpCodes = ["OP4", "OP9", "OP10"];
-const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
+const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
const ftpSetup = {
host: process.env.KAIZEN_HOST,
@@ -30,173 +29,179 @@ const ftpSetup = {
}
};
+const allxmlsToUpload = [];
+const allErrors = [];
+
exports.default = async (req, res) => {
// Only process if in production environment.
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
-
- //Query for the List of Bodyshop Clients.
- logger.log("kaizen-start", "DEBUG", "api", null, null);
- const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
-
- const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, {
- imexshopid: kaizenShopsIDs
- });
-
- const specificShopIds = req.body.bodyshopIds; // ['uuid]
- const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+ // Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
- const allxmlsToUpload = [];
- const allErrors = [];
+
+ // Send immediate response and continue processing.
+ res.status(202).json({
+ success: true,
+ message: "Processing request ...",
+ timestamp: new Date().toISOString()
+ });
+
try {
- for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
+ logger.log("kaizen-start", "DEBUG", "api", null, null);
+ const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, { imexshopid: kaizenShopsIDs }); //Query for the List of Bodyshop Clients.
+ const specificShopIds = req.body.bodyshopIds; // ['uuid];
+ const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+
+ const batchSize = 10;
+
+ const shopsToProcess =
+ specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
+ logger.log("kaizen-shopsToProcess-generated", "DEBUG", "api", null, null);
+
+ if (shopsToProcess.length === 0) {
+ logger.log("kaizen-shopsToProcess-empty", "DEBUG", "api", null, null);
+ return;
+ }
+ const batchPromises = [];
+ for (let i = 0; i < shopsToProcess.length; i += batchSize) {
+ const batch = shopsToProcess.slice(i, i + batchSize);
+ const batchPromise = (async () => {
+ await processBatch(batch, start, end);
+
+ if (skipUpload) {
+ for (const xmlObj of allxmlsToUpload) {
+ fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
+ }
+ } else {
+ await uploadViaSFTP(allxmlsToUpload);
+ }
+ })();
+ batchPromises.push(batchPromise);
+ }
+ await Promise.all(batchPromises);
+ await sendServerEmail({
+ subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
+ text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
+ allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
+ null,
+ 2
+ )}`
+ });
+
+ logger.log("kaizen-end", "DEBUG", "api", null, null);
+ } catch (error) {
+ logger.log("kaizen-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ }
+};
+
+async function processBatch(batch, start, end) {
+ for (const bodyshop of batch) {
+ const erroredJobs = [];
+ try {
logger.log("kaizen-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
- const erroredJobs = [];
- try {
- const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
- bodyshopid: bodyshop.id,
- start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
- ...(end && { end: moment(end).endOf("day") })
- });
- const kaizenObject = {
- DataFeed: {
- ShopInfo: {
- ShopName: bodyshops_by_pk.shopname,
- Jobs: jobs.map((j) =>
- CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
- erroredJobs.push({ job: job, error: error.toString() });
- })
- )
- }
- }
- };
-
- if (erroredJobs.length > 0) {
- logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
- count: erroredJobs.length,
- jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
- });
- }
-
- var ret = builder
- .create(
- {
- // version: "1.0",
- // encoding: "UTF-8",
- //keepNullNodes: true,
- },
- kaizenObject
- )
- .end({ allowEmptyTags: true });
-
- allxmlsToUpload.push({
- count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
- xml: ret,
- filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
- });
-
- logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
- shopname: bodyshop.shopname
- });
- } catch (error) {
- //Error at the shop level.
- logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, {
- ...error
- });
-
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- shopname: bodyshop.shopname,
- fatal: true,
- errors: [error.toString()]
- });
- } finally {
- allErrors.push({
- bodyshopid: bodyshop.id,
- imexshopid: bodyshop.imexshopid,
- shopname: bodyshop.shopname,
- errors: erroredJobs.map((ej) => ({
- ro_number: ej.job?.ro_number,
- jobid: ej.job?.id,
- error: ej.error
- }))
- });
- }
- }
-
- if (skipUpload) {
- for (const xmlObj of allxmlsToUpload) {
- fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
- }
-
- res.json(allxmlsToUpload);
- sendServerEmail({
- subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
+ const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
+ bodyshopid: bodyshop.id,
+ start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
+ ...(end && { end: moment(end).endOf("day") })
});
- return;
- }
- let sftp = new Client();
- sftp.on("error", (errors) =>
- logger.log("kaizen-sftp-error", "ERROR", "api", null, {
- ...errors
- })
- );
- try {
- //Connect to the FTP and upload all.
+ const kaizenObject = {
+ DataFeed: {
+ ShopInfo: {
+ ShopName: bodyshops_by_pk.shopname,
+ Jobs: jobs.map((j) =>
+ CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
+ erroredJobs.push({ job: job, error: error.toString() });
+ })
+ )
+ }
+ }
+ };
- await sftp.connect(ftpSetup);
-
- for (const xmlObj of allxmlsToUpload) {
- logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
- filename: xmlObj.filename
- });
-
- const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
- logger.log("kaizen-sftp-upload-result", "DEBUG", "api", null, {
- uploadResult
+ if (erroredJobs.length > 0) {
+ logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
+ count: erroredJobs.length,
+ jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
- //***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
+ const ret = builder.create({}, kaizenObject).end({ allowEmptyTags: true });
+
+ allxmlsToUpload.push({
+ count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
+ xml: ret,
+ filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
+ });
+
+ logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
+ shopname: bodyshop.shopname
+ });
} catch (error) {
- logger.log("kaizen-sftp-error", "ERROR", "api", null, {
- ...error
+ //Error at the shop level.
+ logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
+
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ shopname: bodyshop.shopname,
+ fatal: true,
+ errors: [error.toString()]
});
} finally {
- sftp.end();
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ shopname: bodyshop.shopname,
+ errors: erroredJobs.map((ej) => ({
+ ro_number: ej.job?.ro_number,
+ jobid: ej.job?.id,
+ error: ej.error
+ }))
+ });
}
- sendServerEmail({
- subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
- text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
- Uploaded: ${JSON.stringify(
- allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
- null,
- 2
- )}
- `
- });
- res.sendStatus(200);
- } catch (error) {
- res.status(200).json(error);
}
-};
+}
+
+async function uploadViaSFTP(allxmlsToUpload) {
+ const sftp = new Client();
+ sftp.on("error", (errors) =>
+ logger.log("kaizen-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
+ );
+ try {
+ //Connect to the FTP and upload all.
+ await sftp.connect(ftpSetup);
+
+ for (const xmlObj of allxmlsToUpload) {
+ try {
+ xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
+ logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
+ filename: xmlObj.filename,
+ result: xmlObj.result
+ });
+ } catch (error) {
+ logger.log("kaizen-sftp-upload-error", "ERROR", "api", null, {
+ filename: xmlObj.filename,
+ error: error.message,
+ stack: error.stack
+ });
+ throw error;
+ }
+ }
+ } catch (error) {
+ logger.log("kaizen-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
+ throw error;
+ } finally {
+ sftp.end();
+ }
+}
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
@@ -420,10 +425,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
- logger.log("kaizen-job-calculate-error", "ERROR", "api", null, {
- error
- });
-
+ logger.log("kaizen-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};
diff --git a/server/email/mailer.js b/server/email/mailer.js
index 654f56cb3..6134053b6 100644
--- a/server/email/mailer.js
+++ b/server/email/mailer.js
@@ -1,6 +1,6 @@
const { isString, isEmpty } = require("lodash");
const { defaultProvider } = require("@aws-sdk/credential-provider-node");
-const { default: InstanceManager } = require("../utils/instanceMgr");
+const { InstanceRegion } = require("../utils/instanceMgr");
const aws = require("@aws-sdk/client-ses");
const nodemailer = require("nodemailer");
const logger = require("../utils/logger");
@@ -10,12 +10,7 @@ const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.e
const sesConfig = {
apiVersion: "latest",
credentials: defaultProvider(),
- region: isLocal
- ? "ca-central-1"
- : InstanceManager({
- imex: "ca-central-1",
- rome: "us-east-2"
- })
+ region: InstanceRegion()
};
if (isLocal) {
diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js
index c231f8261..080deec90 100644
--- a/server/intellipay/intellipay.js
+++ b/server/intellipay/intellipay.js
@@ -17,12 +17,10 @@ require("dotenv").config({
const domain = process.env.NODE_ENV ? "secure" : "test";
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
+const { InstanceRegion } = require("../utils/instanceMgr");
const client = new SecretsManagerClient({
- region: InstanceManager({
- imex: "ca-central-1",
- rome: "us-east-2"
- })
+ region: InstanceRegion()
});
const gqlClient = require("../graphql-client/graphql-client").client;
diff --git a/server/job/job-lifecycle.js b/server/job/job-lifecycle.js
index 744639bcf..7076069f6 100644
--- a/server/job/job-lifecycle.js
+++ b/server/job/job-lifecycle.js
@@ -78,16 +78,20 @@ const jobLifecycle = async (req, res) => {
Object.keys(flatGroupedAllDurations).forEach((status) => {
const value = flatGroupedAllDurations[status].reduce((acc, curr) => acc + curr.value, 0);
const humanReadable = durationToHumanReadable(moment.duration(value));
- const percentage = (value / finalTotal) * 100;
+ const percentage = finalTotal > 0 ? (value / finalTotal) * 100 : 0;
const color = getLifecycleStatusColor(status);
const roundedPercentage = `${Math.round(percentage)}%`;
+ const averageValue = _.size(jobIDs) > 0 ? value / jobIDs.length : 0;
+ const averageHumanReadable = durationToHumanReadable(moment.duration(averageValue));
finalSummations.push({
status,
value,
humanReadable,
percentage,
color,
- roundedPercentage
+ roundedPercentage,
+ averageValue,
+ averageHumanReadable
});
});
@@ -100,7 +104,12 @@ const jobLifecycle = async (req, res) => {
totalStatuses: finalSummations.length,
total: finalTotal,
statusCounts: finalStatusCounts,
- humanReadable: durationToHumanReadable(moment.duration(finalTotal))
+ humanReadable: durationToHumanReadable(moment.duration(finalTotal)),
+ averageValue: _.size(jobIDs) > 0 ? finalTotal / jobIDs.length : 0,
+ averageHumanReadable:
+ _.size(jobIDs) > 0
+ ? durationToHumanReadable(moment.duration(finalTotal / jobIDs.length))
+ : durationToHumanReadable(moment.duration(0))
}
});
};
diff --git a/server/routes/miscellaneousRoutes.js b/server/routes/miscellaneousRoutes.js
index fded5f44b..ff643a4b0 100644
--- a/server/routes/miscellaneousRoutes.js
+++ b/server/routes/miscellaneousRoutes.js
@@ -12,9 +12,10 @@ const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebas
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails");
const { canvastest } = require("../render/canvas-handler");
+const { alertCheck } = require("../alerts/alertcheck");
//Test route to ensure Express is responding.
-router.get("/test", async function (req, res) {
+router.get("/test", eventAuthorizationMiddleware, async function (req, res) {
const commit = require("child_process").execSync("git rev-parse --short HEAD");
// console.log(app.get('trust proxy'));
// console.log("remoteAddress", req.socket.remoteAddress);
@@ -31,6 +32,32 @@ router.get("/test", async function (req, res) {
res.status(200).send(`OK - ${commit}`);
});
+router.get("/test-logs", eventAuthorizationMiddleware, (req, res) => {
+ const { logger } = req;
+ // // Test 1: Log with a message that exceeds the size limit, triggering an upload to S3.
+ const largeMessage = "A".repeat(256 * 1024 + 1); // Message larger than the log size limit
+ logger.log(largeMessage, "error", "user123", null, { detail: "large log entry" });
+
+ // Test 2: Log with a message that is within the size limit, should log directly using winston.
+ const smallMessage = "A small log message";
+ logger.log(smallMessage, "info", "user123", null, { detail: "small log entry" });
+
+ // Test 3: Log with the `upload` flag set to `true`, forcing the log to be uploaded to S3.
+ logger.log(
+ "This log will be uploaded to S3 regardless of size",
+ "warning",
+ "user123",
+ null,
+ { detail: "upload log" },
+ true
+ );
+
+ // Test 4: Log with a message that doesn't exceed the size limit and doesn't require an upload.
+ logger.log("Normal log entry", "debug", "user123", { id: 4 }, { detail: "normal log entry" });
+
+ return res.status(500).send("Logs tested.");
+});
+
// Search
router.post("/search", validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, os.search);
router.post("/opensearch", eventAuthorizationMiddleware, os.handler);
@@ -53,4 +80,7 @@ router.post("/taskHandler", validateFirebaseIdTokenMiddleware, taskHandler.taskH
// Canvas Test
router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest);
+// Alert Check
+router.post("/alertcheck", eventAuthorizationMiddleware, alertCheck);
+
module.exports = router;
diff --git a/server/utils/instanceMgr.js b/server/utils/instanceMgr.js
index e83e32661..6a01e6904 100644
--- a/server/utils/instanceMgr.js
+++ b/server/utils/instanceMgr.js
@@ -44,4 +44,10 @@ function InstanceManager({ args, instance, debug, executeFunction, rome, promana
return propToReturn === undefined ? null : propToReturn;
}
+exports.InstanceRegion = () =>
+ InstanceManager({
+ imex: "ca-central-1",
+ rome: "us-east-2"
+ });
+
exports.default = InstanceManager;
diff --git a/server/utils/logger.js b/server/utils/logger.js
index bfb0c0cda..b6f9e865c 100644
--- a/server/utils/logger.js
+++ b/server/utils/logger.js
@@ -9,6 +9,9 @@ const winston = require("winston");
const WinstonCloudWatch = require("winston-cloudwatch");
const { isString, isEmpty } = require("lodash");
const { networkInterfaces, hostname } = require("node:os");
+const { uploadFileToS3 } = require("./s3");
+const { v4 } = require("uuid");
+const { InstanceRegion } = require("./instanceMgr");
const LOG_LEVELS = {
error: { level: 0, name: "error" },
@@ -20,6 +23,30 @@ const LOG_LEVELS = {
silly: { level: 6, name: "silly" }
};
+const LOG_LENGTH_LIMIT = 256 * 1024; // 256KB
+
+const S3_BUCKET_NAME = InstanceManager({
+ imex: "imex-large-log",
+ rome: "rome-large-log"
+});
+
+const region = InstanceRegion();
+
+const estimateLogSize = (logEntry) => {
+ let estimatedSize = 0;
+ for (const key in logEntry) {
+ if (logEntry.hasOwnProperty(key)) {
+ const value = logEntry[key];
+ if (value === undefined || value === null) {
+ estimatedSize += key.length; // Only count the key length if value is undefined or null
+ } else {
+ estimatedSize += key.length + (typeof value === "string" ? value.length : JSON.stringify(value).length);
+ }
+ }
+ }
+ return estimatedSize;
+};
+
const normalizeLevel = (level) => (level ? level.toLowerCase() : LOG_LEVELS.debug.name);
const createLogger = () => {
@@ -30,10 +57,7 @@ const createLogger = () => {
const winstonCloudwatchTransportDefaults = {
logGroupName: logGroupName,
awsOptions: {
- region: InstanceManager({
- imex: "ca-central-1",
- rome: "us-east-2"
- })
+ region
},
jsonMessage: true
};
@@ -124,15 +148,66 @@ const createLogger = () => {
);
}
- const log = (message, type, user, record, meta) => {
- winstonLogger.log({
+ const log = (message, type, user, record, meta, upload) => {
+ const logEntry = {
level: normalizeLevel(type),
message,
user,
record,
hostname: internalHostname,
meta
- });
+ };
+
+ const uploadLogToS3 = (logEntry, message, type, user) => {
+ const uniqueId = v4();
+ const dateTimeString = new Date().toISOString().replace(/:/g, "-");
+ const envName = process.env?.NODE_ENV ? process.env.NODE_ENV : "";
+ const logStreamName = `${envName}-${internalHostname}-${dateTimeString}-${uniqueId}.json`;
+ const logString = JSON.stringify(logEntry);
+ const webPath = isLocal
+ ? `https://${S3_BUCKET_NAME}.s3.localhost.localstack.cloud:4566/${logStreamName}`
+ : `https://${S3_BUCKET_NAME}.s3.${region}.amazonaws.com/${logStreamName}`;
+
+ uploadFileToS3({ bucketName: S3_BUCKET_NAME, key: logStreamName, content: logString })
+ .then(() => {
+ log("A log file has been uploaded to S3", "info", "S3", null, {
+ logStreamName,
+ webPath,
+ message: message?.slice(0, 200),
+ type,
+ user
+ });
+ })
+ .catch((err) => {
+ log("Error in S3 Upload", "error", "S3", null, {
+ logStreamName,
+ webPath,
+ message: message?.slice(0, 100),
+ type,
+ user,
+ errorMessage: err?.message?.slice(0, 100)
+ });
+ });
+ };
+
+ const checkAndUploadLog = () => {
+ const estimatedSize = estimateLogSize(logEntry);
+
+ if (estimatedSize > LOG_LENGTH_LIMIT * 0.9 || estimatedSize > LOG_LENGTH_LIMIT) {
+ uploadLogToS3(logEntry, message, type, user);
+ return true;
+ }
+ return false;
+ };
+ // Upload log immediately if upload is true, otherwise check the log size.
+ if (upload) {
+ uploadLogToS3(logEntry, message, type, user);
+ return;
+ }
+
+ if (checkAndUploadLog()) return;
+
+ winstonLogger.log(logEntry);
};
return {
diff --git a/server/utils/s3.js b/server/utils/s3.js
new file mode 100644
index 000000000..8b9251e03
--- /dev/null
+++ b/server/utils/s3.js
@@ -0,0 +1,109 @@
+const {
+ S3Client,
+ PutObjectCommand,
+ GetObjectCommand,
+ ListObjectsV2Command,
+ DeleteObjectCommand,
+ CopyObjectCommand
+} = require("@aws-sdk/client-s3");
+const { defaultProvider } = require("@aws-sdk/credential-provider-node");
+const { InstanceRegion } = require("./instanceMgr");
+const { isString, isEmpty } = require("lodash");
+
+const createS3Client = () => {
+ const S3Options = {
+ region: InstanceRegion(),
+ credentials: defaultProvider()
+ };
+
+ const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME);
+
+ if (isLocal) {
+ S3Options.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`;
+ S3Options.forcePathStyle = true; // Needed for LocalStack to avoid bucket name as hostname
+ }
+
+ const s3Client = new S3Client(S3Options);
+
+ /**
+ * Uploads a file to the specified S3 bucket and key.
+ */
+ const uploadFileToS3 = async ({ bucketName, key, content, contentType }) => {
+ const params = {
+ Bucket: bucketName,
+ Key: key,
+ Body: content,
+ ContentType: contentType ?? "application/json"
+ };
+ const command = new PutObjectCommand(params);
+ return await s3Client.send(command);
+ };
+
+ /**
+ * Downloads a file from the specified S3 bucket and key.
+ */
+ const downloadFileFromS3 = async ({ bucketName, key }) => {
+ const params = { Bucket: bucketName, Key: key };
+ const command = new GetObjectCommand(params);
+ const data = await s3Client.send(command);
+ return data.Body;
+ };
+
+ /**
+ * Lists objects in the specified S3 bucket.
+ */
+ const listFilesInS3Bucket = async (bucketName, prefix = "") => {
+ const params = { Bucket: bucketName, Prefix: prefix };
+ const command = new ListObjectsV2Command(params);
+ const data = await s3Client.send(command);
+ return data.Contents || [];
+ };
+
+ /**
+ * Deletes a file from the specified S3 bucket and key.
+ */
+ const deleteFileFromS3 = async ({ bucketName, key }) => {
+ const params = { Bucket: bucketName, Key: key };
+ const command = new DeleteObjectCommand(params);
+ return await s3Client.send(command);
+ };
+
+ /**
+ * Copies a file within S3 from a source bucket/key to a destination bucket/key.
+ */
+ const copyFileInS3 = async ({ sourceBucket, sourceKey, destinationBucket, destinationKey }) => {
+ const params = {
+ CopySource: `/${sourceBucket}/${sourceKey}`,
+ Bucket: destinationBucket,
+ Key: destinationKey
+ };
+ const command = new CopyObjectCommand(params);
+ return await s3Client.send(command);
+ };
+
+ /**
+ * Checks if a file exists in the specified S3 bucket and key.
+ */
+ const fileExistsInS3 = async ({ bucketName, key }) => {
+ try {
+ await downloadFileFromS3({ bucketName, key });
+ return true;
+ } catch (error) {
+ if (error.name === "NoSuchKey" || error.name === "NotFound") {
+ return false;
+ }
+ throw error;
+ }
+ };
+ return {
+ uploadFileToS3,
+ downloadFileFromS3,
+ listFilesInS3Bucket,
+ deleteFileFromS3,
+ copyFileInS3,
+ fileExistsInS3,
+ ...s3Client
+ };
+};
+
+module.exports = createS3Client();
diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js
index c5e5012c8..1e468c387 100644
--- a/server/web-sockets/web-socket.js
+++ b/server/web-sockets/web-socket.js
@@ -155,10 +155,17 @@ function createJsonEvent(socket, level, message, json) {
message
});
}
- logger.log("ws-log-event-json", level, socket.user.email, socket.recordid, {
- wsmessage: message,
- json
- });
+ logger.log(
+ "ws-log-event-json",
+ level,
+ socket.user.email,
+ socket.recordid,
+ {
+ wsmessage: message,
+ json
+ },
+ true
+ );
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
@@ -189,7 +196,8 @@ function createXmlEvent(socket, xml, message, isError = false) {
{
wsmessage: message,
xml
- }
+ },
+ true
);
if (socket.logEvents && isArray(socket.logEvents)) {
diff --git a/upload/.gitignore b/upload/.gitignore
new file mode 100644
index 000000000..d6b7ef32c
--- /dev/null
+++ b/upload/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore