diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..17605d357 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +# Directories to exclude +.circleci +.idea +.platform +.vscode +_reference +client +redis/dockerdata +hasura +node_modules +# Files to exclude +.ebignore +.editorconfig +.eslintrc.json +.gitignore +.prettierrc.js +Dockerfile +README.MD +bodyshop_translations.babel +docker-compose.yml +ecosystem.config.js + +# Optional: Exclude logs and temporary files +*.log diff --git a/.localstack/.gitkeep b/.localstack/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.platform/nginx/conf.d/proxy.conf b/.platform/nginx/conf.d/proxy.conf index ae3fb47f6..2dc60b344 100644 --- a/.platform/nginx/conf.d/proxy.conf +++ b/.platform/nginx/conf.d/proxy.conf @@ -1 +1,2 @@ -client_max_body_size 50M; \ No newline at end of file +client_max_body_size 50M; +client_body_buffer_size 5M; diff --git a/.vscode/launch.json b/.vscode/launch.json index 032b2ea99..e9045d2fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,21 @@ "request": "launch", "url": "http://localhost:3000", "webRoot": "${workspaceRoot}/client/src" + }, + { + "name": "Attach to Node.js in Docker", + "type": "node", + "request": "attach", + "address": "localhost", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "/app", + "protocol": "inspector", + "restart": true, + "sourceMaps": true, + "skipFiles": [ + "/**" + ] } ] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..b1d253808 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +# Use Amazon Linux 2023 as the base image +FROM amazonlinux:2023 + +# Install Git and Node.js (Amazon Linux 2023 uses the DNF package manager) +RUN dnf install -y git \ + && curl -sL https://rpm.nodesource.com/setup_20.x | bash - \ + && dnf install -y nodejs \ + && dnf clean all + + +# Install dependencies required by node-canvas +RUN dnf install -y \ + gcc \ + gcc-c++ \ + cairo-devel \ + pango-devel \ + libjpeg-turbo-devel \ + giflib-devel \ + libpng-devel \ + make \ + python3 \ + python3-pip \ + && dnf clean all + +# Set the working directory +WORKDIR /app + +# This is because our test route uses a git commit hash +RUN git config --global --add safe.directory /app + +# Copy package.json and package-lock.json +COPY package.json ./ + +# Install Nodemon +RUN npm install -g nodemon + +# Install dependencies +RUN npm i --no-package-lock + +# Copy the rest of your application code +COPY . . + +# Expose the port your app runs on (adjust if necessary) +EXPOSE 4000 9229 + +# Start the application +CMD ["nodemon", "--legacy-watch", "--inspect=0.0.0.0:9229", "server.js"] diff --git a/_reference/DeploymentChecklist.md b/_reference/Documents/DeploymentChecklist.md similarity index 100% rename from _reference/DeploymentChecklist.md rename to _reference/Documents/DeploymentChecklist.md diff --git a/_reference/JSReportSetup.md b/_reference/Documents/JSReportSetup.md similarity index 100% rename from _reference/JSReportSetup.md rename to _reference/Documents/JSReportSetup.md diff --git a/_reference/Jest CheatSheet.md b/_reference/Documents/Jest CheatSheet.md similarity index 100% rename from _reference/Jest CheatSheet.md rename to _reference/Documents/Jest CheatSheet.md diff --git a/_reference/New Hasura Deployment.md b/_reference/Documents/New Hasura Deployment.md similarity index 100% rename from _reference/New Hasura Deployment.md rename to _reference/Documents/New Hasura Deployment.md diff --git a/_reference/Responsibility Center Setup.md b/_reference/Documents/Responsibility Center Setup.md similarity index 100% rename from _reference/Responsibility Center Setup.md rename to _reference/Documents/Responsibility Center Setup.md diff --git a/_reference/SampleMetadata.md b/_reference/Documents/SampleMetadata.md similarity index 100% rename from _reference/SampleMetadata.md rename to _reference/Documents/SampleMetadata.md diff --git a/_reference/TeamsSlackInvestigationFinalVersion.md b/_reference/Documents/TeamsSlackInvestigationFinalVersion.md similarity index 100% rename from _reference/TeamsSlackInvestigationFinalVersion.md rename to _reference/Documents/TeamsSlackInvestigationFinalVersion.md diff --git a/_reference/Documents/dockerreadme.md b/_reference/Documents/dockerreadme.md new file mode 100644 index 000000000..d1201564d --- /dev/null +++ b/_reference/Documents/dockerreadme.md @@ -0,0 +1,64 @@ +# Setting up External Networking and Static IP for WSL2 using Hyper-V + +This guide will walk you through the steps to configure your WSL2 (Windows Subsystem for Linux) instance to use an external Hyper-V virtual switch, enabling it to connect directly to your local network. Additionally, you'll learn how to assign a static IP address to your WSL2 instance. + +## Prerequisites + +1. **Windows 11** +2. **Docker Desktop For Windows (Latest Version) + +# Docker Setup +Inside the root of the project exists the `docker-compose.yaml` file, you can simply run +`docker-compose up` to launch the backend. + +Things to note: +- When installing NPM packages, you will need to rebuild the `node-app` container +- Making changes to the server files will restart the `node-app` + +# Local Stack +- LocalStack Front end (Optional) - https://apps.microsoft.com/detail/9ntrnft9zws2?hl=en-us&gl=US +- http://localhost:4566/_aws/ses will allow you to see emails sent + +# Docker Commands + +## General `docker-compose` Commands: +1. Bring up the services, force a rebuild of all services, and do not use the cache: `docker-compose up --build --no-cache` +2. Start Containers in Detached Mode: This will run the containers in the background (detached mode): `docker-compose up -d` +3. Stop and Remove Containers: Stops and removes the containers gracefully: `docker-compose down` +4. Stop containers without removing them: `docker-compose stop` +5. Remove Containers, Volumes, and Networks: `docker-compose down --volumes` +6. Force rebuild of containers: `docker-compose build --no-cache` +7. View running Containers: `docker-compose ps` +8. View a specific containers logs: `docker-compose logs ` +9. Scale services (multiple instances of a service): `docker-compose up --scale = -d` +10. Watch a specific containers logs in realtime with timestamps: `docker-compose logs -f --timestamps ` + +## Volume Management Commands +1. List Docker volumes: `docker volume ls` +2. Remove Unused volumes `docker volume prune` +3. Remove specific volumes `docker volume rm ` +4. Inspect a volume: `docker volume inspect ` + +## Container Image Management Commands: +1. List running containers: `docker ps` +2. List all containers: `docker os -a` +3. Remove Stopped containers: `docker container prune` +4. Remove a specific container: `docker container rm ` +5. Remove a specific image: `docker rmi :` +6. Remove all unused images: `docker image prune -a` + +## Network Management Commands: +1. List networks: `docker network ls` +2. Inspect a specific network: `docker network inspect ` +3. Remove a specific network: `docker network rm ` +4. Remove unused networks: `docker network prune` + +## Debugging and maintenance: +1. Enter a Running container: `docker exec -it /bin/bash` (could also be `/bin/sh` or for example `redis-cli` on a redis node) +2. View container resource usage: `docker stats` +3. Check Disk space used by Docker: `docker system df` +4. Remove all unused Data (Nuclear option): `docker system prune` + +## Specific examples +1. To simulate a Clean state, one should run `docker system prune` followed by `docker volume prune -a` +2. You can run `docker-compose up` without the `-d` option, and you will get what is identical to the experience you were used to, this includes being able to control-c and bring the entire stack down diff --git a/_reference/dropletSetup.md b/_reference/Documents/dropletSetup.md similarity index 100% rename from _reference/dropletSetup.md rename to _reference/Documents/dropletSetup.md diff --git a/_reference/firebase.md b/_reference/Documents/firebase.md similarity index 100% rename from _reference/firebase.md rename to _reference/Documents/firebase.md diff --git a/_reference/productionBoardNotes.md b/_reference/Documents/productionBoardNotes.md similarity index 100% rename from _reference/productionBoardNotes.md rename to _reference/Documents/productionBoardNotes.md diff --git a/_reference/reportFiltersAndSorters.md b/_reference/Documents/reportFiltersAndSorters.md similarity index 100% rename from _reference/reportFiltersAndSorters.md rename to _reference/Documents/reportFiltersAndSorters.md diff --git a/_reference/test api setup.md b/_reference/Documents/test api setup.md similarity index 100% rename from _reference/test api setup.md rename to _reference/Documents/test api setup.md diff --git a/_reference/BodyshopFeatures.json b/_reference/JSON/BodyshopFeatures.json similarity index 100% rename from _reference/BodyshopFeatures.json rename to _reference/JSON/BodyshopFeatures.json diff --git a/_reference/CiecaOpCodesReference.json b/_reference/JSON/CiecaOpCodesReference.json similarity index 100% rename from _reference/CiecaOpCodesReference.json rename to _reference/JSON/CiecaOpCodesReference.json diff --git a/_reference/Test_CDK_Acct Config.json b/_reference/JSON/Test_CDK_Acct Config.json similarity index 100% rename from _reference/Test_CDK_Acct Config.json rename to _reference/JSON/Test_CDK_Acct Config.json diff --git a/_reference/AuditTriggerFunctions.sql b/_reference/SQL/AuditTriggerFunctions.sql similarity index 100% rename from _reference/AuditTriggerFunctions.sql rename to _reference/SQL/AuditTriggerFunctions.sql diff --git a/_reference/localEmailViewer/.gitignore b/_reference/localEmailViewer/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/_reference/localEmailViewer/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/_reference/localEmailViewer/README.md b/_reference/localEmailViewer/README.md new file mode 100644 index 000000000..3f8a6fc71 --- /dev/null +++ b/_reference/localEmailViewer/README.md @@ -0,0 +1,7 @@ +This will connect to your dockers local stack session and render the email in HTML. + +```shell +node index.js +``` + +http://localhost:3334 diff --git a/_reference/localEmailViewer/index.js b/_reference/localEmailViewer/index.js new file mode 100644 index 000000000..2b4c21444 --- /dev/null +++ b/_reference/localEmailViewer/index.js @@ -0,0 +1,116 @@ +// index.js + +import express from 'express'; +import fetch from 'node-fetch'; +import {simpleParser} from 'mailparser'; + +const app = express(); +const PORT = 3334; + +app.get('/', async (req, res) => { + try { + const response = await fetch('http://localhost:4566/_aws/ses'); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const data = await response.json(); + const messagesHtml = await parseMessages(data.messages); + res.send(renderHtml(messagesHtml)); + } catch (error) { + console.error('Error fetching messages:', error); + res.status(500).send('Error fetching messages'); + } +}); + +async function parseMessages(messages) { + const parsedMessages = await Promise.all( + messages.map(async (message, index) => { + try { + const parsed = await simpleParser(message.RawData); + return ` +
+
+
+ Message ${index + 1} +
+
+ From: ${message.Source} +
+
+ Region: ${message.Region} +
+
+ Timestamp: ${message.Timestamp} +
+
+
+ ${parsed.html || parsed.textAsHtml || 'No HTML content available'} +
+
+ `; + } catch (error) { + console.error('Error parsing email:', error); + return ` +
+
+ Message ${index + 1} +
+
+ From: ${message.Source} +
+
+ Region: ${message.Region} +
+
+ Timestamp: ${message.Timestamp} +
+
+ Error parsing email content +
+
+ `; + } + }) + ); + return parsedMessages.join(''); +} + +function renderHtml(messagesHtml) { + return ` + + + + + + Email Messages Viewer + + + + +
+

Email Messages Viewer

+
+ ${messagesHtml} +
+
+ + + `; +} + +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); \ No newline at end of file diff --git a/_reference/localEmailViewer/package-lock.json b/_reference/localEmailViewer/package-lock.json new file mode 100644 index 000000000..d8d53da2e --- /dev/null +++ b/_reference/localEmailViewer/package-lock.json @@ -0,0 +1,1214 @@ +{ + "name": "localemailviewer", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "localemailviewer", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.21.1", + "mailparser": "^3.7.1", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-japanese": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.1.0.tgz", + "integrity": "sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w==", + "license": "MIT", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "license": "MIT", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", + "license": "MIT" + }, + "node_modules/libmime": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.5.tgz", + "integrity": "sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg==", + "license": "MIT", + "dependencies": { + "encoding-japanese": "2.1.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.0", + "libqp": "2.1.0" + } + }, + "node_modules/libmime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libqp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.0.tgz", + "integrity": "sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==", + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/mailparser": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.1.tgz", + "integrity": "sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==", + "license": "MIT", + "dependencies": { + "encoding-japanese": "2.1.0", + "he": "1.2.0", + "html-to-text": "9.0.5", + "iconv-lite": "0.6.3", + "libmime": "5.3.5", + "linkify-it": "5.0.0", + "mailsplit": "5.4.0", + "nodemailer": "6.9.13", + "punycode.js": "2.3.1", + "tlds": "1.252.0" + } + }, + "node_modules/mailparser/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mailsplit": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.0.tgz", + "integrity": "sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==", + "license": "(MIT OR EUPL-1.1+)", + "dependencies": { + "libbase64": "1.2.1", + "libmime": "5.2.0", + "libqp": "2.0.1" + } + }, + "node_modules/mailsplit/node_modules/encoding-japanese": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", + "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", + "license": "MIT", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mailsplit/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mailsplit/node_modules/libbase64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", + "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==", + "license": "MIT" + }, + "node_modules/mailsplit/node_modules/libmime": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", + "integrity": "sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==", + "license": "MIT", + "dependencies": { + "encoding-japanese": "2.0.0", + "iconv-lite": "0.6.3", + "libbase64": "1.2.1", + "libqp": "2.0.1" + } + }, + "node_modules/mailsplit/node_modules/libqp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", + "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==", + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/nodemailer": { + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "license": "MIT", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "license": "MIT", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tlds": { + "version": "1.252.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.252.0.tgz", + "integrity": "sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==", + "license": "MIT", + "bin": { + "tlds": "bin.js" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + } + } +} diff --git a/_reference/localEmailViewer/package.json b/_reference/localEmailViewer/package.json new file mode 100644 index 000000000..ad795b60f --- /dev/null +++ b/_reference/localEmailViewer/package.json @@ -0,0 +1,18 @@ +{ + "name": "localemailviewer", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "express": "^4.21.1", + "mailparser": "^3.7.1", + "node-fetch": "^3.3.2" + } +} diff --git a/_reference/prHelper.html b/_reference/prHelper/index.html similarity index 100% rename from _reference/prHelper.html rename to _reference/prHelper/index.html diff --git a/certs/id_rsa b/certs/id_rsa new file mode 100644 index 000000000..5e60af253 --- /dev/null +++ b/certs/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAvNl5fuVmLNv72BZNxnTqX5CHf5Xi8UxjYaYxHITSCx7blnhpVYLd +qXvcOWXzbsfjch/den73QiW4n2FYz75oGMhUGlOYzdWKA9I9Sj09Qy1R06RhwDiZGd5qaM +swEeXpkNmi2u4Qd2kJeDfUQUigjC09V81O/vrniGtQAJScfiG/itdm+Ufn09Z4MYk0HWjq +iDokNEskoEPsibYIrb+Q6vdtuPkZO+wU/smXhPtgw5ST6oQdmm/gVNsRg5XNzxrire+z1G +WatnnVL3hPnnfpnf8W589dyms7GGJwhPerSGTN1bn0T4+9C69Cd7LBJtxiuFdRmdlGLLLP +RR48Rur71wAAA9AEfVsdBH1bHQAAAAdzc2gtcnNhAAABAQC82Xl+5WYs2/vYFk3GdOpfkI +d/leLxTGNhpjEchNILHtuWeGlVgt2pe9w5ZfNux+NyH916fvdCJbifYVjPvmgYyFQaU5jN +1YoD0j1KPT1DLVHTpGHAOJkZ3mpoyzAR5emQ2aLa7hB3aQl4N9RBSKCMLT1XzU7++ueIa1 +AAlJx+Ib+K12b5R+fT1ngxiTQdaOqIOiQ0SySgQ+yJtgitv5Dq9224+Rk77BT+yZeE+2DD +lJPqhB2ab+BU2xGDlc3PGuKt77PUZZq2edUveE+ed+md/xbnz13KazsYYnCE96tIZM3Vuf +RPj70Lr0J3ssEm3GK4V1GZ2UYsss9FHjxG6vvXAAAAAwEAAQAAAQAQTosSLQbMmtY9S3e9 +yjyusdExcCTfhyQRu4MEHmfws+JsNMuLqbgwOVTD1AzYJQR7x0qdmDcLjCxL/uDnV16vvS +Sd/Vf1dhnryIyoS29tzI0DRG94ZKq7tBvmHp1w/jRT4KcSVnovhW9e5Rs74+SRFhr06PKI +S+wQOIv48Nwue9+QUMsMCpWgKXHx7SHNTHvnAfqdhi9O29SWlMA+v+mELZ5Cl+HU0UTt2I +A1BxOe1N8FjN7KE2viJexsl3is1PuqMkpLl/wyHBJTVzUadl6DRALJQIm7/YO5goE72YOV +Lpo27do3zjhC87dlKdATvZUzfKV0LuUVdxq/PNDZMUbBAAAAgQDShAqDZiDrdTUaGXfUVm +QzcnVNbh2/KgZh4uux9QNHST562W6cnN7qxoRwVrM4BCOk1Kl73QQZW4nDvXX3PVC5j038 +8AXkcBHS9j9f4h72ue7D2jqlbHFa7aGU9zYgk9mbBF+GX3tDntkAIQjLtwOLfj1iiJ/clX +mHFUAY1V4L8AAAAIEA3E4t/v0yU5D9AOI0r17UNYqfeyDoKAEDR4QbbFjO1l0kLnEJy7Zx +Mhj18GilYg2y0P0v8dSM/oWXS8Hua2t5i9Exlv6gHhGlQ80mwYcVGIxewZ/pPeCPw0U+kt +EKUjt09m9Oe7+6xHQsTBj9hY8/vqPmQwRalZFcLdhHiDiVKTcAAACBANtykaPXdVzEFx7D +UOlsjVL7zM0EVOFXf9JJQ6BhazhmsEI2PYt3IpgGMo8cXkoUofAOIYjf421AabN1BqSO5J +XTMxM0ZV3JmLLi804Mu9h1iFrVTBdLYOMJdc2VCo1EwHWpo9SXOyjxce/znvcIOU04aZhu +TaPg816X+E+gw5JhAAAAFGRhdmVARGF2ZVJpY2hlci1JTUVYAQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/certs/id_rsa.pub b/certs/id_rsa.pub new file mode 100644 index 000000000..11c1d78dc --- /dev/null +++ b/certs/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC82Xl+5WYs2/vYFk3GdOpfkId/leLxTGNhpjEchNILHtuWeGlVgt2pe9w5ZfNux+NyH916fvdCJbifYVjPvmgYyFQaU5jN1YoD0j1KPT1DLVHTpGHAOJkZ3mpoyzAR5emQ2aLa7hB3aQl4N9RBSKCMLT1XzU7++ueIa1AAlJx+Ib+K12b5R+fT1ngxiTQdaOqIOiQ0SySgQ+yJtgitv5Dq9224+Rk77BT+yZeE+2DDlJPqhB2ab+BU2xGDlc3PGuKt77PUZZq2edUveE+ed+md/xbnz13KazsYYnCE96tIZM3VufRPj70Lr0J3ssEm3GK4V1GZ2UYsss9FHjxG6vvX dave@DaveRicher-IMEX diff --git a/client/src/App/App.container.jsx b/client/src/App/App.container.jsx index e4df37c61..d6f3a53fd 100644 --- a/client/src/App/App.container.jsx +++ b/client/src/App/App.container.jsx @@ -2,8 +2,6 @@ import { ApolloProvider } from "@apollo/client"; import { SplitFactoryProvider, SplitSdk } from "@splitsoftware/splitio-react"; import { ConfigProvider } from "antd"; import enLocale from "antd/es/locale/en_US"; -import dayjs from "../utils/day"; -import "dayjs/locale/en"; import React from "react"; import { useTranslation } from "react-i18next"; import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component"; @@ -19,8 +17,6 @@ if (import.meta.env.DEV) { Userpilot.initialize("NX-69145f08"); } -dayjs.locale("en"); - const config = { core: { authorizationKey: import.meta.env.VITE_APP_SPLIT_API, diff --git a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx index f501b3dbb..1e21ebe34 100644 --- a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx +++ b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx @@ -9,7 +9,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils"; import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { DateFormatter } from "../../utils/DateFormatter"; -import { pageLimit } from "../../utils/config"; +import { exportPageLimit } from "../../utils/config"; import { alphaSort, dateSort } from "../../utils/sorters"; import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component"; import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component"; @@ -175,7 +175,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref { setFieldsValue({ @@ -105,7 +112,7 @@ export function BillEnterModalLinesComponent({ title: t("billlines.fields.line_desc"), dataIndex: "line_desc", editable: true, - + width: "20rem", formItemProps: (field) => { return { key: `${field.index}line_desc`, @@ -119,7 +126,7 @@ export function BillEnterModalLinesComponent({ ] }; }, - formInput: (record, index) => + formInput: (record, index) => }, { title: t("billlines.fields.quantity"), diff --git a/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx b/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx index a52bfd145..0da329d6f 100644 --- a/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx +++ b/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx @@ -11,7 +11,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, disabled={disabled} ref={ref} showSearch - popupMatchSelectWidth={false} + popupMatchSelectWidth={true} optionLabelProp={"name"} // optionFilterProp="line_desc" filterOption={(inputValue, option) => { @@ -43,7 +43,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, item.oem_partno ? ` - ${item.oem_partno}` : "" }${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(), label: ( - <> +
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${ item.oem_partno ? ` - ${item.oem_partno}` : "" @@ -57,7 +57,7 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, {item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``} - +
) })) ]} diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx index c608f8b11..d557ad91d 100644 --- a/client/src/components/chat-popup/chat-popup.component.jsx +++ b/client/src/components/chat-popup/chat-popup.component.jsx @@ -45,7 +45,7 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh if (fcmToken) { setpollInterval(0); } else { - setpollInterval(60000); + setpollInterval(90000); } }, [fcmToken]); diff --git a/client/src/components/contract-form/contract-form.component.jsx b/client/src/components/contract-form/contract-form.component.jsx index 1795449f4..b71d7f92f 100644 --- a/client/src/components/contract-form/contract-form.component.jsx +++ b/client/src/components/contract-form/contract-form.component.jsx @@ -1,15 +1,17 @@ import { WarningFilled } from "@ant-design/icons"; import { Form, Input, InputNumber, Space } from "antd"; -import dayjs from "../../utils/day"; import React from "react"; import { useTranslation } from "react-i18next"; import { DateFormatter } from "../../utils/DateFormatter"; +import dayjs from "../../utils/day"; //import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component"; import ContractStatusSelector from "../contract-status-select/contract-status-select.component"; import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component"; import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component"; -import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component"; -import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component"; +import { + default as DateTimePicker, + default as FormDateTimePicker +} from "../form-date-time-picker/form-date-time-picker.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import InputPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; @@ -18,10 +20,10 @@ import ContractFormJobPrefill from "./contract-form-job-prefill.component"; export default function ContractFormComponent({ form, create = false, selectedJobState, selectedCar }) { const { t } = useTranslation(); return ( -
- + <> + {!create && } - {create ? null : ( + {!create && ( - {create ? null : ( + {!create && ( @@ -122,7 +124,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo }} )} - {create ? null : ( + {!create && ( @@ -145,25 +147,21 @@ export default function ContractFormComponent({ form, create = false, selectedJo > - {create ? null : ( + {!create && ( )} -
- - {selectedJobState && ( -
- -
- )} - { - // - } -
-
+ + {create && selectedJobState && ( + + )} + {/* {} */} + + + + <> {t("contracts.labels.dlexpirebeforereturn")} )} -
+ ); }} - @@ -315,6 +311,6 @@ export default function ContractFormComponent({ form, create = false, selectedJo - + ); } diff --git a/client/src/components/dms-log-events/dms-log-events.component.jsx b/client/src/components/dms-log-events/dms-log-events.component.jsx index 037664900..b73e5dbe7 100644 --- a/client/src/components/dms-log-events/dms-log-events.component.jsx +++ b/client/src/components/dms-log-events/dms-log-events.component.jsx @@ -40,13 +40,11 @@ export function DmsLogEvents({ socket, logs, bodyshop }) { function LogLevelHierarchy(level) { switch (level) { - case "TRACE": - return "pink"; case "DEBUG": return "orange"; case "INFO": return "blue"; - case "WARNING": + case "WARN": return "yellow"; case "ERROR": return "red"; diff --git a/client/src/components/eula/eula.component.jsx b/client/src/components/eula/eula.component.jsx index 8d2227e02..399a52b40 100644 --- a/client/src/components/eula/eula.component.jsx +++ b/client/src/components/eula/eula.component.jsx @@ -8,7 +8,7 @@ import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries"; import { useMutation } from "@apollo/client"; import { acceptEula } from "../../redux/user/user.actions"; import { useTranslation } from "react-i18next"; -import day from "../../utils/day"; +import dayjs from "../../utils/day"; import "./eula.styles.scss"; import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; @@ -208,7 +208,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => ( { required: true, validator: (_, value) => { - if (day(value).isSame(day(), "day")) { + if (dayjs(value).isSame(dayjs(), "day")) { return Promise.resolve(); } return Promise.reject(new Error(t("eula.messages.date_accepted"))); 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 e34ae14f7..c9bbc0dd2 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 @@ -2,21 +2,38 @@ import { DatePicker } from "antd"; import PropTypes from "prop-types"; import React, { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors.js"; import dayjs from "../../utils/day"; import { fuzzyMatchDate } from "./formats.js"; -const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, isDateOnly = false, ...restProps }) => { +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop +}); + +const DateTimePicker = ({ + value, + onChange, + onBlur, + id, + onlyFuture, + onlyToday, + isDateOnly = false, + bodyshop, + ...restProps +}) => { const [isManualInput, setIsManualInput] = useState(false); const { t } = useTranslation(); const handleChange = useCallback( (newDate) => { if (onChange) { - onChange(newDate || null); + onChange(bodyshop?.timezone && newDate ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate); } setIsManualInput(false); }, - [onChange] + [onChange, bodyshop?.timezone] ); const handleBlur = useCallback( @@ -102,4 +119,4 @@ DateTimePicker.propTypes = { isDateOnly: PropTypes.bool }; -export default React.memo(DateTimePicker); +export default connect(mapStateToProps, null)(DateTimePicker); diff --git a/client/src/components/global-search/global-search-os.component.jsx b/client/src/components/global-search/global-search-os.component.jsx index f46a4c811..e8e51b3ac 100644 --- a/client/src/components/global-search/global-search-os.component.jsx +++ b/client/src/components/global-search/global-search-os.component.jsx @@ -3,13 +3,15 @@ import axios from "axios"; import _ from "lodash"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import PhoneNumberFormatter from "../../utils/PhoneFormatter"; import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component"; import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component"; export default function GlobalSearchOs() { const { t } = useTranslation(); + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); const [data, setData] = useState(false); @@ -177,7 +179,18 @@ export default function GlobalSearchOs() { }; return ( - setData([])}> + { + if (e.key !== "Enter") return; + const firstUrlForSearch = data?.[0]?.options?.[0]?.label?.props?.to; + if (!firstUrlForSearch) return; + navigate(firstUrlForSearch); + }} + defaultActiveFirstOption + onClear={() => setData([])} + > { if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v); @@ -20,7 +21,6 @@ export default function GlobalSearch() { const debouncedExecuteSearch = _.debounce(executeSearch, 750); const handleSearch = (value) => { - console.log("Handle Search"); debouncedExecuteSearch({ variables: { search: value } }); }; @@ -156,7 +156,17 @@ export default function GlobalSearch() { if (error) return ; return ( - + { + if (e.key !== "Enter") return; + const firstUrlForSearch = options?.[0]?.options?.[0]?.label?.props?.to; + if (!firstUrlForSearch) return; + navigate(firstUrlForSearch); + }} + > { - const cookieExists = document.cookie.split("; ").some((row) => row.startsWith(`betaSwitchImex=`)); - if (cookieExists) { - const domain = window.location.hostname.split(".").slice(-2).join("."); - document.cookie = `betaSwitchImex=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${domain}`; - console.log(`betaSwitchImex cookie deleted`); - } else { - console.log(`betaSwitchImex cookie does not exist`); - } - }; - - deleteBetaCookie(); + // const deleteBetaCookie = () => { + // const cookieExists = document.cookie.split("; ").some((row) => row.startsWith(`betaSwitchImex=`)); + // if (cookieExists) { + // const domain = window.location.hostname.split(".").slice(-2).join("."); + // document.cookie = `betaSwitchImex=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${domain}`; + // } + // }; + // + // deleteBetaCookie(); const accountingChildren = []; diff --git a/client/src/components/job-at-change/schedule-event.component.jsx b/client/src/components/job-at-change/schedule-event.component.jsx index b34e90668..e7f70ff65 100644 --- a/client/src/components/job-at-change/schedule-event.component.jsx +++ b/client/src/components/job-at-change/schedule-event.component.jsx @@ -23,6 +23,7 @@ import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventNote from "./schedule-event.note.component"; import { useMutation } from "@apollo/client"; import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries"; +import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -127,6 +128,9 @@ export function ScheduleEventComponent({ {(event.job && event.job.alt_transport) || ""} + + + ) : ( @@ -316,6 +320,7 @@ export function ScheduleEventComponent({ })`} {event.job && event.job.alt_transport &&
{event.job.alt_transport}
} + {event?.job?.comment && `C: ${event.job.comment}`} ) : (
DateTimeFormatterFunction(text), - sorter: (a, b) => day(a.start).unix() - day(b.start).unix() + sorter: (a, b) => dayjs(a.start).unix() - dayjs(b.start).unix() }, { title: t("job_lifecycle.columns.relative_start"), @@ -90,7 +90,7 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { } return isEmpty(a.end) ? 1 : -1; } - return day(a.end).unix() - day(b.end).unix(); + return dayjs(a.end).unix() - dayjs(b.end).unix(); }, render: (text) => (isEmpty(text) ? t("job_lifecycle.content.not_available") : DateTimeFormatterFunction(text)) }, diff --git a/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx b/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx index 8e570ab2d..ce0b6e4fe 100644 --- a/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx +++ b/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { useMutation } from "@apollo/client"; import { Button, Form, notification, Popover, Select, Space } from "antd"; -import day from "../../utils/day"; +import dayjs from "../../utils/day"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -48,7 +48,7 @@ export function JobLineDispatchButton({ const result = await dispatchLines({ variables: { partsDispatch: { - dispatched_at: day(), + dispatched_at: dayjs(), employeeid: values.employeeid, jobid: job.id, dispatched_by: currentUser.email, @@ -138,7 +138,11 @@ export function JobLineDispatchButton({ return ( - diff --git a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx index 39cf2a2af..76f8b52bb 100644 --- a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx +++ b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx @@ -1,5 +1,8 @@ import { useMutation } from "@apollo/client"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { notification } from "antd"; +import Axios from "axios"; +import Dinero from "dinero.js"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -7,13 +10,10 @@ import { createStructuredSelector } from "reselect"; import { INSERT_NEW_JOB_LINE, UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries"; import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { selectJobLineEditModal } from "../../redux/modals/modals.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import CriticalPartsScan from "../../utils/criticalPartsScan"; import UndefinedToNull from "../../utils/undefinedtonull"; import JobLinesUpdsertModal from "./job-lines-upsert-modal.component"; -import Axios from "axios"; -import Dinero from "dinero.js"; -import CriticalPartsScan from "../../utils/criticalPartsScan"; -import { selectBodyshop } from "../../redux/user/user.selectors"; -import { useSplitTreatments } from "@splitsoftware/splitio-react"; const mapStateToProps = createStructuredSelector({ jobLineEditModal: selectJobLineEditModal, @@ -82,13 +82,15 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo variables: { lineId: jobLineEditModal.context.id, line: { - ...values, - prt_dsmk_m: Dinero({ - amount: Math.round(values.act_price * 100) + ...UndefinedToNull({ + ...values, + prt_dsmk_m: Dinero({ + amount: Math.round(values.act_price * 100) + }) + .percentage(Math.abs(values.prt_dsmk_p || 0)) + .multiply(values.prt_dsmk_p >= 0 ? 1 : -1) + .toFormat(0.0) }) - .percentage(Math.abs(values.prt_dsmk_p || 0)) - .multiply(values.prt_dsmk_p >= 0 ? 1 : -1) - .toFormat(0.0) } }, refetchQueries: ["GET_LINE_TICKET_BY_PK"] diff --git a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx index 70f7763f2..e149f39f0 100644 --- a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx +++ b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx @@ -219,7 +219,7 @@ export function JobsExportAllButton({ }; return ( - ); diff --git a/client/src/components/parts-dispatch-expander/parts-dispatch-expander.component.jsx b/client/src/components/parts-dispatch-expander/parts-dispatch-expander.component.jsx index 477240357..2a84527cc 100644 --- a/client/src/components/parts-dispatch-expander/parts-dispatch-expander.component.jsx +++ b/client/src/components/parts-dispatch-expander/parts-dispatch-expander.component.jsx @@ -1,6 +1,6 @@ import { useMutation } from "@apollo/client"; import { Button, Card, Col, notification, Row, Table } from "antd"; -import day from "../../utils/day"; +import dayjs from "../../utils/day"; import React from "react"; import { useTranslation } from "react-i18next"; import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries"; @@ -11,7 +11,7 @@ export default function PartsDispatchExpander({ dispatch, job }) { const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE); const handleAccept = async ({ partsDispatchLineId }) => { - const accepted_at = day(); + const accepted_at = dayjs(); const result = await updateDispatchLine({ variables: { id: partsDispatchLineId, line: { accepted_at } }, optimisticResponse: { diff --git a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx index 31234c1d1..18eeef23f 100644 --- a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx +++ b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx @@ -242,7 +242,8 @@ export function PartsOrderListTableComponent({ title: t("general.labels.actions"), dataIndex: "actions", key: "actions", - render: (text, record) => recordActions(record, true) + render: (text, record) => recordActions(record, true), + id: "parts-order-list-table-actions" } ]; diff --git a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx index 4194983bb..dc8316be8 100644 --- a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx +++ b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx @@ -200,7 +200,7 @@ export function PayableExportAll({ ); return ( - ); diff --git a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx index ba3334d14..5c4246890 100644 --- a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx +++ b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx @@ -180,7 +180,7 @@ export function PaymentsExportAllButton({ }; return ( - ); diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx index 0c515132b..00333198c 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx @@ -1,12 +1,14 @@ import { SyncOutlined } from "@ant-design/icons"; -import { useApolloClient } from "@apollo/client"; -import Board from "./trello-board/index"; -import { Button, notification, Skeleton, Space } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; +import { useApolloClient } from "@apollo/client"; +import { Button, notification, Skeleton, Space } from "antd"; +import cloneDeep from "lodash/cloneDeep"; +import isEqual from "lodash/isEqual"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; @@ -15,14 +17,13 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings"; import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component"; import ProductionBoardFilters from "../production-board-filters/production-board-filters.component"; import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component"; +import ProductionListPrint from "../production-list-table/production-list-print.component.jsx"; import CardColorLegend from "./production-board-kanban-card-color-legend.component.jsx"; import "./production-board-kanban.styles.scss"; import { createBoardData } from "./production-board-kanban.utils.js"; -import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx"; -import cloneDeep from "lodash/cloneDeep"; -import isEqual from "lodash/isEqual"; import { defaultFilters, mergeWithDefaults } from "./settings/defaultKanbanSettings.js"; -import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container"; +import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx"; +import Board from "./trello-board/index"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -185,7 +186,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr const cardSettings = useMemo(() => { const kanbanSettings = associationSettings?.kanban_settings; return mergeWithDefaults(kanbanSettings); - }, [associationSettings]); + }, [associationSettings?.kanban_settings]); const handleSettingsChange = () => { setFilter(defaultFilters); @@ -214,6 +215,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr bodyshop={bodyshop} data={data} /> + } /> diff --git a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js index 87b364ea2..c9a8c59c6 100644 --- a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js +++ b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js @@ -1,15 +1,66 @@ +import InstanceRenderManager from "../../../utils/instanceRenderMgr.js"; + const statisticsItems = [ { id: 0, name: "totalHrs", label: "total_hours_in_production" }, { id: 1, name: "totalAmountInProduction", label: "total_amount_in_production" }, { id: 2, name: "totalLAB", label: "total_lab_in_production" }, { id: 3, name: "totalLAR", label: "total_lar_in_production" }, { id: 4, name: "jobsInProduction", label: "jobs_in_production" }, - { id: 5, name: "totalHrsOnBoard", label: "total_hours_on_board" }, - { id: 6, name: "totalAmountOnBoard", label: "total_amount_on_board" }, - { id: 7, name: "totalLABOnBoard", label: "total_lab_on_board" }, - { id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" }, - { id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" }, - { id: 10, name: "tasksOnBoard", label: "tasks_on_board" }, + + { + id: 5, + name: "totalHrsOnBoard", + label: InstanceRenderManager({ + imex: "total_hours_in_view", + rome: "total_hours_on_board", + promanager: "total_hours_on_board" + }) + }, + { + id: 6, + name: "totalAmountOnBoard", + label: InstanceRenderManager({ + imex: "total_amount_in_view", + rome: "total_amount_on_board", + promanager: "total_amount_on_board" + }) + }, + { + id: 7, + name: "totalLABOnBoard", + label: InstanceRenderManager({ + imex: "total_lab_in_view", + rome: "total_lab_on_board", + promanager: "total_lab_on_board" + }) + }, + { + id: 8, + name: "totalLAROnBoard", + label: InstanceRenderManager({ + imex: "total_lar_in_view", + rome: "total_lar_on_board", + promanager: "total_lar_on_board" + }) + }, + { + id: 9, + name: "jobsOnBoard", + label: InstanceRenderManager({ + imex: "total_jobs_in_view", + rome: "total_jobs_on_board", + promanager: "total_jobs_on_board" + }) + }, + { + id: 10, + name: "tasksOnBoard", + label: InstanceRenderManager({ + imex: "tasks_in_view", + rome: "tasks_on_board", + promanager: "tasks_on_board" + }) + }, { id: 11, name: "tasksInProduction", label: "tasks_in_production" } ]; diff --git a/client/src/components/production-list-columns/production-list-columns.status.component.jsx b/client/src/components/production-list-columns/production-list-columns.status.component.jsx index 23dc378ef..820f7f80a 100644 --- a/client/src/components/production-list-columns/production-list-columns.status.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.status.component.jsx @@ -21,25 +21,26 @@ export function ProductionListColumnStatus({ record, bodyshop, insertAuditTrail const [loading, setLoading] = useState(false); const handleSetStatus = async (e) => { - logImEXEvent("production_change_status"); - // e.stopPropagation(); - setLoading(true); - const { key } = e; - await updateJob({ - variables: { - jobId: record.id, - job: { - status: key + if (bodyshop.md_ro_statuses.production_statuses.includes(record.status) && !bodyshop.md_ro_statuses.post_production_statuses.includes(record.status)) { + logImEXEvent("production_change_status"); + // e.stopPropagation(); + setLoading(true); + const { key } = e; + await updateJob({ + variables: { + jobId: record.id, + job: { + status: key + } } - } - }); - insertAuditTrail({ - jobid: record.id, - operation: AuditTrailMapping.jobstatuschange(key), - type: "jobstatuschange" - }); - - setLoading(false); + }); + insertAuditTrail({ + jobid: record.id, + operation: AuditTrailMapping.jobstatuschange(key), + type: "jobstatuschange" + }); + setLoading(false); + } }; const menu = { diff --git a/client/src/components/production-list-table/production-list-config-manager.component.jsx b/client/src/components/production-list-table/production-list-config-manager.component.jsx index b34fb92d7..ef8b07af7 100644 --- a/client/src/components/production-list-table/production-list-config-manager.component.jsx +++ b/client/src/components/production-list-table/production-list-config-manager.component.jsx @@ -457,41 +457,42 @@ export function ProductionListConfigManager({ value={activeView} disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile > - {bodyshop.production_config - .slice() - .sort((a, b) => - a.name === t("production.constants.main_profile") - ? -1 - : b.name === t("production.constants.main_profile") - ? 1 - : 0 - ) // - .map((config) => ( - -
- - {config.name} - - {config.name !== t("production.constants.main_profile") && ( - handleTrash(config.name)} - onCancel={(e) => e.stopPropagation()} + {bodyshop?.production_config && + bodyshop.production_config + .slice() + .sort((a, b) => + a.name === t("production.constants.main_profile") + ? -1 + : b.name === t("production.constants.main_profile") + ? 1 + : 0 + ) // + .map((config) => ( + +
+ - e.stopPropagation()} /> - - )} -
-
- ))} + {config.name} + + {config.name !== t("production.constants.main_profile") && ( + handleTrash(config.name)} + onCancel={(e) => e.stopPropagation()} + > + e.stopPropagation()} /> + + )} +
+
+ ))}
diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx index ea3d9c2a2..09e41b5e8 100644 --- a/client/src/components/production-list-table/production-list-table.component.jsx +++ b/client/src/components/production-list-table/production-list-table.component.jsx @@ -51,8 +51,8 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici const initialColumnsRef = useRef( (initialStateRef.current && - bodyshop.production_config - .find((p) => p.name === defaultView) + bodyshop?.production_config + ?.find((p) => p.name === defaultView) ?.columns.columnKeys.map((k) => { return { ...ProductionListColumns({ @@ -76,7 +76,7 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici const { t } = useTranslation(); const matchingColumnConfig = useMemo(() => { - return bodyshop.production_config.find((p) => p.name === defaultView); + return bodyshop?.production_config?.find((p) => p.name === defaultView); }, [bodyshop.production_config, defaultView]); useEffect(() => { diff --git a/client/src/components/schedule-calendar-wrapper/localizer.js b/client/src/components/schedule-calendar-wrapper/localizer.js index e91016416..9935252b4 100644 --- a/client/src/components/schedule-calendar-wrapper/localizer.js +++ b/client/src/components/schedule-calendar-wrapper/localizer.js @@ -120,14 +120,6 @@ var formats = { }; const localizer = (dayjsLib) => { - // load dayjs plugins - dayjsLib.extend(isBetween); - dayjsLib.extend(isSameOrAfter); - dayjsLib.extend(isSameOrBefore); - dayjsLib.extend(localeData); - dayjsLib.extend(localizedFormat); - dayjsLib.extend(minMax); - dayjsLib.extend(utc); var locale = function locale(dj, c) { return c ? dj.locale(c) : dj; }; @@ -136,7 +128,8 @@ const localizer = (dayjsLib) => { // then use the timezone aware version //TODO This was the issue entirely... - // var dayjs = dayjsLib.tz ? dayjsLib.tz : dayjsLib; + // var dayjs = dayjsLib.tz ? dayjsLib.tz : dayjsLib; + var dayjs = dayjsLib; function getTimezoneOffset(date) { diff --git a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx index fb866d89d..4d6f8fe78 100644 --- a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx +++ b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx @@ -20,6 +20,7 @@ const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, problemJobs: selectProblemJobs }); + const localizer = local(dayjs); export function ScheduleCalendarWrapperComponent({ diff --git a/client/src/components/scoreboard-display/scoreboard-display.component.jsx b/client/src/components/scoreboard-display/scoreboard-display.component.jsx index c74cecdc0..5a4e6fe72 100644 --- a/client/src/components/scoreboard-display/scoreboard-display.component.jsx +++ b/client/src/components/scoreboard-display/scoreboard-display.component.jsx @@ -4,12 +4,12 @@ import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component"; import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component"; import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component"; +import { useApolloClient, useQuery } from "@apollo/client"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { GET_BLOCKED_DAYS, QUERY_SCOREBOARD } from "../../graphql/scoreboard.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import dayjs from "../../utils/day"; -import { useApolloClient, useQuery } from "@apollo/client"; -import { GET_BLOCKED_DAYS, QUERY_SCOREBOARD } from "../../graphql/scoreboard.queries"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -26,7 +26,7 @@ export function ScoreboardDisplayComponent({ bodyshop }) { start: dayjs().startOf("month"), end: dayjs().endOf("month") }, - pollInterval: 60000 + pollInterval: 60000*5 }); const { data } = scoreboardSubscription; diff --git a/client/src/components/scoreboard-timetickets-stats/scoreboard-timetickets.component.jsx b/client/src/components/scoreboard-timetickets-stats/scoreboard-timetickets.component.jsx index 489467740..07e77d479 100644 --- a/client/src/components/scoreboard-timetickets-stats/scoreboard-timetickets.component.jsx +++ b/client/src/components/scoreboard-timetickets-stats/scoreboard-timetickets.component.jsx @@ -1,13 +1,13 @@ import { useQuery } from "@apollo/client"; import { Col, Row } from "antd"; import _ from "lodash"; -import dayjs from "../../utils/day"; import React, { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import dayjs from "../../utils/day"; import AlertComponent from "../alert/alert.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util"; @@ -86,7 +86,7 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) { }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", - pollInterval: 60000, + pollInterval: 60000*5, skip: !fixedPeriods }); diff --git a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx index 2587cb74e..fb970be5d 100644 --- a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx +++ b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx @@ -1,11 +1,11 @@ import { useQuery } from "@apollo/client"; import { Col, Row } from "antd"; import _ from "lodash"; -import dayjs from "../../utils/day"; import queryString from "query-string"; import React, { useMemo } from "react"; import { useLocation } from "react-router-dom"; import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries"; +import dayjs from "../../utils/day"; import AlertComponent from "../alert/alert.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util"; @@ -68,7 +68,7 @@ export default function ScoreboardTimeTickets() { }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", - pollInterval: 60000, + pollInterval: 60000*5, skip: !fixedPeriods }); diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index 57e145761..ba7d71b17 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -1,10 +1,3 @@ -import { Button, Card, Space, Switch, Table } from "antd"; -import queryString from "query-string"; -import React, { useCallback, useEffect } from "react"; -import { useTranslation } from "react-i18next"; -import { Link, useLocation, useNavigate } from "react-router-dom"; -import { pageLimit } from "../../utils/config"; -import dayjs from "../../utils/day"; import { CheckCircleFilled, CheckCircleOutlined, @@ -15,9 +8,16 @@ import { PlusCircleFilled, SyncOutlined } from "@ant-design/icons"; -import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter.jsx"; +import { Button, Card, Space, Switch, Table } from "antd"; +import queryString from "query-string"; +import React, { useCallback, useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { Link, useLocation, useNavigate } from "react-router-dom"; import { setModalContext } from "../../redux/modals/modals.actions"; +import { pageLimit } from "../../utils/config"; +import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter.jsx"; +import dayjs from "../../utils/day"; /** * Task List Component @@ -140,6 +140,17 @@ function TaskListComponent({ render: (text, record) => {record.created_at} }); + columns.push({ + title: t("tasks.fields.created_by"), + dataIndex: "created_by", + key: "created_by", + width: "8%", + defaultSortOrder: "descend", + sorter: true, + sortOrder: sortcolumn === "created_by" && sortorder, + render: (text, record) => record.created_by + }); + if (!onlyMine) { columns.push({ title: t("tasks.fields.assigned_to"), @@ -155,65 +166,70 @@ function TaskListComponent({ }); } - if (showRo) { - columns.push({ - title: t("tasks.fields.job.ro_number"), - dataIndex: ["job", "ro_number"], - key: "job.ro_number", - width: "8%", - render: (text, record) => - record.job ? ( - {record.job.ro_number || t("general.labels.na")} - ) : ( - t("general.labels.na") - ) - }); - } + columns.push({ + title: t("tasks.fields.related_items"), + key: "related_items", + width: "12%", + render: (text, record) => { + const items = []; + + // Job + if (showRo && record.job) { + items.push( + + {t("tasks.fields.job.ro_number")}: {record.job.ro_number} + + ); + } + if (showRo && !record.job) { + items.push(`${t("tasks.fields.job.ro_number")}: ${t("general.labels.na")}`); + } + + // Jobline + if (record.jobline?.line_desc) { + items.push( + + {t("tasks.fields.jobline")}: {record.jobline.line_desc} + + ); + } + + // Parts Order + if (record.parts_order) { + const { order_number, vendor } = record.parts_order; + const partsOrderText = + order_number && vendor?.name ? `${order_number} - ${vendor.name}` : t("general.labels.na"); + items.push( + + {t("tasks.fields.parts_order")}: {partsOrderText} + + ); + } + + // Bill + if (record.bill) { + const { invoice_number, vendor } = record.bill; + const billText = invoice_number && vendor?.name ? `${invoice_number} - ${vendor.name}` : t("general.labels.na"); + items.push( + + {t("tasks.fields.bill")}: {billText} + + ); + } + + return items.length > 0 ? {items} : null; + } + }); columns.push( - { - title: t("tasks.fields.jobline"), - dataIndex: ["jobline", "id"], - key: "jobline.id", - width: "8%", - render: (text, record) => record?.jobline?.line_desc || "" - }, - { - title: t("tasks.fields.parts_order"), - dataIndex: ["parts_order", "id"], - key: "part_order.id", - width: "8%", - render: (text, record) => - record.parts_order ? ( - - {record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name - ? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}` - : t("general.labels.na")} - - ) : ( - "" - ) - }, - { - title: t("tasks.fields.bill"), - dataIndex: ["bill", "id"], - key: "bill.id", - width: "10%", - render: (text, record) => - record.bill ? ( - - {record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name - ? `${record.bill.invoice_number} - ${record.bill.vendor.name}` - : t("general.labels.na")} - - ) : ( - "" - ) - }, { title: t("tasks.fields.title"), dataIndex: "title", key: "title", + minWidth: "20%", sorter: true, sortOrder: sortcolumn === "title" && sortorder }, @@ -247,7 +263,7 @@ function TaskListComponent({ { title: t("tasks.fields.actions"), key: "toggleCompleted", - width: "5%", + width: "8%", render: (text, record) => ( diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx index 9cbc3ea50..3a8ac77b0 100644 --- a/client/src/pages/dms/dms.container.jsx +++ b/client/src/pages/dms/dms.container.jsx @@ -90,7 +90,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse ...logs, { timestamp: new Date(), - level: "WARNING", + level: "warn", message: "Reconnected to CDK Export Service" } ]; @@ -173,10 +173,9 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse socket.emit("set-log-level", value); }} > - TRACE DEBUG INFO - WARNING + WARN ERROR diff --git a/client/src/redux/user/user.sagas.js b/client/src/redux/user/user.sagas.js index b399c0052..423b40283 100644 --- a/client/src/redux/user/user.sagas.js +++ b/client/src/redux/user/user.sagas.js @@ -28,7 +28,7 @@ import { } from "../../firebase/firebase.utils"; import { QUERY_EULA } from "../../graphql/bodyshop.queries"; import client from "../../utils/GraphQLClient"; -import day from "../../utils/day"; +import dayjs from "../../utils/day"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; import { checkInstanceId, @@ -96,7 +96,7 @@ export function* isUserAuthenticated() { const eulaQuery = yield client.query({ query: QUERY_EULA, variables: { - now: day() + now: dayjs() } }); @@ -314,8 +314,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) { try { const userEmail = yield select((state) => state.user.currentUser.email); try { - //console.log("Setting shop timezone."); - // dayjs.tz.setDefault(payload.timezone); + dayjs.tz.setDefault(payload.timezone); } catch (error) { console.log(error); } diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index bd4738eb8..38f13e032 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -119,7 +119,7 @@ "jobclosedwithbypass": "Job was invoiced using the master bypass password. ", "jobconverted": "Job converted and assigned number {{ro_number}}.", "jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.", - "jobexported": "", + "jobexported": "Job has been exported", "jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.", "jobimported": "Job imported.", "jobinproductionchange": "Job production status set to {{inproduction}}", @@ -2849,15 +2849,21 @@ "jobs_in_production": "Jobs in Production", "tasks_in_production": "Tasks in Production", "tasks_on_board": "Tasks on Board", + "tasks_in_view": "Tasks in View", "total_amount_in_production": "Dollars in Production", "total_amount_on_board": "Dollars on Board", + "total_amount_in_view": "Dollars in View", "total_hours_in_production": "Hours in Production", "total_hours_on_board": "Hours on Board", + "total_hours_in_view": "Hours in View", "total_jobs_on_board": "Jobs on Board", + "total_jobs_in_view": "Jobs in View", "total_lab_in_production": "Body Hours in Production", "total_lab_on_board": "Body Hours on Board", + "total_lab_in_view": "Body Hours in View", "total_lar_in_production": "Refinish Hours in Production", - "total_lar_on_board": "Refinish Hours on Board" + "total_lar_on_board": "Refinish Hours on Board", + "total_lar_in_view": "Refinish Hours in View" }, "statistics_title": "Statistics" }, @@ -2869,15 +2875,21 @@ "tasks": "Tasks", "tasks_in_production": "Tasks in Production", "tasks_on_board": "Tasks on Board", + "tasks_in_view": "Tasks in View", "total_amount_in_production": "Dollars in Production", "total_amount_on_board": "Dollars on Board", + "total_amount_in_view": "Dollars in View", "total_hours_in_production": "Hours in Production", "total_hours_on_board": "Hours on Board", + "total_hours_in_view": "Hours in View", "total_jobs_on_board": "Jobs on Board", + "total_jobs_in_view": "Jobs in View", "total_lab_in_production": "Body Hours in Production", "total_lab_on_board": "Body Hours on Board", + "total_lab_in_view": "Body Hours in View", "total_lar_in_production": "Refinish Hours in Production", - "total_lar_on_board": "Refinish Hours on Board" + "total_lar_on_board": "Refinish Hours on Board", + "total_lar_in_view": "Refinish Hours in View" }, "successes": { "removed": "Job removed from production." @@ -3032,6 +3044,7 @@ "production_by_target_date": "Production by Target Date", "production_by_technician": "Production by Technician", "production_by_technician_one": "Production filtered by Technician", + "production_not_production_status": "Production not in Production Status", "production_over_time": "Production Level over Time", "psr_by_make": "Percent of Sales by Vehicle Make", "purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)", @@ -3174,6 +3187,7 @@ "billid": "Bill", "completed": "Completed", "created_at": "Created At", + "created_by": "Created By", "description": "Description", "due_date": "Due Date", "job": { @@ -3190,6 +3204,7 @@ "medium": "Medium" }, "priority": "Priority", + "related_items": "Related Items", "remind_at": "Remind At", "title": "Title" }, diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 6e9074c44..ed7760e9d 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2849,15 +2849,21 @@ "jobs_in_production": "", "tasks_in_production": "", "tasks_on_board": "", + "tasks_in_view": "", "total_amount_in_production": "", "total_amount_on_board": "", + "total_amount_in_view": "", "total_hours_in_production": "", "total_hours_on_board": "", + "total_hours_in_view": "", "total_jobs_on_board": "", + "total_jobs_in_view": "", "total_lab_in_production": "", "total_lab_on_board": "", + "total_lab_in_view": "", "total_lar_in_production": "", - "total_lar_on_board": "" + "total_lar_on_board": "", + "total_lar_in_view": "" }, "statistics_title": "" }, @@ -2869,15 +2875,21 @@ "tasks": "", "tasks_in_production": "", "tasks_on_board": "", + "tasks_in_view": "", "total_amount_in_production": "", "total_amount_on_board": "", + "total_amount_in_view": "", "total_hours_in_production": "", "total_hours_on_board": "", + "total_hours_in_view": "", "total_jobs_on_board": "", + "total_jobs_in_view": "", "total_lab_in_production": "", "total_lab_on_board": "", + "total_lab_in_view": "", "total_lar_in_production": "", - "total_lar_on_board": "" + "total_lar_on_board": "", + "total_lar_in_view": "" }, "successes": { "removed": "" @@ -3032,6 +3044,7 @@ "production_by_target_date": "", "production_by_technician": "", "production_by_technician_one": "", + "production_not_production_status": "", "production_over_time": "", "psr_by_make": "", "purchase_return_ratio_grouped_by_vendor_detail": "", @@ -3174,6 +3187,7 @@ "billid": "", "completed": "", "created_at": "", + "created_by": "", "description": "", "due_date": "", "job": { @@ -3190,6 +3204,7 @@ "medium": "" }, "priority": "", + "related_items": "", "remind_at": "", "title": "" }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 48d5a05af..4a64bcc55 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1,3586 +1,3601 @@ { - "translation": { - "allocations": { - "actions": { - "assign": "Attribuer" - }, - "errors": { - "deleting": "", - "saving": "", - "validation": "" - }, - "fields": { - "employee": "Alloué à" - }, - "successes": { - "deleted": "", - "save": "" - } - }, - "appointments": { - "actions": { - "block": "", - "calculate": "", - "cancel": "annuler", - "intake": "Admission", - "new": "Nouveau rendez-vous", - "preview": "", - "reschedule": "Replanifier", - "sendreminder": "", - "unblock": "", - "viewjob": "Voir le travail" - }, - "errors": { - "blocking": "", - "canceling": "Erreur lors de l'annulation du rendez-vous. {{message}}", - "saving": "Erreur lors de la planification du rendez-vous. {{message}}" - }, - "fields": { - "alt_transport": "", - "color": "", - "end": "", - "note": "", - "start": "", - "time": "", - "title": "Titre" - }, - "labels": { - "arrivedon": "Arrivé le:", - "arrivingjobs": "", - "blocked": "", - "cancelledappointment": "Rendez-vous annulé pour:", - "completingjobs": "", - "dataconsistency": "", - "expectedjobs": "", - "expectedprodhrs": "", - "history": "", - "inproduction": "", - "manualevent": "", - "noarrivingjobs": "", - "nocompletingjobs": "", - "nodateselected": "Aucune date n'a été sélectionnée.", - "priorappointments": "Rendez-vous précédents", - "reminder": "", - "scheduledfor": "Rendez-vous prévu pour:", - "severalerrorsfound": "", - "smartscheduling": "", - "smspaymentreminder": "", - "suggesteddates": "" - }, - "successes": { - "canceled": "Rendez-vous annulé avec succès.", - "created": "Rendez-vous planifié avec succès.", - "saved": "" - } - }, - "associations": { - "actions": { - "activate": "Activer" - }, - "fields": { - "active": "Actif?", - "shopname": "nom de la boutique" - }, - "labels": { - "actions": "actes" - } - }, - "audit": { - "fields": { - "cc": "", - "contents": "", - "created": "", - "operation": "", - "status": "", - "subject": "", - "to": "", - "useremail": "", - "values": "" - } - }, - "audit_trail": { - "messages": { - "admin_job_remove_from_ar": "", - "admin_jobmarkexported": "", - "admin_jobmarkforreexport": "", - "admin_jobuninvoice": "", - "admin_jobunvoid": "", - "alerttoggle": "", - "appointmentcancel": "", - "appointmentinsert": "", - "assignedlinehours": "", - "billdeleted": "", - "billposted": "", - "billupdated": "", - "failedpayment": "", - "jobassignmentchange": "", - "jobassignmentremoved": "", - "jobchecklist": "", - "jobclosedwithbypass": "", - "jobconverted": "", - "jobdelivery": "", - "jobexported": "", - "jobfieldchanged": "", - "jobimported": "", - "jobinproductionchange": "", - "jobintake": "", - "jobinvoiced": "", - "jobioucreated": "", - "jobmodifylbradj": "", - "jobnoteadded": "", - "jobnotedeleted": "", - "jobnoteupdated": "", - "jobspartsorder": "", - "jobspartsreturn": "", - "jobstatuschange": "", - "jobsupplement": "", - "jobsuspend": "", - "jobvoid": "", - "tasks_completed": "", - "tasks_created": "", - "tasks_deleted": "", - "tasks_uncompleted": "", - "tasks_undeleted": "", - "tasks_updated": "" - } - }, - "billlines": { - "actions": { - "newline": "" - }, - "fields": { - "actual_cost": "", - "actual_price": "", - "cost_center": "", - "federal_tax_applicable": "", - "jobline": "", - "line_desc": "", - "local_tax_applicable": "", - "location": "", - "quantity": "", - "state_tax_applicable": "" - }, - "labels": { - "deductedfromlbr": "", - "entered": "", - "from": "", - "mod_lbr_adjustment": "", - "other": "", - "reconciled": "", - "unreconciled": "" - }, - "validation": { - "atleastone": "" - } - }, - "bills": { - "actions": { - "deductallhours": "", - "edit": "", - "receive": "", - "return": "" - }, - "errors": { - "creating": "", - "deleting": "", - "existinginventoryline": "", - "exporting": "", - "exporting-partner": "", - "invalidro": "", - "invalidvendor": "", - "validation": "" - }, - "fields": { - "allpartslocation": "", - "date": "", - "exported": "", - "federal_tax_rate": "", - "invoice_number": "", - "is_credit_memo": "", - "is_credit_memo_short": "", - "local_tax_rate": "", - "ro_number": "", - "state_tax_rate": "", - "total": "", - "vendor": "", - "vendorname": "" - }, - "labels": { - "actions": "", - "bill_lines": "", - "bill_total": "", - "billcmtotal": "", - "bills": "", - "calculatedcreditsnotreceived": "", - "creditsnotreceived": "", - "creditsreceived": "", - "dedfromlbr": "", - "deleteconfirm": "", - "discrepancy": "", - "discrepwithcms": "", - "discrepwithlbradj": "", - "editadjwarning": "", - "entered_total": "", - "enteringcreditmemo": "", - "federal_tax": "", - "federal_tax_exempt": "", - "generatepartslabel": "", - "iouexists": "", - "local_tax": "", - "markexported": "", - "markforreexport": "", - "new": "", - "nobilllines": "", - "noneselected": "", - "onlycmforinvoiced": "", - "printlabels": "", - "retailtotal": "", - "returnfrombill": "", - "savewithdiscrepancy": "", - "state_tax": "", - "subtotal": "", - "totalreturns": "" - }, - "successes": { - "created": "", - "deleted": "", - "exported": "", - "markexported": "", - "reexport": "" - }, - "validation": { - "closingperiod": "", - "inventoryquantity": "", - "manualinhouse": "", - "unique_invoice_number": "" - } - }, - "bodyshop": { - "actions": { - "add_task_preset": "", - "addapptcolor": "", - "addbucket": "", - "addpartslocation": "", - "addpartsrule": "", - "addspeedprint": "", - "addtemplate": "", - "newlaborrate": "", - "newsalestaxcode": "", - "newstatus": "", - "testrender": "" - }, - "errors": { - "creatingdefaultview": "", - "loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique.", - "saving": "" - }, - "fields": { - "ReceivableCustomField": "", - "address1": "", - "address2": "", - "appt_alt_transport": "", - "appt_colors": { - "color": "", - "label": "" - }, - "appt_length": "", - "attach_pdf_to_email": "", - "batchid": "", - "bill_allow_post_to_closed": "", - "bill_federal_tax_rate": "", - "bill_local_tax_rate": "", - "bill_state_tax_rate": "", - "city": "", - "closingperiod": "", - "companycode": "", - "country": "", - "dailybodytarget": "", - "dailypainttarget": "", - "default_adjustment_rate": "", - "deliver": { - "require_actual_delivery_date": "", - "templates": "" - }, - "dms": { - "apcontrol": "", - "appostingaccount": "", - "cashierid": "", - "default_journal": "", - "disablebillwip": "", - "disablecontactvehiclecreation": "", - "dms_acctnumber": "", - "dms_control_override": "", - "dms_wip_acctnumber": "", - "generic_customer_number": "", - "itc_federal": "", - "itc_local": "", - "itc_state": "", - "mappingname": "", - "sendmaterialscosting": "", - "srcco": "" - }, - "email": "", - "enforce_class": "", - "enforce_conversion_category": "", - "enforce_conversion_csr": "", - "enforce_referral": "", - "federal_tax_id": "", - "ignoreblockeddays": "", - "inhousevendorid": "", - "insurance_vendor_id": "", - "intake": { - "next_contact_hours": "", - "templates": "" - }, - "intellipay_config": { - "cash_discount_percentage": "", - "enable_cash_discount": "" - }, - "invoice_federal_tax_rate": "", - "invoice_local_tax_rate": "", - "invoice_state_tax_rate": "", - "jc_hourly_rates": { - "mapa": "", - "mash": "" - }, - "last_name_first": "", - "lastnumberworkingdays": "", - "localmediaserverhttp": "", - "localmediaservernetwork": "", - "localmediatoken": "", - "logo_img_footer_margin": "", - "logo_img_header_margin": "", - "logo_img_path": "", - "logo_img_path_height": "", - "logo_img_path_width": "", - "md_categories": "", - "md_ccc_rates": "", - "md_classes": "", - "md_ded_notes": "", - "md_email_cc": "", - "md_from_emails": "", - "md_functionality_toggles": { - "parts_queue_toggle": "" - }, - "md_hour_split": { - "paint": "", - "prep": "" - }, - "md_ins_co": { - "city": "", - "name": "", - "private": "", - "state": "", - "street1": "", - "street2": "", - "zip": "" - }, - "md_jobline_presets": "", - "md_lost_sale_reasons": "", - "md_parts_order_comment": "", - "md_parts_scan": { - "expression": "", - "flags": "" - }, - "md_payment_types": "", - "md_referral_sources": "", - "md_ro_guard": { - "enabled": "", - "enforce_ar": "", - "enforce_bills": "", - "enforce_cm": "", - "enforce_labor": "", - "enforce_ppd": "", - "enforce_profit": "", - "enforce_sublet": "", - "masterbypass": "", - "totalgppercent_minimum": "" - }, - "md_tasks_presets": { - "enable_tasks": "", - "hourstype": "", - "memo": "", - "name": "", - "nextstatus": "", - "percent": "", - "use_approvals": "" - }, - "messaginglabel": "", - "messagingtext": "", - "noteslabel": "", - "notestext": "", - "partslocation": "", - "phone": "", - "prodtargethrs": "", - "rbac": { - "accounting": { - "exportlog": "", - "payables": "", - "payments": "", - "receivables": "" - }, - "bills": { - "delete": "", - "enter": "", - "list": "", - "reexport": "", - "view": "" - }, - "contracts": { - "create": "", - "detail": "", - "list": "" - }, - "courtesycar": { - "create": "", - "detail": "", - "list": "" - }, - "csi": { - "export": "", - "page": "" - }, - "employee_teams": { - "page": "" - }, - "employees": { - "page": "" - }, - "inventory": { - "delete": "", - "list": "" - }, - "jobs": { - "admin": "", - "available-list": "", - "checklist-view": "", - "close": "", - "create": "", - "deliver": "", - "detail": "", - "intake": "", - "list-active": "", - "list-all": "", - "list-ready": "", - "partsqueue": "", - "void": "" - }, - "owners": { - "detail": "", - "list": "" - }, - "payments": { - "enter": "", - "list": "" - }, - "phonebook": { - "edit": "", - "view": "" - }, - "production": { - "board": "", - "list": "" - }, - "schedule": { - "view": "" - }, - "scoreboard": { - "view": "" - }, - "shiftclock": { - "view": "" - }, - "shop": { - "config": "", - "dashboard": "", - "rbac": "", - "reportcenter": "", - "templates": "", - "vendors": "" - }, - "temporarydocs": { - "view": "" - }, - "timetickets": { - "edit": "", - "editcommitted": "", - "enter": "", - "list": "", - "shiftedit": "" - }, - "ttapprovals": { - "approve": "", - "view": "" - }, - "users": { - "editaccess": "" - } - }, - "responsibilitycenter": "", - "responsibilitycenter_accountdesc": "", - "responsibilitycenter_accountitem": "", - "responsibilitycenter_accountname": "", - "responsibilitycenter_accountnumber": "", - "responsibilitycenter_rate": "", - "responsibilitycenter_tax_rate": "", - "responsibilitycenter_tax_sur": "", - "responsibilitycenter_tax_thres": "", - "responsibilitycenter_tax_tier": "", - "responsibilitycenter_tax_type": "", - "responsibilitycenters": { - "ap": "", - "ar": "", - "ats": "", - "federal_tax": "", - "federal_tax_itc": "", - "gst_override": "", - "invoiceexemptcode": "", - "itemexemptcode": "", - "la1": "", - "la2": "", - "la3": "", - "la4": "", - "laa": "", - "lab": "", - "lad": "", - "lae": "", - "laf": "", - "lag": "", - "lam": "", - "lar": "", - "las": "", - "lau": "", - "local_tax": "", - "mapa": "", - "mash": "", - "paa": "", - "pac": "", - "pag": "", - "pal": "", - "pam": "", - "pan": "", - "pao": "", - "pap": "", - "par": "", - "pas": "", - "pasl": "", - "refund": "", - "sales_tax_codes": { - "code": "", - "description": "", - "federal": "", - "local": "", - "state": "" - }, - "state_tax": "", - "tow": "" - }, - "schedule_end_time": "", - "schedule_start_time": "", - "shopname": "", - "speedprint": { - "id": "", - "label": "", - "templates": "" - }, - "ss_configuration": { - "dailyhrslimit": "" - }, - "ssbuckets": { - "color": "", - "gte": "", - "id": "", - "label": "", - "lt": "", - "target": "" - }, - "state": "", - "state_tax_id": "", - "status": "", - "statuses": { - "active_statuses": "", - "additional_board_statuses": "", - "color": "", - "default_arrived": "", - "default_bo": "", - "default_canceled": "", - "default_completed": "", - "default_delivered": "", - "default_exported": "", - "default_imported": "", - "default_invoiced": "", - "default_ordered": "", - "default_quote": "", - "default_received": "", - "default_returned": "", - "default_scheduled": "", - "default_void": "", - "open_statuses": "", - "post_production_statuses": "", - "pre_production_statuses": "", - "production_colors": "", - "production_statuses": "", - "ready_statuses": "" - }, - "target_touchtime": "", - "timezone": "", - "tt_allow_post_to_invoiced": "", - "tt_enforce_hours_for_tech_console": "", - "use_fippa": "", - "use_paint_scale_data": "", - "uselocalmediaserver": "", - "website": "", - "zip_post": "" - }, - "labels": { - "2tiername": "", - "2tiersetup": "", - "2tiersource": "", - "accountingsetup": "", - "accountingtiers": "", - "alljobstatuses": "", - "allopenjobstatuses": "", - "apptcolors": "", - "businessinformation": "", - "checklists": "", - "csiq": "", - "customtemplates": "", - "defaultcostsmapping": "", - "defaultprofitsmapping": "", - "deliverchecklist": "", - "dms": { - "cdk": { - "controllist": "", - "payers": "" - }, - "cdk_dealerid": "", - "costsmapping": "", - "dms_allocations": "", - "pbs_serialnumber": "", - "profitsmapping": "", - "title": "" - }, - "emaillater": "", - "employee_teams": "", - "employees": "", - "estimators": "", - "filehandlers": "", - "insurancecos": "", - "intakechecklist": "", - "intellipay": "", - "intellipay_cash_discount": "", - "jobstatuses": "", - "laborrates": "", - "licensing": "", - "md_parts_scan": "", - "md_ro_guard": "", - "md_tasks_presets": "", - "md_to_emails": "", - "md_to_emails_emails": "", - "messagingpresets": "", - "notemplatesavailable": "", - "notespresets": "", - "orderstatuses": "", - "partslocations": "", - "partsscan": "", - "printlater": "", - "qbo": "", - "qbo_departmentid": "", - "qbo_usa": "", - "rbac": "", - "responsibilitycenters": { - "costs": "", - "profits": "", - "sales_tax_codes": "", - "tax_accounts": "", - "title": "" - }, - "roguard": { - "title": "" - }, - "scheduling": "", - "scoreboardsetup": "", - "shopinfo": "", - "speedprint": "", - "ssbuckets": "", - "systemsettings": "", - "task-presets": "", - "workingdays": "" - }, - "successes": { - "areyousure": "", - "defaultviewcreated": "", - "save": "", - "unsavedchanges": "" - }, - "validation": { - "centermustexist": "", - "larsplit": "", - "useremailmustexist": "" - } - }, - "checklist": { - "actions": { - "printall": "" - }, - "errors": { - "complete": "", - "nochecklist": "" - }, - "labels": { - "addtoproduction": "", - "allow_text_message": "", - "checklist": "", - "printpack": "", - "removefromproduction": "" - }, - "successes": { - "completed": "" - } - }, - "contracts": { - "actions": { - "changerate": "", - "convertoro": "", - "decodelicense": "", - "find": "", - "printcontract": "", - "senddltoform": "" - }, - "errors": { - "fetchingjobinfo": "", - "returning": "", - "saving": "", - "selectjobandcar": "" - }, - "fields": { - "actax": "", - "actualreturn": "", - "agreementnumber": "", - "cc_cardholder": "", - "cc_expiry": "", - "cc_num": "", - "cleanupcharge": "", - "coverage": "", - "dailyfreekm": "", - "dailyrate": "", - "damage": "", - "damagewaiver": "", - "driver": "", - "driver_addr1": "", - "driver_addr2": "", - "driver_city": "", - "driver_dlexpiry": "", - "driver_dlnumber": "", - "driver_dlst": "", - "driver_dob": "", - "driver_fn": "", - "driver_ln": "", - "driver_ph1": "", - "driver_state": "", - "driver_zip": "", - "excesskmrate": "", - "federaltax": "", - "fuelin": "", - "fuelout": "", - "kmend": "", - "kmstart": "", - "length": "", - "localtax": "", - "refuelcharge": "", - "scheduledreturn": "", - "start": "", - "statetax": "", - "status": "" - }, - "labels": { - "agreement": "", - "availablecars": "", - "cardueforservice": "", - "convertform": { - "applycleanupcharge": "", - "refuelqty": "" - }, - "correctdataonform": "", - "dateinpast": "", - "dlexpirebeforereturn": "", - "driverinformation": "", - "findcontract": "", - "findermodal": "", - "insuranceexpired": "", - "noteconvertedfrom": "", - "populatefromjob": "", - "rates": "", - "time": "", - "vehicle": "", - "waitingforscan": "" - }, - "status": { - "new": "", - "out": "", - "returned": "" - }, - "successes": { - "saved": "" - } - }, - "courtesycars": { - "actions": { - "new": "", - "return": "" - }, - "errors": { - "saving": "" - }, - "fields": { - "color": "", - "dailycost": "", - "damage": "", - "fleetnumber": "", - "fuel": "", - "insuranceexpires": "", - "leaseenddate": "", - "make": "", - "mileage": "", - "model": "", - "nextservicedate": "", - "nextservicekm": "", - "notes": "", - "plate": "", - "purchasedate": "", - "readiness": "", - "registrationexpires": "", - "serviceenddate": "", - "servicestartdate": "", - "status": "", - "vin": "", - "year": "" - }, - "labels": { - "courtesycar": "", - "fuel": { - "12": "", - "14": "", - "18": "", - "34": "", - "38": "", - "58": "", - "78": "", - "empty": "", - "full": "" - }, - "outwith": "", - "return": "", - "status": "", - "uniquefleet": "", - "usage": "", - "vehicle": "" - }, - "readiness": { - "notready": "", - "ready": "" - }, - "status": { - "in": "", - "inservice": "", - "leasereturn": "", - "out": "", - "sold": "", - "unavailable": "" - }, - "successes": { - "saved": "" - } - }, - "csi": { - "actions": { - "activate": "" - }, - "errors": { - "creating": "", - "notconfigured": "", - "notfoundsubtitle": "", - "notfoundtitle": "", - "surveycompletesubtitle": "", - "surveycompletetitle": "" - }, - "fields": { - "completedon": "", - "created_at": "", - "surveyid": "", - "validuntil": "" - }, - "labels": { - "copyright": "", - "greeting": "", - "intro": "", - "nologgedinuser": "", - "nologgedinuser_sub": "", - "noneselected": "", - "title": "" - }, - "successes": { - "created": "", - "submitted": "", - "submittedsub": "" - } - }, - "dashboard": { - "actions": { - "addcomponent": "" - }, - "errors": { - "refreshrequired": "", - "updatinglayout": "" - }, - "labels": { - "bodyhrs": "", - "dollarsinproduction": "", - "phone": "", - "prodhrs": "", - "refhrs": "" - }, - "titles": { - "joblifecycle": "", - "labhours": "", - "larhours": "", - "monthlyemployeeefficiency": "", - "monthlyjobcosting": "", - "monthlylaborsales": "", - "monthlypartssales": "", - "monthlyrevenuegraph": "", - "prodhrssummary": "", - "productiondollars": "", - "productionhours": "", - "projectedmonthlysales": "", - "scheduledindate": "", - "scheduledintoday": "", - "scheduledoutdate": "", - "scheduledouttoday": "", - "tasks": "" - } - }, - "dms": { - "errors": { - "alreadyexported": "" - }, - "labels": { - "refreshallocations": "" - } - }, - "documents": { - "actions": { - "delete": "", - "download": "", - "reassign": "", - "selectallimages": "", - "selectallotherdocuments": "" - }, - "errors": { - "deletes3": "Erreur lors de la suppression du document du stockage.", - "deleting": "", - "deleting_cloudinary": "", - "getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}", - "insert": "Incapable de télécharger le fichier. {{message}}", - "nodocuments": "Il n'y a pas de documents.", - "updating": "" - }, - "labels": { - "confirmdelete": "", - "doctype": "", - "newjobid": "", - "openinexplorer": "", - "optimizedimage": "", - "reassign_limitexceeded": "", - "reassign_limitexceeded_title": "", - "storageexceeded": "", - "storageexceeded_title": "", - "upload": "Télécharger", - "upload_limitexceeded": "", - "upload_limitexceeded_title": "", - "uploading": "", - "usage": "" - }, - "successes": { - "delete": "Le document a bien été supprimé.", - "edituploaded": "", - "insert": "Document téléchargé avec succès.", - "updated": "" - } - }, - "emails": { - "errors": { - "notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}" - }, - "fields": { - "cc": "", - "from": "", - "subject": "", - "to": "" - }, - "labels": { - "attachments": "", - "documents": "", - "emailpreview": "", - "generatingemail": "", - "pdfcopywillbeattached": "", - "preview": "" - }, - "successes": { - "sent": "E-mail envoyé avec succès." - } - }, - "employee_teams": { - "actions": { - "new": "", - "newmember": "" - }, - "fields": { - "active": "", - "employeeid": "", - "max_load": "", - "name": "", - "percentage": "" - } - }, - "employees": { - "actions": { - "addvacation": "", - "new": "Nouvel employé", - "newrate": "" - }, - "errors": { - "delete": "Erreur rencontrée lors de la suppression de l'employé. {{message}}", - "save": "Une erreur s'est produite lors de l'enregistrement de l'employé. {{message}}", - "validation": "Veuillez cocher tous les champs.", - "validationtitle": "Impossible d'enregistrer l'employé." - }, - "fields": { - "active": "Actif?", - "base_rate": "Taux de base", - "cost_center": "Centre de coûts", - "employee_number": "Numéro d'employé", - "external_id": "", - "first_name": "Prénom", - "flat_rate": "Taux fixe (désactivé est le temps normal)", - "hire_date": "Date d'embauche", - "last_name": "Nom de famille", - "pin": "", - "rate": "", - "termination_date": "Date de résiliation", - "user_email": "", - "vacation": { - "end": "", - "length": "", - "start": "" - } - }, - "labels": { - "actions": "", - "active": "", - "endmustbeafterstart": "", - "flat_rate": "", - "inactive": "", - "name": "", - "rate_type": "", - "status": "", - "straight_time": "" - }, - "successes": { - "delete": "L'employé a bien été supprimé.", - "save": "L'employé a enregistré avec succès.", - "vacationadded": "" - }, - "validation": { - "unique_employee_number": "" - } - }, - "eula": { - "buttons": { - "accept": "Accept EULA" - }, - "content": { - "never_scrolled": "You must scroll to the bottom of the Terms and Conditions before accepting." - }, - "errors": { - "acceptance": { - "description": "Something went wrong while accepting the EULA. Please try again.", - "message": "Eula Acceptance Error" - } - }, - "labels": { - "accepted_terms": "I accept the terms and conditions of this agreement.", - "address": "Address", - "business_name": "Legal Business Name", - "date_accepted": "Date Accepted", - "first_name": "First Name", - "last_name": "Last Name", - "phone_number": "Phone Number" - }, - "messages": { - "accepted_terms": "Please accept the terms and conditions of this agreement.", - "business_name": "Please enter your legal business name.", - "date_accepted": "Please enter Today's Date.", - "first_name": "Please enter your first name.", - "last_name": "Please enter your last name.", - "phone_number": "Please enter your phone number." - }, - "titles": { - "modal": "Terms and Conditions", - "upper_card": "Acknowledgement" - } - }, - "exportlogs": { - "fields": { - "createdat": "" - }, - "labels": { - "attempts": "", - "priorsuccesfulexport": "" - } - }, - "general": { - "actions": { - "add": "", - "autoupdate": "", - "calculate": "", - "cancel": "", - "clear": "", - "close": "", - "copied": "", - "copylink": "", - "create": "", - "defaults": "", - "delay": "", - "delete": "Effacer", - "deleteall": "", - "deselectall": "", - "download": "", - "edit": "modifier", - "login": "", - "next": "", - "previous": "", - "print": "", - "refresh": "", - "remove": "", - "remove_alert": "", - "reset": " Rétablir l'original.", - "resetpassword": "", - "save": "sauvegarder", - "saveandnew": "", - "saveas": "", - "selectall": "", - "send": "", - "sendbysms": "", - "senderrortosupport": "", - "submit": "", - "tryagain": "", - "view": "", - "viewreleasenotes": "" - }, - "errors": { - "fcm": "", - "notfound": "", - "sizelimit": "" - }, - "itemtypes": { - "contract": "", - "courtesycar": "", - "job": "", - "owner": "", - "vehicle": "" - }, - "labels": { - "actions": "actes", - "areyousure": "", - "barcode": "code à barre", - "cancel": "", - "clear": "", - "confirmpassword": "", - "created_at": "", - "date": "", - "datetime": "", - "email": "", - "errors": "", - "excel": "", - "exceptiontitle": "", - "friday": "", - "globalsearch": "", - "help": "", - "hours": "", - "in": "dans", - "instanceconflictext": "", - "instanceconflictitle": "", - "item": "", - "label": "", - "loading": "Chargement...", - "loadingapp": "Chargement de {{app}}", - "loadingshop": "Chargement des données de la boutique ...", - "loggingin": "Vous connecter ...", - "markedexported": "", - "media": "", - "message": "", - "monday": "", - "na": "N / A", - "newpassword": "", - "no": "", - "nointernet": "", - "nointernet_sub": "", - "none": "", - "out": "En dehors", - "password": "", - "passwordresetsuccess": "", - "passwordresetsuccess_sub": "", - "passwordresetvalidatesuccess": "", - "passwordresetvalidatesuccess_sub": "", - "passwordsdonotmatch": "", - "print": "", - "refresh": "", - "reports": "", - "required": "", - "saturday": "", - "search": "Chercher...", - "searchresults": "", - "selectdate": "", - "sendagain": "", - "sendby": "", - "signin": "", - "sms": "", - "status": "", - "sub_status": { - "expired": "" - }, - "successful": "", - "sunday": "", - "text": "", - "thursday": "", - "total": "", - "totals": "", - "tuesday": "", - "tvmode": "", - "unknown": "Inconnu", - "unsavedchanges": "", - "username": "", - "view": "", - "wednesday": "", - "yes": "" - }, - "languages": { - "english": "Anglais", - "french": "Francais", - "spanish": "Espanol" - }, - "messages": { - "exception": "", - "newversionmessage": "", - "newversiontitle": "", - "noacctfilepath": "", - "nofeatureaccess": "", - "noshop": "", - "notfoundsub": "", - "notfoundtitle": "", - "partnernotrunning": "", - "rbacunauth": "", - "unsavedchanges": "Vous avez des changements non enregistrés.", - "unsavedchangespopup": "" - }, - "validation": { - "invalidemail": "S'il vous plaît entrer un email valide.", - "invalidphone": "", - "required": "Ce champ est requis." - } - }, - "help": { - "actions": { - "connect": "" - }, - "labels": { - "codeplacholder": "", - "rescuedesc": "", - "rescuetitle": "" - } - }, - "intake": { - "labels": { - "printpack": "" - } - }, - "inventory": { - "actions": { - "addtoinventory": "", - "addtoro": "", - "consumefrominventory": "", - "edit": "", - "new": "" - }, - "errors": { - "inserting": "" - }, - "fields": { - "comment": "", - "manualinvoicenumber": "", - "manualvendor": "" - }, - "labels": { - "consumedbyjob": "", - "deleteconfirm": "", - "frombillinvoicenumber": "", - "fromvendor": "", - "inventory": "", - "showall": "", - "showavailable": "" - }, - "successes": { - "deleted": "", - "inserted": "", - "updated": "" - } - }, - "job_lifecycle": { - "columns": { - "duration": "", - "end": "", - "human_readable": "", - "percentage": "", - "relative_end": "", - "relative_start": "", - "start": "", - "status": "", - "status_count": "", - "value": "" - }, - "content": { - "calculated_based_on": "", - "current_status_accumulated_time": "", - "data_unavailable": "", - "jobs_in_since": "", - "legend_title": "", - "loading": "", - "not_available": "", - "previous_status_accumulated_time": "", - "title": "", - "title_durations": "", - "title_loading": "", - "title_transitions": "" - }, - "errors": { - "fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches" - }, - "titles": { - "dashboard": "", - "top_durations": "" - } - }, - "job_payments": { - "buttons": { - "create_short_link": "", - "goback": "", - "proceedtopayment": "", - "refundpayment": "" - }, - "notifications": { - "error": { - "description": "", - "openingip": "", - "title": "" - } - }, - "titles": { - "amount": "", - "dateOfPayment": "", - "descriptions": "", - "hint": "", - "payer": "", - "payername": "", - "paymentid": "", - "paymentnum": "", - "paymenttype": "", - "refundamount": "", - "transactionid": "" - } - }, - "joblines": { - "actions": { - "assign_team": "", - "converttolabor": "", - "dispatchparts": "", - "new": "" - }, - "errors": { - "creating": "", - "updating": "" - }, - "fields": { - "act_price": "Prix actuel", - "act_price_before_ppc": "", - "adjustment": "", - "ah_detail_line": "", - "amount": "", - "assigned_team": "", - "assigned_team_name": "", - "create_ppc": "", - "db_price": "Prix de la base de données", - "lbr_types": { - "LA1": "", - "LA2": "", - "LA3": "", - "LA4": "", - "LAA": "", - "LAB": "", - "LAD": "", - "LAE": "", - "LAF": "", - "LAG": "", - "LAM": "", - "LAR": "", - "LAS": "", - "LAU": "" - }, - "line_desc": "Description de la ligne", - "line_ind": "S#", - "line_no": "", - "location": "", - "mod_lb_hrs": "Heures de travail", - "mod_lbr_ty": "Type de travail", - "notes": "", - "oem_partno": "Pièce OEM #", - "op_code_desc": "", - "part_qty": "", - "part_type": "Type de pièce", - "part_types": { - "CCC": "", - "CCD": "", - "CCDR": "", - "CCF": "", - "CCM": "", - "PAA": "", - "PAC": "", - "PAE": "", - "PAG": "", - "PAL": "", - "PAM": "", - "PAN": "", - "PAO": "", - "PAP": "", - "PAR": "", - "PAS": "", - "PASL": "" - }, - "profitcenter_labor": "", - "profitcenter_part": "", - "prt_dsmk_m": "", - "prt_dsmk_p": "", - "status": "Statut", - "tax_part": "", - "total": "", - "unq_seq": "Seq #" - }, - "labels": { - "adjustmenttobeadded": "", - "billref": "", - "convertedtolabor": "", - "edit": "Ligne d'édition", - "ioucreated": "", - "new": "Nouvelle ligne", - "nostatus": "", - "presets": "" - }, - "successes": { - "created": "", - "saved": "", - "updated": "" - }, - "validations": { - "ahdetailonlyonuserdefinedtypes": "", - "hrsrequirediflbrtyp": "", - "requiredifparttype": "", - "zeropriceexistingpart": "" - } - }, - "jobs": { - "actions": { - "addDocuments": "Ajouter des documents de travail", - "addNote": "Ajouter une note", - "addtopartsqueue": "", - "addtoproduction": "", - "addtoscoreboard": "", - "allocate": "", - "autoallocate": "", - "changefilehandler": "", - "changelaborrate": "", - "changestatus": "Changer le statut", - "changestimator": "", - "convert": "Convertir", - "createiou": "", - "deliver": "", - "dms": { - "addpayer": "", - "createnewcustomer": "", - "findmakemodelcode": "", - "getmakes": "", - "labels": { - "refreshallocations": "" - }, - "post": "", - "refetchmakesmodels": "", - "usegeneric": "", - "useselected": "" - }, - "dmsautoallocate": "", - "export": "", - "exportcustdata": "", - "exportselected": "", - "filterpartsonly": "", - "generatecsi": "", - "gotojob": "", - "intake": "", - "manualnew": "", - "mark": "", - "markasexported": "", - "markpstexempt": "", - "markpstexemptconfirm": "", - "postbills": "Poster des factures", - "printCenter": "Centre d'impression", - "recalculate": "", - "reconcile": "", - "removefromproduction": "", - "schedule": "Programme", - "sendcsi": "", - "sendpartspricechange": "", - "sendtodms": "", - "sync": "", - "taxprofileoverride": "", - "taxprofileoverride_confirm": "", - "uninvoice": "", - "unvoid": "", - "viewchecklist": "", - "viewdetail": "" - }, - "errors": { - "addingtoproduction": "", - "cannotintake": "", - "closing": "", - "creating": "", - "deleted": "Erreur lors de la suppression du travail.", - "exporting": "", - "exporting-partner": "", - "invoicing": "", - "noaccess": "Ce travail n'existe pas ou vous n'y avez pas accès.", - "nodamage": "", - "nodates": "Aucune date spécifiée pour ce travail.", - "nofinancial": "", - "nojobselected": "Aucun travail n'est sélectionné.", - "noowner": "Aucun propriétaire associé.", - "novehicle": "Aucun véhicule associé.", - "partspricechange": "", - "saving": "Erreur rencontrée lors de la sauvegarde de l'enregistrement.", - "scanimport": "", - "totalscalc": "", - "updating": "", - "validation": "Veuillez vous assurer que tous les champs sont correctement entrés.", - "validationtitle": "Erreur de validation", - "voiding": "" - }, - "fields": { - "active_tasks": "", - "actual_completion": "Achèvement réel", - "actual_delivery": "Livraison réelle", - "actual_in": "En réel", - "adjustment_bottom_line": "Ajustements", - "adjustmenthours": "", - "alt_transport": "", - "area_of_damage_impact": { - "10": "", - "11": "", - "12": "", - "13": "", - "14": "", - "15": "", - "16": "", - "25": "", - "26": "", - "27": "", - "28": "", - "34": "", - "01": "", - "02": "", - "03": "", - "04": "", - "05": "", - "06": "", - "07": "", - "08": "", - "09": "" - }, - "auto_add_ats": "", - "ca_bc_pvrt": "", - "ca_customer_gst": "", - "ca_gst_registrant": "", - "category": "", - "ccc": "", - "ccd": "", - "ccdr": "", - "ccf": "", - "ccm": "", - "cieca_id": "CIECA ID", - "cieca_pfl": { - "lbr_adjp": "", - "lbr_tax_in": "", - "lbr_taxp": "", - "lbr_tx_in1": "", - "lbr_tx_in2": "", - "lbr_tx_in3": "", - "lbr_tx_in4": "", - "lbr_tx_in5": "" - }, - "cieca_pfo": { - "stor_t_in1": "", - "stor_t_in2": "", - "stor_t_in3": "", - "stor_t_in4": "", - "stor_t_in5": "", - "tow_t_in1": "", - "tow_t_in2": "", - "tow_t_in3": "", - "tow_t_in4": "", - "tow_t_in5": "" - }, - "claim_total": "Total réclamation", - "class": "", - "clm_no": "Prétendre #", - "clm_total": "Total réclamation", - "comment": "", - "customerowing": "Client propriétaire", - "date_estimated": "Date estimée", - "date_exported": "Exportés", - "date_invoiced": "Facturé", - "date_last_contacted": "", - "date_lost_sale": "", - "date_next_contact": "", - "date_open": "Ouvrir", - "date_rentalresp": "", - "date_repairstarted": "", - "date_scheduled": "Prévu", - "date_towin": "", - "date_void": "", - "ded_amt": "Déductible", - "ded_note": "", - "ded_status": "Statut de franchise", - "depreciation_taxes": "Amortissement / taxes", - "dms": { - "address": "", - "amount": "", - "center": "", - "control_type": { - "account_number": "" - }, - "cost": "", - "cost_dms_acctnumber": "", - "dms_make": "", - "dms_model": "", - "dms_model_override": "", - "dms_unsold": "", - "dms_wip_acctnumber": "", - "id": "", - "inservicedate": "", - "journal": "", - "lines": "", - "name1": "", - "payer": { - "amount": "", - "control_type": "", - "controlnumber": "", - "dms_acctnumber": "", - "name": "" - }, - "sale": "", - "sale_dms_acctnumber": "", - "story": "", - "vinowner": "" - }, - "dms_allocation": "", - "driveable": "", - "employee_body": "", - "employee_csr": "représentant du service à la clientèle", - "employee_csr_writer": "", - "employee_prep": "", - "employee_refinish": "", - "est_addr1": "Adresse de l'évaluateur", - "est_co_nm": "Expert", - "est_ct_fn": "Prénom de l'évaluateur", - "est_ct_ln": "Nom de l'évaluateur", - "est_ea": "Courriel de l'évaluateur", - "est_ph1": "Numéro de téléphone de l'évaluateur", - "federal_tax_payable": "Impôt fédéral à payer", - "federal_tax_rate": "", - "ins_addr1": "Adresse Insurance Co.", - "ins_city": "Insurance City", - "ins_co_id": "ID de la compagnie d'assurance", - "ins_co_nm": "Nom de la compagnie d'assurance", - "ins_co_nm_short": "", - "ins_ct_fn": "Prénom du gestionnaire de fichiers", - "ins_ct_ln": "Nom du gestionnaire de fichiers", - "ins_ea": "Courriel du gestionnaire de fichiers", - "ins_ph1": "Numéro de téléphone du gestionnaire de fichiers", - "intake": { - "label": "", - "max": "", - "min": "", - "name": "", - "required": "", - "type": "" - }, - "invoice_final_note": "", - "kmin": "Kilométrage en", - "kmout": "Kilométrage hors", - "la1": "", - "la2": "", - "la3": "", - "la4": "", - "laa": "", - "lab": "", - "labor_rate_desc": "Nom du taux de main-d'œuvre", - "lad": "", - "lae": "", - "laf": "", - "lag": "", - "lam": "", - "lar": "", - "las": "", - "lau": "", - "local_tax_rate": "", - "loss_date": "Date de perte", - "loss_desc": "", - "loss_of_use": "", - "lost_sale_reason": "", - "ma2s": "", - "ma3s": "", - "mabl": "", - "macs": "", - "mahw": "", - "mapa": "", - "mash": "", - "matd": "", - "materials": { - "MAPA": "", - "MASH": "", - "cal_maxdlr": "", - "cal_opcode": "", - "mat_adjp": "", - "mat_taxp": "", - "mat_tx_in1": "", - "mat_tx_in2": "", - "mat_tx_in3": "", - "mat_tx_in4": "", - "mat_tx_in5": "", - "materials": "", - "tax_ind": "" - }, - "other_amount_payable": "Autre montant à payer", - "owner": "Propriétaire", - "owner_owing": "Cust. Owes", - "ownr_ea": "Email", - "ownr_ph1": "Téléphone 1", - "ownr_ph2": "", - "paa": "", - "pac": "", - "pae": "", - "pag": "", - "pal": "", - "pam": "", - "pan": "", - "pao": "", - "pap": "", - "par": "", - "parts_tax_rates": { - "prt_discp": "", - "prt_mktyp": "", - "prt_mkupp": "", - "prt_tax_in": "", - "prt_tax_rt": "", - "prt_tx_in1": "", - "prt_tx_in2": "", - "prt_tx_in3": "", - "prt_tx_in4": "", - "prt_tx_in5": "", - "prt_tx_ty1": "", - "prt_type": "" - }, - "partsstatus": "", - "pas": "", - "pay_date": "Date d'Pay", - "phoneshort": "PH", - "po_number": "", - "policy_no": "Politique #", - "ponumber": "Numéro de bon de commande", - "production_vars": { - "note": "" - }, - "qb_multiple_payers": { - "amount": "", - "name": "" - }, - "queued_for_parts": "", - "rate_ats": "", - "rate_la1": "Taux LA1", - "rate_la2": "Taux LA2", - "rate_la3": "Taux LA3", - "rate_la4": "Taux LA4", - "rate_laa": "Taux d'aluminium", - "rate_lab": "Taux de la main-d'œuvre", - "rate_lad": "Taux de diagnostic", - "rate_lae": "Tarif électrique", - "rate_laf": "Taux de trame", - "rate_lag": "Taux de verre", - "rate_lam": "Taux mécanique", - "rate_lar": "Taux de finition", - "rate_las": "", - "rate_lau": "Taux d'aluminium", - "rate_ma2s": "Taux de peinture en 2 étapes", - "rate_ma3s": "Taux de peinture en 3 étapes", - "rate_mabl": "MABL ??", - "rate_macs": "MACS ??", - "rate_mahw": "Taux de déchets dangereux", - "rate_mapa": "Taux de matériaux de peinture", - "rate_mash": "Tarif du matériel de la boutique", - "rate_matd": "Taux d'élimination des pneus", - "referral_source_extra": "", - "referral_source_other": "", - "referralsource": "Source de référence", - "regie_number": "Enregistrement #", - "repairtotal": "Réparation totale", - "ro_number": "RO #", - "scheduled_completion": "Achèvement planifié", - "scheduled_delivery": "Livraison programmée", - "scheduled_in": "Planifié dans", - "selling_dealer": "Revendeur vendeur", - "selling_dealer_contact": "Contacter le revendeur", - "servicecar": "Voiture de service", - "servicing_dealer": "Concessionnaire", - "servicing_dealer_contact": "Contacter le concessionnaire", - "special_coverage_policy": "Politique de couverture spéciale", - "specialcoveragepolicy": "Politique de couverture spéciale", - "state_tax_rate": "", - "status": "Statut de l'emploi", - "storage_payable": "Stockage", - "tax_lbr_rt": "", - "tax_levies_rt": "", - "tax_paint_mat_rt": "", - "tax_registration_number": "", - "tax_shop_mat_rt": "", - "tax_str_rt": "", - "tax_sub_rt": "", - "tax_tow_rt": "", - "towin": "", - "towing_payable": "Remorquage à payer", - "unitnumber": "Unité #", - "updated_at": "Mis à jour à", - "uploaded_by": "Telechargé par", - "vehicle": "Véhicule" - }, - "forms": { - "admindates": "", - "appraiserinfo": "", - "claiminfo": "", - "estdates": "", - "laborrates": "", - "lossinfo": "", - "other": "", - "repairdates": "", - "scheddates": "" - }, - "labels": { - "accountsreceivable": "", - "act_price_ppc": "", - "actual_completion_inferred": "", - "actual_delivery_inferred": "", - "actual_in_inferred": "", - "additionalpayeroverallocation": "", - "additionaltotal": "", - "adjustmentrate": "", - "adjustments": "", - "adminwarning": "", - "allocations": "", - "alreadyaddedtoscoreboard": "", - "alreadyclosed": "", - "appointmentconfirmation": "Envoyer une confirmation au client?", - "associationwarning": "", - "audit": "", - "available": "", - "availablejobs": "", - "ca_bc_pvrt": { - "days": "", - "rate": "" - }, - "ca_gst_all_if_null": "", - "calc_repair_days": "", - "calc_repair_days_tt": "", - "calc_scheuled_completion": "", - "cards": { - "customer": "Informations client", - "damage": "Zone de dommages", - "dates": "Rendez-vous", - "documents": "Documents récents", - "estimator": "Estimateur", - "filehandler": "Gestionnaire de fichiers", - "insurance": "Détails de l'assurance", - "more": "Plus", - "notes": "Remarques", - "parts": "les pièces", - "totals": "Totaux", - "vehicle": "Véhicule" - }, - "changeclass": "", - "checklistcompletedby": "", - "checklistdocuments": "", - "checklists": "", - "cieca_pfl": "", - "cieca_pfo": "", - "cieca_pft": "", - "closeconfirm": "", - "closejob": "", - "closingperiod": "", - "contracts": "", - "convertedtolabor": "", - "cost": "", - "cost_Additional": "", - "cost_labor": "", - "cost_parts": "", - "cost_sublet": "", - "costs": "", - "create": { - "jobinfo": "", - "newowner": "", - "newvehicle": "", - "novehicle": "", - "ownerinfo": "", - "vehicleinfo": "" - }, - "createiouwarning": "", - "creating_new_job": "Création d'un nouvel emploi ...", - "deductible": { - "stands": "", - "waived": "" - }, - "deleteconfirm": "", - "deletedelivery": "", - "deleteintake": "", - "deliverchecklist": "", - "difference": "", - "diskscan": "", - "dms": { - "apexported": "", - "damageto": "", - "defaultstory": "", - "disablebillwip": "", - "invoicedatefuture": "", - "kmoutnotgreaterthankmin": "", - "logs": "", - "notallocated": "", - "postingform": "", - "totalallocated": "" - }, - "documents": "Les documents", - "documents-images": "", - "documents-other": "", - "duplicateconfirm": "", - "emailaudit": "", - "employeeassignments": "", - "estimatelines": "", - "estimator": "", - "existing_jobs": "Emplois existants", - "federal_tax_amt": "", - "gpdollars": "", - "gppercent": "", - "hrs_claimed": "", - "hrs_total": "", - "importnote": "", - "inproduction": "", - "intakechecklist": "", - "iou": "", - "job": "", - "jobcosting": "", - "jobtotals": "", - "labor_hrs": "", - "labor_rates_subtotal": "", - "laborallocations": "", - "labortotals": "", - "lines": "Estimer les lignes", - "local_tax_amt": "", - "mapa": "", - "markforreexport": "", - "mash": "", - "masterbypass": "", - "materials": { - "mapa": "" - }, - "missingprofileinfo": "", - "multipayers": "", - "net_repairs": "", - "notes": "Remarques", - "othertotal": "", - "outstanding_ar": "", - "outstanding_credit_memos": "", - "outstanding_ppd": "", - "outstanding_reconciliation_discrep": "", - "outstanding_sublets": "", - "outstandinghours": "", - "override_header": "Remplacer l'en-tête d'estimation à l'importation?", - "ownerassociation": "", - "parts": "les pièces", - "parts_lines": "", - "parts_received": "", - "parts_tax_rates": "", - "partsfilter": "", - "partssubletstotal": "", - "partstotal": "", - "performance": "", - "pimraryamountpayable": "", - "plitooltips": { - "billtotal": "", - "calculatedcreditsnotreceived": "", - "creditmemos": "", - "creditsnotreceived": "", - "discrep1": "", - "discrep2": "", - "discrep3": "", - "laboradj": "", - "partstotal": "", - "totalreturns": "" - }, - "ppc": "", - "ppdnotexported": "", - "profileadjustments": "", - "profitbypassrequired": "", - "profits": "", - "prt_dsmk_total": "", - "rates": "Les taux", - "rates_subtotal": "", - "reconciliation": { - "billlinestotal": "", - "byassoc": "", - "byprice": "", - "clear": "", - "discrepancy": "", - "joblinestotal": "", - "multipleactprices": "", - "multiplebilllines": "", - "multiplebillsforactprice": "", - "removedpartsstrikethrough": "" - }, - "reconciliationheader": "", - "relatedros": "", - "remove_from_ar": "", - "returntotals": "", - "ro_guard": { - "enforce_ar": "", - "enforce_bills": "", - "enforce_cm": "", - "enforce_labor": "", - "enforce_ppd": "", - "enforce_profit": "", - "enforce_sublet": "", - "enforce_validation": "", - "enforced": "" - }, - "roguard": "", - "roguardwarnings": "", - "rosaletotal": "", - "sale_additional": "", - "sale_labor": "", - "sale_parts": "", - "sale_sublet": "", - "sales": "", - "savebeforeconversion": "", - "scheduledinchange": "", - "specialcoveragepolicy": "", - "state_tax_amt": "", - "subletsnotcompleted": "", - "subletstotal": "", - "subtotal": "", - "supplementnote": "", - "suspended": "", - "suspense": "", - "tasks": "", - "threshhold": "", - "total_cost": "", - "total_cust_payable": "", - "total_repairs": "", - "total_sales": "", - "total_sales_tax": "", - "totals": "", - "unvoidnote": "", - "update_scheduled_completion": "", - "vehicle_info": "Véhicule", - "vehicleassociation": "", - "viewallocations": "", - "voidjob": "", - "voidnote": "" - }, - "successes": { - "addedtoproduction": "", - "all_deleted": "{{count}} travaux supprimés avec succès.", - "closed": "", - "converted": "Travail converti avec succès.", - "created": "Le travail a été créé avec succès. Clique pour voir.", - "creatednoclick": "", - "delete": "", - "deleted": "Le travail a bien été supprimé.", - "duplicated": "", - "exported": "", - "invoiced": "", - "ioucreated": "", - "partsqueue": "", - "save": "Le travail a été enregistré avec succès.", - "savetitle": "Enregistrement enregistré avec succès.", - "supplemented": "Travail complété avec succès.", - "updated": "", - "voided": "" - } - }, - "landing": { - "bigfeature": { - "subtitle": "", - "title": "" - }, - "footer": { - "company": { - "about": "", - "contact": "", - "disclaimers": "", - "name": "", - "privacypolicy": "" - }, - "io": { - "help": "", - "name": "", - "status": "" - }, - "slogan": "" - }, - "hero": { - "button": "", - "title": "" - }, - "labels": { - "features": "", - "managemyshop": "", - "pricing": "" - }, - "pricing": { - "basic": { - "name": "", - "sub": "" - }, - "essentials": { - "name": "", - "sub": "" - }, - "pricingtitle": "", - "pro": { - "name": "", - "sub": "" - }, - "title": "", - "unlimited": { - "name": "", - "sub": "" - } - } - }, - "menus": { - "currentuser": { - "languageselector": "La langue", - "profile": "Profil" - }, - "header": { - "accounting": "", - "accounting-payables": "", - "accounting-payments": "", - "accounting-receivables": "", - "activejobs": "Emplois actifs", - "all_tasks": "", - "alljobs": "", - "allpayments": "", - "availablejobs": "Emplois disponibles", - "bills": "", - "courtesycars": "", - "courtesycars-all": "", - "courtesycars-contracts": "", - "courtesycars-newcontract": "", - "create_task": "", - "customers": "Les clients", - "dashboard": "", - "enterbills": "", - "entercardpayment": "", - "enterpayment": "", - "entertimeticket": "", - "export": "", - "export-logs": "", - "help": "", - "home": "Accueil", - "inventory": "", - "jobs": "Emplois", - "my_tasks": "", - "newjob": "", - "owners": "Propriétaires", - "parts-queue": "", - "phonebook": "", - "productionboard": "", - "productionlist": "", - "readyjobs": "", - "recent": "", - "reportcenter": "", - "rescueme": "", - "schedule": "Programme", - "scoreboard": "", - "search": { - "bills": "", - "jobs": "", - "owners": "", - "payments": "", - "phonebook": "", - "vehicles": "" - }, - "shiftclock": "", - "shop": "Mon magasin", - "shop_config": "Configuration", - "shop_csi": "", - "shop_templates": "", - "shop_vendors": "Vendeurs", - "tasks": "", - "temporarydocs": "", - "timetickets": "", - "ttapprovals": "", - "vehicles": "Véhicules" - }, - "jobsactions": { - "admin": "", - "cancelallappointments": "", - "closejob": "", - "deletejob": "", - "duplicate": "", - "duplicatenolines": "", - "newcccontract": "", - "void": "" - }, - "jobsdetail": { - "claimdetail": "Détails de la réclamation", - "dates": "Rendez-vous", - "financials": "", - "general": "", - "insurance": "", - "labor": "La main d'oeuvre", - "lifecycle": "", - "parts": "", - "partssublet": "Pièces / Sous-location", - "rates": "", - "repairdata": "Données de réparation", - "totals": "" - }, - "profilesidebar": { - "profile": "Mon profil", - "shops": "Mes boutiques" - }, - "tech": { - "assignedjobs": "", - "claimtask": "", - "dispatchedparts": "", - "home": "", - "jobclockin": "", - "jobclockout": "", - "joblookup": "", - "login": "", - "logout": "", - "productionboard": "", - "productionlist": "", - "shiftclockin": "" - } - }, - "messaging": { - "actions": { - "link": "", - "new": "" - }, - "errors": { - "invalidphone": "", - "noattachedjobs": "", - "updatinglabel": "" - }, - "labels": { - "addlabel": "", - "archive": "", - "maxtenimages": "", - "messaging": "Messagerie", - "noallowtxt": "", - "nojobs": "", - "nopush": "", - "phonenumber": "", - "presets": "", - "recentonly": "", - "selectmedia": "", - "sentby": "", - "typeamessage": "Envoyer un message...", - "unarchive": "" - }, - "render": { - "conversation_list": "" - } - }, - "notes": { - "actions": { - "actions": "actes", - "deletenote": "Supprimer la note", - "edit": "Note éditée", - "new": "Nouvelle note", - "savetojobnotes": "" - }, - "errors": { - "inserting": "" - }, - "fields": { - "createdby": "Créé par", - "critical": "Critique", - "private": "privé", - "text": "Contenu", - "type": "", - "types": { - "customer": "", - "general": "", - "office": "", - "paint": "", - "parts": "", - "shop": "", - "supplement": "" - }, - "updatedat": "Mis à jour à" - }, - "labels": { - "addtorelatedro": "", - "newnoteplaceholder": "Ajouter une note...", - "notetoadd": "", - "systemnotes": "", - "usernotes": "" - }, - "successes": { - "create": "Remarque créée avec succès.", - "deleted": "Remarque supprimée avec succès.", - "updated": "Remarque mise à jour avec succès." - } - }, - "owner": { - "labels": { - "noownerinfo": "" - } - }, - "owners": { - "actions": { - "update": "" - }, - "errors": { - "deleting": "", - "noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.", - "saving": "", - "selectexistingornew": "" - }, - "fields": { - "address": "Adresse", - "allow_text_message": "Autorisation de texte?", - "name": "Prénom", - "note": "", - "ownr_addr1": "Adresse", - "ownr_addr2": "Adresse 2 ", - "ownr_city": "Ville", - "ownr_co_nm": "", - "ownr_ctry": "Pays", - "ownr_ea": "Email", - "ownr_fn": "Prénom", - "ownr_ln": "Nom de famille", - "ownr_ph1": "Téléphone 1", - "ownr_ph2": "", - "ownr_st": "Etat / Province", - "ownr_title": "Titre", - "ownr_zip": "Zip / code postal", - "preferred_contact": "Méthode de contact préférée", - "tax_number": "" - }, - "forms": { - "address": "", - "contact": "", - "name": "" - }, - "labels": { - "create_new": "Créez un nouvel enregistrement de propriétaire.", - "deleteconfirm": "", - "existing_owners": "Propriétaires existants", - "fromclaim": "", - "fromowner": "", - "relatedjobs": "", - "updateowner": "" - }, - "successes": { - "delete": "", - "save": "Le propriétaire a bien enregistré." - } - }, - "parts": { - "actions": { - "order": "Commander des pièces", - "orderinhouse": "" - } - }, - "parts_dispatch": { - "actions": { - "accept": "" - }, - "errors": { - "accepting": "", - "creating": "" - }, - "fields": { - "number": "", - "percent_accepted": "" - }, - "labels": { - "notyetdispatched": "", - "parts_dispatch": "" - } - }, - "parts_dispatch_lines": { - "fields": { - "accepted_at": "" - } - }, - "parts_orders": { - "actions": { - "backordered": "", - "receive": "", - "receivebill": "" - }, - "errors": { - "associatedbills": "", - "backordering": "", - "creating": "Erreur rencontrée lors de la création de la commande de pièces.", - "oec": "", - "saving": "", - "updating": "" - }, - "fields": { - "act_price": "", - "backordered_eta": "", - "backordered_on": "", - "cm_received": "", - "comments": "", - "cost": "", - "db_price": "", - "deliver_by": "", - "job_line_id": "", - "line_desc": "", - "line_remarks": "", - "lineremarks": "Remarques sur la ligne", - "oem_partno": "", - "order_date": "", - "order_number": "", - "orderedby": "", - "part_type": "", - "quantity": "", - "return": "", - "status": "" - }, - "labels": { - "allpartsto": "", - "confirmdelete": "", - "custompercent": "", - "discount": "", - "email": "Envoyé par email", - "inthisorder": "Pièces dans cette commande", - "is_quote": "", - "mark_as_received": "", - "newpartsorder": "", - "notyetordered": "", - "oec": "", - "order_type": "", - "orderhistory": "Historique des commandes", - "parts_order": "", - "parts_orders": "", - "print": "Afficher le formulaire imprimé", - "receive": "", - "removefrompartsqueue": "", - "returnpartsorder": "", - "sublet_order": "" - }, - "successes": { - "created": "Commande de pièces créée avec succès.", - "line_updated": "", - "received": "", - "return_created": "" - } - }, - "payments": { - "actions": { - "generatepaymentlink": "" - }, - "errors": { - "exporting": "", - "exporting-partner": "", - "inserting": "" - }, - "fields": { - "amount": "", - "created_at": "", - "date": "", - "exportedat": "", - "memo": "", - "payer": "", - "paymentnum": "", - "stripeid": "", - "transactionid": "", - "type": "" - }, - "labels": { - "balance": "", - "ca_bc_etf_table": "", - "customer": "", - "edit": "", - "electronicpayment": "", - "external": "", - "findermodal": "", - "insurance": "", - "markexported": "", - "markforreexport": "", - "new": "", - "signup": "", - "smspaymentreminder": "", - "title": "", - "totalpayments": "" - }, - "successes": { - "exported": "", - "markexported": "", - "markreexported": "", - "payment": "", - "paymentupdate": "", - "stripe": "" - } - }, - "phonebook": { - "actions": { - "new": "" - }, - "errors": { - "adding": "", - "saving": "" - }, - "fields": { - "address1": "", - "address2": "", - "category": "", - "city": "", - "company": "", - "country": "", - "email": "", - "fax": "", - "firstname": "", - "lastname": "", - "phone1": "", - "phone2": "", - "state": "" - }, - "labels": { - "noneselected": "", - "onenamerequired": "", - "vendorcategory": "" - }, - "successes": { - "added": "", - "deleted": "", - "saved": "" - } - }, - "printcenter": { - "appointments": { - "appointment_confirmation": "" - }, - "bills": { - "inhouse_invoice": "" - }, - "courtesycarcontract": { - "courtesy_car_contract": "", - "courtesy_car_impound": "", - "courtesy_car_inventory": "", - "courtesy_car_terms": "" - }, - "errors": { - "nocontexttype": "" - }, - "jobs": { - "3rdpartyfields": { - "addr1": "", - "addr2": "", - "addr3": "", - "attn": "", - "city": "", - "custgst": "", - "ded_amt": "", - "depreciation": "", - "other": "", - "ponumber": "", - "refnumber": "", - "sendtype": "", - "state": "", - "zip": "" - }, - "3rdpartypayer": "", - "ab_proof_of_loss": "", - "appointment_confirmation": "", - "appointment_reminder": "", - "casl_authorization": "", - "committed_timetickets_ro": "", - "coversheet_landscape": "", - "coversheet_portrait": "", - "csi_invitation": "", - "csi_invitation_action": "", - "diagnostic_authorization": "", - "dms_posting_sheet": "", - "envelope_return_address": "", - "estimate": "", - "estimate_detail": "", - "estimate_followup": "", - "express_repair_checklist": "", - "filing_coversheet_landscape": "", - "filing_coversheet_portrait": "", - "final_invoice": "", - "fippa_authorization": "", - "folder_label_multiple": "", - "glass_express_checklist": "", - "guarantee": "", - "individual_job_note": "", - "invoice_customer_payable": "", - "invoice_total_payable": "", - "iou_form": "", - "job_costing_ro": "", - "job_lifecycle_ro": "", - "job_notes": "", - "job_tasks": "", - "key_tag": "", - "labels": { - "count": "", - "labels": "", - "position": "" - }, - "lag_time_ro": "", - "mechanical_authorization": "", - "mpi_animal_checklist": "", - "mpi_eglass_auth": "", - "mpi_final_acct_sheet": "", - "mpi_final_repair_acct_sheet": "", - "paint_grid": "", - "parts_dispatch": "", - "parts_invoice_label_single": "", - "parts_label_multiple": "", - "parts_label_single": "", - "parts_list": "", - "parts_order": "", - "parts_order_confirmation": "", - "parts_order_history": "", - "parts_return_slip": "", - "payment_receipt": "", - "payment_request": "", - "payments_by_job": "", - "purchases_by_ro_detail": "", - "purchases_by_ro_summary": "", - "qc_sheet": "", - "rental_reservation": "", - "ro_totals": "", - "ro_with_description": "", - "sgi_certificate_of_repairs": "", - "sgi_windshield_auth": "", - "stolen_recovery_checklist": "", - "sublet_order": "", - "supplement_request": "", - "thank_you_ro": "", - "thirdpartypayer": "", - "timetickets_ro": "", - "vehicle_check_in": "", - "vehicle_delivery_check": "", - "window_tag": "", - "window_tag_sublet": "", - "work_authorization": "", - "worksheet_by_line_number": "", - "worksheet_sorted_by_operation": "", - "worksheet_sorted_by_operation_no_hours": "", - "worksheet_sorted_by_operation_part_type": "", - "worksheet_sorted_by_operation_type": "", - "worksheet_sorted_by_team": "" - }, - "labels": { - "groups": { - "authorization": "", - "financial": "", - "post": "", - "pre": "", - "ro": "", - "worksheet": "" - }, - "misc": "", - "repairorder": "", - "reportcentermodal": "", - "speedprint": "", - "title": "" - }, - "payments": { - "ca_bc_etf_table": "", - "exported_payroll": "" - }, - "special": { - "attendance_detail_csv": "" - }, - "subjects": { - "jobs": { - "individual_job_note": "", - "parts_dispatch": "", - "parts_order": "", - "parts_return_slip": "", - "sublet_order": "" - } - }, - "vendors": { - "purchases_by_vendor_detailed": "", - "purchases_by_vendor_summary": "" - } - }, - "production": { - "actions": { - "addcolumns": "", - "bodypriority-clear": "", - "bodypriority-set": "", - "detailpriority-clear": "", - "detailpriority-set": "", - "paintpriority-clear": "", - "paintpriority-set": "", - "remove": "", - "removecolumn": "", - "saveconfig": "", - "suspend": "", - "unsuspend": "" - }, - "constants": { - "main_profile": "" - }, - "errors": { - "boardupdate": "", - "name_exists": "", - "name_required": "", - "removing": "", - "settings": "" - }, - "labels": { - "actual_in": "", - "addnewprofile": "", - "alert": "", - "alertoff": "", - "alerton": "", - "alerts": "", - "ats": "", - "bodyhours": "", - "bodypriority": "", - "bodyshop": { - "labels": { - "qbo_departmentid": "", - "qbo_usa": "" - } - }, - "card_size": "", - "cardcolor": "", - "cardsettings": "", - "clm_no": "", - "comment": "", - "compact": "", - "detailpriority": "", - "employeeassignments": "", - "employeesearch": "", - "estimator": "", - "horizontal": "", - "ins_co_nm": "", - "jobdetail": "", - "kiosk_mode": "", - "laborhrs": "", - "legend": "", - "model_info": "", - "note": "", - "off": "", - "on": "", - "orientation": "", - "ownr_nm": "", - "paintpriority": "", - "partsstatus": "", - "production_note": "", - "refinishhours": "", - "scheduled_completion": "", - "selectview": "", - "stickyheader": "", - "sublets": "", - "subtotal": "", - "tall": "", - "tasks": "", - "totalhours": "", - "touchtime": "", - "vertical": "", - "viewname": "", - "wide": "" - }, - "options": { - "horizontal": "", - "large": "", - "medium": "", - "small": "", - "vertical": "" - }, - "settings": { - "board_settings": "", - "filters": { - "md_estimators": "", - "md_ins_cos": "" - }, - "filters_title": "", - "information": "", - "layout": "", - "statistics": { - "jobs_in_production": "", - "tasks_in_production": "", - "tasks_on_board": "", - "total_amount_in_production": "", - "total_amount_on_board": "", - "total_hours_in_production": "", - "total_hours_on_board": "", - "total_jobs_on_board": "", - "total_lab_in_production": "", - "total_lab_on_board": "", - "total_lar_in_production": "", - "total_lar_on_board": "" - }, - "statistics_title": "" - }, - "statistics": { - "currency_symbol": "", - "hours": "", - "jobs": "", - "jobs_in_production": "", - "tasks": "", - "tasks_in_production": "", - "tasks_on_board": "", - "total_amount_in_production": "", - "total_amount_on_board": "", - "total_hours_in_production": "", - "total_hours_on_board": "", - "total_jobs_on_board": "", - "total_lab_in_production": "", - "total_lab_on_board": "", - "total_lar_in_production": "", - "total_lar_on_board": "" - }, - "successes": { - "removed": "" - } - }, - "profile": { - "errors": { - "state": "Erreur lors de la lecture de l'état de la page. Rafraichissez, s'il vous plait." - }, - "labels": { - "activeshop": "" - }, - "successes": { - "updated": "" - } - }, - "reportcenter": { - "actions": { - "generate": "" - }, - "labels": { - "advanced_filters": "", - "advanced_filters_false": "", - "advanced_filters_filter_field": "", - "advanced_filters_filter_operator": "", - "advanced_filters_filter_value": "", - "advanced_filters_filters": "", - "advanced_filters_hide": "", - "advanced_filters_show": "", - "advanced_filters_sorter_direction": "", - "advanced_filters_sorter_field": "", - "advanced_filters_sorters": "", - "advanced_filters_true": "", - "dates": "", - "employee": "", - "filterson": "", - "generateasemail": "", - "groups": { - "customers": "", - "jobs": "", - "payroll": "", - "purchases": "", - "sales": "" - }, - "key": "", - "objects": { - "appointments": "", - "bills": "", - "csi": "", - "exportlogs": "", - "jobs": "", - "parts_orders": "", - "payments": "", - "scoreboard": "", - "tasks": "", - "timetickets": "" - }, - "vendor": "" - }, - "templates": { - "adp_payroll_flat": "", - "adp_payroll_straight": "", - "anticipated_revenue": "", - "ar_aging": "", - "attendance_detail": "", - "attendance_employee": "", - "attendance_summary": "", - "committed_timetickets": "", - "committed_timetickets_employee": "", - "committed_timetickets_summary": "", - "credits_not_received_date": "", - "credits_not_received_date_vendorid": "", - "csi": "", - "customer_list": "", - "cycle_time_analysis": "", - "estimates_written_converted": "", - "estimator_detail": "", - "estimator_summary": "", - "export_payables": "", - "export_payments": "", - "export_receivables": "", - "exported_gsr_by_ro": "", - "exported_gsr_by_ro_labor": "", - "gsr_by_atp": "", - "gsr_by_ats": "", - "gsr_by_category": "", - "gsr_by_csr": "", - "gsr_by_delivery_date": "", - "gsr_by_estimator": "", - "gsr_by_exported_date": "", - "gsr_by_ins_co": "", - "gsr_by_make": "", - "gsr_by_referral": "", - "gsr_by_ro": "", - "gsr_labor_only": "", - "hours_sold_detail_closed": "", - "hours_sold_detail_closed_csr": "", - "hours_sold_detail_closed_estimator": "", - "hours_sold_detail_closed_ins_co": "", - "hours_sold_detail_closed_status": "", - "hours_sold_detail_open": "", - "hours_sold_detail_open_csr": "", - "hours_sold_detail_open_estimator": "", - "hours_sold_detail_open_ins_co": "", - "hours_sold_detail_open_status": "", - "hours_sold_summary_closed": "", - "hours_sold_summary_closed_csr": "", - "hours_sold_summary_closed_estimator": "", - "hours_sold_summary_closed_ins_co": "", - "hours_sold_summary_closed_status": "", - "hours_sold_summary_open": "", - "hours_sold_summary_open_csr": "", - "hours_sold_summary_open_estimator": "", - "hours_sold_summary_open_ins_co": "", - "hours_sold_summary_open_status": "", - "job_costing_ro_csr": "", - "job_costing_ro_date_detail": "", - "job_costing_ro_date_summary": "", - "job_costing_ro_estimator": "", - "job_costing_ro_ins_co": "", - "job_lifecycle_date_detail": "", - "job_lifecycle_date_summary": "", - "jobs_completed_not_invoiced": "", - "jobs_invoiced_not_exported": "", - "jobs_reconcile": "", - "jobs_scheduled_completion": "", - "lag_time": "", - "load_level": "", - "lost_sales": "", - "open_orders": "", - "open_orders_csr": "", - "open_orders_estimator": "", - "open_orders_excel": "", - "open_orders_ins_co": "", - "open_orders_referral": "", - "open_orders_specific_csr": "", - "open_orders_status": "", - "parts_backorder": "", - "parts_not_recieved": "", - "parts_not_recieved_vendor": "", - "parts_received_not_scheduled": "", - "payments_by_date": "", - "payments_by_date_payment": "", - "payments_by_date_type": "", - "production_by_category": "", - "production_by_category_one": "", - "production_by_csr": "", - "production_by_last_name": "", - "production_by_repair_status": "", - "production_by_repair_status_one": "", - "production_by_ro": "", - "production_by_target_date": "", - "production_by_technician": "", - "production_by_technician_one": "", - "production_over_time": "", - "psr_by_make": "", - "purchase_return_ratio_grouped_by_vendor_detail": "", - "purchase_return_ratio_grouped_by_vendor_summary": "", - "purchases_by_cost_center_detail": "", - "purchases_by_cost_center_summary": "", - "purchases_by_date_range_detail": "", - "purchases_by_date_range_summary": "", - "purchases_by_ro_detail_date": "", - "purchases_by_ro_summary_date": "", - "purchases_by_vendor_detailed_date_range": "", - "purchases_by_vendor_summary_date_range": "", - "purchases_grouped_by_vendor_detailed": "", - "purchases_grouped_by_vendor_summary": "", - "returns_grouped_by_vendor_detailed": "", - "returns_grouped_by_vendor_summary": "", - "schedule": "", - "scheduled_parts_list": "", - "scoreboard_detail": "", - "scoreboard_summary": "", - "supplement_ratio_ins_co": "", - "tasks_date": "", - "tasks_date_employee": "", - "thank_you_date": "", - "timetickets": "", - "timetickets_employee": "", - "timetickets_summary": "", - "unclaimed_hrs": "", - "void_ros": "", - "work_in_progress_committed_labour": "", - "work_in_progress_jobs": "", - "work_in_progress_labour": "", - "work_in_progress_payables": "" - } - }, - "schedule": { - "labels": { - "atssummary": "", - "employeevacation": "", - "estimators": "", - "ins_co_nm_filter": "", - "intake": "", - "manual": "", - "manualevent": "" - } - }, - "scoreboard": { - "actions": { - "edit": "" - }, - "errors": { - "adding": "", - "removing": "", - "updating": "" - }, - "fields": { - "bodyhrs": "", - "date": "", - "painthrs": "" - }, - "labels": { - "allemployeetimetickets": "", - "asoftodaytarget": "", - "body": "", - "bodyabbrev": "", - "bodycharttitle": "", - "calendarperiod": "", - "combinedcharttitle": "", - "dailyactual": "", - "dailytarget": "", - "efficiencyoverperiod": "", - "entries": "", - "jobs": "", - "jobscompletednotinvoiced": "", - "lastmonth": "", - "lastweek": "", - "monthlytarget": "", - "priorweek": "", - "productivestatistics": "", - "productivetimeticketsoverdate": "", - "refinish": "", - "refinishabbrev": "", - "refinishcharttitle": "", - "targets": "", - "thismonth": "", - "thisweek": "", - "timetickets": "", - "timeticketsemployee": "", - "todateactual": "", - "total": "", - "totalhrs": "", - "totaloverperiod": "", - "weeklyactual": "", - "weeklytarget": "", - "workingdays": "" - }, - "successes": { - "added": "", - "removed": "", - "updated": "" - } - }, - "tasks": { - "actions": { - "edit": "", - "new": "" - }, - "buttons": { - "allTasks": "", - "complete": "", - "create": "", - "delete": "", - "edit": "", - "myTasks": "", - "refresh": "" - }, - "date_presets": { - "completion": "", - "day": "", - "days": "", - "delivery": "", - "next_week": "", - "one_month": "", - "three_months": "", - "three_weeks": "", - "today": "", - "tomorrow": "", - "two_weeks": "" - }, - "failures": { - "completed": "", - "created": "", - "deleted": "", - "updated": "" - }, - "fields": { - "actions": "", - "assigned_to": "", - "bill": "", - "billid": "", - "completed": "", - "created_at": "", - "description": "", - "due_date": "", - "job": { - "ro_number": "" - }, - "jobid": "", - "jobline": "", - "joblineid": "", - "parts_order": "", - "partsorderid": "", - "priorities": { - "high": "", - "low": "", - "medium": "" - }, - "priority": "", - "remind_at": "", - "title": "" - }, - "placeholders": { - "assigned_to": "", - "billid": "", - "description": "", - "jobid": "", - "joblineid": "", - "partsorderid": "" - }, - "successes": { - "completed": "", - "created": "", - "deleted": "", - "updated": "" - }, - "titles": { - "all_tasks": "", - "completed": "", - "deleted": "", - "job_tasks": "", - "mine": "", - "my_tasks": "" - }, - "validation": { - "due_at_error_message": "", - "remind_at_error_message": "" - } - }, - "tech": { - "fields": { - "employeeid": "", - "pin": "" - }, - "labels": { - "loggedin": "", - "notloggedin": "" - } - }, - "templates": { - "errors": { - "updating": "" - }, - "successes": { - "updated": "" - } - }, - "timetickets": { - "actions": { - "claimtasks": "", - "clockin": "", - "clockout": "", - "commit": "", - "commitone": "", - "enter": "", - "payall": "", - "printemployee": "", - "uncommit": "" - }, - "errors": { - "clockingin": "", - "clockingout": "", - "creating": "", - "deleting": "", - "noemployeeforuser": "", - "noemployeeforuser_sub": "", - "payall": "", - "shiftalreadyclockedon": "" - }, - "fields": { - "actualhrs": "", - "ciecacode": "", - "clockhours": "", - "clockoff": "", - "clockon": "", - "committed": "", - "committed_at": "", - "cost_center": "", - "created_by": "", - "date": "", - "efficiency": "", - "employee": "", - "employee_team": "", - "flat_rate": "", - "memo": "", - "productivehrs": "", - "ro_number": "", - "task_name": "" - }, - "labels": { - "alreadyclockedon": "", - "ambreak": "", - "amshift": "", - "claimtaskpreview": "", - "clockhours": "", - "clockintojob": "", - "deleteconfirm": "", - "edit": "", - "efficiency": "", - "flat_rate": "", - "jobhours": "", - "lunch": "", - "new": "", - "payrollclaimedtasks": "", - "pmbreak": "", - "pmshift": "", - "shift": "", - "shiftalreadyclockedon": "", - "straight_time": "", - "task": "", - "timetickets": "", - "unassigned": "", - "zeroactualnegativeprod": "" - }, - "successes": { - "clockedin": "", - "clockedout": "", - "committed": "", - "created": "", - "deleted": "", - "payall": "" - }, - "validation": { - "clockoffmustbeafterclockon": "", - "clockoffwithoutclockon": "", - "hoursenteredmorethanavailable": "", - "unassignedlines": "" - } - }, - "titles": { - "accounting-payables": "", - "accounting-payments": "", - "accounting-receivables": "", - "all_tasks": "", - "app": "", - "bc": { - "accounting-payables": "", - "accounting-payments": "", - "accounting-receivables": "", - "all_tasks": "", - "availablejobs": "", - "bills-list": "", - "contracts": "", - "contracts-create": "", - "contracts-detail": "", - "courtesycars": "", - "courtesycars-detail": "", - "courtesycars-new": "", - "dashboard": "", - "dms": "", - "export-logs": "", - "inventory": "", - "jobs": "", - "jobs-active": "", - "jobs-admin": "", - "jobs-all": "", - "jobs-checklist": "", - "jobs-close": "", - "jobs-deliver": "", - "jobs-detail": "", - "jobs-intake": "", - "jobs-new": "", - "jobs-ready": "", - "my_tasks": "", - "owner-detail": "", - "owners": "", - "parts-queue": "", - "payments-all": "", - "phonebook": "", - "productionboard": "", - "productionlist": "", - "profile": "", - "schedule": "", - "scoreboard": "", - "shop": "", - "shop-csi": "", - "shop-templates": "", - "shop-vendors": "", - "tasks": "", - "temporarydocs": "", - "timetickets": "", - "ttapprovals": "", - "vehicle-details": "", - "vehicles": "" - }, - "bills-list": "", - "contracts": "", - "contracts-create": "", - "contracts-detail": "", - "courtesycars": "", - "courtesycars-create": "", - "courtesycars-detail": "", - "dashboard": "", - "dms": "", - "export-logs": "", - "imexonline": "", - "inventory": "", - "jobs": "Tous les emplois | {{app}}", - "jobs-admin": "", - "jobs-all": "", - "jobs-checklist": "", - "jobs-close": "", - "jobs-create": "", - "jobs-deliver": "", - "jobs-intake": "", - "jobsavailable": "Emplois disponibles | {{app}}", - "jobsdetail": "Travail {{ro_number}} | {{app}}", - "jobsdocuments": "Documents de travail {{ro_number}} | {{app}}", - "manageroot": "Accueil | {{app}}", - "my_tasks": "", - "owners": "Tous les propriétaires | {{app}}", - "owners-detail": "", - "parts-queue": "", - "payments-all": "", - "phonebook": "", - "productionboard": "", - "productionlist": "", - "profile": "Mon profil | {{app}}", - "promanager": "", - "readyjobs": "", - "resetpassword": "", - "resetpasswordvalidate": "", - "romeonline": "", - "schedule": "Horaire | {{app}}", - "scoreboard": "", - "shop": "Mon magasin | {{app}}", - "shop-csi": "", - "shop-templates": "", - "shop_vendors": "Vendeurs | {{app}}", - "tasks": "", - "techconsole": "{{app}}", - "techjobclock": "{{app}}", - "techjoblookup": "{{app}}", - "techshiftclock": "{{app}}", - "temporarydocs": "", - "timetickets": "", - "ttapprovals": "", - "vehicledetail": "Détails du véhicule {{vehicle} | {{app}}", - "vehicles": "Tous les véhicules | {{app}}" - }, - "trello": { - "labels": { - "add_card": "", - "add_lane": "", - "cancel": "", - "delete_lane": "", - "description": "", - "label": "", - "lane_actions": "", - "title": "" - } - }, - "tt_approvals": { - "actions": { - "approveselected": "" - }, - "labels": { - "approval_queue_in_use": "", - "calculate": "" - } - }, - "user": { - "actions": { - "changepassword": "", - "signout": "Déconnexion", - "updateprofile": "Mettre à jour le profil" - }, - "errors": { - "updating": "" - }, - "fields": { - "authlevel": "", - "displayname": "Afficher un nom", - "email": "", - "photourl": "URL de l'avatar" - }, - "labels": { - "actions": "", - "changepassword": "", - "profileinfo": "" - }, - "successess": { - "passwordchanged": "" - } - }, - "users": { - "errors": { - "signinerror": { - "auth/user-disabled": "", - "auth/user-not-found": "", - "auth/wrong-password": "" - } - } - }, - "vehicles": { - "errors": { - "deleting": "", - "noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.", - "selectexistingornew": "", - "validation": "Veuillez vous assurer que tous les champs sont correctement entrés.", - "validationtitle": "Erreur de validation" - }, - "fields": { - "description": "Description du véhicule", - "notes": "", - "plate_no": "Plaque d'immatriculation", - "plate_st": "Juridiction de la plaque", - "trim_color": "Couleur de garniture", - "v_bstyle": "Style corporel", - "v_color": "Couleur", - "v_cond": "Etat", - "v_engine": "moteur", - "v_make_desc": "Faire", - "v_makecode": "Faire du code", - "v_mldgcode": "Code de moulage", - "v_model_desc": "Modèle", - "v_model_yr": "année", - "v_options": "Les options", - "v_paint_codes": "Codes de peinture", - "v_prod_dt": "Date de production", - "v_stage": "Étape", - "v_tone": "ton", - "v_trimcode": "Code de coupe", - "v_type": "Type", - "v_vin": "V.I.N." - }, - "forms": { - "detail": "", - "misc": "", - "registration": "" - }, - "labels": { - "deleteconfirm": "", - "fromvehicle": "", - "novehinfo": "", - "relatedjobs": "", - "updatevehicle": "" - }, - "successes": { - "delete": "", - "save": "Le véhicule a été enregistré avec succès." - } - }, - "vendors": { - "actions": { - "addtophonebook": "", - "new": "Nouveau vendeur", - "newpreferredmake": "" - }, - "errors": { - "deleting": "Erreur rencontrée lors de la suppression du fournisseur.", - "saving": "Erreur rencontrée lors de l'enregistrement du fournisseur." - }, - "fields": { - "active": "", - "am": "", - "city": "Ville", - "cost_center": "Centre de coûts", - "country": "Pays", - "discount": "Remise %", - "display_name": "Afficher un nom", - "dmsid": "", - "due_date": "Date limite de paiement", - "email": "Email du contact", - "favorite": "Préféré?", - "lkq": "", - "make": "", - "name": "Nom du vendeur", - "oem": "", - "phone": "", - "prompt_discount": "Remise rapide%", - "state": "Etat / Province", - "street1": "rue", - "street2": "Adresse 2 ", - "taxid": "Identifiant de taxe", - "terms": "Modalités de paiement", - "zip": "Zip / code postal" - }, - "labels": { - "noneselected": "Aucun fournisseur n'est sélectionné.", - "preferredmakes": "", - "search": "Tapez le nom d'un vendeur" - }, - "successes": { - "deleted": "Le fournisseur a bien été supprimé.", - "saved": "Le fournisseur a bien enregistré." - }, - "validation": { - "unique_vendor_name": "" - } - } - } + "translation": { + "allocations": { + "actions": { + "assign": "Attribuer" + }, + "errors": { + "deleting": "", + "saving": "", + "validation": "" + }, + "fields": { + "employee": "Alloué à" + }, + "successes": { + "deleted": "", + "save": "" + } + }, + "appointments": { + "actions": { + "block": "", + "calculate": "", + "cancel": "annuler", + "intake": "Admission", + "new": "Nouveau rendez-vous", + "preview": "", + "reschedule": "Replanifier", + "sendreminder": "", + "unblock": "", + "viewjob": "Voir le travail" + }, + "errors": { + "blocking": "", + "canceling": "Erreur lors de l'annulation du rendez-vous. {{message}}", + "saving": "Erreur lors de la planification du rendez-vous. {{message}}" + }, + "fields": { + "alt_transport": "", + "color": "", + "end": "", + "note": "", + "start": "", + "time": "", + "title": "Titre" + }, + "labels": { + "arrivedon": "Arrivé le:", + "arrivingjobs": "", + "blocked": "", + "cancelledappointment": "Rendez-vous annulé pour:", + "completingjobs": "", + "dataconsistency": "", + "expectedjobs": "", + "expectedprodhrs": "", + "history": "", + "inproduction": "", + "manualevent": "", + "noarrivingjobs": "", + "nocompletingjobs": "", + "nodateselected": "Aucune date n'a été sélectionnée.", + "priorappointments": "Rendez-vous précédents", + "reminder": "", + "scheduledfor": "Rendez-vous prévu pour:", + "severalerrorsfound": "", + "smartscheduling": "", + "smspaymentreminder": "", + "suggesteddates": "" + }, + "successes": { + "canceled": "Rendez-vous annulé avec succès.", + "created": "Rendez-vous planifié avec succès.", + "saved": "" + } + }, + "associations": { + "actions": { + "activate": "Activer" + }, + "fields": { + "active": "Actif?", + "shopname": "nom de la boutique" + }, + "labels": { + "actions": "actes" + } + }, + "audit": { + "fields": { + "cc": "", + "contents": "", + "created": "", + "operation": "", + "status": "", + "subject": "", + "to": "", + "useremail": "", + "values": "" + } + }, + "audit_trail": { + "messages": { + "admin_job_remove_from_ar": "", + "admin_jobmarkexported": "", + "admin_jobmarkforreexport": "", + "admin_jobuninvoice": "", + "admin_jobunvoid": "", + "alerttoggle": "", + "appointmentcancel": "", + "appointmentinsert": "", + "assignedlinehours": "", + "billdeleted": "", + "billposted": "", + "billupdated": "", + "failedpayment": "", + "jobassignmentchange": "", + "jobassignmentremoved": "", + "jobchecklist": "", + "jobclosedwithbypass": "", + "jobconverted": "", + "jobdelivery": "", + "jobexported": "", + "jobfieldchanged": "", + "jobimported": "", + "jobinproductionchange": "", + "jobintake": "", + "jobinvoiced": "", + "jobioucreated": "", + "jobmodifylbradj": "", + "jobnoteadded": "", + "jobnotedeleted": "", + "jobnoteupdated": "", + "jobspartsorder": "", + "jobspartsreturn": "", + "jobstatuschange": "", + "jobsupplement": "", + "jobsuspend": "", + "jobvoid": "", + "tasks_completed": "", + "tasks_created": "", + "tasks_deleted": "", + "tasks_uncompleted": "", + "tasks_undeleted": "", + "tasks_updated": "" + } + }, + "billlines": { + "actions": { + "newline": "" + }, + "fields": { + "actual_cost": "", + "actual_price": "", + "cost_center": "", + "federal_tax_applicable": "", + "jobline": "", + "line_desc": "", + "local_tax_applicable": "", + "location": "", + "quantity": "", + "state_tax_applicable": "" + }, + "labels": { + "deductedfromlbr": "", + "entered": "", + "from": "", + "mod_lbr_adjustment": "", + "other": "", + "reconciled": "", + "unreconciled": "" + }, + "validation": { + "atleastone": "" + } + }, + "bills": { + "actions": { + "deductallhours": "", + "edit": "", + "receive": "", + "return": "" + }, + "errors": { + "creating": "", + "deleting": "", + "existinginventoryline": "", + "exporting": "", + "exporting-partner": "", + "invalidro": "", + "invalidvendor": "", + "validation": "" + }, + "fields": { + "allpartslocation": "", + "date": "", + "exported": "", + "federal_tax_rate": "", + "invoice_number": "", + "is_credit_memo": "", + "is_credit_memo_short": "", + "local_tax_rate": "", + "ro_number": "", + "state_tax_rate": "", + "total": "", + "vendor": "", + "vendorname": "" + }, + "labels": { + "actions": "", + "bill_lines": "", + "bill_total": "", + "billcmtotal": "", + "bills": "", + "calculatedcreditsnotreceived": "", + "creditsnotreceived": "", + "creditsreceived": "", + "dedfromlbr": "", + "deleteconfirm": "", + "discrepancy": "", + "discrepwithcms": "", + "discrepwithlbradj": "", + "editadjwarning": "", + "entered_total": "", + "enteringcreditmemo": "", + "federal_tax": "", + "federal_tax_exempt": "", + "generatepartslabel": "", + "iouexists": "", + "local_tax": "", + "markexported": "", + "markforreexport": "", + "new": "", + "nobilllines": "", + "noneselected": "", + "onlycmforinvoiced": "", + "printlabels": "", + "retailtotal": "", + "returnfrombill": "", + "savewithdiscrepancy": "", + "state_tax": "", + "subtotal": "", + "totalreturns": "" + }, + "successes": { + "created": "", + "deleted": "", + "exported": "", + "markexported": "", + "reexport": "" + }, + "validation": { + "closingperiod": "", + "inventoryquantity": "", + "manualinhouse": "", + "unique_invoice_number": "" + } + }, + "bodyshop": { + "actions": { + "add_task_preset": "", + "addapptcolor": "", + "addbucket": "", + "addpartslocation": "", + "addpartsrule": "", + "addspeedprint": "", + "addtemplate": "", + "newlaborrate": "", + "newsalestaxcode": "", + "newstatus": "", + "testrender": "" + }, + "errors": { + "creatingdefaultview": "", + "loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique.", + "saving": "" + }, + "fields": { + "ReceivableCustomField": "", + "address1": "", + "address2": "", + "appt_alt_transport": "", + "appt_colors": { + "color": "", + "label": "" + }, + "appt_length": "", + "attach_pdf_to_email": "", + "batchid": "", + "bill_allow_post_to_closed": "", + "bill_federal_tax_rate": "", + "bill_local_tax_rate": "", + "bill_state_tax_rate": "", + "city": "", + "closingperiod": "", + "companycode": "", + "country": "", + "dailybodytarget": "", + "dailypainttarget": "", + "default_adjustment_rate": "", + "deliver": { + "require_actual_delivery_date": "", + "templates": "" + }, + "dms": { + "apcontrol": "", + "appostingaccount": "", + "cashierid": "", + "default_journal": "", + "disablebillwip": "", + "disablecontactvehiclecreation": "", + "dms_acctnumber": "", + "dms_control_override": "", + "dms_wip_acctnumber": "", + "generic_customer_number": "", + "itc_federal": "", + "itc_local": "", + "itc_state": "", + "mappingname": "", + "sendmaterialscosting": "", + "srcco": "" + }, + "email": "", + "enforce_class": "", + "enforce_conversion_category": "", + "enforce_conversion_csr": "", + "enforce_referral": "", + "federal_tax_id": "", + "ignoreblockeddays": "", + "inhousevendorid": "", + "insurance_vendor_id": "", + "intake": { + "next_contact_hours": "", + "templates": "" + }, + "intellipay_config": { + "cash_discount_percentage": "", + "enable_cash_discount": "" + }, + "invoice_federal_tax_rate": "", + "invoice_local_tax_rate": "", + "invoice_state_tax_rate": "", + "jc_hourly_rates": { + "mapa": "", + "mash": "" + }, + "last_name_first": "", + "lastnumberworkingdays": "", + "localmediaserverhttp": "", + "localmediaservernetwork": "", + "localmediatoken": "", + "logo_img_footer_margin": "", + "logo_img_header_margin": "", + "logo_img_path": "", + "logo_img_path_height": "", + "logo_img_path_width": "", + "md_categories": "", + "md_ccc_rates": "", + "md_classes": "", + "md_ded_notes": "", + "md_email_cc": "", + "md_from_emails": "", + "md_functionality_toggles": { + "parts_queue_toggle": "" + }, + "md_hour_split": { + "paint": "", + "prep": "" + }, + "md_ins_co": { + "city": "", + "name": "", + "private": "", + "state": "", + "street1": "", + "street2": "", + "zip": "" + }, + "md_jobline_presets": "", + "md_lost_sale_reasons": "", + "md_parts_order_comment": "", + "md_parts_scan": { + "expression": "", + "flags": "" + }, + "md_payment_types": "", + "md_referral_sources": "", + "md_ro_guard": { + "enabled": "", + "enforce_ar": "", + "enforce_bills": "", + "enforce_cm": "", + "enforce_labor": "", + "enforce_ppd": "", + "enforce_profit": "", + "enforce_sublet": "", + "masterbypass": "", + "totalgppercent_minimum": "" + }, + "md_tasks_presets": { + "enable_tasks": "", + "hourstype": "", + "memo": "", + "name": "", + "nextstatus": "", + "percent": "", + "use_approvals": "" + }, + "messaginglabel": "", + "messagingtext": "", + "noteslabel": "", + "notestext": "", + "partslocation": "", + "phone": "", + "prodtargethrs": "", + "rbac": { + "accounting": { + "exportlog": "", + "payables": "", + "payments": "", + "receivables": "" + }, + "bills": { + "delete": "", + "enter": "", + "list": "", + "reexport": "", + "view": "" + }, + "contracts": { + "create": "", + "detail": "", + "list": "" + }, + "courtesycar": { + "create": "", + "detail": "", + "list": "" + }, + "csi": { + "export": "", + "page": "" + }, + "employee_teams": { + "page": "" + }, + "employees": { + "page": "" + }, + "inventory": { + "delete": "", + "list": "" + }, + "jobs": { + "admin": "", + "available-list": "", + "checklist-view": "", + "close": "", + "create": "", + "deliver": "", + "detail": "", + "intake": "", + "list-active": "", + "list-all": "", + "list-ready": "", + "partsqueue": "", + "void": "" + }, + "owners": { + "detail": "", + "list": "" + }, + "payments": { + "enter": "", + "list": "" + }, + "phonebook": { + "edit": "", + "view": "" + }, + "production": { + "board": "", + "list": "" + }, + "schedule": { + "view": "" + }, + "scoreboard": { + "view": "" + }, + "shiftclock": { + "view": "" + }, + "shop": { + "config": "", + "dashboard": "", + "rbac": "", + "reportcenter": "", + "templates": "", + "vendors": "" + }, + "temporarydocs": { + "view": "" + }, + "timetickets": { + "edit": "", + "editcommitted": "", + "enter": "", + "list": "", + "shiftedit": "" + }, + "ttapprovals": { + "approve": "", + "view": "" + }, + "users": { + "editaccess": "" + } + }, + "responsibilitycenter": "", + "responsibilitycenter_accountdesc": "", + "responsibilitycenter_accountitem": "", + "responsibilitycenter_accountname": "", + "responsibilitycenter_accountnumber": "", + "responsibilitycenter_rate": "", + "responsibilitycenter_tax_rate": "", + "responsibilitycenter_tax_sur": "", + "responsibilitycenter_tax_thres": "", + "responsibilitycenter_tax_tier": "", + "responsibilitycenter_tax_type": "", + "responsibilitycenters": { + "ap": "", + "ar": "", + "ats": "", + "federal_tax": "", + "federal_tax_itc": "", + "gst_override": "", + "invoiceexemptcode": "", + "itemexemptcode": "", + "la1": "", + "la2": "", + "la3": "", + "la4": "", + "laa": "", + "lab": "", + "lad": "", + "lae": "", + "laf": "", + "lag": "", + "lam": "", + "lar": "", + "las": "", + "lau": "", + "local_tax": "", + "mapa": "", + "mash": "", + "paa": "", + "pac": "", + "pag": "", + "pal": "", + "pam": "", + "pan": "", + "pao": "", + "pap": "", + "par": "", + "pas": "", + "pasl": "", + "refund": "", + "sales_tax_codes": { + "code": "", + "description": "", + "federal": "", + "local": "", + "state": "" + }, + "state_tax": "", + "tow": "" + }, + "schedule_end_time": "", + "schedule_start_time": "", + "shopname": "", + "speedprint": { + "id": "", + "label": "", + "templates": "" + }, + "ss_configuration": { + "dailyhrslimit": "" + }, + "ssbuckets": { + "color": "", + "gte": "", + "id": "", + "label": "", + "lt": "", + "target": "" + }, + "state": "", + "state_tax_id": "", + "status": "", + "statuses": { + "active_statuses": "", + "additional_board_statuses": "", + "color": "", + "default_arrived": "", + "default_bo": "", + "default_canceled": "", + "default_completed": "", + "default_delivered": "", + "default_exported": "", + "default_imported": "", + "default_invoiced": "", + "default_ordered": "", + "default_quote": "", + "default_received": "", + "default_returned": "", + "default_scheduled": "", + "default_void": "", + "open_statuses": "", + "post_production_statuses": "", + "pre_production_statuses": "", + "production_colors": "", + "production_statuses": "", + "ready_statuses": "" + }, + "target_touchtime": "", + "timezone": "", + "tt_allow_post_to_invoiced": "", + "tt_enforce_hours_for_tech_console": "", + "use_fippa": "", + "use_paint_scale_data": "", + "uselocalmediaserver": "", + "website": "", + "zip_post": "" + }, + "labels": { + "2tiername": "", + "2tiersetup": "", + "2tiersource": "", + "accountingsetup": "", + "accountingtiers": "", + "alljobstatuses": "", + "allopenjobstatuses": "", + "apptcolors": "", + "businessinformation": "", + "checklists": "", + "csiq": "", + "customtemplates": "", + "defaultcostsmapping": "", + "defaultprofitsmapping": "", + "deliverchecklist": "", + "dms": { + "cdk": { + "controllist": "", + "payers": "" + }, + "cdk_dealerid": "", + "costsmapping": "", + "dms_allocations": "", + "pbs_serialnumber": "", + "profitsmapping": "", + "title": "" + }, + "emaillater": "", + "employee_teams": "", + "employees": "", + "estimators": "", + "filehandlers": "", + "insurancecos": "", + "intakechecklist": "", + "intellipay": "", + "intellipay_cash_discount": "", + "jobstatuses": "", + "laborrates": "", + "licensing": "", + "md_parts_scan": "", + "md_ro_guard": "", + "md_tasks_presets": "", + "md_to_emails": "", + "md_to_emails_emails": "", + "messagingpresets": "", + "notemplatesavailable": "", + "notespresets": "", + "orderstatuses": "", + "partslocations": "", + "partsscan": "", + "printlater": "", + "qbo": "", + "qbo_departmentid": "", + "qbo_usa": "", + "rbac": "", + "responsibilitycenters": { + "costs": "", + "profits": "", + "sales_tax_codes": "", + "tax_accounts": "", + "title": "" + }, + "roguard": { + "title": "" + }, + "scheduling": "", + "scoreboardsetup": "", + "shopinfo": "", + "speedprint": "", + "ssbuckets": "", + "systemsettings": "", + "task-presets": "", + "workingdays": "" + }, + "successes": { + "areyousure": "", + "defaultviewcreated": "", + "save": "", + "unsavedchanges": "" + }, + "validation": { + "centermustexist": "", + "larsplit": "", + "useremailmustexist": "" + } + }, + "checklist": { + "actions": { + "printall": "" + }, + "errors": { + "complete": "", + "nochecklist": "" + }, + "labels": { + "addtoproduction": "", + "allow_text_message": "", + "checklist": "", + "printpack": "", + "removefromproduction": "" + }, + "successes": { + "completed": "" + } + }, + "contracts": { + "actions": { + "changerate": "", + "convertoro": "", + "decodelicense": "", + "find": "", + "printcontract": "", + "senddltoform": "" + }, + "errors": { + "fetchingjobinfo": "", + "returning": "", + "saving": "", + "selectjobandcar": "" + }, + "fields": { + "actax": "", + "actualreturn": "", + "agreementnumber": "", + "cc_cardholder": "", + "cc_expiry": "", + "cc_num": "", + "cleanupcharge": "", + "coverage": "", + "dailyfreekm": "", + "dailyrate": "", + "damage": "", + "damagewaiver": "", + "driver": "", + "driver_addr1": "", + "driver_addr2": "", + "driver_city": "", + "driver_dlexpiry": "", + "driver_dlnumber": "", + "driver_dlst": "", + "driver_dob": "", + "driver_fn": "", + "driver_ln": "", + "driver_ph1": "", + "driver_state": "", + "driver_zip": "", + "excesskmrate": "", + "federaltax": "", + "fuelin": "", + "fuelout": "", + "kmend": "", + "kmstart": "", + "length": "", + "localtax": "", + "refuelcharge": "", + "scheduledreturn": "", + "start": "", + "statetax": "", + "status": "" + }, + "labels": { + "agreement": "", + "availablecars": "", + "cardueforservice": "", + "convertform": { + "applycleanupcharge": "", + "refuelqty": "" + }, + "correctdataonform": "", + "dateinpast": "", + "dlexpirebeforereturn": "", + "driverinformation": "", + "findcontract": "", + "findermodal": "", + "insuranceexpired": "", + "noteconvertedfrom": "", + "populatefromjob": "", + "rates": "", + "time": "", + "vehicle": "", + "waitingforscan": "" + }, + "status": { + "new": "", + "out": "", + "returned": "" + }, + "successes": { + "saved": "" + } + }, + "courtesycars": { + "actions": { + "new": "", + "return": "" + }, + "errors": { + "saving": "" + }, + "fields": { + "color": "", + "dailycost": "", + "damage": "", + "fleetnumber": "", + "fuel": "", + "insuranceexpires": "", + "leaseenddate": "", + "make": "", + "mileage": "", + "model": "", + "nextservicedate": "", + "nextservicekm": "", + "notes": "", + "plate": "", + "purchasedate": "", + "readiness": "", + "registrationexpires": "", + "serviceenddate": "", + "servicestartdate": "", + "status": "", + "vin": "", + "year": "" + }, + "labels": { + "courtesycar": "", + "fuel": { + "12": "", + "14": "", + "18": "", + "34": "", + "38": "", + "58": "", + "78": "", + "empty": "", + "full": "" + }, + "outwith": "", + "return": "", + "status": "", + "uniquefleet": "", + "usage": "", + "vehicle": "" + }, + "readiness": { + "notready": "", + "ready": "" + }, + "status": { + "in": "", + "inservice": "", + "leasereturn": "", + "out": "", + "sold": "", + "unavailable": "" + }, + "successes": { + "saved": "" + } + }, + "csi": { + "actions": { + "activate": "" + }, + "errors": { + "creating": "", + "notconfigured": "", + "notfoundsubtitle": "", + "notfoundtitle": "", + "surveycompletesubtitle": "", + "surveycompletetitle": "" + }, + "fields": { + "completedon": "", + "created_at": "", + "surveyid": "", + "validuntil": "" + }, + "labels": { + "copyright": "", + "greeting": "", + "intro": "", + "nologgedinuser": "", + "nologgedinuser_sub": "", + "noneselected": "", + "title": "" + }, + "successes": { + "created": "", + "submitted": "", + "submittedsub": "" + } + }, + "dashboard": { + "actions": { + "addcomponent": "" + }, + "errors": { + "refreshrequired": "", + "updatinglayout": "" + }, + "labels": { + "bodyhrs": "", + "dollarsinproduction": "", + "phone": "", + "prodhrs": "", + "refhrs": "" + }, + "titles": { + "joblifecycle": "", + "labhours": "", + "larhours": "", + "monthlyemployeeefficiency": "", + "monthlyjobcosting": "", + "monthlylaborsales": "", + "monthlypartssales": "", + "monthlyrevenuegraph": "", + "prodhrssummary": "", + "productiondollars": "", + "productionhours": "", + "projectedmonthlysales": "", + "scheduledindate": "", + "scheduledintoday": "", + "scheduledoutdate": "", + "scheduledouttoday": "", + "tasks": "" + } + }, + "dms": { + "errors": { + "alreadyexported": "" + }, + "labels": { + "refreshallocations": "" + } + }, + "documents": { + "actions": { + "delete": "", + "download": "", + "reassign": "", + "selectallimages": "", + "selectallotherdocuments": "" + }, + "errors": { + "deletes3": "Erreur lors de la suppression du document du stockage.", + "deleting": "", + "deleting_cloudinary": "", + "getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}", + "insert": "Incapable de télécharger le fichier. {{message}}", + "nodocuments": "Il n'y a pas de documents.", + "updating": "" + }, + "labels": { + "confirmdelete": "", + "doctype": "", + "newjobid": "", + "openinexplorer": "", + "optimizedimage": "", + "reassign_limitexceeded": "", + "reassign_limitexceeded_title": "", + "storageexceeded": "", + "storageexceeded_title": "", + "upload": "Télécharger", + "upload_limitexceeded": "", + "upload_limitexceeded_title": "", + "uploading": "", + "usage": "" + }, + "successes": { + "delete": "Le document a bien été supprimé.", + "edituploaded": "", + "insert": "Document téléchargé avec succès.", + "updated": "" + } + }, + "emails": { + "errors": { + "notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}" + }, + "fields": { + "cc": "", + "from": "", + "subject": "", + "to": "" + }, + "labels": { + "attachments": "", + "documents": "", + "emailpreview": "", + "generatingemail": "", + "pdfcopywillbeattached": "", + "preview": "" + }, + "successes": { + "sent": "E-mail envoyé avec succès." + } + }, + "employee_teams": { + "actions": { + "new": "", + "newmember": "" + }, + "fields": { + "active": "", + "employeeid": "", + "max_load": "", + "name": "", + "percentage": "" + } + }, + "employees": { + "actions": { + "addvacation": "", + "new": "Nouvel employé", + "newrate": "" + }, + "errors": { + "delete": "Erreur rencontrée lors de la suppression de l'employé. {{message}}", + "save": "Une erreur s'est produite lors de l'enregistrement de l'employé. {{message}}", + "validation": "Veuillez cocher tous les champs.", + "validationtitle": "Impossible d'enregistrer l'employé." + }, + "fields": { + "active": "Actif?", + "base_rate": "Taux de base", + "cost_center": "Centre de coûts", + "employee_number": "Numéro d'employé", + "external_id": "", + "first_name": "Prénom", + "flat_rate": "Taux fixe (désactivé est le temps normal)", + "hire_date": "Date d'embauche", + "last_name": "Nom de famille", + "pin": "", + "rate": "", + "termination_date": "Date de résiliation", + "user_email": "", + "vacation": { + "end": "", + "length": "", + "start": "" + } + }, + "labels": { + "actions": "", + "active": "", + "endmustbeafterstart": "", + "flat_rate": "", + "inactive": "", + "name": "", + "rate_type": "", + "status": "", + "straight_time": "" + }, + "successes": { + "delete": "L'employé a bien été supprimé.", + "save": "L'employé a enregistré avec succès.", + "vacationadded": "" + }, + "validation": { + "unique_employee_number": "" + } + }, + "eula": { + "buttons": { + "accept": "Accept EULA" + }, + "content": { + "never_scrolled": "You must scroll to the bottom of the Terms and Conditions before accepting." + }, + "errors": { + "acceptance": { + "description": "Something went wrong while accepting the EULA. Please try again.", + "message": "Eula Acceptance Error" + } + }, + "labels": { + "accepted_terms": "I accept the terms and conditions of this agreement.", + "address": "Address", + "business_name": "Legal Business Name", + "date_accepted": "Date Accepted", + "first_name": "First Name", + "last_name": "Last Name", + "phone_number": "Phone Number" + }, + "messages": { + "accepted_terms": "Please accept the terms and conditions of this agreement.", + "business_name": "Please enter your legal business name.", + "date_accepted": "Please enter Today's Date.", + "first_name": "Please enter your first name.", + "last_name": "Please enter your last name.", + "phone_number": "Please enter your phone number." + }, + "titles": { + "modal": "Terms and Conditions", + "upper_card": "Acknowledgement" + } + }, + "exportlogs": { + "fields": { + "createdat": "" + }, + "labels": { + "attempts": "", + "priorsuccesfulexport": "" + } + }, + "general": { + "actions": { + "add": "", + "autoupdate": "", + "calculate": "", + "cancel": "", + "clear": "", + "close": "", + "copied": "", + "copylink": "", + "create": "", + "defaults": "", + "delay": "", + "delete": "Effacer", + "deleteall": "", + "deselectall": "", + "download": "", + "edit": "modifier", + "login": "", + "next": "", + "previous": "", + "print": "", + "refresh": "", + "remove": "", + "remove_alert": "", + "reset": " Rétablir l'original.", + "resetpassword": "", + "save": "sauvegarder", + "saveandnew": "", + "saveas": "", + "selectall": "", + "send": "", + "sendbysms": "", + "senderrortosupport": "", + "submit": "", + "tryagain": "", + "view": "", + "viewreleasenotes": "" + }, + "errors": { + "fcm": "", + "notfound": "", + "sizelimit": "" + }, + "itemtypes": { + "contract": "", + "courtesycar": "", + "job": "", + "owner": "", + "vehicle": "" + }, + "labels": { + "actions": "actes", + "areyousure": "", + "barcode": "code à barre", + "cancel": "", + "clear": "", + "confirmpassword": "", + "created_at": "", + "date": "", + "datetime": "", + "email": "", + "errors": "", + "excel": "", + "exceptiontitle": "", + "friday": "", + "globalsearch": "", + "help": "", + "hours": "", + "in": "dans", + "instanceconflictext": "", + "instanceconflictitle": "", + "item": "", + "label": "", + "loading": "Chargement...", + "loadingapp": "Chargement de {{app}}", + "loadingshop": "Chargement des données de la boutique ...", + "loggingin": "Vous connecter ...", + "markedexported": "", + "media": "", + "message": "", + "monday": "", + "na": "N / A", + "newpassword": "", + "no": "", + "nointernet": "", + "nointernet_sub": "", + "none": "", + "out": "En dehors", + "password": "", + "passwordresetsuccess": "", + "passwordresetsuccess_sub": "", + "passwordresetvalidatesuccess": "", + "passwordresetvalidatesuccess_sub": "", + "passwordsdonotmatch": "", + "print": "", + "refresh": "", + "reports": "", + "required": "", + "saturday": "", + "search": "Chercher...", + "searchresults": "", + "selectdate": "", + "sendagain": "", + "sendby": "", + "signin": "", + "sms": "", + "status": "", + "sub_status": { + "expired": "" + }, + "successful": "", + "sunday": "", + "text": "", + "thursday": "", + "total": "", + "totals": "", + "tuesday": "", + "tvmode": "", + "unknown": "Inconnu", + "unsavedchanges": "", + "username": "", + "view": "", + "wednesday": "", + "yes": "" + }, + "languages": { + "english": "Anglais", + "french": "Francais", + "spanish": "Espanol" + }, + "messages": { + "exception": "", + "newversionmessage": "", + "newversiontitle": "", + "noacctfilepath": "", + "nofeatureaccess": "", + "noshop": "", + "notfoundsub": "", + "notfoundtitle": "", + "partnernotrunning": "", + "rbacunauth": "", + "unsavedchanges": "Vous avez des changements non enregistrés.", + "unsavedchangespopup": "" + }, + "validation": { + "invalidemail": "S'il vous plaît entrer un email valide.", + "invalidphone": "", + "required": "Ce champ est requis." + } + }, + "help": { + "actions": { + "connect": "" + }, + "labels": { + "codeplacholder": "", + "rescuedesc": "", + "rescuetitle": "" + } + }, + "intake": { + "labels": { + "printpack": "" + } + }, + "inventory": { + "actions": { + "addtoinventory": "", + "addtoro": "", + "consumefrominventory": "", + "edit": "", + "new": "" + }, + "errors": { + "inserting": "" + }, + "fields": { + "comment": "", + "manualinvoicenumber": "", + "manualvendor": "" + }, + "labels": { + "consumedbyjob": "", + "deleteconfirm": "", + "frombillinvoicenumber": "", + "fromvendor": "", + "inventory": "", + "showall": "", + "showavailable": "" + }, + "successes": { + "deleted": "", + "inserted": "", + "updated": "" + } + }, + "job_lifecycle": { + "columns": { + "duration": "", + "end": "", + "human_readable": "", + "percentage": "", + "relative_end": "", + "relative_start": "", + "start": "", + "status": "", + "status_count": "", + "value": "" + }, + "content": { + "calculated_based_on": "", + "current_status_accumulated_time": "", + "data_unavailable": "", + "jobs_in_since": "", + "legend_title": "", + "loading": "", + "not_available": "", + "previous_status_accumulated_time": "", + "title": "", + "title_durations": "", + "title_loading": "", + "title_transitions": "" + }, + "errors": { + "fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches" + }, + "titles": { + "dashboard": "", + "top_durations": "" + } + }, + "job_payments": { + "buttons": { + "create_short_link": "", + "goback": "", + "proceedtopayment": "", + "refundpayment": "" + }, + "notifications": { + "error": { + "description": "", + "openingip": "", + "title": "" + } + }, + "titles": { + "amount": "", + "dateOfPayment": "", + "descriptions": "", + "hint": "", + "payer": "", + "payername": "", + "paymentid": "", + "paymentnum": "", + "paymenttype": "", + "refundamount": "", + "transactionid": "" + } + }, + "joblines": { + "actions": { + "assign_team": "", + "converttolabor": "", + "dispatchparts": "", + "new": "" + }, + "errors": { + "creating": "", + "updating": "" + }, + "fields": { + "act_price": "Prix actuel", + "act_price_before_ppc": "", + "adjustment": "", + "ah_detail_line": "", + "amount": "", + "assigned_team": "", + "assigned_team_name": "", + "create_ppc": "", + "db_price": "Prix de la base de données", + "lbr_types": { + "LA1": "", + "LA2": "", + "LA3": "", + "LA4": "", + "LAA": "", + "LAB": "", + "LAD": "", + "LAE": "", + "LAF": "", + "LAG": "", + "LAM": "", + "LAR": "", + "LAS": "", + "LAU": "" + }, + "line_desc": "Description de la ligne", + "line_ind": "S#", + "line_no": "", + "location": "", + "mod_lb_hrs": "Heures de travail", + "mod_lbr_ty": "Type de travail", + "notes": "", + "oem_partno": "Pièce OEM #", + "op_code_desc": "", + "part_qty": "", + "part_type": "Type de pièce", + "part_types": { + "CCC": "", + "CCD": "", + "CCDR": "", + "CCF": "", + "CCM": "", + "PAA": "", + "PAC": "", + "PAE": "", + "PAG": "", + "PAL": "", + "PAM": "", + "PAN": "", + "PAO": "", + "PAP": "", + "PAR": "", + "PAS": "", + "PASL": "" + }, + "profitcenter_labor": "", + "profitcenter_part": "", + "prt_dsmk_m": "", + "prt_dsmk_p": "", + "status": "Statut", + "tax_part": "", + "total": "", + "unq_seq": "Seq #" + }, + "labels": { + "adjustmenttobeadded": "", + "billref": "", + "convertedtolabor": "", + "edit": "Ligne d'édition", + "ioucreated": "", + "new": "Nouvelle ligne", + "nostatus": "", + "presets": "" + }, + "successes": { + "created": "", + "saved": "", + "updated": "" + }, + "validations": { + "ahdetailonlyonuserdefinedtypes": "", + "hrsrequirediflbrtyp": "", + "requiredifparttype": "", + "zeropriceexistingpart": "" + } + }, + "jobs": { + "actions": { + "addDocuments": "Ajouter des documents de travail", + "addNote": "Ajouter une note", + "addtopartsqueue": "", + "addtoproduction": "", + "addtoscoreboard": "", + "allocate": "", + "autoallocate": "", + "changefilehandler": "", + "changelaborrate": "", + "changestatus": "Changer le statut", + "changestimator": "", + "convert": "Convertir", + "createiou": "", + "deliver": "", + "dms": { + "addpayer": "", + "createnewcustomer": "", + "findmakemodelcode": "", + "getmakes": "", + "labels": { + "refreshallocations": "" + }, + "post": "", + "refetchmakesmodels": "", + "usegeneric": "", + "useselected": "" + }, + "dmsautoallocate": "", + "export": "", + "exportcustdata": "", + "exportselected": "", + "filterpartsonly": "", + "generatecsi": "", + "gotojob": "", + "intake": "", + "manualnew": "", + "mark": "", + "markasexported": "", + "markpstexempt": "", + "markpstexemptconfirm": "", + "postbills": "Poster des factures", + "printCenter": "Centre d'impression", + "recalculate": "", + "reconcile": "", + "removefromproduction": "", + "schedule": "Programme", + "sendcsi": "", + "sendpartspricechange": "", + "sendtodms": "", + "sync": "", + "taxprofileoverride": "", + "taxprofileoverride_confirm": "", + "uninvoice": "", + "unvoid": "", + "viewchecklist": "", + "viewdetail": "" + }, + "errors": { + "addingtoproduction": "", + "cannotintake": "", + "closing": "", + "creating": "", + "deleted": "Erreur lors de la suppression du travail.", + "exporting": "", + "exporting-partner": "", + "invoicing": "", + "noaccess": "Ce travail n'existe pas ou vous n'y avez pas accès.", + "nodamage": "", + "nodates": "Aucune date spécifiée pour ce travail.", + "nofinancial": "", + "nojobselected": "Aucun travail n'est sélectionné.", + "noowner": "Aucun propriétaire associé.", + "novehicle": "Aucun véhicule associé.", + "partspricechange": "", + "saving": "Erreur rencontrée lors de la sauvegarde de l'enregistrement.", + "scanimport": "", + "totalscalc": "", + "updating": "", + "validation": "Veuillez vous assurer que tous les champs sont correctement entrés.", + "validationtitle": "Erreur de validation", + "voiding": "" + }, + "fields": { + "active_tasks": "", + "actual_completion": "Achèvement réel", + "actual_delivery": "Livraison réelle", + "actual_in": "En réel", + "adjustment_bottom_line": "Ajustements", + "adjustmenthours": "", + "alt_transport": "", + "area_of_damage_impact": { + "10": "", + "11": "", + "12": "", + "13": "", + "14": "", + "15": "", + "16": "", + "25": "", + "26": "", + "27": "", + "28": "", + "34": "", + "01": "", + "02": "", + "03": "", + "04": "", + "05": "", + "06": "", + "07": "", + "08": "", + "09": "" + }, + "auto_add_ats": "", + "ca_bc_pvrt": "", + "ca_customer_gst": "", + "ca_gst_registrant": "", + "category": "", + "ccc": "", + "ccd": "", + "ccdr": "", + "ccf": "", + "ccm": "", + "cieca_id": "CIECA ID", + "cieca_pfl": { + "lbr_adjp": "", + "lbr_tax_in": "", + "lbr_taxp": "", + "lbr_tx_in1": "", + "lbr_tx_in2": "", + "lbr_tx_in3": "", + "lbr_tx_in4": "", + "lbr_tx_in5": "" + }, + "cieca_pfo": { + "stor_t_in1": "", + "stor_t_in2": "", + "stor_t_in3": "", + "stor_t_in4": "", + "stor_t_in5": "", + "tow_t_in1": "", + "tow_t_in2": "", + "tow_t_in3": "", + "tow_t_in4": "", + "tow_t_in5": "" + }, + "claim_total": "Total réclamation", + "class": "", + "clm_no": "Prétendre #", + "clm_total": "Total réclamation", + "comment": "", + "customerowing": "Client propriétaire", + "date_estimated": "Date estimée", + "date_exported": "Exportés", + "date_invoiced": "Facturé", + "date_last_contacted": "", + "date_lost_sale": "", + "date_next_contact": "", + "date_open": "Ouvrir", + "date_rentalresp": "", + "date_repairstarted": "", + "date_scheduled": "Prévu", + "date_towin": "", + "date_void": "", + "ded_amt": "Déductible", + "ded_note": "", + "ded_status": "Statut de franchise", + "depreciation_taxes": "Amortissement / taxes", + "dms": { + "address": "", + "amount": "", + "center": "", + "control_type": { + "account_number": "" + }, + "cost": "", + "cost_dms_acctnumber": "", + "dms_make": "", + "dms_model": "", + "dms_model_override": "", + "dms_unsold": "", + "dms_wip_acctnumber": "", + "id": "", + "inservicedate": "", + "journal": "", + "lines": "", + "name1": "", + "payer": { + "amount": "", + "control_type": "", + "controlnumber": "", + "dms_acctnumber": "", + "name": "" + }, + "sale": "", + "sale_dms_acctnumber": "", + "story": "", + "vinowner": "" + }, + "dms_allocation": "", + "driveable": "", + "employee_body": "", + "employee_csr": "représentant du service à la clientèle", + "employee_csr_writer": "", + "employee_prep": "", + "employee_refinish": "", + "est_addr1": "Adresse de l'évaluateur", + "est_co_nm": "Expert", + "est_ct_fn": "Prénom de l'évaluateur", + "est_ct_ln": "Nom de l'évaluateur", + "est_ea": "Courriel de l'évaluateur", + "est_ph1": "Numéro de téléphone de l'évaluateur", + "federal_tax_payable": "Impôt fédéral à payer", + "federal_tax_rate": "", + "ins_addr1": "Adresse Insurance Co.", + "ins_city": "Insurance City", + "ins_co_id": "ID de la compagnie d'assurance", + "ins_co_nm": "Nom de la compagnie d'assurance", + "ins_co_nm_short": "", + "ins_ct_fn": "Prénom du gestionnaire de fichiers", + "ins_ct_ln": "Nom du gestionnaire de fichiers", + "ins_ea": "Courriel du gestionnaire de fichiers", + "ins_ph1": "Numéro de téléphone du gestionnaire de fichiers", + "intake": { + "label": "", + "max": "", + "min": "", + "name": "", + "required": "", + "type": "" + }, + "invoice_final_note": "", + "kmin": "Kilométrage en", + "kmout": "Kilométrage hors", + "la1": "", + "la2": "", + "la3": "", + "la4": "", + "laa": "", + "lab": "", + "labor_rate_desc": "Nom du taux de main-d'œuvre", + "lad": "", + "lae": "", + "laf": "", + "lag": "", + "lam": "", + "lar": "", + "las": "", + "lau": "", + "local_tax_rate": "", + "loss_date": "Date de perte", + "loss_desc": "", + "loss_of_use": "", + "lost_sale_reason": "", + "ma2s": "", + "ma3s": "", + "mabl": "", + "macs": "", + "mahw": "", + "mapa": "", + "mash": "", + "matd": "", + "materials": { + "MAPA": "", + "MASH": "", + "cal_maxdlr": "", + "cal_opcode": "", + "mat_adjp": "", + "mat_taxp": "", + "mat_tx_in1": "", + "mat_tx_in2": "", + "mat_tx_in3": "", + "mat_tx_in4": "", + "mat_tx_in5": "", + "materials": "", + "tax_ind": "" + }, + "other_amount_payable": "Autre montant à payer", + "owner": "Propriétaire", + "owner_owing": "Cust. Owes", + "ownr_ea": "Email", + "ownr_ph1": "Téléphone 1", + "ownr_ph2": "", + "paa": "", + "pac": "", + "pae": "", + "pag": "", + "pal": "", + "pam": "", + "pan": "", + "pao": "", + "pap": "", + "par": "", + "parts_tax_rates": { + "prt_discp": "", + "prt_mktyp": "", + "prt_mkupp": "", + "prt_tax_in": "", + "prt_tax_rt": "", + "prt_tx_in1": "", + "prt_tx_in2": "", + "prt_tx_in3": "", + "prt_tx_in4": "", + "prt_tx_in5": "", + "prt_tx_ty1": "", + "prt_type": "" + }, + "partsstatus": "", + "pas": "", + "pay_date": "Date d'Pay", + "phoneshort": "PH", + "po_number": "", + "policy_no": "Politique #", + "ponumber": "Numéro de bon de commande", + "production_vars": { + "note": "" + }, + "qb_multiple_payers": { + "amount": "", + "name": "" + }, + "queued_for_parts": "", + "rate_ats": "", + "rate_la1": "Taux LA1", + "rate_la2": "Taux LA2", + "rate_la3": "Taux LA3", + "rate_la4": "Taux LA4", + "rate_laa": "Taux d'aluminium", + "rate_lab": "Taux de la main-d'œuvre", + "rate_lad": "Taux de diagnostic", + "rate_lae": "Tarif électrique", + "rate_laf": "Taux de trame", + "rate_lag": "Taux de verre", + "rate_lam": "Taux mécanique", + "rate_lar": "Taux de finition", + "rate_las": "", + "rate_lau": "Taux d'aluminium", + "rate_ma2s": "Taux de peinture en 2 étapes", + "rate_ma3s": "Taux de peinture en 3 étapes", + "rate_mabl": "MABL ??", + "rate_macs": "MACS ??", + "rate_mahw": "Taux de déchets dangereux", + "rate_mapa": "Taux de matériaux de peinture", + "rate_mash": "Tarif du matériel de la boutique", + "rate_matd": "Taux d'élimination des pneus", + "referral_source_extra": "", + "referral_source_other": "", + "referralsource": "Source de référence", + "regie_number": "Enregistrement #", + "repairtotal": "Réparation totale", + "ro_number": "RO #", + "scheduled_completion": "Achèvement planifié", + "scheduled_delivery": "Livraison programmée", + "scheduled_in": "Planifié dans", + "selling_dealer": "Revendeur vendeur", + "selling_dealer_contact": "Contacter le revendeur", + "servicecar": "Voiture de service", + "servicing_dealer": "Concessionnaire", + "servicing_dealer_contact": "Contacter le concessionnaire", + "special_coverage_policy": "Politique de couverture spéciale", + "specialcoveragepolicy": "Politique de couverture spéciale", + "state_tax_rate": "", + "status": "Statut de l'emploi", + "storage_payable": "Stockage", + "tax_lbr_rt": "", + "tax_levies_rt": "", + "tax_paint_mat_rt": "", + "tax_registration_number": "", + "tax_shop_mat_rt": "", + "tax_str_rt": "", + "tax_sub_rt": "", + "tax_tow_rt": "", + "towin": "", + "towing_payable": "Remorquage à payer", + "unitnumber": "Unité #", + "updated_at": "Mis à jour à", + "uploaded_by": "Telechargé par", + "vehicle": "Véhicule" + }, + "forms": { + "admindates": "", + "appraiserinfo": "", + "claiminfo": "", + "estdates": "", + "laborrates": "", + "lossinfo": "", + "other": "", + "repairdates": "", + "scheddates": "" + }, + "labels": { + "accountsreceivable": "", + "act_price_ppc": "", + "actual_completion_inferred": "", + "actual_delivery_inferred": "", + "actual_in_inferred": "", + "additionalpayeroverallocation": "", + "additionaltotal": "", + "adjustmentrate": "", + "adjustments": "", + "adminwarning": "", + "allocations": "", + "alreadyaddedtoscoreboard": "", + "alreadyclosed": "", + "appointmentconfirmation": "Envoyer une confirmation au client?", + "associationwarning": "", + "audit": "", + "available": "", + "availablejobs": "", + "ca_bc_pvrt": { + "days": "", + "rate": "" + }, + "ca_gst_all_if_null": "", + "calc_repair_days": "", + "calc_repair_days_tt": "", + "calc_scheuled_completion": "", + "cards": { + "customer": "Informations client", + "damage": "Zone de dommages", + "dates": "Rendez-vous", + "documents": "Documents récents", + "estimator": "Estimateur", + "filehandler": "Gestionnaire de fichiers", + "insurance": "Détails de l'assurance", + "more": "Plus", + "notes": "Remarques", + "parts": "les pièces", + "totals": "Totaux", + "vehicle": "Véhicule" + }, + "changeclass": "", + "checklistcompletedby": "", + "checklistdocuments": "", + "checklists": "", + "cieca_pfl": "", + "cieca_pfo": "", + "cieca_pft": "", + "closeconfirm": "", + "closejob": "", + "closingperiod": "", + "contracts": "", + "convertedtolabor": "", + "cost": "", + "cost_Additional": "", + "cost_labor": "", + "cost_parts": "", + "cost_sublet": "", + "costs": "", + "create": { + "jobinfo": "", + "newowner": "", + "newvehicle": "", + "novehicle": "", + "ownerinfo": "", + "vehicleinfo": "" + }, + "createiouwarning": "", + "creating_new_job": "Création d'un nouvel emploi ...", + "deductible": { + "stands": "", + "waived": "" + }, + "deleteconfirm": "", + "deletedelivery": "", + "deleteintake": "", + "deliverchecklist": "", + "difference": "", + "diskscan": "", + "dms": { + "apexported": "", + "damageto": "", + "defaultstory": "", + "disablebillwip": "", + "invoicedatefuture": "", + "kmoutnotgreaterthankmin": "", + "logs": "", + "notallocated": "", + "postingform": "", + "totalallocated": "" + }, + "documents": "Les documents", + "documents-images": "", + "documents-other": "", + "duplicateconfirm": "", + "emailaudit": "", + "employeeassignments": "", + "estimatelines": "", + "estimator": "", + "existing_jobs": "Emplois existants", + "federal_tax_amt": "", + "gpdollars": "", + "gppercent": "", + "hrs_claimed": "", + "hrs_total": "", + "importnote": "", + "inproduction": "", + "intakechecklist": "", + "iou": "", + "job": "", + "jobcosting": "", + "jobtotals": "", + "labor_hrs": "", + "labor_rates_subtotal": "", + "laborallocations": "", + "labortotals": "", + "lines": "Estimer les lignes", + "local_tax_amt": "", + "mapa": "", + "markforreexport": "", + "mash": "", + "masterbypass": "", + "materials": { + "mapa": "" + }, + "missingprofileinfo": "", + "multipayers": "", + "net_repairs": "", + "notes": "Remarques", + "othertotal": "", + "outstanding_ar": "", + "outstanding_credit_memos": "", + "outstanding_ppd": "", + "outstanding_reconciliation_discrep": "", + "outstanding_sublets": "", + "outstandinghours": "", + "override_header": "Remplacer l'en-tête d'estimation à l'importation?", + "ownerassociation": "", + "parts": "les pièces", + "parts_lines": "", + "parts_received": "", + "parts_tax_rates": "", + "partsfilter": "", + "partssubletstotal": "", + "partstotal": "", + "performance": "", + "pimraryamountpayable": "", + "plitooltips": { + "billtotal": "", + "calculatedcreditsnotreceived": "", + "creditmemos": "", + "creditsnotreceived": "", + "discrep1": "", + "discrep2": "", + "discrep3": "", + "laboradj": "", + "partstotal": "", + "totalreturns": "" + }, + "ppc": "", + "ppdnotexported": "", + "profileadjustments": "", + "profitbypassrequired": "", + "profits": "", + "prt_dsmk_total": "", + "rates": "Les taux", + "rates_subtotal": "", + "reconciliation": { + "billlinestotal": "", + "byassoc": "", + "byprice": "", + "clear": "", + "discrepancy": "", + "joblinestotal": "", + "multipleactprices": "", + "multiplebilllines": "", + "multiplebillsforactprice": "", + "removedpartsstrikethrough": "" + }, + "reconciliationheader": "", + "relatedros": "", + "remove_from_ar": "", + "returntotals": "", + "ro_guard": { + "enforce_ar": "", + "enforce_bills": "", + "enforce_cm": "", + "enforce_labor": "", + "enforce_ppd": "", + "enforce_profit": "", + "enforce_sublet": "", + "enforce_validation": "", + "enforced": "" + }, + "roguard": "", + "roguardwarnings": "", + "rosaletotal": "", + "sale_additional": "", + "sale_labor": "", + "sale_parts": "", + "sale_sublet": "", + "sales": "", + "savebeforeconversion": "", + "scheduledinchange": "", + "specialcoveragepolicy": "", + "state_tax_amt": "", + "subletsnotcompleted": "", + "subletstotal": "", + "subtotal": "", + "supplementnote": "", + "suspended": "", + "suspense": "", + "tasks": "", + "threshhold": "", + "total_cost": "", + "total_cust_payable": "", + "total_repairs": "", + "total_sales": "", + "total_sales_tax": "", + "totals": "", + "unvoidnote": "", + "update_scheduled_completion": "", + "vehicle_info": "Véhicule", + "vehicleassociation": "", + "viewallocations": "", + "voidjob": "", + "voidnote": "" + }, + "successes": { + "addedtoproduction": "", + "all_deleted": "{{count}} travaux supprimés avec succès.", + "closed": "", + "converted": "Travail converti avec succès.", + "created": "Le travail a été créé avec succès. Clique pour voir.", + "creatednoclick": "", + "delete": "", + "deleted": "Le travail a bien été supprimé.", + "duplicated": "", + "exported": "", + "invoiced": "", + "ioucreated": "", + "partsqueue": "", + "save": "Le travail a été enregistré avec succès.", + "savetitle": "Enregistrement enregistré avec succès.", + "supplemented": "Travail complété avec succès.", + "updated": "", + "voided": "" + } + }, + "landing": { + "bigfeature": { + "subtitle": "", + "title": "" + }, + "footer": { + "company": { + "about": "", + "contact": "", + "disclaimers": "", + "name": "", + "privacypolicy": "" + }, + "io": { + "help": "", + "name": "", + "status": "" + }, + "slogan": "" + }, + "hero": { + "button": "", + "title": "" + }, + "labels": { + "features": "", + "managemyshop": "", + "pricing": "" + }, + "pricing": { + "basic": { + "name": "", + "sub": "" + }, + "essentials": { + "name": "", + "sub": "" + }, + "pricingtitle": "", + "pro": { + "name": "", + "sub": "" + }, + "title": "", + "unlimited": { + "name": "", + "sub": "" + } + } + }, + "menus": { + "currentuser": { + "languageselector": "La langue", + "profile": "Profil" + }, + "header": { + "accounting": "", + "accounting-payables": "", + "accounting-payments": "", + "accounting-receivables": "", + "activejobs": "Emplois actifs", + "all_tasks": "", + "alljobs": "", + "allpayments": "", + "availablejobs": "Emplois disponibles", + "bills": "", + "courtesycars": "", + "courtesycars-all": "", + "courtesycars-contracts": "", + "courtesycars-newcontract": "", + "create_task": "", + "customers": "Les clients", + "dashboard": "", + "enterbills": "", + "entercardpayment": "", + "enterpayment": "", + "entertimeticket": "", + "export": "", + "export-logs": "", + "help": "", + "home": "Accueil", + "inventory": "", + "jobs": "Emplois", + "my_tasks": "", + "newjob": "", + "owners": "Propriétaires", + "parts-queue": "", + "phonebook": "", + "productionboard": "", + "productionlist": "", + "readyjobs": "", + "recent": "", + "reportcenter": "", + "rescueme": "", + "schedule": "Programme", + "scoreboard": "", + "search": { + "bills": "", + "jobs": "", + "owners": "", + "payments": "", + "phonebook": "", + "vehicles": "" + }, + "shiftclock": "", + "shop": "Mon magasin", + "shop_config": "Configuration", + "shop_csi": "", + "shop_templates": "", + "shop_vendors": "Vendeurs", + "tasks": "", + "temporarydocs": "", + "timetickets": "", + "ttapprovals": "", + "vehicles": "Véhicules" + }, + "jobsactions": { + "admin": "", + "cancelallappointments": "", + "closejob": "", + "deletejob": "", + "duplicate": "", + "duplicatenolines": "", + "newcccontract": "", + "void": "" + }, + "jobsdetail": { + "claimdetail": "Détails de la réclamation", + "dates": "Rendez-vous", + "financials": "", + "general": "", + "insurance": "", + "labor": "La main d'oeuvre", + "lifecycle": "", + "parts": "", + "partssublet": "Pièces / Sous-location", + "rates": "", + "repairdata": "Données de réparation", + "totals": "" + }, + "profilesidebar": { + "profile": "Mon profil", + "shops": "Mes boutiques" + }, + "tech": { + "assignedjobs": "", + "claimtask": "", + "dispatchedparts": "", + "home": "", + "jobclockin": "", + "jobclockout": "", + "joblookup": "", + "login": "", + "logout": "", + "productionboard": "", + "productionlist": "", + "shiftclockin": "" + } + }, + "messaging": { + "actions": { + "link": "", + "new": "" + }, + "errors": { + "invalidphone": "", + "noattachedjobs": "", + "updatinglabel": "" + }, + "labels": { + "addlabel": "", + "archive": "", + "maxtenimages": "", + "messaging": "Messagerie", + "noallowtxt": "", + "nojobs": "", + "nopush": "", + "phonenumber": "", + "presets": "", + "recentonly": "", + "selectmedia": "", + "sentby": "", + "typeamessage": "Envoyer un message...", + "unarchive": "" + }, + "render": { + "conversation_list": "" + } + }, + "notes": { + "actions": { + "actions": "actes", + "deletenote": "Supprimer la note", + "edit": "Note éditée", + "new": "Nouvelle note", + "savetojobnotes": "" + }, + "errors": { + "inserting": "" + }, + "fields": { + "createdby": "Créé par", + "critical": "Critique", + "private": "privé", + "text": "Contenu", + "type": "", + "types": { + "customer": "", + "general": "", + "office": "", + "paint": "", + "parts": "", + "shop": "", + "supplement": "" + }, + "updatedat": "Mis à jour à" + }, + "labels": { + "addtorelatedro": "", + "newnoteplaceholder": "Ajouter une note...", + "notetoadd": "", + "systemnotes": "", + "usernotes": "" + }, + "successes": { + "create": "Remarque créée avec succès.", + "deleted": "Remarque supprimée avec succès.", + "updated": "Remarque mise à jour avec succès." + } + }, + "owner": { + "labels": { + "noownerinfo": "" + } + }, + "owners": { + "actions": { + "update": "" + }, + "errors": { + "deleting": "", + "noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.", + "saving": "", + "selectexistingornew": "" + }, + "fields": { + "address": "Adresse", + "allow_text_message": "Autorisation de texte?", + "name": "Prénom", + "note": "", + "ownr_addr1": "Adresse", + "ownr_addr2": "Adresse 2 ", + "ownr_city": "Ville", + "ownr_co_nm": "", + "ownr_ctry": "Pays", + "ownr_ea": "Email", + "ownr_fn": "Prénom", + "ownr_ln": "Nom de famille", + "ownr_ph1": "Téléphone 1", + "ownr_ph2": "", + "ownr_st": "Etat / Province", + "ownr_title": "Titre", + "ownr_zip": "Zip / code postal", + "preferred_contact": "Méthode de contact préférée", + "tax_number": "" + }, + "forms": { + "address": "", + "contact": "", + "name": "" + }, + "labels": { + "create_new": "Créez un nouvel enregistrement de propriétaire.", + "deleteconfirm": "", + "existing_owners": "Propriétaires existants", + "fromclaim": "", + "fromowner": "", + "relatedjobs": "", + "updateowner": "" + }, + "successes": { + "delete": "", + "save": "Le propriétaire a bien enregistré." + } + }, + "parts": { + "actions": { + "order": "Commander des pièces", + "orderinhouse": "" + } + }, + "parts_dispatch": { + "actions": { + "accept": "" + }, + "errors": { + "accepting": "", + "creating": "" + }, + "fields": { + "number": "", + "percent_accepted": "" + }, + "labels": { + "notyetdispatched": "", + "parts_dispatch": "" + } + }, + "parts_dispatch_lines": { + "fields": { + "accepted_at": "" + } + }, + "parts_orders": { + "actions": { + "backordered": "", + "receive": "", + "receivebill": "" + }, + "errors": { + "associatedbills": "", + "backordering": "", + "creating": "Erreur rencontrée lors de la création de la commande de pièces.", + "oec": "", + "saving": "", + "updating": "" + }, + "fields": { + "act_price": "", + "backordered_eta": "", + "backordered_on": "", + "cm_received": "", + "comments": "", + "cost": "", + "db_price": "", + "deliver_by": "", + "job_line_id": "", + "line_desc": "", + "line_remarks": "", + "lineremarks": "Remarques sur la ligne", + "oem_partno": "", + "order_date": "", + "order_number": "", + "orderedby": "", + "part_type": "", + "quantity": "", + "return": "", + "status": "" + }, + "labels": { + "allpartsto": "", + "confirmdelete": "", + "custompercent": "", + "discount": "", + "email": "Envoyé par email", + "inthisorder": "Pièces dans cette commande", + "is_quote": "", + "mark_as_received": "", + "newpartsorder": "", + "notyetordered": "", + "oec": "", + "order_type": "", + "orderhistory": "Historique des commandes", + "parts_order": "", + "parts_orders": "", + "print": "Afficher le formulaire imprimé", + "receive": "", + "removefrompartsqueue": "", + "returnpartsorder": "", + "sublet_order": "" + }, + "successes": { + "created": "Commande de pièces créée avec succès.", + "line_updated": "", + "received": "", + "return_created": "" + } + }, + "payments": { + "actions": { + "generatepaymentlink": "" + }, + "errors": { + "exporting": "", + "exporting-partner": "", + "inserting": "" + }, + "fields": { + "amount": "", + "created_at": "", + "date": "", + "exportedat": "", + "memo": "", + "payer": "", + "paymentnum": "", + "stripeid": "", + "transactionid": "", + "type": "" + }, + "labels": { + "balance": "", + "ca_bc_etf_table": "", + "customer": "", + "edit": "", + "electronicpayment": "", + "external": "", + "findermodal": "", + "insurance": "", + "markexported": "", + "markforreexport": "", + "new": "", + "signup": "", + "smspaymentreminder": "", + "title": "", + "totalpayments": "" + }, + "successes": { + "exported": "", + "markexported": "", + "markreexported": "", + "payment": "", + "paymentupdate": "", + "stripe": "" + } + }, + "phonebook": { + "actions": { + "new": "" + }, + "errors": { + "adding": "", + "saving": "" + }, + "fields": { + "address1": "", + "address2": "", + "category": "", + "city": "", + "company": "", + "country": "", + "email": "", + "fax": "", + "firstname": "", + "lastname": "", + "phone1": "", + "phone2": "", + "state": "" + }, + "labels": { + "noneselected": "", + "onenamerequired": "", + "vendorcategory": "" + }, + "successes": { + "added": "", + "deleted": "", + "saved": "" + } + }, + "printcenter": { + "appointments": { + "appointment_confirmation": "" + }, + "bills": { + "inhouse_invoice": "" + }, + "courtesycarcontract": { + "courtesy_car_contract": "", + "courtesy_car_impound": "", + "courtesy_car_inventory": "", + "courtesy_car_terms": "" + }, + "errors": { + "nocontexttype": "" + }, + "jobs": { + "3rdpartyfields": { + "addr1": "", + "addr2": "", + "addr3": "", + "attn": "", + "city": "", + "custgst": "", + "ded_amt": "", + "depreciation": "", + "other": "", + "ponumber": "", + "refnumber": "", + "sendtype": "", + "state": "", + "zip": "" + }, + "3rdpartypayer": "", + "ab_proof_of_loss": "", + "appointment_confirmation": "", + "appointment_reminder": "", + "casl_authorization": "", + "committed_timetickets_ro": "", + "coversheet_landscape": "", + "coversheet_portrait": "", + "csi_invitation": "", + "csi_invitation_action": "", + "diagnostic_authorization": "", + "dms_posting_sheet": "", + "envelope_return_address": "", + "estimate": "", + "estimate_detail": "", + "estimate_followup": "", + "express_repair_checklist": "", + "filing_coversheet_landscape": "", + "filing_coversheet_portrait": "", + "final_invoice": "", + "fippa_authorization": "", + "folder_label_multiple": "", + "glass_express_checklist": "", + "guarantee": "", + "individual_job_note": "", + "invoice_customer_payable": "", + "invoice_total_payable": "", + "iou_form": "", + "job_costing_ro": "", + "job_lifecycle_ro": "", + "job_notes": "", + "job_tasks": "", + "key_tag": "", + "labels": { + "count": "", + "labels": "", + "position": "" + }, + "lag_time_ro": "", + "mechanical_authorization": "", + "mpi_animal_checklist": "", + "mpi_eglass_auth": "", + "mpi_final_acct_sheet": "", + "mpi_final_repair_acct_sheet": "", + "paint_grid": "", + "parts_dispatch": "", + "parts_invoice_label_single": "", + "parts_label_multiple": "", + "parts_label_single": "", + "parts_list": "", + "parts_order": "", + "parts_order_confirmation": "", + "parts_order_history": "", + "parts_return_slip": "", + "payment_receipt": "", + "payment_request": "", + "payments_by_job": "", + "purchases_by_ro_detail": "", + "purchases_by_ro_summary": "", + "qc_sheet": "", + "rental_reservation": "", + "ro_totals": "", + "ro_with_description": "", + "sgi_certificate_of_repairs": "", + "sgi_windshield_auth": "", + "stolen_recovery_checklist": "", + "sublet_order": "", + "supplement_request": "", + "thank_you_ro": "", + "thirdpartypayer": "", + "timetickets_ro": "", + "vehicle_check_in": "", + "vehicle_delivery_check": "", + "window_tag": "", + "window_tag_sublet": "", + "work_authorization": "", + "worksheet_by_line_number": "", + "worksheet_sorted_by_operation": "", + "worksheet_sorted_by_operation_no_hours": "", + "worksheet_sorted_by_operation_part_type": "", + "worksheet_sorted_by_operation_type": "", + "worksheet_sorted_by_team": "" + }, + "labels": { + "groups": { + "authorization": "", + "financial": "", + "post": "", + "pre": "", + "ro": "", + "worksheet": "" + }, + "misc": "", + "repairorder": "", + "reportcentermodal": "", + "speedprint": "", + "title": "" + }, + "payments": { + "ca_bc_etf_table": "", + "exported_payroll": "" + }, + "special": { + "attendance_detail_csv": "" + }, + "subjects": { + "jobs": { + "individual_job_note": "", + "parts_dispatch": "", + "parts_order": "", + "parts_return_slip": "", + "sublet_order": "" + } + }, + "vendors": { + "purchases_by_vendor_detailed": "", + "purchases_by_vendor_summary": "" + } + }, + "production": { + "actions": { + "addcolumns": "", + "bodypriority-clear": "", + "bodypriority-set": "", + "detailpriority-clear": "", + "detailpriority-set": "", + "paintpriority-clear": "", + "paintpriority-set": "", + "remove": "", + "removecolumn": "", + "saveconfig": "", + "suspend": "", + "unsuspend": "" + }, + "constants": { + "main_profile": "" + }, + "errors": { + "boardupdate": "", + "name_exists": "", + "name_required": "", + "removing": "", + "settings": "" + }, + "labels": { + "actual_in": "", + "addnewprofile": "", + "alert": "", + "alertoff": "", + "alerton": "", + "alerts": "", + "ats": "", + "bodyhours": "", + "bodypriority": "", + "bodyshop": { + "labels": { + "qbo_departmentid": "", + "qbo_usa": "" + } + }, + "card_size": "", + "cardcolor": "", + "cardsettings": "", + "clm_no": "", + "comment": "", + "compact": "", + "detailpriority": "", + "employeeassignments": "", + "employeesearch": "", + "estimator": "", + "horizontal": "", + "ins_co_nm": "", + "jobdetail": "", + "kiosk_mode": "", + "laborhrs": "", + "legend": "", + "model_info": "", + "note": "", + "off": "", + "on": "", + "orientation": "", + "ownr_nm": "", + "paintpriority": "", + "partsstatus": "", + "production_note": "", + "refinishhours": "", + "scheduled_completion": "", + "selectview": "", + "stickyheader": "", + "sublets": "", + "subtotal": "", + "tall": "", + "tasks": "", + "totalhours": "", + "touchtime": "", + "vertical": "", + "viewname": "", + "wide": "" + }, + "options": { + "horizontal": "", + "large": "", + "medium": "", + "small": "", + "vertical": "" + }, + "settings": { + "board_settings": "", + "filters": { + "md_estimators": "", + "md_ins_cos": "" + }, + "filters_title": "", + "information": "", + "layout": "", + "statistics": { + "jobs_in_production": "", + "tasks_in_production": "", + "tasks_on_board": "", + "tasks_in_view": "", + "total_amount_in_production": "", + "total_amount_on_board": "", + "total_amount_in_view": "", + "total_hours_in_production": "", + "total_hours_on_board": "", + "total_hours_in_view": "", + "total_jobs_on_board": "", + "total_jobs_in_view": "", + "total_lab_in_production": "", + "total_lab_on_board": "", + "total_lab_in_view": "", + "total_lar_in_production": "", + "total_lar_on_board": "", + "total_lar_in_view": "" + }, + "statistics_title": "" + }, + "statistics": { + "currency_symbol": "", + "hours": "", + "jobs": "", + "jobs_in_production": "", + "tasks": "", + "tasks_in_production": "", + "tasks_on_board": "", + "tasks_in_view": "", + "total_amount_in_production": "", + "total_amount_on_board": "", + "total_amount_in_view": "", + "total_hours_in_production": "", + "total_hours_on_board": "", + "total_hours_in_view": "", + "total_jobs_on_board": "", + "total_jobs_in_view": "", + "total_lab_in_production": "", + "total_lab_on_board": "", + "total_lab_in_view": "", + "total_lar_in_production": "", + "total_lar_on_board": "", + "total_lar_in_view": "" + }, + "successes": { + "removed": "" + } + }, + "profile": { + "errors": { + "state": "Erreur lors de la lecture de l'état de la page. Rafraichissez, s'il vous plait." + }, + "labels": { + "activeshop": "" + }, + "successes": { + "updated": "" + } + }, + "reportcenter": { + "actions": { + "generate": "" + }, + "labels": { + "advanced_filters": "", + "advanced_filters_false": "", + "advanced_filters_filter_field": "", + "advanced_filters_filter_operator": "", + "advanced_filters_filter_value": "", + "advanced_filters_filters": "", + "advanced_filters_hide": "", + "advanced_filters_show": "", + "advanced_filters_sorter_direction": "", + "advanced_filters_sorter_field": "", + "advanced_filters_sorters": "", + "advanced_filters_true": "", + "dates": "", + "employee": "", + "filterson": "", + "generateasemail": "", + "groups": { + "customers": "", + "jobs": "", + "payroll": "", + "purchases": "", + "sales": "" + }, + "key": "", + "objects": { + "appointments": "", + "bills": "", + "csi": "", + "exportlogs": "", + "jobs": "", + "parts_orders": "", + "payments": "", + "scoreboard": "", + "tasks": "", + "timetickets": "" + }, + "vendor": "" + }, + "templates": { + "adp_payroll_flat": "", + "adp_payroll_straight": "", + "anticipated_revenue": "", + "ar_aging": "", + "attendance_detail": "", + "attendance_employee": "", + "attendance_summary": "", + "committed_timetickets": "", + "committed_timetickets_employee": "", + "committed_timetickets_summary": "", + "credits_not_received_date": "", + "credits_not_received_date_vendorid": "", + "csi": "", + "customer_list": "", + "cycle_time_analysis": "", + "estimates_written_converted": "", + "estimator_detail": "", + "estimator_summary": "", + "export_payables": "", + "export_payments": "", + "export_receivables": "", + "exported_gsr_by_ro": "", + "exported_gsr_by_ro_labor": "", + "gsr_by_atp": "", + "gsr_by_ats": "", + "gsr_by_category": "", + "gsr_by_csr": "", + "gsr_by_delivery_date": "", + "gsr_by_estimator": "", + "gsr_by_exported_date": "", + "gsr_by_ins_co": "", + "gsr_by_make": "", + "gsr_by_referral": "", + "gsr_by_ro": "", + "gsr_labor_only": "", + "hours_sold_detail_closed": "", + "hours_sold_detail_closed_csr": "", + "hours_sold_detail_closed_estimator": "", + "hours_sold_detail_closed_ins_co": "", + "hours_sold_detail_closed_status": "", + "hours_sold_detail_open": "", + "hours_sold_detail_open_csr": "", + "hours_sold_detail_open_estimator": "", + "hours_sold_detail_open_ins_co": "", + "hours_sold_detail_open_status": "", + "hours_sold_summary_closed": "", + "hours_sold_summary_closed_csr": "", + "hours_sold_summary_closed_estimator": "", + "hours_sold_summary_closed_ins_co": "", + "hours_sold_summary_closed_status": "", + "hours_sold_summary_open": "", + "hours_sold_summary_open_csr": "", + "hours_sold_summary_open_estimator": "", + "hours_sold_summary_open_ins_co": "", + "hours_sold_summary_open_status": "", + "job_costing_ro_csr": "", + "job_costing_ro_date_detail": "", + "job_costing_ro_date_summary": "", + "job_costing_ro_estimator": "", + "job_costing_ro_ins_co": "", + "job_lifecycle_date_detail": "", + "job_lifecycle_date_summary": "", + "jobs_completed_not_invoiced": "", + "jobs_invoiced_not_exported": "", + "jobs_reconcile": "", + "jobs_scheduled_completion": "", + "lag_time": "", + "load_level": "", + "lost_sales": "", + "open_orders": "", + "open_orders_csr": "", + "open_orders_estimator": "", + "open_orders_excel": "", + "open_orders_ins_co": "", + "open_orders_referral": "", + "open_orders_specific_csr": "", + "open_orders_status": "", + "parts_backorder": "", + "parts_not_recieved": "", + "parts_not_recieved_vendor": "", + "parts_received_not_scheduled": "", + "payments_by_date": "", + "payments_by_date_payment": "", + "payments_by_date_type": "", + "production_by_category": "", + "production_by_category_one": "", + "production_by_csr": "", + "production_by_last_name": "", + "production_by_repair_status": "", + "production_by_repair_status_one": "", + "production_by_ro": "", + "production_by_target_date": "", + "production_by_technician": "", + "production_by_technician_one": "", + "production_not_production_status": "", + "production_over_time": "", + "psr_by_make": "", + "purchase_return_ratio_grouped_by_vendor_detail": "", + "purchase_return_ratio_grouped_by_vendor_summary": "", + "purchases_by_cost_center_detail": "", + "purchases_by_cost_center_summary": "", + "purchases_by_date_range_detail": "", + "purchases_by_date_range_summary": "", + "purchases_by_ro_detail_date": "", + "purchases_by_ro_summary_date": "", + "purchases_by_vendor_detailed_date_range": "", + "purchases_by_vendor_summary_date_range": "", + "purchases_grouped_by_vendor_detailed": "", + "purchases_grouped_by_vendor_summary": "", + "returns_grouped_by_vendor_detailed": "", + "returns_grouped_by_vendor_summary": "", + "schedule": "", + "scheduled_parts_list": "", + "scoreboard_detail": "", + "scoreboard_summary": "", + "supplement_ratio_ins_co": "", + "tasks_date": "", + "tasks_date_employee": "", + "thank_you_date": "", + "timetickets": "", + "timetickets_employee": "", + "timetickets_summary": "", + "unclaimed_hrs": "", + "void_ros": "", + "work_in_progress_committed_labour": "", + "work_in_progress_jobs": "", + "work_in_progress_labour": "", + "work_in_progress_payables": "" + } + }, + "schedule": { + "labels": { + "atssummary": "", + "employeevacation": "", + "estimators": "", + "ins_co_nm_filter": "", + "intake": "", + "manual": "", + "manualevent": "" + } + }, + "scoreboard": { + "actions": { + "edit": "" + }, + "errors": { + "adding": "", + "removing": "", + "updating": "" + }, + "fields": { + "bodyhrs": "", + "date": "", + "painthrs": "" + }, + "labels": { + "allemployeetimetickets": "", + "asoftodaytarget": "", + "body": "", + "bodyabbrev": "", + "bodycharttitle": "", + "calendarperiod": "", + "combinedcharttitle": "", + "dailyactual": "", + "dailytarget": "", + "efficiencyoverperiod": "", + "entries": "", + "jobs": "", + "jobscompletednotinvoiced": "", + "lastmonth": "", + "lastweek": "", + "monthlytarget": "", + "priorweek": "", + "productivestatistics": "", + "productivetimeticketsoverdate": "", + "refinish": "", + "refinishabbrev": "", + "refinishcharttitle": "", + "targets": "", + "thismonth": "", + "thisweek": "", + "timetickets": "", + "timeticketsemployee": "", + "todateactual": "", + "total": "", + "totalhrs": "", + "totaloverperiod": "", + "weeklyactual": "", + "weeklytarget": "", + "workingdays": "" + }, + "successes": { + "added": "", + "removed": "", + "updated": "" + } + }, + "tasks": { + "actions": { + "edit": "", + "new": "" + }, + "buttons": { + "allTasks": "", + "complete": "", + "create": "", + "delete": "", + "edit": "", + "myTasks": "", + "refresh": "" + }, + "date_presets": { + "completion": "", + "day": "", + "days": "", + "delivery": "", + "next_week": "", + "one_month": "", + "three_months": "", + "three_weeks": "", + "today": "", + "tomorrow": "", + "two_weeks": "" + }, + "failures": { + "completed": "", + "created": "", + "deleted": "", + "updated": "" + }, + "fields": { + "actions": "", + "assigned_to": "", + "bill": "", + "billid": "", + "completed": "", + "created_at": "", + "created_by": "", + "description": "", + "due_date": "", + "job": { + "ro_number": "" + }, + "jobid": "", + "jobline": "", + "joblineid": "", + "parts_order": "", + "partsorderid": "", + "priorities": { + "high": "", + "low": "", + "medium": "" + }, + "priority": "", + "related_items": "", + "remind_at": "", + "title": "" + }, + "placeholders": { + "assigned_to": "", + "billid": "", + "description": "", + "jobid": "", + "joblineid": "", + "partsorderid": "" + }, + "successes": { + "completed": "", + "created": "", + "deleted": "", + "updated": "" + }, + "titles": { + "all_tasks": "", + "completed": "", + "deleted": "", + "job_tasks": "", + "mine": "", + "my_tasks": "" + }, + "validation": { + "due_at_error_message": "", + "remind_at_error_message": "" + } + }, + "tech": { + "fields": { + "employeeid": "", + "pin": "" + }, + "labels": { + "loggedin": "", + "notloggedin": "" + } + }, + "templates": { + "errors": { + "updating": "" + }, + "successes": { + "updated": "" + } + }, + "timetickets": { + "actions": { + "claimtasks": "", + "clockin": "", + "clockout": "", + "commit": "", + "commitone": "", + "enter": "", + "payall": "", + "printemployee": "", + "uncommit": "" + }, + "errors": { + "clockingin": "", + "clockingout": "", + "creating": "", + "deleting": "", + "noemployeeforuser": "", + "noemployeeforuser_sub": "", + "payall": "", + "shiftalreadyclockedon": "" + }, + "fields": { + "actualhrs": "", + "ciecacode": "", + "clockhours": "", + "clockoff": "", + "clockon": "", + "committed": "", + "committed_at": "", + "cost_center": "", + "created_by": "", + "date": "", + "efficiency": "", + "employee": "", + "employee_team": "", + "flat_rate": "", + "memo": "", + "productivehrs": "", + "ro_number": "", + "task_name": "" + }, + "labels": { + "alreadyclockedon": "", + "ambreak": "", + "amshift": "", + "claimtaskpreview": "", + "clockhours": "", + "clockintojob": "", + "deleteconfirm": "", + "edit": "", + "efficiency": "", + "flat_rate": "", + "jobhours": "", + "lunch": "", + "new": "", + "payrollclaimedtasks": "", + "pmbreak": "", + "pmshift": "", + "shift": "", + "shiftalreadyclockedon": "", + "straight_time": "", + "task": "", + "timetickets": "", + "unassigned": "", + "zeroactualnegativeprod": "" + }, + "successes": { + "clockedin": "", + "clockedout": "", + "committed": "", + "created": "", + "deleted": "", + "payall": "" + }, + "validation": { + "clockoffmustbeafterclockon": "", + "clockoffwithoutclockon": "", + "hoursenteredmorethanavailable": "", + "unassignedlines": "" + } + }, + "titles": { + "accounting-payables": "", + "accounting-payments": "", + "accounting-receivables": "", + "all_tasks": "", + "app": "", + "bc": { + "accounting-payables": "", + "accounting-payments": "", + "accounting-receivables": "", + "all_tasks": "", + "availablejobs": "", + "bills-list": "", + "contracts": "", + "contracts-create": "", + "contracts-detail": "", + "courtesycars": "", + "courtesycars-detail": "", + "courtesycars-new": "", + "dashboard": "", + "dms": "", + "export-logs": "", + "inventory": "", + "jobs": "", + "jobs-active": "", + "jobs-admin": "", + "jobs-all": "", + "jobs-checklist": "", + "jobs-close": "", + "jobs-deliver": "", + "jobs-detail": "", + "jobs-intake": "", + "jobs-new": "", + "jobs-ready": "", + "my_tasks": "", + "owner-detail": "", + "owners": "", + "parts-queue": "", + "payments-all": "", + "phonebook": "", + "productionboard": "", + "productionlist": "", + "profile": "", + "schedule": "", + "scoreboard": "", + "shop": "", + "shop-csi": "", + "shop-templates": "", + "shop-vendors": "", + "tasks": "", + "temporarydocs": "", + "timetickets": "", + "ttapprovals": "", + "vehicle-details": "", + "vehicles": "" + }, + "bills-list": "", + "contracts": "", + "contracts-create": "", + "contracts-detail": "", + "courtesycars": "", + "courtesycars-create": "", + "courtesycars-detail": "", + "dashboard": "", + "dms": "", + "export-logs": "", + "imexonline": "", + "inventory": "", + "jobs": "Tous les emplois | {{app}}", + "jobs-admin": "", + "jobs-all": "", + "jobs-checklist": "", + "jobs-close": "", + "jobs-create": "", + "jobs-deliver": "", + "jobs-intake": "", + "jobsavailable": "Emplois disponibles | {{app}}", + "jobsdetail": "Travail {{ro_number}} | {{app}}", + "jobsdocuments": "Documents de travail {{ro_number}} | {{app}}", + "manageroot": "Accueil | {{app}}", + "my_tasks": "", + "owners": "Tous les propriétaires | {{app}}", + "owners-detail": "", + "parts-queue": "", + "payments-all": "", + "phonebook": "", + "productionboard": "", + "productionlist": "", + "profile": "Mon profil | {{app}}", + "promanager": "", + "readyjobs": "", + "resetpassword": "", + "resetpasswordvalidate": "", + "romeonline": "", + "schedule": "Horaire | {{app}}", + "scoreboard": "", + "shop": "Mon magasin | {{app}}", + "shop-csi": "", + "shop-templates": "", + "shop_vendors": "Vendeurs | {{app}}", + "tasks": "", + "techconsole": "{{app}}", + "techjobclock": "{{app}}", + "techjoblookup": "{{app}}", + "techshiftclock": "{{app}}", + "temporarydocs": "", + "timetickets": "", + "ttapprovals": "", + "vehicledetail": "Détails du véhicule {{vehicle} | {{app}}", + "vehicles": "Tous les véhicules | {{app}}" + }, + "trello": { + "labels": { + "add_card": "", + "add_lane": "", + "cancel": "", + "delete_lane": "", + "description": "", + "label": "", + "lane_actions": "", + "title": "" + } + }, + "tt_approvals": { + "actions": { + "approveselected": "" + }, + "labels": { + "approval_queue_in_use": "", + "calculate": "" + } + }, + "user": { + "actions": { + "changepassword": "", + "signout": "Déconnexion", + "updateprofile": "Mettre à jour le profil" + }, + "errors": { + "updating": "" + }, + "fields": { + "authlevel": "", + "displayname": "Afficher un nom", + "email": "", + "photourl": "URL de l'avatar" + }, + "labels": { + "actions": "", + "changepassword": "", + "profileinfo": "" + }, + "successess": { + "passwordchanged": "" + } + }, + "users": { + "errors": { + "signinerror": { + "auth/user-disabled": "", + "auth/user-not-found": "", + "auth/wrong-password": "" + } + } + }, + "vehicles": { + "errors": { + "deleting": "", + "noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.", + "selectexistingornew": "", + "validation": "Veuillez vous assurer que tous les champs sont correctement entrés.", + "validationtitle": "Erreur de validation" + }, + "fields": { + "description": "Description du véhicule", + "notes": "", + "plate_no": "Plaque d'immatriculation", + "plate_st": "Juridiction de la plaque", + "trim_color": "Couleur de garniture", + "v_bstyle": "Style corporel", + "v_color": "Couleur", + "v_cond": "Etat", + "v_engine": "moteur", + "v_make_desc": "Faire", + "v_makecode": "Faire du code", + "v_mldgcode": "Code de moulage", + "v_model_desc": "Modèle", + "v_model_yr": "année", + "v_options": "Les options", + "v_paint_codes": "Codes de peinture", + "v_prod_dt": "Date de production", + "v_stage": "Étape", + "v_tone": "ton", + "v_trimcode": "Code de coupe", + "v_type": "Type", + "v_vin": "V.I.N." + }, + "forms": { + "detail": "", + "misc": "", + "registration": "" + }, + "labels": { + "deleteconfirm": "", + "fromvehicle": "", + "novehinfo": "", + "relatedjobs": "", + "updatevehicle": "" + }, + "successes": { + "delete": "", + "save": "Le véhicule a été enregistré avec succès." + } + }, + "vendors": { + "actions": { + "addtophonebook": "", + "new": "Nouveau vendeur", + "newpreferredmake": "" + }, + "errors": { + "deleting": "Erreur rencontrée lors de la suppression du fournisseur.", + "saving": "Erreur rencontrée lors de l'enregistrement du fournisseur." + }, + "fields": { + "active": "", + "am": "", + "city": "Ville", + "cost_center": "Centre de coûts", + "country": "Pays", + "discount": "Remise %", + "display_name": "Afficher un nom", + "dmsid": "", + "due_date": "Date limite de paiement", + "email": "Email du contact", + "favorite": "Préféré?", + "lkq": "", + "make": "", + "name": "Nom du vendeur", + "oem": "", + "phone": "", + "prompt_discount": "Remise rapide%", + "state": "Etat / Province", + "street1": "rue", + "street2": "Adresse 2 ", + "taxid": "Identifiant de taxe", + "terms": "Modalités de paiement", + "zip": "Zip / code postal" + }, + "labels": { + "noneselected": "Aucun fournisseur n'est sélectionné.", + "preferredmakes": "", + "search": "Tapez le nom d'un vendeur" + }, + "successes": { + "deleted": "Le fournisseur a bien été supprimé.", + "saved": "Le fournisseur a bien enregistré." + }, + "validation": { + "unique_vendor_name": "" + } + } + } } diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 4fdcf6e3e..9f80986d2 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -2333,6 +2333,14 @@ export const TemplateList = (type, context) => { key: "production_by_technician", //idtype: "vendor", disabled: false + }, + production_not_production_status: { + title: i18n.t("reportcenter.templates.production_not_production_status"), + description: "", + subject: i18n.t("reportcenter.templates.production_not_production_status"), + key: "production_not_production_status", + //idtype: "vendor", + disabled: false } } : {}), diff --git a/client/src/utils/config.js b/client/src/utils/config.js index 06c6d2362..844872b7c 100644 --- a/client/src/utils/config.js +++ b/client/src/utils/config.js @@ -1,3 +1,4 @@ // Sometimes referred to as PageSize, this variable controls the amount of records // to show on one page during pagination. export const pageLimit = 50; +export const exportPageLimit = 10; diff --git a/client/src/utils/day.js b/client/src/utils/day.js index 3fb824b11..3194cfb31 100644 --- a/client/src/utils/day.js +++ b/client/src/utils/day.js @@ -1,5 +1,6 @@ import dayjs from "dayjs"; +import "dayjs/locale/en"; import dayjsBusinessDays from "dayjs-business-days2"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter"; import updateLocale from "dayjs/plugin/updateLocale"; @@ -64,4 +65,6 @@ dayjs.extend(minMax); dayjs.extend(isBetween); dayjs.extend(dayjsBusinessDays); +dayjs.locale("en"); + export default dayjs; diff --git a/client/src/utils/localmedia.js b/client/src/utils/localmedia.js index 7734d4b2b..4c776bc25 100644 --- a/client/src/utils/localmedia.js +++ b/client/src/utils/localmedia.js @@ -2,5 +2,5 @@ import { store } from "../redux/store"; export function CreateExplorerLinkForJob({ jobid }) { const bodyshop = store.getState().user.bodyshop; - return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`; + return `imexmedia://`.concat(encodeURIComponent(`${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`)); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..503d874b3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,180 @@ +############################# +# Ports Exposed +# 4000 - Imex Node API +# 4556 - LocalStack (Local AWS) +# 3333 - SocketIO Admin-UI (Optional) +# 3334 - Redis-Insights (Optional) +############################# + +services: + + # Redis Node 1 + redis-node-1: + build: + context: ./redis + container_name: redis-node-1 + hostname: redis-node-1 + restart: unless-stopped + networks: + - redis-cluster-net + volumes: + - redis-node-1-data:/data + - redis-lock:/redis-lock + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 + + # Redis Node 2 + redis-node-2: + build: + context: ./redis + container_name: redis-node-2 + hostname: redis-node-2 + restart: unless-stopped + networks: + - redis-cluster-net + volumes: + - redis-node-2-data:/data + - redis-lock:/redis-lock + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 + + # Redis Node 3 + redis-node-3: + build: + context: ./redis + container_name: redis-node-3 + hostname: redis-node-3 + restart: unless-stopped + networks: + - redis-cluster-net + volumes: + - redis-node-3-data:/data + - redis-lock:/redis-lock + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 + + # LocalStack: Used to emulate AWS services locally, currently setup for SES + # Notes: Set the ENV Debug to 1 for additional logging + localstack: + image: localstack/localstack + container_name: localstack + hostname: localstack + networks: + - redis-cluster-net + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - SERVICES=ses,secretsmanager,cloudwatch,logs + - DEBUG=0 + - AWS_ACCESS_KEY_ID=test + - AWS_SECRET_ACCESS_KEY=test + - AWS_DEFAULT_REGION=ca-central-1 + - EXTRA_CORS_ALLOWED_HEADERS=Authorization,Content-Type + - EXTRA_CORS_ALLOWED_ORIGINS=* + - EXTRA_CORS_EXPOSE_HEADERS=Authorization,Content-Type + ports: + - "4566:4566" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + + # AWS-CLI - Used in conjunction with LocalStack to set required permission to send emails + aws-cli: + image: amazon/aws-cli + container_name: aws-cli + hostname: aws-cli + networks: + - redis-cluster-net + depends_on: + localstack: + condition: service_healthy + volumes: + - './localstack:/tmp/localstack' + - './certs:/tmp/certs' + + environment: + - AWS_ACCESS_KEY_ID=test + - AWS_SECRET_ACCESS_KEY=test + - AWS_DEFAULT_REGION=ca-central-1 + entrypoint: /bin/sh -c + command: > + " + 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 + " + # Node App: The Main IMEX API + node-app: + build: + context: . + container_name: node-app + hostname: imex-api + networks: + - redis-cluster-net + env_file: + - .env.development + depends_on: + redis-node-1: + condition: service_healthy + redis-node-2: + condition: service_healthy + redis-node-3: + condition: service_healthy + localstack: + condition: service_healthy + aws-cli: + condition: service_completed_successfully + ports: + - "4000:4000" + - "9229:9229" + volumes: + - .:/app + - node-app-npm-cache:/app/node_modules + +# ## Optional Container to Observe SocketIO data +# socketio-admin-ui: +# image: maitrungduc1410/socket.io-admin-ui +# container_name: socketio-admin-ui +# networks: +# - redis-cluster-net +# ports: +# - "3333:80" + +# ##Optional Container to Observe Redis Cluster Data +# redis-insight: +# image: redislabs/redisinsight:latest +# container_name: redis-insight +# hostname: redis-insight +# restart: unless-stopped +# ports: +# - "3334:5540" +# networks: +# - redis-cluster-net +# volumes: +# - redis-insight-data:/db + +networks: + redis-cluster-net: + driver: bridge + +volumes: + node-app-npm-cache: + redis-node-1-data: + redis-node-2-data: + redis-node-3-data: + redis-lock: + redis-insight-data: diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 70be46ad0..240b218a4 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -69,7 +69,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: jobline: job: @@ -180,7 +179,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -387,7 +385,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -504,7 +501,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bill: job: @@ -671,7 +667,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _and: - job: @@ -1285,7 +1280,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: courtesycar: bodyshop: @@ -1526,7 +1520,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -1786,7 +1779,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -1920,7 +1912,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _or: - job: @@ -2105,7 +2096,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: employee_team: bodyshop: @@ -2268,7 +2258,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: employee: bodyshop: @@ -2449,7 +2438,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -2696,7 +2684,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -2808,7 +2795,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: conversation: bodyshop: @@ -3123,7 +3109,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -4232,7 +4217,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -4248,41 +4232,41 @@ enable_manual: false update: columns: - - clm_no - - v_make_desc - - date_next_contact - - status - - employee_csr - employee_prep - clm_total - suspended - employee_body - ro_number - - actual_in - ownr_co_nm - - v_model_yr - - comment - - job_totals - v_vin - - ownr_fn - scheduled_completion - special_coverage_policy - - v_color - - ca_gst_registrant - scheduled_delivery - actual_delivery - actual_completion - kanbanparent - est_ct_fn + - alt_transport + - v_model_desc + - clm_no + - v_make_desc + - date_next_contact + - status + - employee_csr + - actual_in + - v_model_yr + - comment + - job_totals + - ownr_fn + - v_color + - ca_gst_registrant - employee_refinish - ownr_ph1 - date_last_contacted - - alt_transport - inproduction - est_ct_ln - production_vars - category - - v_model_desc - date_invoiced - est_co_nm - ownr_ln @@ -4295,6 +4279,12 @@ - name: event-secret value_from_env: EVENT_SECRET request_transform: + body: + action: transform + template: |- + { + "data": {{$body?.event?.data?.new}} + } method: POST query_params: {} template_engine: Kriti @@ -4496,7 +4486,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: conversation: bodyshop: @@ -4670,7 +4659,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -4805,7 +4793,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -5110,7 +5097,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: parts_order: job: @@ -5243,7 +5229,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -5419,7 +5404,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -5559,7 +5543,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -5670,7 +5653,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _or: - parentjob_rel: @@ -5760,7 +5742,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -6045,7 +6026,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -6541,7 +6521,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -6698,7 +6677,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 000000000..e267a23c7 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,35 @@ +{ + "watch": [ + "server.js", + "server" + ], + "ignore": [ + "client", + ".circleci", + ".platform", + ".idea", + ".vscode", + "_reference", + "firebase", + "hasura", + "logs", + "redis", + ".dockerignore", + ".ebignore", + ".editorconfig", + ".env.development", + ".env.development.rome", + ".eslintrc.json", + ".gitignore", + ".npmmrc", + ".prettierrc.js", + "bodyshop_translations.babel", + "Dockerfile", + "ecosystem.config.js", + "job-totals-testing-util.js", + "nodemon.json", + "package.json", + "package-lock.json", + "setadmin.js" + ] +} diff --git a/package-lock.json b/package-lock.json index be917a0e0..2f0edfad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,11 @@ "version": "0.2.0", "license": "UNLICENSED", "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.654.0", - "@aws-sdk/client-ses": "^3.654.0", - "@aws-sdk/credential-provider-node": "^3.654.0", + "@aws-sdk/client-cloudwatch-logs": "^3.679.0", + "@aws-sdk/client-elasticache": "^3.675.0", + "@aws-sdk/client-secrets-manager": "^3.675.0", + "@aws-sdk/client-ses": "^3.675.0", + "@aws-sdk/credential-provider-node": "^3.675.0", "@opensearch-project/opensearch": "^2.12.0", "@socket.io/admin-ui": "^0.5.1", "@socket.io/redis-adapter": "^8.3.0", @@ -21,39 +23,41 @@ "bluebird": "^3.7.2", "body-parser": "^1.20.3", "canvas": "^2.11.2", - "chart.js": "^4.4.4", - "cloudinary": "^2.5.0", + "chart.js": "^4.4.5", + "cloudinary": "^2.5.1", "compression": "^1.7.4", - "cookie-parser": "^1.4.6", + "cookie-parser": "^1.4.7", "cors": "2.8.5", "csrf": "^3.1.0", "dinero.js": "^1.9.1", "dotenv": "^16.4.5", - "express": "^4.21.0", - "firebase-admin": "^12.5.0", + "express": "^4.21.1", + "firebase-admin": "^12.6.0", "graphql": "^16.9.0", "graphql-request": "^6.1.0", - "graylog2": "^0.2.1", "inline-css": "^4.0.2", "intuit-oauth": "^4.1.2", - "json-2-csv": "^5.5.5", + "ioredis": "^5.4.1", + "json-2-csv": "^5.5.6", "lodash": "^4.17.21", "moment": "^2.30.1", - "moment-timezone": "^0.5.45", + "moment-timezone": "^0.5.46", "multer": "^1.4.5-lts.1", "node-mailjet": "^6.0.6", "node-persist": "^4.0.3", "nodemailer": "^6.9.15", - "phone": "^3.1.50", + "phone": "^3.1.51", "recursive-diff": "^1.0.9", "redis": "^4.7.0", "rimraf": "^6.0.1", - "soap": "^1.1.4", + "soap": "^1.1.5", "socket.io": "^4.8.0", "socket.io-adapter": "^2.5.5", "ssh2-sftp-client": "^10.0.3", "twilio": "^4.23.0", "uuid": "^10.0.0", + "winston": "^3.15.0", + "winston-cloudwatch": "^6.3.0", "xml2js": "^0.6.2", "xmlbuilder2": "^3.1.1" }, @@ -68,6 +72,20 @@ "npm": ">=8.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.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", @@ -183,52 +201,654 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.654.0.tgz", - "integrity": "sha512-Fua3kW8XCU2+gWcv8vHpsaR5g4ABuE4/tqv2B7vbMThLZ2Aam6Rw9b5dIMhpbje+edhAUBS0x6UyE88zQeHWZQ==", + "node_modules/@aws-sdk/client-cloudwatch-logs": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.679.0.tgz", + "integrity": "sha512-A1qTVNX8KdpqvXgULd4Suo88uuNWPa8DiuBL8Qkw/WefYT7TSWsOpwuVK0oFkMpCfB0rQN9fXZh2DBiaz8YmZg==", "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.654.0", - "@aws-sdk/client-sts": "3.654.0", - "@aws-sdk/core": "3.654.0", - "@aws-sdk/credential-provider-node": "3.654.0", - "@aws-sdk/middleware-host-header": "3.654.0", - "@aws-sdk/middleware-logger": "3.654.0", - "@aws-sdk/middleware-recursion-detection": "3.654.0", - "@aws-sdk/middleware-user-agent": "3.654.0", - "@aws-sdk/region-config-resolver": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@aws-sdk/util-user-agent-browser": "3.654.0", - "@aws-sdk/util-user-agent-node": "3.654.0", - "@smithy/config-resolver": "^3.0.8", - "@smithy/core": "^2.4.3", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/hash-node": "^3.0.6", - "@smithy/invalid-dependency": "^3.0.6", - "@smithy/middleware-content-length": "^3.0.8", - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@aws-sdk/client-sso-oidc": "3.679.0", + "@aws-sdk/client-sts": "3.679.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.679.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.679.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/eventstream-serde-browser": "^3.0.10", + "@smithy/eventstream-serde-config-resolver": "^3.0.7", + "@smithy/eventstream-serde-node": "^3.0.9", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "@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.18", - "@smithy/util-defaults-mode-node": "^3.0.18", - "@smithy/util-endpoints": "^2.1.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.679.0.tgz", + "integrity": "sha512-/0cAvYnpOZTo/Y961F1kx2fhDDLUYZ0SQQ5/75gh3xVImLj7Zw+vp74ieqFbqWLYGMaq8z1Arr9A8zG95mbLdg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.679.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.679.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.679.0.tgz", + "integrity": "sha512-/dBYWcCwbA/id4sFCIVZvf0UsvzHCC68SryxeNQk/PDkY9N4n5yRcMUkZDaEyQCjowc3kY4JOXp2AdUP037nhA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.679.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.679.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sts": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.679.0.tgz", + "integrity": "sha512-3CvrT8w1RjFu1g8vKA5Azfr5V83r2/b68Ock43WE003Bq/5Y38mwmYX7vk0fPHzC3qejt4YMAWk/C3fSKOy25g==", + "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.679.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.679.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.679.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.679.0.tgz", + "integrity": "sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.679.0.tgz", + "integrity": "sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.679.0.tgz", + "integrity": "sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.679.0.tgz", + "integrity": "sha512-Rg7t8RwUzKcumpipG4neZqaeJ6DF+Bco1+FHn5BZB68jpvwvjBjcQUuWkxj18B6ctYHr1fkunnzeKEn/+vy7+w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.679.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.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.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.679.0.tgz", + "integrity": "sha512-E3lBtaqCte8tWs6Rkssc8sLzvGoJ10TLGvpkijOlz43wPd6xCRh1YLwg6zolf9fVFtEyUs/GsgymiASOyxhFtw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.679.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.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.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.679.0.tgz", + "integrity": "sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.679.0.tgz", + "integrity": "sha512-SAtWonhi9asxn0ukEbcE81jkyanKgqpsrtskvYPpO9Z9KOednM4Cqt6h1bfcS9zaHjN2zu815Gv8O7WiV+F/DQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.679.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.679.0.tgz", + "integrity": "sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.679.0.tgz", + "integrity": "sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.679.0.tgz", + "integrity": "sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.679.0.tgz", + "integrity": "sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.679.0.tgz", + "integrity": "sha512-4hdeXhPDURPqQLPd9jCpUEo9fQITXl3NM3W1MwcJpE0gdUM36uXkQOYsTPeeU/IRCLVjK8Htlh2oCaM9iJrLCA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.679.0.tgz", + "integrity": "sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.679.0.tgz", + "integrity": "sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.679.0.tgz", + "integrity": "sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.679.0.tgz", + "integrity": "sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.679.0.tgz", + "integrity": "sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.679.0.tgz", + "integrity": "sha512-Bw4uXZ+NU5ed6TNfo4tBbhBSW+2eQxXYjYBGl5gLUNUpg2pDFToQAP6rXBFiwcG52V2ny5oLGiD82SoYuYkAVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.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-cloudwatch-logs/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-elasticache": { + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-elasticache/-/client-elasticache-3.675.0.tgz", + "integrity": "sha512-OMdlxBrsrDdBw2/RHj80NSAbb4wQYR0TJMvSbuC/XYnF1W9OpNpNpzQ9ZdZwiLymkjQMgfrVk27FiEivEBwj1A==", + "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.675.0", + "@aws-sdk/client-sts": "3.675.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.675.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "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", + "integrity": "sha512-qC9e56BzlAbKOtvAfbRuGCNDkGjFLi856SeYQ1U9kpegd6+yrFNScKUCJHEZ/clX1zfGPaJCpbEwCtiEaayADw==", + "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.675.0", + "@aws-sdk/client-sts": "3.675.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.675.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -249,52 +869,52 @@ } }, "node_modules/@aws-sdk/client-ses": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.654.0.tgz", - "integrity": "sha512-DeM1ocK/ct98xDiZ5G4weSVDf4r8y+G2VOcJAiq4BnUYI0VGYB8yYkl1/Dy8hjlu2Ow8UgUz719pzBYhJvD9aQ==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.675.0.tgz", + "integrity": "sha512-4/OyFFpHMIahDc063vk4viETLtNPjopcUpwmWMtV8rhOns8KjJ2b1tvpvV7lNYT53mUm+g3fhYok9McHFDeeMA==", "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.654.0", - "@aws-sdk/client-sts": "3.654.0", - "@aws-sdk/core": "3.654.0", - "@aws-sdk/credential-provider-node": "3.654.0", - "@aws-sdk/middleware-host-header": "3.654.0", - "@aws-sdk/middleware-logger": "3.654.0", - "@aws-sdk/middleware-recursion-detection": "3.654.0", - "@aws-sdk/middleware-user-agent": "3.654.0", - "@aws-sdk/region-config-resolver": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@aws-sdk/util-user-agent-browser": "3.654.0", - "@aws-sdk/util-user-agent-node": "3.654.0", - "@smithy/config-resolver": "^3.0.8", - "@smithy/core": "^2.4.3", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/hash-node": "^3.0.6", - "@smithy/invalid-dependency": "^3.0.6", - "@smithy/middleware-content-length": "^3.0.8", - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@aws-sdk/client-sso-oidc": "3.675.0", + "@aws-sdk/client-sts": "3.675.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.675.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "@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.18", - "@smithy/util-defaults-mode-node": "^3.0.18", - "@smithy/util-endpoints": "^2.1.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.5", + "@smithy/util-waiter": "^3.1.6", "tslib": "^2.6.2" }, "engines": { @@ -302,47 +922,47 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.654.0.tgz", - "integrity": "sha512-4kBxs2IzCDtj6a6lRXa/lXK5wWpMGzwKtb+HMXf/rJYVM6x7wYRzc1hYrOd3DYkFQ/sR3dUFj+0mTP0os3aAbA==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.675.0.tgz", + "integrity": "sha512-2goBCEr4acZJ1YJ69eWPTsIfZUbO7enog+lBA5kZShDiwovqzwYSHSlf6OGz4ETs2xT1n7n+QfKY0p+TluTfEw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.654.0", - "@aws-sdk/middleware-host-header": "3.654.0", - "@aws-sdk/middleware-logger": "3.654.0", - "@aws-sdk/middleware-recursion-detection": "3.654.0", - "@aws-sdk/middleware-user-agent": "3.654.0", - "@aws-sdk/region-config-resolver": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@aws-sdk/util-user-agent-browser": "3.654.0", - "@aws-sdk/util-user-agent-node": "3.654.0", - "@smithy/config-resolver": "^3.0.8", - "@smithy/core": "^2.4.3", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/hash-node": "^3.0.6", - "@smithy/invalid-dependency": "^3.0.6", - "@smithy/middleware-content-length": "^3.0.8", - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "@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.18", - "@smithy/util-defaults-mode-node": "^3.0.18", - "@smithy/util-endpoints": "^2.1.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -351,48 +971,48 @@ } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.654.0.tgz", - "integrity": "sha512-gbHrKsEnaAtmkNCVQzLyiqMzpDaThV/bWl/ODEklI+t6stW3Pe3oDMstEHLfJ6JU5g8sYnx4VLuxlnJMtUkvPw==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.675.0.tgz", + "integrity": "sha512-4kEcaa2P/BFz+xy5tagbtzM08gbjHXyYqW+n6SJuUFK7N6bZNnA4cu1hVgHcqOqk8Dbwv7fiseGT0x3Hhqjwqg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.654.0", - "@aws-sdk/credential-provider-node": "3.654.0", - "@aws-sdk/middleware-host-header": "3.654.0", - "@aws-sdk/middleware-logger": "3.654.0", - "@aws-sdk/middleware-recursion-detection": "3.654.0", - "@aws-sdk/middleware-user-agent": "3.654.0", - "@aws-sdk/region-config-resolver": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@aws-sdk/util-user-agent-browser": "3.654.0", - "@aws-sdk/util-user-agent-node": "3.654.0", - "@smithy/config-resolver": "^3.0.8", - "@smithy/core": "^2.4.3", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/hash-node": "^3.0.6", - "@smithy/invalid-dependency": "^3.0.6", - "@smithy/middleware-content-length": "^3.0.8", - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.675.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "@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.18", - "@smithy/util-defaults-mode-node": "^3.0.18", - "@smithy/util-endpoints": "^2.1.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -400,53 +1020,53 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.654.0" + "@aws-sdk/client-sts": "^3.675.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.654.0.tgz", - "integrity": "sha512-tyHa8jsBy+/NQZFHm6Q2Q09Vi9p3EH4yPy6PU8yPewpi2klreObtrUd0anJa6nzjS9SSuqnlZWsRic3cQ4QwCg==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.675.0.tgz", + "integrity": "sha512-zgjyR4GyuONeDGJBKNt9lFJ8HfDX7rpxZZVR7LSXr9lUkjf6vUGgD2k/K4UAoOTWCKKCor6TA562ezGlA8su6Q==", "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.654.0", - "@aws-sdk/core": "3.654.0", - "@aws-sdk/credential-provider-node": "3.654.0", - "@aws-sdk/middleware-host-header": "3.654.0", - "@aws-sdk/middleware-logger": "3.654.0", - "@aws-sdk/middleware-recursion-detection": "3.654.0", - "@aws-sdk/middleware-user-agent": "3.654.0", - "@aws-sdk/region-config-resolver": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@aws-sdk/util-user-agent-browser": "3.654.0", - "@aws-sdk/util-user-agent-node": "3.654.0", - "@smithy/config-resolver": "^3.0.8", - "@smithy/core": "^2.4.3", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/hash-node": "^3.0.6", - "@smithy/invalid-dependency": "^3.0.6", - "@smithy/middleware-content-length": "^3.0.8", - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@aws-sdk/client-sso-oidc": "3.675.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.675.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.675.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "@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.18", - "@smithy/util-defaults-mode-node": "^3.0.18", - "@smithy/util-endpoints": "^2.1.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -455,19 +1075,20 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.654.0.tgz", - "integrity": "sha512-4Rwx7BVaNaFqmXBDmnOkMbyuIFFbpZ+ru4lr660p45zY1QoNNSalechfoRffcokLFOZO+VWEJkdcorPUUU993w==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.667.0.tgz", + "integrity": "sha512-pMcDVI7Tmdsc8R3sDv0Omj/4iRParGY+uJtAfF669WnZfDfaBQaix2Mq7+Mu08vdjqO9K3gicFvjk9S1VLmOKA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.4.3", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/signature-v4": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/util-middleware": "^3.0.6", + "@aws-sdk/types": "3.667.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -476,14 +1097,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.654.0.tgz", - "integrity": "sha512-kogsx3Ql81JouHS7DkheCDU9MYAvK0AokxjcshDveGmf7BbgbWCA8Fnb9wjQyNDaOXNvkZu8Z8rgkX91z324/w==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.667.0.tgz", + "integrity": "sha512-zZbrkkaPc54WXm+QAnpuv0LPNfsts0HPPd+oCECGs7IQRaFsGj187cwvPg9RMWDFZqpm64MdBDoA8OQHsqzYCw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/property-provider": "^3.1.6", - "@smithy/types": "^3.4.2", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -491,19 +1113,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.654.0.tgz", - "integrity": "sha512-tgmAH4MBi/aDR882lfw48+tDV95ZH3GWc1Eoe6DpNLiM3GN2VfU/cZwuHmi6aq+vAbdIlswBHJ/+va0fOvlyjw==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.667.0.tgz", + "integrity": "sha512-sjtybFfERZWiqTY7fswBxKQLvUkiCucOWyqh3IaPo/4nE1PXRnaZCVG0+kRBPrYIxWqiVwytvZzMJy8sVZcG0A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/property-provider": "^3.1.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/util-stream": "^3.1.6", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", "tslib": "^2.6.2" }, "engines": { @@ -511,47 +1134,48 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.654.0.tgz", - "integrity": "sha512-DKSdaNu2hwdmuvnm9KnA0NLqMWxxmxSOLWjSUSoFIm++wGXUjPrRMFYKvMktaXnPuyf5my8gF/yGbwzPZ8wlTg==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.675.0.tgz", + "integrity": "sha512-kCBlC6grpbpCvgowk9T4JHZxJ88VfN0r77bDZClcadFRAKQ8UHyO02zhgFCfUdnU1lNv1mr3ngEcGN7XzJlYWA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.654.0", - "@aws-sdk/credential-provider-http": "3.654.0", - "@aws-sdk/credential-provider-process": "3.654.0", - "@aws-sdk/credential-provider-sso": "3.654.0", - "@aws-sdk/credential-provider-web-identity": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@smithy/credential-provider-imds": "^3.2.3", - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-env": "3.667.0", + "@aws-sdk/credential-provider-http": "3.667.0", + "@aws-sdk/credential-provider-process": "3.667.0", + "@aws-sdk/credential-provider-sso": "3.675.0", + "@aws-sdk/credential-provider-web-identity": "3.667.0", + "@aws-sdk/types": "3.667.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.5.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.654.0" + "@aws-sdk/client-sts": "^3.675.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.654.0.tgz", - "integrity": "sha512-wPV7CNYaXDEc+SS+3R0v8SZwkHRUE1z2k2j1d49tH5QBDT4tb/k2V/biXWkwSk3hbR+IMWXmuhJDv/5lybhIvg==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.675.0.tgz", + "integrity": "sha512-VO1WVZCDmAYu4sY/6qIBzdm5vJTxLhWKJWvL5kVFfSe8WiNNoHlTqYYUK9vAm/JYpIgFLTefPbIc5W4MK7o6Pg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.654.0", - "@aws-sdk/credential-provider-http": "3.654.0", - "@aws-sdk/credential-provider-ini": "3.654.0", - "@aws-sdk/credential-provider-process": "3.654.0", - "@aws-sdk/credential-provider-sso": "3.654.0", - "@aws-sdk/credential-provider-web-identity": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@smithy/credential-provider-imds": "^3.2.3", - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/credential-provider-env": "3.667.0", + "@aws-sdk/credential-provider-http": "3.667.0", + "@aws-sdk/credential-provider-ini": "3.675.0", + "@aws-sdk/credential-provider-process": "3.667.0", + "@aws-sdk/credential-provider-sso": "3.675.0", + "@aws-sdk/credential-provider-web-identity": "3.667.0", + "@aws-sdk/types": "3.667.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.5.0", "tslib": "^2.6.2" }, "engines": { @@ -559,15 +1183,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.654.0.tgz", - "integrity": "sha512-PmQoo8sZ9Q2Ow8OMzK++Z9lI7MsRUG7sNq3E72DVA215dhtTICTDQwGlXH2AAmIp7n+G9LLRds+4wo2ehG4mkg==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.667.0.tgz", + "integrity": "sha512-HZHnvop32fKgsNHkdhVaul7UzQ25sEc0j9yqA4bjhtbk0ECl42kj3f1pJ+ZU/YD9ut8lMJs/vVqiOdNThVdeBw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -575,17 +1200,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.654.0.tgz", - "integrity": "sha512-7GFme6fWEdA/XYKzZPOAdj/jS6fMBy1NdSIZsDXikS0v9jU+ZzHrAaWt13YLzHyjgxB9Sg9id9ncdY1IiubQXQ==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.675.0.tgz", + "integrity": "sha512-p/EE2c0ebSgRhg1Fe1OH2+xNl7j1P4DTc7kZy1mX1NJ72fkqnGgBuf1vk5J9RmiRpbauPNMlm+xohjkGS7iodA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.654.0", - "@aws-sdk/token-providers": "3.654.0", - "@aws-sdk/types": "3.654.0", - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/client-sso": "3.675.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/token-providers": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -593,32 +1219,33 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.654.0.tgz", - "integrity": "sha512-6a2g9gMtZToqSu+CusjNK5zvbLJahQ9di7buO3iXgbizXpLXU1rnawCpWxwslMpT5fLgMSKDnKDrr6wdEk7jSw==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.667.0.tgz", + "integrity": "sha512-t8CFlZMD/1p/8Cli3rvRiTJpjr/8BO64gw166AHgFZYSN2h95L2l1tcW0jpsc3PprA32nLg1iQVKYt4WGM4ugw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/property-provider": "^3.1.6", - "@smithy/types": "^3.4.2", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.654.0" + "@aws-sdk/client-sts": "^3.667.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.654.0.tgz", - "integrity": "sha512-rxGgVHWKp8U2ubMv+t+vlIk7QYUaRCHaVpmUlJv0Wv6Q0KeO9a42T9FxHphjOTlCGQOLcjCreL9CF8Qhtb4mdQ==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.667.0.tgz", + "integrity": "sha512-Z7fIAMQnPegs7JjAQvlOeWXwpMRfegh5eCoIP6VLJIeR6DLfYKbP35JBtt98R6DXslrN2RsbTogjbxPEDQfw1w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -626,13 +1253,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.654.0.tgz", - "integrity": "sha512-OQYb+nWlmASyXfRb989pwkJ9EVUMP1CrKn2eyTk3usl20JZmKo2Vjis6I0tLUkMSxMhnBJJlQKyWkRpD/u1FVg==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.667.0.tgz", + "integrity": "sha512-PtTRNpNm/5c746jRgZCNg4X9xEJIwggkGJrF0GP9AB1ANg4pc/sF2Fvn1NtqPe9wtQ2stunJprnm5WkCHN7QiA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -640,14 +1267,14 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.654.0.tgz", - "integrity": "sha512-gKSomgltKVmsT8sC6W7CrADZ4GHwX9epk3GcH6QhebVO3LA9LRbkL3TwOPUXakxxOLLUTYdOZLIOtFf7iH00lg==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.667.0.tgz", + "integrity": "sha512-U5glWD3ehFohzpUpopLtmqAlDurGWo2wRGPNgi4SwhWU7UDt6LS7E/UvJjqC0CUrjlzOw+my2A+Ncf+fisMhxQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -655,15 +1282,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.654.0.tgz", - "integrity": "sha512-liCcqPAyRsr53cy2tYu4qeH4MMN0eh9g6k56XzI5xd4SghXH5YWh4qOYAlQ8T66ZV4nPMtD8GLtLXGzsH8moFg==", + "version": "3.669.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.669.0.tgz", + "integrity": "sha512-K8ScPi45zjJrj5Y2gRqVsvKKQCQbvQBfYGcBw9ZOx9TTavH80bOCBjWg/GFnvs4f37tqVc1wMN2oGvcTF6HveQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@aws-sdk/util-endpoints": "3.654.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -671,16 +1300,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.654.0.tgz", - "integrity": "sha512-ydGOrXJxj3x0sJhsXyTmvJVLAE0xxuTWFJihTl67RtaO7VRNtd82I3P3bwoMMaDn5WpmV5mPo8fEUDRlBm3fPg==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.667.0.tgz", + "integrity": "sha512-iNr+JhhA902JMKHG9IwT9YdaEx6KGl6vjAL5BRNeOjfj4cZYMog6Lz/IlfOAltMtT0w88DAHDEFrBd2uO0l2eg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -688,31 +1317,31 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.654.0.tgz", - "integrity": "sha512-D8GeJYmvbfWkQDtTB4owmIobSMexZel0fOoetwvgCQ/7L8VPph3Q2bn1TRRIXvH7wdt6DcDxA3tKMHPBkT3GlA==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.667.0.tgz", + "integrity": "sha512-ZecJlG8p6D4UTYlBHwOWX6nknVtw/OBJ3yPXTSajBjhUlj9lE2xvejI8gl4rqkyLXk7z3bki+KR4tATbMaM9yg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.654.0" + "@aws-sdk/client-sso-oidc": "^3.667.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.654.0.tgz", - "integrity": "sha512-VWvbED3SV+10QJIcmU/PKjsKilsTV16d1I7/on4bvD/jo1qGeMXqLDBSen3ks/tuvXZF/mFc7ZW/W2DiLVtO7A==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.667.0.tgz", + "integrity": "sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -720,14 +1349,14 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.654.0.tgz", - "integrity": "sha512-i902fcBknHs0Irgdpi62+QMvzxE+bczvILXigYrlHL4+PiEnlMVpni5L5W1qCkNZXf8AaMrSBuR1NZAGp6UOUw==", + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.667.0.tgz", + "integrity": "sha512-X22SYDAuQJWnkF1/q17pkX3nGw5XMD9YEUbmt87vUnRq7iyJ3JOpl6UKOBeUBaL838wA5yzdbinmCITJ/VZ1QA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/types": "^3.4.2", - "@smithy/util-endpoints": "^2.1.2", + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", "tslib": "^2.6.2" }, "engines": { @@ -746,26 +1375,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.654.0.tgz", - "integrity": "sha512-ykYAJqvnxLt7wfrqya28wuH3/7NdrwzfiFd7NqEVQf7dXVxL5RPEpD7DxjcyQo3DsHvvdUvGZVaQhozycn1pzA==", + "version": "3.675.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.675.0.tgz", + "integrity": "sha512-HW4vGfRiX54RLcsYjLuAhcBBJ6lRVEZd7njfGpAwBB9s7BH8t48vrpYbyA5XbbqbTvXfYBnugQCUw9HWjEa1ww==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/types": "^3.4.2", + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.654.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.654.0.tgz", - "integrity": "sha512-a0ojjdBN6pqv6gB4H/QPPSfhs7mFtlVwnmKCM/QrTaFzN0U810PJ1BST3lBx5sa23I5jWHGaoFY+5q65C3clLQ==", + "version": "3.669.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.669.0.tgz", + "integrity": "sha512-9jxCYrgggy2xd44ZASqI7AMiRVaSiFp+06Kg8BQSU0ijKpBJlwcsqIS8pDT/n6LxuOw2eV5ipvM2C0r1iKzrGA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.654.0", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/types": "3.667.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -1179,6 +1809,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -1397,6 +2028,12 @@ "node": ">=6" } }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1823,12 +2460,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", - "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", + "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -1836,15 +2473,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.8.tgz", - "integrity": "sha512-Tv1obAC18XOd2OnDAjSWmmthzx6Pdeh63FbLin8MlPiuJ2ATpKkq0NcNOJFr0dO+JmZXnwu8FQxKJ3TKJ3Hulw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", + "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -1852,19 +2489,19 @@ } }, "node_modules/@smithy/core": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.6.tgz", - "integrity": "sha512-6lQQp99hnyuNNIzeTYSzCUXJHwvvFLY7hfdFGSJM95tjRDJGfzWYFRBXPaM9766LiiTsQ561KErtbufzUFSYUg==", + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.8.tgz", + "integrity": "sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.21", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.5", - "@smithy/types": "^3.4.2", + "@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/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -1873,15 +2510,82 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.3.tgz", - "integrity": "sha512-VoxMzSzdvkkjMJNE38yQgx4CfnmT+Z+5EUXkg4x7yag93eQkVQgZvN3XBSHC/ylfBbLbAtdu7flTCChX9I+mVg==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.7.tgz", + "integrity": "sha512-kVSXScIiRN7q+s1x7BrQtZ1Aa9hvvP9FeCqCdBxv37GimIHgBCOnZ5Ip80HLt0DhnAKpiobFdGqTFgbaJNrazA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.6.0", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.11.tgz", + "integrity": "sha512-Pd1Wnq3CQ/v2SxRifDUihvpXzirJYbbtXfEnnLV/z0OGCTx/btVX74P86IgrZkjOydOASBGXdPpupYQI+iO/6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.10", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.8.tgz", + "integrity": "sha512-zkFIG2i1BLbfoGQnf1qEeMqX0h5qAznzaZmMVNnvPZz9J5AWBPkOMckZWPedGUPcVITacwIdQXoPcdIQq5FRcg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.10.tgz", + "integrity": "sha512-hjpU1tIsJ9qpcoZq9zGHBJPBOeBGYt+n8vfhDwnITPhEre6APrvqq/y3XMDEGUT2cWQ4ramNqBPRbx3qn55rhw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.10", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.10.tgz", + "integrity": "sha512-ewG1GHbbqsFZ4asaq40KmxCmXO+AFSM1b+DcO2C03dyJj/ZH71CiTg853FSE/3SHK9q3jiYQIFjlGSwfxQ9kww==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.7", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -1889,25 +2593,25 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.8.tgz", - "integrity": "sha512-Lqe0B8F5RM7zkw//6avq1SJ8AfaRd3ubFUS1eVp5WszV7p6Ne5hQ4dSuMHDpNRPhgTvj4va9Kd/pcVigHEHRow==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.3", - "@smithy/querystring-builder": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/hash-node": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.6.tgz", - "integrity": "sha512-c/FHEdKK/7DU2z6ZE91L36ahyXWayR3B+FzELjnYq7wH5YqIseM24V+pWCS9kFn1Ln8OFGTf+pyYPiHZuX0s/Q==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -1917,12 +2621,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.6.tgz", - "integrity": "sha512-czM7Ioq3s8pIXht7oD+vmgy4Wfb4XavU/k/irO8NdXFFOx7YAlsCCcKOh/lJD1mJSYQqiR7NmpZ9JviryD/7AQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", + "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -1938,13 +2642,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.8.tgz", - "integrity": "sha512-VuyszlSO49WKh3H9/kIO2kf07VUwGV80QRiaDxUfP8P8UKlokz381ETJvwLhwuypBYhLymCYyNhB3fLAGBX2og==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -1952,17 +2656,17 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.3.tgz", - "integrity": "sha512-KeM/OrK8MVFUsoJsmCN0MZMVPjKKLudn13xpgwIMpGTYpA8QZB2Xq5tJ+RE6iu3A6NhOI4VajDTwBsm8pwwrhg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", + "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", - "@smithy/util-middleware": "^3.0.6", + "@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", "tslib": "^2.6.2" }, "engines": { @@ -1970,18 +2674,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.21.tgz", - "integrity": "sha512-/h0fElV95LekVVEJuSw+aI11S1Y3zIUwBc6h9ZbUv43Gl2weXsbQwjLoet6j/Qtb0phfrSxS6pNg6FqgJOWZkA==", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz", + "integrity": "sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.3", - "@smithy/service-error-classification": "^3.0.6", - "@smithy/smithy-client": "^3.3.5", - "@smithy/types": "^3.4.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@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", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -2003,12 +2707,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.6.tgz", - "integrity": "sha512-KKTUSl1MzOM0MAjGbudeaVNtIDo+PpekTBkCNwvfZlKndodrnvRo+00USatiyLOc0ujjO9UydMRu3O9dYML7ag==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", + "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2016,12 +2720,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.6.tgz", - "integrity": "sha512-2c0eSYhTQ8xQqHMcRxLMpadFbTXg6Zla5l0mwNftFCZMQmuhI7EbAJMx6R5eqfuV3YbJ3QGyS3d5uSmrHV8Khg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", + "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2029,14 +2733,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.7.tgz", - "integrity": "sha512-g3mfnC3Oo8pOI0dYuPXLtdW1WGVb3bR2tkV21GNkm0ZvQjLTtamXAwCWt/FCb0HGvKt3gHHmF1XerG0ICfalOg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2044,15 +2748,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.3.tgz", - "integrity": "sha512-/gcm5DJ3k1b1zEInzBGAZC8ntJ+jwrz1NcSIu+9dSXd1FfG0G6QgkDI40tt8/WYUbHtLyo8fEqtm2v29koWo/w==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/protocol-http": "^4.1.3", - "@smithy/querystring-builder": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/abort-controller": "^3.1.5", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2060,12 +2764,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.6.tgz", - "integrity": "sha512-NK3y/T7Q/Bw+Z8vsVs9MYIQ5v7gOX7clyrXcwhhIBQhbPgRl6JDrZbusO9qWDhcEus75Tg+VCxtIRfo3H76fpw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", + "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2073,12 +2777,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.3.tgz", - "integrity": "sha512-GcbMmOYpH9iRqtC05RbRnc/0FssxSTHlmaNhYBTgSgNCYpdR3Kt88u5GAZTBmouzv+Zlj/VRv92J9ruuDeJuEw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.5.tgz", + "integrity": "sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -2086,12 +2790,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.6.tgz", - "integrity": "sha512-sQe08RunoObe+Usujn9+R2zrLuQERi3CWvRO3BvnoWSYUaIrLKuAIeY7cMeDax6xGyfIP3x/yFWbEKSXvOnvVg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", + "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -2100,12 +2804,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.6.tgz", - "integrity": "sha512-UJKw4LlEkytzz2Wq+uIdHf6qOtFfee/o7ruH0jF5I6UAuU+19r9QV7nU3P/uI0l6+oElRHmG/5cBBcGJrD7Ozg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", + "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2113,24 +2817,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.6.tgz", - "integrity": "sha512-53SpchU3+DUZrN7J6sBx9tBiCVGzsib2e4sc512Q7K9fpC5zkJKs6Z9s+qbMxSYrkEkle6hnMtrts7XNkMJJMg==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2" + "@smithy/types": "^3.5.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.7.tgz", - "integrity": "sha512-IA4K2qTJYXkF5OfVN4vsY1hfnUZjaslEE8Fsr/gGFza4TAC2A9NfnZuSY2srQIbt9bwtjHiAayrRVgKse4Q7fA==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2138,16 +2842,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.4.tgz", - "integrity": "sha512-72MiK7xYukNsnLJI9NqvUHqTu0ziEsfMsYNlWpiJfuGQnCTFKpckThlEatirvcA/LmT1h7rRO+pJD06PYsPu9Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.1.tgz", + "integrity": "sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.8", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -2157,16 +2861,16 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.5.tgz", - "integrity": "sha512-7IZi8J3Dr9n3tX+lcpmJ/5tCYIqoXdblFBaPuv0SEKZFRpCxE+TqIWL6I3t7jLlk9TWu3JSvEZAhtjB9yvB+zA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.0.tgz", + "integrity": "sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", - "@smithy/util-stream": "^3.1.8", + "@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", "tslib": "^2.6.2" }, "engines": { @@ -2174,9 +2878,9 @@ } }, "node_modules/@smithy/types": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", - "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.6.0.tgz", + "integrity": "sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2186,13 +2890,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.6.tgz", - "integrity": "sha512-47Op/NU8Opt49KyGpHtVdnmmJMsp2hEwBdyjuFB9M2V5QVOwA7pBhhxKN5z6ztKGrMw76gd8MlbPuzzvaAncuQ==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/querystring-parser": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -2253,14 +2957,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.21.tgz", - "integrity": "sha512-M/FhTBk4c/SsB91dD/M4gMGfJO7z/qJaM9+XQQIqBOf4qzZYMExnP7R4VdGwxxH8IKMGW+8F0I4rNtVRrcfPoA==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.5", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -2269,17 +2973,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.21.tgz", - "integrity": "sha512-NiLinPvF86U3S2Pdx/ycqd4bnY5dmFSPNL5KYRwbNjqQFS09M5Wzqk8BNk61/47xCYz1X/6KeiSk9qgYPTtuDw==", + "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==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.8", - "@smithy/credential-provider-imds": "^3.2.3", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.5", - "@smithy/types": "^3.4.2", + "@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", "tslib": "^2.6.2" }, "engines": { @@ -2287,13 +2991,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.2.tgz", - "integrity": "sha512-FEISzffb4H8DLzGq1g4MuDpcv6CIG15fXoQzDH9SjpRJv6h7J++1STFWWinilG0tQh9H1v2UKWG19Jjr2B16zQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", + "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2313,12 +3017,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.6.tgz", - "integrity": "sha512-BxbX4aBhI1O9p87/xM+zWy0GzT3CEVcXFPBRDoHAM+pV0eSW156pR+PSYEz0DQHDMYDsYAflC2bQNz2uaDBUZQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.8.tgz", + "integrity": "sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -2326,13 +3030,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.6.tgz", - "integrity": "sha512-BRZiuF7IwDntAbevqMco67an0Sr9oLQJqqRCsSPZZHYRnehS0LHDAkJk/pSmI7Z8c/1Vet294H7fY2fWUgB+Rg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", + "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2340,14 +3044,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.8.tgz", - "integrity": "sha512-hoKOqSmb8FD3WLObuB5hwbM7bNIWgcnvkThokTvVq7J5PKjlLUK5qQQcB9zWLHIoSaIlf3VIv2OxZY2wtQjcRQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", + "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.8", - "@smithy/node-http-handler": "^3.2.3", - "@smithy/types": "^3.4.2", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -2383,13 +3087,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", - "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.6.tgz", + "integrity": "sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/types": "^3.4.2", + "@smithy/abort-controller": "^3.1.5", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -2626,7 +3330,14 @@ "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" }, "node_modules/@xmldom/is-dom-node": { "version": "1.0.1", @@ -2653,7 +3364,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "optional": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -2866,8 +3576,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/base64id": { "version": "2.0.0", @@ -3005,6 +3714,30 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -3080,7 +3813,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3096,7 +3828,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3105,9 +3836,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", - "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz", + "integrity": "sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -3175,9 +3906,9 @@ } }, "node_modules/cloudinary": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.5.0.tgz", - "integrity": "sha512-gPkyylFpyBAXiErAnMgZBlpwztHuZoik/OTLQM9oswjlzYHLMtQGoh0oisPBQvVHQxHmBUjcgwsAi393HDedqQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.5.1.tgz", + "integrity": "sha512-CNg6uU53Hl4FEVynkTGpt5bQEAQWDHi3H+Sm62FzKf5uQHipSN2v7qVqS8GRVqeb0T1WNV+22+75DOJeRXYeSQ==", "license": "MIT", "dependencies": { "lodash": "^4.17.21", @@ -3454,17 +4185,27 @@ } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -3701,6 +4442,15 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4064,15 +4814,23 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "optional": true, "engines": { "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -4080,7 +4838,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -4112,9 +4870,10 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -4228,7 +4987,8 @@ "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" }, "node_modules/file-uri-to-path": { "version": "2.0.0", @@ -4289,9 +5049,9 @@ "license": "MIT" }, "node_modules/firebase-admin": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.5.0.tgz", - "integrity": "sha512-ad8vnlPcuuZN9scSgY8UnAxPI4mzP2/Q+dsrVLTf+j3h7bIq0FOelDCDGz4StgKJdk244v2kpOxqJjPG3grBHg==", + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.6.0.tgz", + "integrity": "sha512-gc0pDiUmxscxBhcjMcttmjvExJmnQdVRb+IIth95CvMm7F9rLdabrQZThW2mK02HR696P+rzd6NqkdUA3URu4w==", "license": "Apache-2.0", "dependencies": { "@fastify/busboy": "^3.0.0", @@ -4764,14 +5524,6 @@ "graphql": "14 - 16" } }, - "node_modules/graylog2": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/graylog2/-/graylog2-0.2.1.tgz", - "integrity": "sha512-vjysakwOhrAqMeIvSK0WZcmzKvkpxY6pCfT9QqtdSVAidPFIynuin7adqbdFp9MCCTbTE402WIxvg8cph5OWTA==", - "engines": { - "node": ">=0.6.11" - } - }, "node_modules/gtoken": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", @@ -4804,7 +5556,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -4960,6 +5711,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5008,6 +5779,30 @@ "node": ">=10" } }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -5197,9 +5992,9 @@ } }, "node_modules/json-2-csv": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.5.tgz", - "integrity": "sha512-xLeiOE+jtDMX4SMn9JlD6BVI9c5SYVFmtlsNBSelGlq9iUHdVmwlxQ/uUI/BEVQuKDVLlxNrsOfwlI3rfYy1zA==", + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.6.tgz", + "integrity": "sha512-N673XbJgHwUq9JreKpk530jSywPF/rEAQ08dV99QQpkluP/4HTwshpoP9hmDz26iSFqu7eNAPgyJfu/77HvPGA==", "license": "MIT", "dependencies": { "deeks": "3.1.0", @@ -5346,6 +6141,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -5357,16 +6158,46 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==", + "license": "MIT" + }, + "node_modules/lodash.iserror": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.iserror/-/lodash.iserror-3.1.1.tgz", + "integrity": "sha512-eT/VeNns9hS7vAj1NKW/rRX6b+C3UX3/IAAqEE7aC4Oo2C0iD82NaP5IS4bSlQsammTii4qBJ8G1zd1LTL8hCw==", + "license": "MIT" + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -5393,9 +6224,10 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "license": "MIT", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -5621,9 +6453,10 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.45", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", - "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "version": "0.5.46", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz", + "integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==", + "license": "MIT", "dependencies": { "moment": "^2.29.4" }, @@ -6019,9 +6852,9 @@ "license": "MIT" }, "node_modules/phone": { - "version": "3.1.50", - "resolved": "https://registry.npmjs.org/phone/-/phone-3.1.50.tgz", - "integrity": "sha512-TRmb2bX3sX+rrOrc8FRd8hmy4exoH2Lu3vjBP/dLgwwci1lv7DbjJ2iHMe7X4Hm8Pa0rJcfqTbq/O1vjU4NgxQ==", + "version": "3.1.51", + "resolved": "https://registry.npmjs.org/phone/-/phone-3.1.51.tgz", + "integrity": "sha512-ggy58LoEAb6LPZEyz1iXz7dQKbDAwp6V6yQgRQUDSlx4HgKm2YGETA5Ns/39/HQ+QL74+OBR05xzdUhi6TMHLQ==", "license": "MIT", "engines": { "node": ">=12" @@ -6058,6 +6891,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6284,6 +7126,27 @@ "@redis/time-series": "1.1.0" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", @@ -6380,9 +7243,10 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", "engines": { "node": ">=10" } @@ -6635,9 +7499,9 @@ } }, "node_modules/soap": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/soap/-/soap-1.1.4.tgz", - "integrity": "sha512-9uALgOdm/Dxcr2VWlB4VjsZbJ6k8L+fR1Dp/ICHFWSKXP3cFr9v9rIu+UBntVHmrJsErXwezv+dYsWte6lbzCA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/soap/-/soap-1.1.5.tgz", + "integrity": "sha512-6YJrwY+tXHwlk/wtS7+XSc0jyEWgNw8xJQYvY9m1jZlPaGkc2nzmwKAq98fwGIw51acywhsraaeq/6GFggaNYw==", "license": "MIT", "dependencies": { "axios": "^1.7.7", @@ -6934,6 +7798,12 @@ "node": "*" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -7348,6 +8218,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", "engines": { "node": ">= 14.0.0" } @@ -7587,39 +8458,76 @@ } }, "node_modules/winston": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", - "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.15.0.tgz", + "integrity": "sha512-RhruH2Cj0bV0WgNL+lOfoUBI4DVfdUNjVnJGVovWZmrcKtrFTTRzgXYK2O9cymSGjrERCtaAeHwMNnUWXlwZow==", + "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.4.0", + "logform": "^2.6.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" + "winston-transport": "^4.7.0" }, "engines": { "node": ">= 12.0.0" } }, - "node_modules/winston-transport": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", - "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", + "node_modules/winston-cloudwatch": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/winston-cloudwatch/-/winston-cloudwatch-6.3.0.tgz", + "integrity": "sha512-ffLMBUtas4qCpAyNfA6yUjZUQPepl6XduwHjukxRtI8hSWE4dKmy1k1lcLpyYiglrsgZop+OQYAV/iJJ+7Z94g==", + "license": "MIT", "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "async": "^3.1.0", + "chalk": "^4.0.0", + "fast-safe-stringify": "^2.0.7", + "lodash.assign": "^4.2.0", + "lodash.find": "^4.6.0", + "lodash.isempty": "^4.4.0", + "lodash.iserror": "^3.1.1" + }, + "peerDependencies": { + "@aws-sdk/client-cloudwatch-logs": "^3.0.0", + "winston": "^3.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.8.0.tgz", + "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==", + "license": "MIT", + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^4.5.2", "triple-beam": "^1.3.0" }, "engines": { "node": ">= 12.0.0" } }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index ef95ef36d..2a3c24ded 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,11 @@ "makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\"" }, "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.654.0", - "@aws-sdk/client-ses": "^3.654.0", - "@aws-sdk/credential-provider-node": "^3.654.0", + "@aws-sdk/client-cloudwatch-logs": "^3.679.0", + "@aws-sdk/client-elasticache": "^3.675.0", + "@aws-sdk/client-secrets-manager": "^3.675.0", + "@aws-sdk/client-ses": "^3.675.0", + "@aws-sdk/credential-provider-node": "^3.675.0", "@opensearch-project/opensearch": "^2.12.0", "@socket.io/admin-ui": "^0.5.1", "@socket.io/redis-adapter": "^8.3.0", @@ -31,39 +33,41 @@ "bluebird": "^3.7.2", "body-parser": "^1.20.3", "canvas": "^2.11.2", - "chart.js": "^4.4.4", - "cloudinary": "^2.5.0", + "chart.js": "^4.4.5", + "cloudinary": "^2.5.1", "compression": "^1.7.4", - "cookie-parser": "^1.4.6", + "cookie-parser": "^1.4.7", "cors": "2.8.5", "csrf": "^3.1.0", "dinero.js": "^1.9.1", "dotenv": "^16.4.5", - "express": "^4.21.0", - "firebase-admin": "^12.5.0", + "express": "^4.21.1", + "firebase-admin": "^12.6.0", "graphql": "^16.9.0", "graphql-request": "^6.1.0", - "graylog2": "^0.2.1", "inline-css": "^4.0.2", "intuit-oauth": "^4.1.2", - "json-2-csv": "^5.5.5", + "ioredis": "^5.4.1", + "json-2-csv": "^5.5.6", "lodash": "^4.17.21", "moment": "^2.30.1", - "moment-timezone": "^0.5.45", + "moment-timezone": "^0.5.46", "multer": "^1.4.5-lts.1", "node-mailjet": "^6.0.6", "node-persist": "^4.0.3", "nodemailer": "^6.9.15", - "phone": "^3.1.50", + "phone": "^3.1.51", "recursive-diff": "^1.0.9", "redis": "^4.7.0", "rimraf": "^6.0.1", - "soap": "^1.1.4", + "soap": "^1.1.5", "socket.io": "^4.8.0", "socket.io-adapter": "^2.5.5", "ssh2-sftp-client": "^10.0.3", "twilio": "^4.23.0", "uuid": "^10.0.0", + "winston": "^3.15.0", + "winston-cloudwatch": "^6.3.0", "xml2js": "^0.6.2", "xmlbuilder2": "^3.1.1" }, diff --git a/redis/Dockerfile b/redis/Dockerfile new file mode 100644 index 000000000..b68d8fb2b --- /dev/null +++ b/redis/Dockerfile @@ -0,0 +1,20 @@ +# Use the official Redis image +FROM redis:7.0-alpine + +# Copy the Redis configuration file +COPY redis.conf /usr/local/etc/redis/redis.conf + +# Copy the entrypoint script +COPY entrypoint.sh /usr/local/bin/entrypoint.sh + +# Make the entrypoint script executable +RUN chmod +x /usr/local/bin/entrypoint.sh + +# Debugging step: List contents of /usr/local/bin +RUN ls -l /usr/local/bin + +# Expose Redis ports +EXPOSE 6379 16379 + +# Set the entrypoint +ENTRYPOINT ["entrypoint.sh"] diff --git a/redis/entrypoint.sh b/redis/entrypoint.sh new file mode 100644 index 000000000..a5a527e30 --- /dev/null +++ b/redis/entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +LOCKFILE="/redis-lock/redis-cluster-init.lock" + +# Start Redis server in the background +redis-server /usr/local/etc/redis/redis.conf & + +# Wait for Redis server to start +sleep 5 + +# Only initialize the cluster if the lock file doesn't exist +if [ ! -f "$LOCKFILE" ]; then + echo "Initializing Redis Cluster..." + + # Create lock file to prevent further initialization attempts + touch "$LOCKFILE" + if [ $? -eq 0 ]; then + echo "Lock file created successfully at $LOCKFILE." + else + echo "Failed to create lock file." + fi + + # Initialize the Redis cluster + yes yes | redis-cli --cluster create \ + redis-node-1:6379 \ + redis-node-2:6379 \ + redis-node-3:6379 \ + --cluster-replicas 0 + + echo "Redis Cluster initialized." +else + echo "Cluster already initialized, skipping initialization." +fi + +# Keep the container running +tail -f /dev/null diff --git a/redis/redis.conf b/redis/redis.conf new file mode 100644 index 000000000..22533e22d --- /dev/null +++ b/redis/redis.conf @@ -0,0 +1,6 @@ +bind 0.0.0.0 +port 6379 +cluster-enabled yes +cluster-config-file nodes.conf +cluster-node-timeout 5000 +appendonly yes diff --git a/server.js b/server.js index 0ccfd9ed3..a92ab7241 100644 --- a/server.js +++ b/server.js @@ -1,26 +1,32 @@ -const express = require("express"); -const cors = require("cors"); -const bodyParser = require("body-parser"); +// Load environment variables THIS MUST BE AT THE TOP const path = require("path"); -const compression = require("compression"); -const cookieParser = require("cookie-parser"); -const http = require("http"); -const { Server } = require("socket.io"); -const { createClient } = require("redis"); -const { createAdapter } = require("@socket.io/redis-adapter"); -const logger = require("./server/utils/logger"); -const { redisSocketEvents } = require("./server/web-sockets/redisSocketEvents"); -const { instrument } = require("@socket.io/admin-ui"); - -const { isString, isEmpty } = require("lodash"); -const applyRedisHelpers = require("./server/utils/redisHelpers"); -const applyIOHelpers = require("./server/utils/ioHelpers"); - -// Load environment variables require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); +const cors = require("cors"); +const http = require("http"); +const Redis = require("ioredis"); +const express = require("express"); +const bodyParser = require("body-parser"); +const compression = require("compression"); +const cookieParser = require("cookie-parser"); +const { Server } = require("socket.io"); +const { createAdapter } = require("@socket.io/redis-adapter"); +const { instrument } = require("@socket.io/admin-ui"); +const { isString, isEmpty } = require("lodash"); + +const logger = require("./server/utils/logger"); +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 CLUSTER_RETRY_BASE_DELAY = 100; +const CLUSTER_RETRY_MAX_DELAY = 5000; +const CLUSTER_RETRY_JITTER = 100; + /** * CORS Origin for Socket.IO * @type {string[][]} @@ -49,16 +55,16 @@ const SOCKETIO_CORS_ORIGIN = [ "https://old.imex.online", "https://www.old.imex.online", "https://wsadmin.imex.online", - "https://www.wsadmin.imex.online", - "http://localhost:3333", - "https://localhost:3333" + "https://www.wsadmin.imex.online" ]; +const SOCKETIO_CORS_ORIGIN_DEV = ["http://localhost:3333", "https://localhost:3333"]; + /** * Middleware for Express app * @param app */ -const applyMiddleware = (app) => { +const applyMiddleware = ({ app }) => { app.use(compression()); app.use(cookieParser()); app.use(bodyParser.json({ limit: "50mb" })); @@ -76,7 +82,7 @@ const applyMiddleware = (app) => { * Route groupings for Express app * @param app */ -const applyRoutes = (app) => { +const applyRoutes = ({ app }) => { app.use("/", require("./server/routes/miscellaneousRoutes")); app.use("/notifications", require("./server/routes/notificationsRoutes")); app.use("/render", require("./server/routes/renderRoutes")); @@ -102,37 +108,130 @@ const applyRoutes = (app) => { }); }; +/** + * Fetch Redis nodes from AWS ElastiCache + * @returns {Promise} + */ +const getRedisNodesFromAWS = async () => { + const client = new ElastiCacheClient({ + region: InstanceManager({ + imex: "ca-central-1", + rome: "us-east-2" + }) + }); + + const params = { + ReplicationGroupId: process.env.REDIS_CLUSTER_ID, + ShowCacheNodeInfo: true + }; + + try { + // Fetch the cache clusters associated with the replication group + const command = new DescribeCacheClustersCommand(params); + const response = await client.send(command); + const cacheClusters = response.CacheClusters; + + return cacheClusters.flatMap((cluster) => + cluster.CacheNodes.map((node) => `${node.Endpoint.Address}:${node.Endpoint.Port}`) + ); + } catch (err) { + logger.log(`Error fetching Redis nodes from AWS: ${err.message}`, "ERROR", "redis", "api"); + throw err; + } +}; + +/** + * Connect to Redis Cluster + * @returns {Promise} + */ +const connectToRedisCluster = async () => { + let redisServers; + + if (isString(process.env?.REDIS_CLUSTER_ID) && !isEmpty(process.env?.REDIS_CLUSTER_ID)) { + // Fetch Redis nodes from AWS if AWS environment variables are present + redisServers = await getRedisNodesFromAWS(); + } else { + // Use the Dockerized Redis cluster in development + if (isEmpty(process.env?.REDIS_URL) || !isString(process.env?.REDIS_URL)) { + logger.log(`No or Malformed REDIS_URL present.`, "ERROR", "redis", "api"); + process.exit(1); + } + try { + redisServers = JSON.parse(process.env.REDIS_URL); + } catch (error) { + logger.log(`Failed to parse REDIS_URL: ${error.message}. Exiting...`, "ERROR", "redis", "api"); + process.exit(1); + } + } + + const clusterRetryStrategy = (times) => { + const delay = + Math.min(CLUSTER_RETRY_BASE_DELAY + times * 50, CLUSTER_RETRY_MAX_DELAY) + Math.random() * CLUSTER_RETRY_JITTER; + logger.log(`Redis cluster not yet ready. Retrying in ${delay.toFixed(2)}ms`, "WARN", "redis", "api"); + return delay; + }; + + const redisCluster = new Redis.Cluster(redisServers, { + clusterRetryStrategy, + enableAutoPipelining: true, + enableReadyCheck: true, + redisOptions: { + // connectTimeout: 10000, // Timeout for connecting in ms + // idleTimeoutMillis: 30000, // Close idle connections after 30s + // maxRetriesPerRequest: 5 // Retry a maximum of 5 times per request + } + }); + + return new Promise((resolve, reject) => { + redisCluster.on("ready", () => { + logger.log(`Redis cluster connection established.`, "INFO", "redis", "api"); + resolve(redisCluster); + }); + + redisCluster.on("error", (err) => { + logger.log(`Redis cluster connection failed: ${err.message}`, "ERROR", "redis", "api"); + reject(err); + }); + }); +}; + /** * Apply Redis to the server * @param server * @param app */ -const applySocketIO = async (server, app) => { - // Redis client setup for Pub/Sub and Key-Value Store - const pubClient = createClient({ url: process.env.REDIS_URL || "redis://localhost:6379" }); +const applySocketIO = async ({ server, app }) => { + const redisCluster = await connectToRedisCluster(); + + // Handle errors + redisCluster.on("error", (err) => { + logger.log(`Redis ERROR`, "ERROR", "redis", "api"); + }); + + const pubClient = redisCluster; const subClient = pubClient.duplicate(); pubClient.on("error", (err) => logger.log(`Redis pubClient error: ${err}`, "ERROR", "redis")); subClient.on("error", (err) => logger.log(`Redis subClient error: ${err}`, "ERROR", "redis")); - try { - await Promise.all([pubClient.connect(), subClient.connect()]); - logger.log(`[${process.env.NODE_ENV}] Connected to Redis`, "INFO", "redis", "api"); - } catch (redisError) { - logger.log("Failed to connect to Redis", "ERROR", "redis", redisError); - } - process.on("SIGINT", async () => { logger.log("Closing Redis connections...", "INFO", "redis", "api"); - await Promise.all([pubClient.disconnect(), subClient.disconnect()]); - process.exit(0); + try { + await Promise.all([pubClient.disconnect(), subClient.disconnect()]); + logger.log("Redis connections closed. Process will exit.", "INFO", "redis", "api"); + } catch (error) { + logger.log(`Error closing Redis connections: ${error.message}`, "ERROR", "redis", "api"); + } }); const ioRedis = new Server(server, { path: "/wss", adapter: createAdapter(pubClient, subClient), cors: { - origin: SOCKETIO_CORS_ORIGIN, + origin: + process.env?.NODE_ENV === "development" + ? [...SOCKETIO_CORS_ORIGIN, ...SOCKETIO_CORS_ORIGIN_DEV] + : SOCKETIO_CORS_ORIGIN, methods: ["GET", "POST"], credentials: true, exposedHeaders: ["set-cookie"] @@ -140,13 +239,14 @@ const applySocketIO = async (server, app) => { }); if (isString(process.env.REDIS_ADMIN_PASS) && !isEmpty(process.env.REDIS_ADMIN_PASS)) { - logger.log(`[${process.env.NODE_ENV}] Initializing Redis Admin UI....`, "INFO", "redis", "api"); + logger.log(`Initializing Redis Admin UI....`, "INFO", "redis", "api"); instrument(ioRedis, { auth: { type: "basic", username: "admin", password: process.env.REDIS_ADMIN_PASS }, + mode: process.env.REDIS_ADMIN_MODE || "development" }); } @@ -161,19 +261,22 @@ const applySocketIO = async (server, app) => { } }); + const api = { + pubClient, + io, + ioRedis, + redisCluster + }; + app.use((req, res, next) => { - Object.assign(req, { - pubClient, - io, - ioRedis - }); + Object.assign(req, api); next(); }); - Object.assign(module.exports, { io, pubClient, ioRedis }); + Object.assign(module.exports, api); - return { pubClient, io, ioRedis }; + return api; }; /** @@ -186,22 +289,22 @@ const main = async () => { const server = http.createServer(app); - const { pubClient, ioRedis } = await applySocketIO(server, app); - const api = applyRedisHelpers(pubClient, app, logger); - const ioHelpers = applyIOHelpers(app, api, ioRedis, logger); + const { pubClient, ioRedis } = await applySocketIO({ server, app }); + const redisHelpers = applyRedisHelpers({ pubClient, app, logger }); + const ioHelpers = applyIOHelpers({ app, redisHelpers, ioRedis, logger }); // Legacy Socket Events require("./server/web-sockets/web-socket"); - applyMiddleware(app); - applyRoutes(app); - redisSocketEvents(ioRedis, api, ioHelpers); + applyMiddleware({ app }); + applyRoutes({ app }); + redisSocketEvents({ io: ioRedis, redisHelpers, ioHelpers, logger }); try { await server.listen(port); - logger.log(`[${process.env.NODE_ENV}] Server started on port ${port}`, "INFO", "api"); + logger.log(`Server started on port ${port}`, "INFO", "api"); } catch (error) { - logger.log(`[${process.env.NODE_ENV}] Server failed to start on port ${port}`, "ERROR", "api", error); + logger.log(`Server failed to start on port ${port}`, "ERROR", "api", error); } }; diff --git a/server/accounting/pbs/pbs-ap-allocations.js b/server/accounting/pbs/pbs-ap-allocations.js index 3b8a20996..a969e82c8 100644 --- a/server/accounting/pbs/pbs-ap-allocations.js +++ b/server/accounting/pbs/pbs-ap-allocations.js @@ -24,9 +24,9 @@ axios.interceptors.request.use((x) => { const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${ x.url } | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`; - console.log(printable); + //console.log(printable); - CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data); + CdkBase.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data); return x; }); @@ -35,8 +35,8 @@ axios.interceptors.response.use((x) => { const socket = x.config.socket; const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`; - console.log(printable); - CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data); + //console.log(printable); + CdkBase.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data); return x; }); @@ -181,7 +181,7 @@ async function QueryBillData(socket, billids) { const result = await client .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) .request(queries.GET_PBS_AP_ALLOCATIONS, { billids: billids }); - CdkBase.createLogEvent(socket, "TRACE", `Bill data query result ${JSON.stringify(result, null, 2)}`); + CdkBase.createLogEvent(socket, "SILLY", `Bill data query result ${JSON.stringify(result, null, 2)}`); return result; } diff --git a/server/accounting/pbs/pbs-job-export.js b/server/accounting/pbs/pbs-job-export.js index b43aa0e3b..c38560293 100644 --- a/server/accounting/pbs/pbs-job-export.js +++ b/server/accounting/pbs/pbs-job-export.js @@ -26,9 +26,9 @@ axios.interceptors.request.use((x) => { const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${ x.url } | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`; - console.log(printable); + //console.log(printable); - CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data); + CdkBase.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data); return x; }); @@ -37,8 +37,8 @@ axios.interceptors.response.use((x) => { const socket = x.config.socket; const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`; - console.log(printable); - CdkBase.createJsonEvent(socket, "TRACE", `Raw Response: ${printable}`, x.data); + //console.log(printable); + CdkBase.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data); return x; }); @@ -118,7 +118,7 @@ async function CheckForErrors(socket, response) { CdkBase.createLogEvent(socket, "DEBUG", `Successful response from DMS. ${response.Message || ""}`); } else { CdkBase.createLogEvent(socket, "ERROR", `Error received from DMS: ${response.Message}`); - CdkBase.createLogEvent(socket, "TRACE", `Error received from DMS: ${JSON.stringify(response)}`); + CdkBase.createLogEvent(socket, "SILLY", `Error received from DMS: ${JSON.stringify(response)}`); } } @@ -130,7 +130,7 @@ async function QueryJobData(socket, jobid) { const result = await client .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) .request(queries.QUERY_JOBS_FOR_PBS_EXPORT, { id: jobid }); - CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`); + CdkBase.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`); return result.jobs_by_pk; } @@ -611,7 +611,7 @@ async function InsertFailedExportLog(socket, error) { bodyshopid: socket.JobData.bodyshop.id, jobid: socket.JobData.id, successful: false, - message: [error], + message: JSON.stringify(error), useremail: socket.user.email } }); diff --git a/server/accounting/qb-receivables-lines.js b/server/accounting/qb-receivables-lines.js index 0dedb4472..043dc8e89 100644 --- a/server/accounting/qb-receivables-lines.js +++ b/server/accounting/qb-receivables-lines.js @@ -63,7 +63,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes ); if (!account) { - logger.log("qbxml-receivables-no-account", "warn", null, jobline.id, null); + logger.log("qbxml-receivables-no-account", "warn", null, jobline.id); throw new Error( `A matching account does not exist for the part allocation. Center: ${jobline.profitcenter_part}` ); @@ -207,7 +207,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes }); if (!hasMapaLine && jobs_by_pk.job_totals.rates.mapa.total.amount > 0) { - // console.log("Adding MAPA Line Manually."); + // //console.log("Adding MAPA Line Manually."); const mapaAccountName = responsibilityCenters.defaults.profits.MAPA; const mapaAccount = responsibilityCenters.profits.find((c) => c.name === mapaAccountName); @@ -272,12 +272,12 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes }); } } else { - //console.log("NO MAPA ACCOUNT FOUND!!"); + ////console.log("NO MAPA ACCOUNT FOUND!!"); } } if (!hasMashLine && jobs_by_pk.job_totals.rates.mash.total.amount > 0) { - // console.log("Adding MASH Line Manually."); + // //console.log("Adding MASH Line Manually."); const mashAccountName = responsibilityCenters.defaults.profits.MASH; @@ -341,7 +341,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes }); } } else { - // console.log("NO MASH ACCOUNT FOUND!!"); + // //console.log("NO MASH ACCOUNT FOUND!!"); } } @@ -795,7 +795,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes : taxCodes[taxAccountCode]; for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { const taxAmount = Dinero(job_totals.totals.us_sales_tax_breakdown[`ty${tyCounter}Tax`]); - console.log(`Tax ${tyCounter}`, taxAmount.toFormat()); + //console.log(`Tax ${tyCounter}`, taxAmount.toFormat()); if (taxAmount.getAmount() > 0) { if (qbo) { InvoiceLineAdd.push({ diff --git a/server/accounting/qbo/qbo-payables.js b/server/accounting/qbo/qbo-payables.js index c0673cbc5..196520de0 100644 --- a/server/accounting/qbo/qbo-payables.js +++ b/server/accounting/qbo/qbo-payables.js @@ -45,7 +45,7 @@ exports.default = async (req, res) => { const BearerToken = req.BearerToken; const client = req.userGraphQLClient; - logger.log("qbo-payable-create", "DEBUG", req.user.email, billsToQuery); + logger.log("qbo-payable-create", "DEBUG", req.user.email, null, { billsToQuery }); const result = await client .setHeaders({ Authorization: BearerToken }) @@ -91,6 +91,12 @@ exports.default = async (req, res) => { ret.push({ billid: bill.id, success: true }); } catch (error) { + logger.log("qbo-paybles-create-error", "ERROR", req.user.email, null, { + error: + (error && error.authResponse && error.authResponse.body) || + error.response?.data?.Fault?.Error.map((e) => e.Detail).join(", ") || + (error && error.message) + }); ret.push({ billid: bill.id, success: false, @@ -121,8 +127,8 @@ exports.default = async (req, res) => { res.status(200).json(ret); } catch (error) { - console.log(error); - logger.log("qbo-payable-create-error", "ERROR", req.user.email, { + //console.log(error); + logger.log("qbo-payable-create-error", "ERROR", req.user.email, null, { error: error.message, stack: error.stack }); diff --git a/server/accounting/qbo/qbo-payments.js b/server/accounting/qbo/qbo-payments.js index 436efca71..6426bfa4f 100644 --- a/server/accounting/qbo/qbo-payments.js +++ b/server/accounting/qbo/qbo-payments.js @@ -49,7 +49,7 @@ exports.default = async (req, res) => { const BearerToken = req.BearerToken; const client = req.userGraphQLClient; - logger.log("qbo-payment-create", "DEBUG", req.user.email, paymentsToQuery); + logger.log("qbo-payment-create", "DEBUG", req.user.email, null, { paymentsToQuery }); const result = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_PAYMENTS_FOR_EXPORT, { payments: paymentsToQuery @@ -152,7 +152,7 @@ exports.default = async (req, res) => { ret.push({ paymentid: payment.id, success: true }); } catch (error) { - logger.log("qbo-payment-create-error", "ERROR", req.user.email, { + logger.log("qbo-payment-create-error", "ERROR", req.user.email, null, { error: (error && error.authResponse && error.authResponse.body) || (error && error.message) }); //Add the export log error. @@ -182,8 +182,8 @@ exports.default = async (req, res) => { res.status(200).json(ret); } catch (error) { - console.log(error); - logger.log("qbo-payment-create-error", "ERROR", req.user.email, { + //console.log(error); + logger.log("qbo-payment-create-error", "ERROR", req.user.email, null, { error: error.message, stack: error.stack }); diff --git a/server/accounting/qbo/qbo-receivables.js b/server/accounting/qbo/qbo-receivables.js index 51aeade0f..f57c6711a 100644 --- a/server/accounting/qbo/qbo-receivables.js +++ b/server/accounting/qbo/qbo-receivables.js @@ -185,7 +185,7 @@ exports.default = async (req, res) => { error?.response?.data || error?.message }); - console.log(error); + //console.log(error); logger.log("qbo-receivable-create-error", "ERROR", req.user.email, { error: error.message, stack: error.stack @@ -211,7 +211,7 @@ exports.default = async (req, res) => { res.status(200).json(ret); } catch (error) { - console.log(error); + //console.log(error); logger.log("qbo-receivable-create-error", "ERROR", req.user.email, { error: error.message, stack: error.stack diff --git a/server/accounting/qbxml/qbxml-payables.js b/server/accounting/qbxml/qbxml-payables.js index 9ffe4513f..ef588e0fb 100644 --- a/server/accounting/qbxml/qbxml-payables.js +++ b/server/accounting/qbxml/qbxml-payables.js @@ -42,9 +42,10 @@ exports.default = async (req, res) => { //For each invoice. res.status(200).json(QbXmlToExecute); } catch (error) { - logger.log("qbxml-payable-error", "ERROR", req.user.email, req.body.billsToQuery, { - error: error.message, - stack: error.stack + logger.log("qbxml-payable-error", "ERROR", req?.user?.email, null, { + billsToQuery: req?.body?.billsToQuery, + error: error?.message, + stack: error?.stack }); res.status(400).send(JSON.stringify(error)); } diff --git a/server/accounting/qbxml/qbxml-payments.js b/server/accounting/qbxml/qbxml-payments.js index cdc08d2e4..68e7b8778 100644 --- a/server/accounting/qbxml/qbxml-payments.js +++ b/server/accounting/qbxml/qbxml-payments.js @@ -21,7 +21,9 @@ exports.default = async (req, res) => { const client = req.userGraphQLClient; try { - logger.log("qbxml-payments-create", "DEBUG", req.user.email, req.body.paymentsToQuery, null); + logger.log("qbxml-payments-create", "DEBUG", req?.user?.email, null, { + paymentsToQuery: req.body?.paymentsToQuery + }); const result = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_PAYMENTS_FOR_EXPORT, { payments: paymentsToQuery @@ -62,7 +64,8 @@ exports.default = async (req, res) => { res.status(200).json(QbXmlToExecute); } catch (error) { - logger.log("qbxml-payments-error", "error", req.user.email, req.body.paymentsToQuery, { + logger.log("qbxml-payments-error", "error", req?.user?.email, null, { + paymentsToQuery: req.body?.paymentsToQuery, error: error.message, stack: error.stack }); diff --git a/server/accounting/qbxml/qbxml-receivables.js b/server/accounting/qbxml/qbxml-receivables.js index 0e8a4f20d..c4353704e 100644 --- a/server/accounting/qbxml/qbxml-receivables.js +++ b/server/accounting/qbxml/qbxml-receivables.js @@ -23,7 +23,9 @@ exports.default = async (req, res) => { const client = req.userGraphQLClient; try { - logger.log("qbxml-receivables-create", "DEBUG", req.user.email, req.body.jobIds, null); + logger.log("qbxml-receivables-create", "DEBUG", req?.user?.email, null, { + jobIds: req?.body?.jobIds + }); const result = await client .setHeaders({ Authorization: BearerToken }) @@ -74,7 +76,8 @@ exports.default = async (req, res) => { res.status(200).json(QbXmlToExecute); } catch (error) { - logger.log("qbxml-receivables-error", "error", req.user.email, req.body.jobIds, { + logger.log("qbxml-receivables-error", "error", req?.user?.email, null, { + jobIds: req.body?.jobIds, error: error.message, stack: error.stack }); diff --git a/server/admin/adminops.js b/server/admin/adminops.js index 7e8b1c676..d134bdcba 100644 --- a/server/admin/adminops.js +++ b/server/admin/adminops.js @@ -9,7 +9,7 @@ require("dotenv").config({ const client = require("../graphql-client/graphql-client").client; exports.createAssociation = async (req, res) => { - logger.log("admin-create-association", "ADMIN", req.user.email, null, { + logger.log("admin-create-association", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -31,7 +31,7 @@ exports.createAssociation = async (req, res) => { res.json(result); }; exports.createShop = async (req, res) => { - logger.log("admin-create-shop", "ADMIN", req.user.email, null, { + logger.log("admin-create-shop", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -64,7 +64,7 @@ exports.createShop = async (req, res) => { } }; exports.updateCounter = async (req, res) => { - logger.log("admin-update-counter", "ADMIN", req.user.email, null, { + logger.log("admin-update-counter", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -91,7 +91,7 @@ exports.updateCounter = async (req, res) => { } }; exports.updateShop = async (req, res) => { - logger.log("admin-update-shop", "ADMIN", req.user.email, null, { + logger.log("admin-update-shop", "debug", req.user.email, null, { request: req.body, ioadmin: true }); diff --git a/server/cdk/cdk-calculate-allocations.js b/server/cdk/cdk-calculate-allocations.js index ddac4ee4c..f78c48b91 100644 --- a/server/cdk/cdk-calculate-allocations.js +++ b/server/cdk/cdk-calculate-allocations.js @@ -22,7 +22,7 @@ exports.defaultRoute = async function (req, res) { const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid); return res.status(200).json({ data: calculateAllocations(req, jobData) }); } catch (error) { - console.log(error); + ////console.log(error); CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`); res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` }); } @@ -33,7 +33,7 @@ exports.default = async function (socket, jobid) { const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid); return calculateAllocations(socket, jobData); } catch (error) { - console.log(error); + ////console.log(error); CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`); } }; @@ -42,7 +42,7 @@ async function QueryJobData(connectionData, token, jobid) { CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`); const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid }); - CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`); + CdkBase.createLogEvent(connectionData, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`); return result.jobs_by_pk; } @@ -212,7 +212,7 @@ function calculateAllocations(connectionData, job) { }); if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) { - // console.log("Adding MAPA Line Manually."); + // //console.log("Adding MAPA Line Manually."); const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA; const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName); @@ -224,7 +224,7 @@ function calculateAllocations(connectionData, job) { Dinero(job.job_totals.rates.mapa.total) ); } else { - //console.log("NO MAPA ACCOUNT FOUND!!"); + ////console.log("NO MAPA ACCOUNT FOUND!!"); } } @@ -373,13 +373,19 @@ function calculateAllocations(connectionData, job) { }); //profile level adjustments for labor and materials Object.keys(job.job_totals.rates).forEach((key) => { - if (job.job_totals.rates[key] && job.job_totals.rates[key].adjustment && Dinero(job.job_totals.rates[key].adjustment).isZero() === false) { + if ( + job.job_totals.rates[key] && + job.job_totals.rates[key].adjustment && + Dinero(job.job_totals.rates[key].adjustment).isZero() === false + ) { const accountName = selectedDmsAllocationConfig.profits[key.toUpperCase()]; const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName); if (otherAccount) { if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero(); - profitCenterHash[accountName] = profitCenterHash[accountName].add(Dinero(job.job_totals.rates[key].adjustments)); + profitCenterHash[accountName] = profitCenterHash[accountName].add( + Dinero(job.job_totals.rates[key].adjustments) + ); } else { CdkBase.createLogEvent( connectionData, diff --git a/server/cdk/cdk-get-makes.js b/server/cdk/cdk-get-makes.js index 06ccc53e2..90accf64e 100644 --- a/server/cdk/cdk-get-makes.js +++ b/server/cdk/cdk-get-makes.js @@ -39,8 +39,8 @@ exports.default = async function ReloadCdkMakes(req, res) { const deleteResult = await client .setHeaders({ Authorization: BearerToken }) .request(queries.DELETE_ALL_DMS_VEHICLES, {}); - console.log("🚀 ~ file: cdk-get-makes.js ~ line 53 ~ deleteResult", deleteResult); + // logger.logger.debug("🚀 ~ file: cdk-get-makes.js ~ line 53 ~ deleteResult", { deleteResult }); //Insert the new ones. const insertResult = await client.setHeaders({ Authorization: BearerToken }).request(queries.INSERT_DMS_VEHICLES, { @@ -59,6 +59,7 @@ exports.default = async function ReloadCdkMakes(req, res) { cdk_dealerid, count: newList.length }); + res.sendStatus(200); } catch (error) { logger.log("cdk-replace-makes-models-error", "ERROR", req.user.email, null, { diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js index 222a2b485..90001fa28 100644 --- a/server/cdk/cdk-job-export.js +++ b/server/cdk/cdk-job-export.js @@ -151,7 +151,7 @@ async function QueryJobData(socket, jobid) { const result = await client .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) .request(queries.QUERY_JOBS_FOR_CDK_EXPORT, { id: jobid }); - CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`); + CdkBase.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`); return result.jobs_by_pk; } @@ -171,7 +171,7 @@ async function CalculateDmsVid(socket, JobData) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientVehicleInsertUpdate.getVehIdsAsync Result ${JSON.stringify(result, null, 2)}` ); CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); @@ -214,7 +214,7 @@ async function QueryDmsVehicleById(socket, JobData, DMSVid) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientVehicleInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`); @@ -246,7 +246,7 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) { CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.readAsync response.`); CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}` ); CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); @@ -295,7 +295,7 @@ async function QueryDmsCustomerByName(socket, JobData) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(result, null, 2)}` ); CheckCdkResponseForError(socket, soapResponseCustomerSearch); @@ -337,7 +337,7 @@ async function GenerateDmsCustomerNumber(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(result, null, 2)}` ); CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); @@ -425,7 +425,7 @@ async function InsertDmsCustomer(socket, newCustomerNumber) { CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.insertAsync response.`); CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}` ); CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); @@ -505,7 +505,7 @@ async function InsertDmsVehicle(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientVehicleInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`); @@ -611,7 +611,7 @@ async function UpdateDmsVehicle(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "DEBUG", `soapClientVehicleInsertUpdate.updateAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`); @@ -650,7 +650,7 @@ async function InsertServiceVehicleHistory(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`); @@ -690,7 +690,7 @@ async function InsertDmsStartWip(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientAccountingGLInsertUpdate.doStartWIPAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`); @@ -721,7 +721,7 @@ async function InsertDmsBatchWip(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`); @@ -885,7 +885,7 @@ async function PostDmsBatchWip(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`); @@ -914,7 +914,7 @@ async function QueryDmsErrWip(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "DEBUG", `soapClientAccountingGLInsertUpdate.doErrWIPAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`); @@ -945,7 +945,7 @@ async function DeleteDmsWip(socket) { CdkBase.createLogEvent( socket, - "TRACE", + "SILLY", `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` ); CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`); @@ -995,7 +995,7 @@ async function InsertFailedExportLog(socket, error) { bodyshopid: socket.JobData.bodyshop.id, jobid: socket.JobData.id, successful: false, - message: [error], + message: JSON.stringify(error), useremail: socket.user.email } }); diff --git a/server/cdk/cdk-wsdl.js b/server/cdk/cdk-wsdl.js index 032d780c2..8926e98ae 100644 --- a/server/cdk/cdk-wsdl.js +++ b/server/cdk/cdk-wsdl.js @@ -20,7 +20,7 @@ function CheckCdkResponseForError(socket, soapResponse) { //The response was null, this might be ok, it might not. CdkBase.createLogEvent( socket, - "WARNING", + "warn", `Warning detected in CDK Response - it appears to be null. Stack: ${new Error().stack}` ); return; diff --git a/server/data/arms.js b/server/data/arms.js index ec3d026e8..70ad3be5e 100644 --- a/server/data/arms.js +++ b/server/data/arms.js @@ -820,7 +820,7 @@ exports.default = async (req, res) => { job: JSON.stringify({ id: job.id, ro_number: job.ro_number }), error: error.message || JSON.stringify(error) }); - console.log(error); + //console.log(error); } } catch (error) { logger.log("arms-failed-job", "ERROR", "api", job.shopid, { @@ -886,7 +886,10 @@ exports.default = async (req, res) => { const [result, rawResponse, , rawRequest] = entegralResponse; } catch (error) { fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml); - console.log(error); + //console.log(error); + logger.log("arms-error-shop", "ERROR", "api", bodyshop.id, { + error + }); } } catch (error) { //Error at the shop level. diff --git a/server/data/autohouse.js b/server/data/autohouse.js index fa52679df..9b6494e67 100644 --- a/server/data/autohouse.js +++ b/server/data/autohouse.js @@ -206,9 +206,6 @@ const CreateRepairOrderTag = (job, errorCallback) => { const repairCosts = CreateCosts(job); - if (job.ro_number === "QBD209") { - console.log("Stop here"); - } //Calculate detail only lines. const detailAdjustments = job.joblines .filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty) diff --git a/server/data/chatter.js b/server/data/chatter.js index 85dbe4a9b..e610f9791 100644 --- a/server/data/chatter.js +++ b/server/data/chatter.js @@ -154,13 +154,14 @@ exports.default = async (req, res) => { async function getPrivateKey() { // Connect to AWS Secrets Manager const client = new SecretsManagerClient({ region: "ca-central-1" }); - const command = new GetSecretValueCommand({ SecretId: CHATTER_PRIVATE_KEY }); + const command = new GetSecretValueCommand({ SecretId: "CHATTER_PRIVATE_KEY" }); logger.log("chatter-get-private-key", "DEBUG", "api", null, null); try { const { SecretString, SecretBinary } = await client.send(command); if (SecretString || SecretBinary) logger.log("chatter-retrieved-private-key", "DEBUG", "api", null, null); - return SecretString || Buffer.from(SecretBinary, "base64").toString("ascii"); + const chatterPrivateKey = SecretString ? JSON.parse(SecretString) : JSON.parse(Buffer.from(SecretBinary, "base64").toString("ascii")); + return chatterPrivateKey.private_key; } catch (error) { logger.log("chatter-get-private-key", "ERROR", "api", null, error); throw err; diff --git a/server/email/mailer.js b/server/email/mailer.js new file mode 100644 index 000000000..654f56cb3 --- /dev/null +++ b/server/email/mailer.js @@ -0,0 +1,32 @@ +const { isString, isEmpty } = require("lodash"); +const { defaultProvider } = require("@aws-sdk/credential-provider-node"); +const { default: InstanceManager } = require("../utils/instanceMgr"); +const aws = require("@aws-sdk/client-ses"); +const nodemailer = require("nodemailer"); +const logger = require("../utils/logger"); + +const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); + +const sesConfig = { + apiVersion: "latest", + credentials: defaultProvider(), + region: isLocal + ? "ca-central-1" + : InstanceManager({ + imex: "ca-central-1", + rome: "us-east-2" + }) +}; + +if (isLocal) { + sesConfig.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + logger.logger.debug(`SES Mailer set to LocalStack end point: ${sesConfig.endpoint}`); +} + +const ses = new aws.SES(sesConfig); + +let transporter = nodemailer.createTransport({ + SES: { ses, aws } +}); + +module.exports = transporter; diff --git a/server/email/sendemail.js b/server/email/sendemail.js index 5dea4a075..9d103ef81 100644 --- a/server/email/sendemail.js +++ b/server/email/sendemail.js @@ -3,30 +3,13 @@ require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); const axios = require("axios"); -let nodemailer = require("nodemailer"); -let aws = require("@aws-sdk/client-ses"); -let { defaultProvider } = require("@aws-sdk/credential-provider-node"); const InstanceManager = require("../utils/instanceMgr").default; const logger = require("../utils/logger"); const client = require("../graphql-client/graphql-client").client; const queries = require("../graphql-client/queries"); const { isObject } = require("lodash"); const generateEmailTemplate = require("./generateTemplate"); -const moment = require("moment"); - -const ses = new aws.SES({ - // The key apiVersion is no longer supported in v3, and can be removed. - // @deprecated The client uses the "latest" apiVersion. - apiVersion: "latest", - defaultProvider, - region: InstanceManager({ - imex: "ca-central-1", - rome: "us-east-2" - }) -}); -let transporter = nodemailer.createTransport({ - SES: { ses, aws } -}); +const mailer = require("./mailer"); // Get the image from the URL and return it as a base64 string const getImage = async (imageUrl) => { @@ -38,7 +21,7 @@ const getImage = async (imageUrl) => { // Log the email in the database const logEmail = async (req, email) => { try { - const insertresult = await client.request(queries.INSERT_EMAIL_AUDIT, { + await client.request(queries.INSERT_EMAIL_AUDIT, { email: { to: email.to, cc: email.cc, @@ -51,13 +34,13 @@ const logEmail = async (req, email) => { status: "Sent" } }); - console.log(insertresult); } catch (error) { - logger.log("email-log-error", "error", req.user.email, null, { + logger.log("email-log-error", "error", req?.user?.email, null, { from: `${req.body.from.name} <${req.body.from.address}>`, - to: req.body.to, - cc: req.body.cc, - subject: req.body.subject + to: req?.body?.to, + cc: req?.body?.cc, + subject: req?.body?.subject, + email // info, }); } @@ -66,7 +49,7 @@ const logEmail = async (req, email) => { const sendServerEmail = async ({ subject, text }) => { if (process.env.NODE_ENV === undefined) return; try { - transporter.sendMail( + mailer.sendMail( { from: InstanceManager({ imex: `ImEX Online API - ${process.env.NODE_ENV} `, @@ -87,32 +70,30 @@ const sendServerEmail = async ({ subject, text }) => { } }, (err, info) => { - console.log(err || info); + logger.log("server-email-failure", err ? "error" : "debug", null, null, { message: err || info }); } ); } catch (error) { - console.log(error); - logger.log("server-email-failure", "error", null, null, error); + logger.log("server-email-failure", "error", null, null, { error }); } }; -const sendProManagerWelcomeEmail = async ({to, subject, html}) => { +const sendProManagerWelcomeEmail = async ({ to, subject, html }) => { try { - await transporter.sendMail({ + await mailer.sendMail({ from: `ProManager `, to, subject, html }); } catch (error) { - console.log(error); - logger.log("server-email-failure", "error", null, null, error); + logger.log("server-email-failure", "error", null, null, { error }); } }; const sendTaskEmail = async ({ to, subject, type = "text", html, text, attachments }) => { try { - transporter.sendMail( + mailer.sendMail( { from: InstanceManager({ imex: `ImEX Online `, @@ -125,12 +106,12 @@ const sendTaskEmail = async ({ to, subject, type = "text", html, text, attachmen attachments: attachments || null }, (err, info) => { - console.log(err || info); + // (message, type, user, record, meta + logger.log("server-email", err ? "error" : "debug", null, null, { message: err || info }); } ); } catch (error) { - console.log(error); - logger.log("server-email-failure", "error", null, null, error); + logger.log("server-email-failure", "error", null, null, { error }); } }; @@ -166,7 +147,7 @@ const sendEmail = async (req, res) => { ); } - transporter.sendMail( + mailer.sendMail( { from: `${req.body.from.name} <${req.body.from.address}>`, replyTo: req.body.ReplyTo.Email, @@ -201,9 +182,8 @@ const sendEmail = async (req, res) => { } }, (err, info) => { - console.log(err || info); if (info) { - logger.log("send-email-success", "DEBUG", req.user.email, null, { + logger.log("send-email-success", "DEBUG", req?.user?.email, null, { from: `${req.body.from.name} <${req.body.from.address}>`, replyTo: req.body.ReplyTo.Email, to: req.body.to, @@ -222,7 +202,7 @@ const sendEmail = async (req, res) => { success: true //response: info }); } else { - logger.log("send-email-failure", "ERROR", req.user.email, null, { + logger.log("send-email-failure", "ERROR", req?.user?.email, null, { from: `${req.body.from.name} <${req.body.from.address}>`, replyTo: req.body.ReplyTo.Email, to: req.body.to, @@ -280,7 +260,7 @@ const emailBounce = async (req, res) => { status: "Bounced", context: message.bounce?.bouncedRecipients }); - transporter.sendMail( + mailer.sendMail( { from: InstanceMgr({ imex: `ImEX Online `, @@ -307,7 +287,9 @@ ${body.bounce?.bouncedRecipients.map( ` }, (err, info) => { - console.log("***", err || info); + logger.log("sns-error", err ? "error" : "debug", "api", null, { + message: err ? JSON.stringify(error) : info + }); } ); } diff --git a/server/email/tasksEmails.js b/server/email/tasksEmails.js index 1f508be31..c05185b6b 100644 --- a/server/email/tasksEmails.js +++ b/server/email/tasksEmails.js @@ -2,30 +2,14 @@ const path = require("path"); require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); -let nodemailer = require("nodemailer"); -let aws = require("@aws-sdk/client-ses"); -let { defaultProvider } = require("@aws-sdk/credential-provider-node"); const InstanceManager = require("../utils/instanceMgr").default; const logger = require("../utils/logger"); const client = require("../graphql-client/graphql-client").client; const queries = require("../graphql-client/queries"); const generateEmailTemplate = require("./generateTemplate"); -const moment = require("moment"); +const moment = require("moment-timezone"); const { taskEmailQueue } = require("./tasksEmailsQueue"); - -const ses = new aws.SES({ - apiVersion: "latest", - defaultProvider, - region: InstanceManager({ - imex: "ca-central-1", - rome: "us-east-2" - }) -}); - -const transporter = nodemailer.createTransport({ - SES: { ses, aws }, - sendingRate: InstanceManager({ imex: 40, rome: 10 }) -}); +const mailer = require("./mailer"); // Initialize the Tasks Email Queue const tasksEmailQueue = taskEmailQueue(); @@ -34,35 +18,39 @@ const tasksEmailQueue = taskEmailQueue(); const tasksEmailQueueCleanup = async () => { try { // Example async operation - console.log("Performing Tasks Email Reminder process cleanup..."); + // console.log("Performing Tasks Email Reminder process cleanup..."); await new Promise((resolve) => tasksEmailQueue.destroy(() => resolve())); } catch (err) { - console.error("Tasks Email Reminder process cleanup failed:", err); + // console.error("Tasks Email Reminder process cleanup failed:", err); } }; -if (process.env.NODE_ENV !== "development") { - // Handling SIGINT (e.g., Ctrl+C) - process.on("SIGINT", async () => { - await tasksEmailQueueCleanup(); - process.exit(0); - }); - // Handling SIGTERM (e.g., sent by system shutdown) - process.on("SIGTERM", async () => { - await tasksEmailQueueCleanup(); - process.exit(0); - }); - // Handling uncaught exceptions - process.on("uncaughtException", async (err) => { - await tasksEmailQueueCleanup(); - process.exit(1); // Exit with an 'error' code - }); - // Handling unhandled promise rejections - process.on("unhandledRejection", async (reason, promise) => { - await tasksEmailQueueCleanup(); - process.exit(1); // Exit with an 'error' code - }); -} +// if (process.env.NODE_ENV !== "development") { +// // Handling SIGINT (e.g., Ctrl+C) +// process.on("SIGINT", async () => { +// console.log("Handling SIGNIT For Tasks Cleanup"); +// await tasksEmailQueueCleanup(); +// process.exit(0); +// }); +// // Handling SIGTERM (e.g., sent by system shutdown) +// process.on("SIGTERM", async () => { +// console.log("Handling SIGTERM For Tasks Cleanup"); +// await tasksEmailQueueCleanup(); +// process.exit(0); +// }); +// // Handling uncaught exceptions +// process.on("uncaughtException", async (err) => { +// console.log("Handling uncaughtException For Tasks Cleanup"); +// await tasksEmailQueueCleanup(); +// process.exit(1); +// }); +// // Handling unhandled promise rejections +// process.on("unhandledRejection", async (reason, promise) => { +// console.log("Handling unhandledRejection For Tasks Cleanup"); +// await tasksEmailQueueCleanup(); +// process.exit(1); +// }); +// } /** * Format the date for the email. @@ -89,6 +77,7 @@ const formatPriority = (priority) => { * @param priority * @param description * @param dueDate + * @param createdBy * @param bodyshop * @param job * @param taskId @@ -108,12 +97,13 @@ const getEndpoints = (bodyshop) => : "https://romeonline.io" }); -const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => { +const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId, dateLine, createdBy) => { const endPoints = getEndpoints(bodyshop); return { header: title, - subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`, - body: `Reference: ${job.ro_number || "N/A"} | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()}
${description ? description.concat("
") : ""}View this task.` + subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)} | Created By: ${createdBy || "N/A"}`, + body: `Reference: ${job.ro_number || "N/A"} | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()}
${description ? description.concat("
") : ""}View this task.`, + dateLine }; }; @@ -125,6 +115,7 @@ const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, j * @param html * @param taskIds * @param successCallback + * @param requestInstance */ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInstance) => { const fromEmails = InstanceManager({ @@ -135,7 +126,7 @@ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInst : "Rome Online " }); - transporter.sendMail( + mailer.sendMail( { from: fromEmails, to, @@ -152,8 +143,6 @@ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInst } } ); - // } - // }); }; /** @@ -178,6 +167,8 @@ const taskAssignedEmail = async (req, res) => { id: newTask.id }); + const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); + sendMail( "assigned", tasks_by_pk.assigned_to_employee.user_email, @@ -190,7 +181,9 @@ const taskAssignedEmail = async (req, res) => { newTask.due_date, tasks_by_pk.bodyshop, tasks_by_pk.job, - newTask.id + newTask.id, + dateLine, + newTask.created_by ) ), null, @@ -247,7 +240,7 @@ const tasksRemindEmail = async (req, res) => { const fromEmails = InstanceManager({ imex: "ImEX Online ", rome: - tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" + tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" ? "ProManager " : "Rome Online " }); @@ -259,6 +252,8 @@ const tasksRemindEmail = async (req, res) => { const taskIds = groupedTasks[recipient.email].map((task) => task.id); + const dateLine = moment().tz(tasksRequest?.tasks[0].bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); + // There is only the one email to send to this author. if (recipient.count === 1) { const onlyTask = groupedTasks[recipient.email][0]; @@ -274,7 +269,9 @@ const tasksRemindEmail = async (req, res) => { onlyTask.due_date, onlyTask.bodyshop, onlyTask.job, - onlyTask.id + onlyTask.id, + dateLine, + onlyTask.created_by ) ); } @@ -283,7 +280,7 @@ const tasksRemindEmail = async (req, res) => { const endPoints = InstanceManager({ imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online", rome: - tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" + tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" ? process.env?.NODE_ENV === "test" ? "https//test.promanager.web-est.com" : "https://promanager.web-est.com" @@ -297,6 +294,7 @@ const tasksRemindEmail = async (req, res) => { emailData.html = generateEmailTemplate({ header: `${allTasks.length} Tasks require your attention`, subHeader: `Please click on the Tasks below to view the Task.`, + dateLine, body: `
    ${allTasks .map((task) => @@ -335,6 +333,29 @@ const tasksRemindEmail = async (req, res) => { } }; +// Note: Uncomment this to test locally, it will call the remind_at email check every 20 seconds +// const callTaskRemindEmailInternally = () => { +// const req = { +// body: { +// // You can mock any request data here if needed +// } +// }; +// +// const res = { +// status: (code) => { +// return { +// json: (data) => { +// console.log(`Response Status: ${code}`, data); +// } +// }; +// } +// }; +// +// // Call the taskRemindEmail function with mock req and res +// tasksRemindEmail(req, res); +// }; +// setInterval(callTaskRemindEmailInternally, 20000); + module.exports = { taskAssignedEmail, tasksRemindEmail, diff --git a/server/email/tasksEmailsQueue.js b/server/email/tasksEmailsQueue.js index dc004abc1..c1828b858 100644 --- a/server/email/tasksEmailsQueue.js +++ b/server/email/tasksEmailsQueue.js @@ -10,8 +10,9 @@ const logger = require("../utils/logger"); const taskEmailQueue = () => new Queue( (taskIds, cb) => { - console.log("Processing reminds for taskIds: ", taskIds.join(", ")); - + logger.log("Processing reminds for taskIds: ", "silly", null, null, { + taskIds: taskIds?.join(", ") + }); // Set the remind_at_sent to the current time. const now = moment().toISOString(); diff --git a/server/firebase/firebase-handler.js b/server/firebase/firebase-handler.js index 2590e6f57..e24c86174 100644 --- a/server/firebase/firebase-handler.js +++ b/server/firebase/firebase-handler.js @@ -16,7 +16,7 @@ admin.initializeApp({ }); const createUser = async (req, res) => { - logger.log("admin-create-user", "ADMIN", req.user.email, null, { + logger.log("admin-create-user", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -97,7 +97,7 @@ const sendPromanagerWelcomeEmail = (req, res) => { // Validate email before proceeding if (!dbUser.validemail) { - logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, { + logger.log("admin-send-welcome-email-skip", "debug", req.user.email, null, { message: "User email is not valid, skipping email.", email }); @@ -107,7 +107,7 @@ const sendPromanagerWelcomeEmail = (req, res) => { // Check if the user's company is ProManager const convenientCompany = dbUser.associations?.[0]?.bodyshop?.convenient_company; if (convenientCompany !== "promanager") { - logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, { + logger.log("admin-send-welcome-email-skip", "debug", req.user.email, null, { message: 'convenient_company is not "promanager", skipping email.', convenientCompany }); @@ -141,7 +141,7 @@ const sendPromanagerWelcomeEmail = (req, res) => { }) .then(() => { // Log success and return response - logger.log("admin-send-welcome-email", "ADMIN", req.user.email, null, { + logger.log("admin-send-welcome-email", "debug", req.user.email, null, { request: req.body, ioadmin: true, emailSentTo: email @@ -161,7 +161,7 @@ const sendPromanagerWelcomeEmail = (req, res) => { }; const updateUser = (req, res) => { - logger.log("admin-update-user", "ADMIN", req.user.email, null, { + logger.log("admin-update-user", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -184,7 +184,7 @@ const updateUser = (req, res) => { .then((userRecord) => { // See the UserRecord reference doc for the contents of userRecord. - logger.log("admin-update-user-success", "ADMIN", req.user.email, null, { + logger.log("admin-update-user-success", "debug", req.user.email, null, { userRecord, ioadmin: true }); @@ -199,7 +199,7 @@ const updateUser = (req, res) => { }; const getUser = (req, res) => { - logger.log("admin-get-user", "ADMIN", req.user.email, null, { + logger.log("admin-get-user", "debug", req.user.email, null, { request: req.body, ioadmin: true }); @@ -269,10 +269,14 @@ const sendNotification = async (req, res) => { }) .then((response) => { // Response is a message ID string. - console.log("Successfully sent message:", response); + logger.log("Successfully sent message:", "debug", req?.user?.email, null, { + response + }); }) .catch((error) => { - console.log("Error sending message:", error); + logger.log("Successfully sent message:", "error", req?.user?.email, null, { + error + }); }); res.sendStatus(200); @@ -321,7 +325,7 @@ module.exports = { // admin.auth().setCustomUserClaims(uid, { // ioadmin: true, // "https://hasura.io/jwt/claims": { -// "x-hasura-default-role": "admin", +// "x-hasura-default-role": "debug", // "x-hasura-allowed-roles": ["admin"], // "x-hasura-user-id": uid, // }, diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index ff643de8b..99e37bebe 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2489,6 +2489,7 @@ exports.QUERY_REMIND_TASKS = ` bodyshop { shopname convenient_company + timezone } bodyshopid } @@ -2505,6 +2506,7 @@ exports.QUERY_TASK_BY_ID = ` query QUERY_TASK_BY_ID($id: uuid!) { tasks_by_pk(id: $id) { id + created_by assigned_to_employee{ id user_email @@ -2512,6 +2514,7 @@ query QUERY_TASK_BY_ID($id: uuid!) { bodyshop{ shopname convenient_company + timezone } job{ ro_number diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js index 3c6289b9b..c231f8261 100644 --- a/server/intellipay/intellipay.js +++ b/server/intellipay/intellipay.js @@ -78,7 +78,7 @@ exports.lightbox_credentials = async (req, res) => { res.send(response.data); } catch (error) { - console.log(error); + //console.log(error); logger.log("intellipay-lightbox-credentials-error", "ERROR", req.user?.email, null, { error: JSON.stringify(error) }); @@ -109,7 +109,7 @@ exports.payment_refund = async (req, res) => { res.send(response.data); } catch (error) { - console.log(error); + //console.log(error); logger.log("intellipay-refund-error", "ERROR", req.user?.email, null, { error: JSON.stringify(error) }); @@ -143,7 +143,7 @@ exports.generate_payment_url = async (req, res) => { res.send(response.data); } catch (error) { - console.log(error); + //console.log(error); logger.log("intellipay-payment-url-error", "ERROR", req.user?.email, null, { error: JSON.stringify(error) }); diff --git a/server/ioevent/ioevent.js b/server/ioevent/ioevent.js index a657e31d8..6ca37f27e 100644 --- a/server/ioevent/ioevent.js +++ b/server/ioevent/ioevent.js @@ -28,14 +28,14 @@ exports.default = async (req, res) => { // } // }); - ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", { - operationName, - useremail - }); + // ioRedis.to(getBodyshopRoom(bodyshopid)).emit("bodyshop-message", { + // operationName, + // useremail + // }); res.sendStatus(200); } catch (error) { - logger.log("ioevent-error", "trace", user, null, { + logger.log("ioevent-error", "silly", user, null, { operationname: operationName, time, dbevent, diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 9ce65f71d..668a35673 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -19,7 +19,8 @@ async function JobCosting(req, res) { const BearerToken = req.BearerToken; const client = req.userGraphQLClient; - logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null); + //Uncomment for further testing + // logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null); try { const resp = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_JOB_COSTING_DETAILS, { @@ -46,7 +47,10 @@ async function JobCostingMulti(req, res) { const BearerToken = req.BearerToken; const client = req.userGraphQLClient; - logger.log("job-costing-multi-start", "DEBUG", req.user.email, jobids, null); + //Uncomment for further testing + // logger.log("job-costing-multi-start", "DEBUG", req?.user?.email, null, { + // jobids + // }); try { const resp = await client @@ -244,7 +248,8 @@ async function JobCostingMulti(req, res) { data: ret }); } catch (error) { - logger.log("job-costing-multi-error", "ERROR", req.user.email, [jobids], { + logger.log("job-costing-multi-error", "ERROR", req?.user?.email, null, { + jobids, message: error.message, stack: error.stack }); @@ -282,7 +287,13 @@ function GenerateCostingData(job) { if (val.mod_lbr_ty) { const laborProfitCenter = val.profitcenter_labor || defaultProfits[val.mod_lbr_ty] || "Unknown"; - if (laborProfitCenter === "Unknown") console.log("Unknown type", val.line_desc, val.mod_lbr_ty); + //Uncomment for further testing + // if (laborProfitCenter === "Unknown") { + // logger.log("job-costing unknown type", "debug", null, null, { + // line_desc: val.line_desc, + // mod_lbr_ty: val.mod_lbr_ty + // }); + // } const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`; @@ -349,10 +360,22 @@ function GenerateCostingData(job) { if (val.part_type && val.part_type !== "PAE" && val.part_type !== "PAS" && val.part_type !== "PASL") { const partsProfitCenter = val.profitcenter_part || defaultProfits[val.part_type] || "Unknown"; - if (partsProfitCenter === "Unknown") console.log("Unknown type", val.line_desc, val.part_type); + //Uncomment for further testing + // if (partsProfitCenter === "Unknown" || !partsProfitCenter) { + // logger.log( + // partsProfitCenter === "Unknown" + // ? "job-costing unknown type" + // : "Unknown cost/profit center mapping for parts.", + // "debug", + // null, + // null, + // { + // line_desc: val.line_desc, + // part_type: val.part_type + // } + // ); + // } - if (!partsProfitCenter) - console.log("Unknown cost/profit center mapping for parts.", val.line_desc, val.part_type); let partsAmount = Dinero({ amount: val.act_price_before_ppc ? Math.round(val.act_price_before_ppc * 100) @@ -409,10 +432,22 @@ function GenerateCostingData(job) { if (val.part_type && val.part_type !== "PAE" && (val.part_type === "PAS" || val.part_type === "PASL")) { const partsProfitCenter = val.profitcenter_part || defaultProfits[val.part_type] || "Unknown"; - if (partsProfitCenter === "Unknown") console.log("Unknown type", val.line_desc, val.part_type); + //Uncomment for further testing + // if (partsProfitCenter === "Unknown" || !partsProfitCenter) { + // logger.log( + // partsProfitCenter === "Unknown" + // ? "job-costing unknown type" + // : "job-costing Unknown cost/profit center mapping for sublet", + // "debug", + // null, + // null, + // { + // line_desc: val.line_desc, + // part_type: val.part_type + // } + // ); + // } - if (!partsProfitCenter) - console.log("Unknown cost/profit center mapping for sublet.", val.line_desc, val.part_type); const partsAmount = Dinero({ amount: Math.round((val.act_price || 0) * 100) }) @@ -443,9 +478,14 @@ function GenerateCostingData(job) { //If so, use it, otherwise try to use the same from the auto-allocate logic in IO app jobs-close-auto-allocate. const partsProfitCenter = val.profitcenter_part || getAdditionalCostCenter(val, defaultProfits) || "Unknown"; - if (partsProfitCenter === "Unknown") { - console.log("Unknown type", val.line_desc, val.part_type); - } + //Uncomment for further testing + // if (partsProfitCenter === "Unknown") { + // logger.log("job-costing unknown type", "debug", null, null, { + // line_desc: val.line_desc, + // part_type: val.part_type + // }); + // } + const partsAmount = Dinero({ amount: Math.round((val.act_price || 0) * 100) }) @@ -476,7 +516,7 @@ function GenerateCostingData(job) { if (!hasMapaLine) { if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]]) jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero(); - + jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = jobLineTotalsByProfitCenter.additional[ defaultProfits["MAPA"] ].add( diff --git a/server/job/job-totals-USA.js b/server/job/job-totals-USA.js index ca9a74bca..c23a64068 100644 --- a/server/job/job-totals-USA.js +++ b/server/job/job-totals-USA.js @@ -26,7 +26,7 @@ exports.totalsSsu = async function (req, res) { const BearerToken = req.BearerToken; const client = req.userGraphQLClient; - logger.log("job-totals-ssu-USA", "DEBUG", req.user.email, id, null); + logger.log("job-totals-ssu-USA", "DEBUG", req?.user?.email, id); try { const job = await client.setHeaders({ Authorization: BearerToken }).request(queries.GET_JOB_BY_PK, { @@ -47,7 +47,7 @@ exports.totalsSsu = async function (req, res) { res.status(200).send(); } catch (error) { - logger.log("job-totals-ssu-USA-error", "ERROR", req.user.email, id, { + logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, { jobid: id, error }); @@ -84,12 +84,10 @@ async function Totals(req, res) { const logger = req.logger; const client = req.userGraphQLClient; - logger.log("job-totals-USA", "DEBUG", req.user.email, job.id, { + logger.log("job-totals-ssu-USA", "DEBUG", req.user.email, job.id, { jobid: job.id }); - logger.log("job-totals-ssu-USA", "DEBUG", req.user.email, id, null); - await AutoAddAtsIfRequired({ job, client }); try { @@ -866,8 +864,8 @@ function CalculateTaxesTotals(job, otherTotals) { } }); - console.log("*** Taxable Amounts***"); - console.table(JSON.parse(JSON.stringify(taxableAmounts))); + // console.log("*** Taxable Amounts***"); + // console.table(JSON.parse(JSON.stringify(taxableAmounts))); //For the taxable amounts, figure out which tax type applies. //Then sum up the total of that tax type and then calculate the thresholds. @@ -961,7 +959,9 @@ function CalculateTaxesTotals(job, otherTotals) { } } } catch (error) { - console.error("Key with issue", key); + logger.log("job-totals-USA Key with issue", "error", null, null, { + key + }); } }); @@ -979,8 +979,8 @@ function CalculateTaxesTotals(job, otherTotals) { } const remainingTaxableAmounts = taxableAmountsByTier; - console.log("*** Taxable Amounts by Tier***"); - console.table(JSON.parse(JSON.stringify(taxableAmountsByTier))); + // console.log("*** Taxable Amounts by Tier***"); + // console.table(JSON.parse(JSON.stringify(taxableAmountsByTier))); Object.keys(taxableAmountsByTier).forEach((taxTierKey) => { try { @@ -1026,12 +1026,14 @@ function CalculateTaxesTotals(job, otherTotals) { totalTaxByTier[taxTierKey] = totalTaxByTier[taxTierKey].add(taxAmountToAdd); } } catch (error) { - console.error("PFP Calculation error", error); + logger.log("job-totals-USA - PFP Calculation Error", "error", null, null, { + error + }); } }); - console.log("*** Total Tax by Tier Amounts***"); - console.table(JSON.parse(JSON.stringify(totalTaxByTier))); + // console.log("*** Total Tax by Tier Amounts***"); + // console.table(JSON.parse(JSON.stringify(totalTaxByTier))); stateTax = stateTax .add(totalTaxByTier.ty1Tax) diff --git a/server/job/job-totals.js b/server/job/job-totals.js index 079f54cba..4cd45e13c 100644 --- a/server/job/job-totals.js +++ b/server/job/job-totals.js @@ -182,7 +182,7 @@ async function AutoAddAtsIfRequired({ job, client }) { job.joblines[atsLineIndex].act_price = atsAmount; } - console.log(job.jobLines); + //console.log(job.jobLines); } } diff --git a/server/job/job-updated.js b/server/job/job-updated.js index f218f83bc..89637a6f8 100644 --- a/server/job/job-updated.js +++ b/server/job/job-updated.js @@ -2,8 +2,16 @@ const { isObject } = require("lodash"); const jobUpdated = async (req, res) => { const { ioRedis, logger, ioHelpers } = req; + // Old Way + if (req?.body?.event?.data?.new || isObject(req?.body?.event?.data?.new)) { + const updatedJob = req.body.event.data.new; + const bodyshopID = updatedJob.shopid; + ioRedis.to(ioHelpers.getBodyshopRoom(bodyshopID)).emit("production-job-updated", updatedJob); + return res.json({ message: "Job updated and event emitted" }); + } - if (!req?.body?.event?.data?.new || !isObject(req?.body?.event?.data?.new)) { + // New way + if (!req?.body?.data || !isObject(req.body.data)) { logger.log("job-update-error", "ERROR", req.user?.email, null, { message: `Malformed Job Update request sent from Hasura`, body: req?.body @@ -15,12 +23,14 @@ const jobUpdated = async (req, res) => { }); } - logger.log("job-update", "INFO", req.user?.email, null, { - message: `Job updated event received from Hasura`, - jobid: req?.body?.event?.data?.new?.id - }); + // Uncomment for further testing + // You can also test this using SocketIOAdmin + // logger.log("job-update", "DEBUG", req.user?.email, null, { + // message: `Job updated event received from Hasura`, + // jobid: req?.body?.event?.data?.new?.id + // }); - const updatedJob = req.body.event.data.new; + const updatedJob = req.body.data; const bodyshopID = updatedJob.shopid; // Emit the job-updated event only to the room corresponding to the bodyshop diff --git a/server/middleware/validateFirebaseIdTokenMiddleware.js b/server/middleware/validateFirebaseIdTokenMiddleware.js index d76cc8019..83ab38eaf 100644 --- a/server/middleware/validateFirebaseIdTokenMiddleware.js +++ b/server/middleware/validateFirebaseIdTokenMiddleware.js @@ -16,7 +16,11 @@ const validateFirebaseIdTokenMiddleware = async (req, res, next) => { (!req.headers.authorization || !req.headers.authorization.startsWith("Bearer ")) && !(req.cookies && req.cookies.__session) ) { - console.error("Unauthorized attempt. No authorization provided."); + logger.log("api-authorization-call", "warn", req?.user?.email, null, { + type: "unauthorized", + path: req.path, + body: req.body + }); return res.status(403).send("Unauthorized"); } @@ -32,10 +36,10 @@ const validateFirebaseIdTokenMiddleware = async (req, res, next) => { idToken = req.cookies.__session; } else { // No cookie - console.error("Unauthorized attempt. No cookie provided."); - logger.log("api-unauthorized-call", "WARN", null, null, { - req, - type: "no-cookie" + logger.log("api-unauthorized-call", "warn", null, null, { + type: "unauthorized", + path: req.path, + body: req.body }); return res.status(403).send("Unauthorized"); @@ -47,11 +51,11 @@ const validateFirebaseIdTokenMiddleware = async (req, res, next) => { req.user = decodedIdToken; next(); } catch (error) { - logger.log("api-unauthorized-call", "WARN", null, null, { + logger.log("api-unauthorized-call", "warn", null, null, { path: req.path, body: req.body, - type: "unauthroized", + type: "unauthorized", ...error }); diff --git a/server/mixdata/mixdata.js b/server/mixdata/mixdata.js index 0dca3705b..919726efd 100644 --- a/server/mixdata/mixdata.js +++ b/server/mixdata/mixdata.js @@ -9,11 +9,9 @@ require("dotenv").config({ }); exports.mixdataUpload = async (req, res) => { - const { bodyshopid } = req.body; - const client = req.userGraphQLClient; - logger.log("job-mixdata-upload", "DEBUG", req.user.email, null, null); + logger.log("job-mixdata-upload", "DEBUG", req?.user?.email, null, null); try { for (const element of req.files) { @@ -23,7 +21,7 @@ exports.mixdataUpload = async (req, res) => { explicitArray: false }); - logger.log("job-mixdata-parse", "DEBUG", req.user.email, inboundRequest); + logger.log("job-mixdata-parse", "DEBUG", req?.user?.email, null, { inboundRequest }); const ScaleType = DetermineScaleType(inboundRequest); const RoNumbersFromInboundRequest = GetListOfRos(inboundRequest, ScaleType); @@ -61,7 +59,7 @@ exports.mixdataUpload = async (req, res) => { res.status(500).json(error); logger.log("job-mixdata-upload-error", "ERROR", null, null, { error: error.message, - ...error + stack: error.stack }); } }; @@ -70,7 +68,7 @@ function DetermineScaleType(inboundRequest) { const ret = { type: "", verson: 0 }; //PPG Mix Data - if (inboundRequest.PPG && inboundRequest.PPG.Header.Protocol.Name === "PPG") { + if (inboundRequest?.PPG?.Header?.Protocol?.Name === "PPG") { return { type: inboundRequest.PPG.Header.Protocol.Name, company: "PPG", @@ -80,13 +78,13 @@ function DetermineScaleType(inboundRequest) { } function GetListOfRos(inboundRequest, ScaleType) { - if (ScaleType.company === "PPG" && ScaleType.version === "1.3.0") { + if (ScaleType?.company === "PPG" && ScaleType?.version === "1.3.0") { return inboundRequest.PPG.MixDataInterface.ROData.RepairOrders.RO.map((r) => r.RONumber); } } function GenerateMixDataArray(inboundRequest, ScaleType, jobHash) { - if (ScaleType.company === "PPG" && ScaleType.version === "1.3.0") { + if (ScaleType?.company === "PPG" && ScaleType?.version === "1.3.0") { return inboundRequest.PPG.MixDataInterface.ROData.RepairOrders.RO.map((r) => { return { jobid: jobHash[r.RONumber]?.jobid, diff --git a/server/opensearch/os-handler.js b/server/opensearch/os-handler.js index ba35eebd9..930933373 100644 --- a/server/opensearch/os-handler.js +++ b/server/opensearch/os-handler.js @@ -148,7 +148,7 @@ async function OpenSearchUpdateHandler(req, res) { }; const response = await osClient.index(payload); - console.log(response.body); + //console.log(response.body); res.status(200).json(response.body); } } catch (error) { @@ -255,7 +255,7 @@ async function OpenSearchSearchHandler(req, res) { res.json(body); } catch (error) { - console.log(error); + //console.log(error); logger.log("os-search-error", "ERROR", req.user.email, null, { error: JSON.stringify(error) }); diff --git a/server/payroll/pay-all.js b/server/payroll/pay-all.js index 9288e256a..03c6fbee8 100644 --- a/server/payroll/pay-all.js +++ b/server/payroll/pay-all.js @@ -45,12 +45,12 @@ exports.payall = async function (req, res) { const path = diffParser(diff); if (diff.op === "add") { - console.log(Object.keys(diff.val)); + // console.log(Object.keys(diff.val)); if (typeof diff.val === "object" && Object.keys(diff.val).length > 1) { //Multiple values to add. Object.keys(diff.val).forEach((key) => { - console.log("Hours", diff.val[key][Object.keys(diff.val[key])[0]]); - console.log("Rate", Object.keys(diff.val[key])[0]); + // console.log("Hours", diff.val[key][Object.keys(diff.val[key])[0]]); + // console.log("Rate", Object.keys(diff.val[key])[0]); ticketsToInsert.push({ task_name: "Pay All", jobid: job.id, diff --git a/server/render/canvas-handler.js b/server/render/canvas-handler.js index 010a3bec3..0af2a7f53 100644 --- a/server/render/canvas-handler.js +++ b/server/render/canvas-handler.js @@ -1,18 +1,19 @@ const { createCanvas } = require("canvas"); const Chart = require("chart.js/auto"); +const logger = require("../utils/logger"); const { backgroundColors, borderColors } = require("./canvas-colors"); const { isObject, defaultsDeep, isNumber } = require("lodash"); exports.canvastest = function (req, res) { - console.log("Incoming test request.", req); + //console.log("Incoming test request.", req); res.status(200).send("OK"); }; exports.canvas = function (req, res) { const { w, h, values, keys, override } = req.body; - console.log("Incoming Canvas Request:", w, h, values, keys, override); - + //console.log("Incoming Canvas Request:", w, h, values, keys, override); + logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override }); // Gate required values if (!values || !keys) { res.status(400).send("Missing required data"); diff --git a/server/scheduling/scheduling-job.js b/server/scheduling/scheduling-job.js index a005a8d72..11b881f70 100644 --- a/server/scheduling/scheduling-job.js +++ b/server/scheduling/scheduling-job.js @@ -49,7 +49,7 @@ exports.job = async (req, res) => { if (bucketId) { load.productionTotal[bucketId].count = load.productionTotal[bucketId].count + 1; } else { - console.log("Uh oh, this job doesn't fit in a bucket!", item); + // console.log("Uh oh, this job doesn't fit in a bucket!", item); } }); @@ -254,7 +254,7 @@ const CalculateLoad = (currentLoad, buckets, jobsIn, jobsOut) => { if (bucketId) { newLoad[bucketId].count = newLoad[bucketId].count + 1; } else { - console.log("[Util Arr Job]Uh oh, this job doesn't fit in a bucket!", job); + // console.log("[Util Arr Job]Uh oh, this job doesn't fit in a bucket!", job); } }); @@ -263,10 +263,10 @@ const CalculateLoad = (currentLoad, buckets, jobsIn, jobsOut) => { if (bucketId) { newLoad[bucketId].count = newLoad[bucketId].count - 1; if (newLoad[bucketId].count < 0) { - console.log("***ERROR: NEGATIVE LOAD Bucket =>", bucketId, job); + // console.log("***ERROR: NEGATIVE LOAD Bucket =>", bucketId, job); } } else { - console.log("[Util Out Job]Uh oh, this job doesn't fit in a bucket!", job); + // console.log("[Util Out Job]Uh oh, this job doesn't fit in a bucket!", job); } }); diff --git a/server/sms/receive.js b/server/sms/receive.js index ad9291a7f..c34147833 100644 --- a/server/sms/receive.js +++ b/server/sms/receive.js @@ -47,7 +47,7 @@ exports.receive = async (req, res) => { //Found a bodyshop - should always happen. if (response.bodyshops[0].conversations.length === 0) { //No conversation Found, create one. - console.log("[SMS Receive] No conversation found. Creating one."); + //console.log("[SMS Receive] No conversation found. Creating one."); newMessage.conversation = { data: { bodyshopid: response.bodyshops[0].id, @@ -56,7 +56,7 @@ exports.receive = async (req, res) => { }; } else if (response.bodyshops[0].conversations.length === 1) { //Just add it to the conversation - console.log("[SMS Receive] Conversation found. Added ID."); + //console.log("[SMS Receive] Conversation found. Added ID."); newMessage.conversationid = response.bodyshops[0].conversations[0].id; } else { //We should never get here. @@ -123,7 +123,14 @@ exports.receive = async (req, res) => { } } } catch (e1) { - console.log("e1", e1); + logger.log("sms-inbound-error", "ERROR", "api", null, { + msid: req.body.SmsMessageSid, + text: req.body.Body, + image: !!req.body.MediaUrl0, + image_path: generateMediaArray(req.body), + messagingServiceSid: req.body.MessagingServiceSid, + error: e1 + }); res.sendStatus(500).json(e1); } } diff --git a/server/stripe/payment.js b/server/stripe/payment.js index 3f79c78d0..7b7bc1dc8 100644 --- a/server/stripe/payment.js +++ b/server/stripe/payment.js @@ -39,7 +39,7 @@ const processor = async (req, res) => { } }); } catch (error) { - console.log("error", error); + // console.log("error", error); res.status(400).send(error); } }; diff --git a/server/tasks/tasks.js b/server/tasks/tasks.js index d59d834eb..2ae944e60 100644 --- a/server/tasks/tasks.js +++ b/server/tasks/tasks.js @@ -6,10 +6,11 @@ const client = require("../graphql-client/graphql-client").client; const emailer = require("../email/sendemail"); const moment = require("moment-timezone"); const converter = require("json-2-csv"); +const logger = require("../utils/logger"); exports.taskHandler = async (req, res) => { try { - const { bodyshopid, query, variables, text, to, subject, timezone } = req.body; + const { query, variables, text, to, subject, timezone } = req.body; //Check the variables to see if they are an object. Object.keys(variables).forEach((key) => { @@ -32,8 +33,10 @@ exports.taskHandler = async (req, res) => { text, attachments: [{ filename: "query.csv", content: csv }] }) - .catch((err) => { - console.error("Errors sending CSV Email."); + .catch((error) => { + logger.log("Tasks - Error sending CSV EMAIL", "error", req?.user?.email, null, { + error + }); }); return res.status(200).send(csv); diff --git a/server/utils/ioHelpers.js b/server/utils/ioHelpers.js index 2a68925f9..3b3b15adb 100644 --- a/server/utils/ioHelpers.js +++ b/server/utils/ioHelpers.js @@ -1,4 +1,4 @@ -const applyIOHelpers = (app, api, io, logger) => { +const applyIOHelpers = ({ app, api, io, logger }) => { const getBodyshopRoom = (bodyshopID) => `bodyshop-broadcast-room:${bodyshopID}`; const ioHelpersAPI = { @@ -14,4 +14,4 @@ const applyIOHelpers = (app, api, io, logger) => { return ioHelpersAPI; }; -module.exports = applyIOHelpers; +module.exports = { applyIOHelpers }; diff --git a/server/utils/logger.js b/server/utils/logger.js index 357dcba3d..bfb0c0cda 100644 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -1,25 +1,152 @@ -const graylog2 = require("graylog2"); - -const logger = new graylog2.graylog({ - servers: [{ host: "logs.bodyshop.app", port: 12201 }] +// Load environment variables THIS MUST BE AT THE TOP +const path = require("path"); +require("dotenv").config({ + path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); -function log(message, type, user, record, object) { - if (type !== "ioevent") - console.log(message, { - type, - env: process.env.NODE_ENV || "development", - user, - record, - ...object - }); - logger.log(message, message, { - type, - env: process.env.NODE_ENV || "development", - user, - record, - ...object - }); -} +const InstanceManager = require("../utils/instanceMgr").default; +const winston = require("winston"); +const WinstonCloudWatch = require("winston-cloudwatch"); +const { isString, isEmpty } = require("lodash"); +const { networkInterfaces, hostname } = require("node:os"); -module.exports = { log }; +const LOG_LEVELS = { + error: { level: 0, name: "error" }, + warn: { level: 1, name: "warn" }, + info: { level: 2, name: "info" }, + http: { level: 3, name: "http" }, + verbose: { level: 4, name: "verbose" }, + debug: { level: 5, name: "debug" }, + silly: { level: 6, name: "silly" } +}; + +const normalizeLevel = (level) => (level ? level.toLowerCase() : LOG_LEVELS.debug.name); + +const createLogger = () => { + try { + const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); + const logGroupName = isLocal ? "development" : process.env.CLOUDWATCH_LOG_GROUP; + + const winstonCloudwatchTransportDefaults = { + logGroupName: logGroupName, + awsOptions: { + region: InstanceManager({ + imex: "ca-central-1", + rome: "us-east-2" + }) + }, + jsonMessage: true + }; + + if (isLocal) { + winstonCloudwatchTransportDefaults.awsOptions.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + } + + const levelFilter = (levels) => { + return winston.format((info) => { + if (Array.isArray(levels)) { + return levels.includes(info.level) ? info : false; + } else { + return info.level === levels ? info : false; + } + })(); + }; + + const getHostNameOrIP = () => { + // Try to get the hostname first + const hostName = hostname(); + if (hostName) return hostName; + + const interfaces = networkInterfaces(); + for (const name of Object.keys(interfaces)) { + for (const iface of interfaces[name]) { + if (iface.family === "IPv4" && !iface.internal) { + return iface.address; + } + } + } + + return "127.0.0.1"; + }; + const createProductionTransport = (level, logStreamName, filters) => { + return new WinstonCloudWatch({ + level, + logStreamName: logStreamName || level, + format: levelFilter(filters || level), + ...winstonCloudwatchTransportDefaults + }); + }; + + const internalHostname = process.env.HOSTNAME || getHostNameOrIP(); + + const getDevelopmentTransports = () => [ + new winston.transports.Console({ + level: "silly", + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp(), + winston.format.printf(({ level, message, timestamp, user, record, meta }) => { + const hostnameColor = `\x1b[34m${internalHostname}\x1b[0m`; // Blue + const timestampColor = `\x1b[36m${timestamp}\x1b[0m`; // Cyan + const labelColor = "\x1b[33m"; // Yellow + const separatorColor = "\x1b[35m|\x1b[0m"; // Magenta for separators + + return `${timestampColor} [${hostnameColor}] [${level}]: ${message} ${ + user ? `${separatorColor} ${labelColor}user:\x1b[0m ${JSON.stringify(user)}` : "" + } ${record ? `${separatorColor} ${labelColor}record:\x1b[0m ${JSON.stringify(record)}` : ""}${ + meta + ? `\n${separatorColor} ${labelColor}meta:\x1b[0m ${JSON.stringify(meta, null, 2)} ${separatorColor}` + : "" + }`; + }) + ) + }) + ]; + + const getProductionTransports = () => [ + createProductionTransport("error"), + createProductionTransport("warn"), + createProductionTransport("info"), + createProductionTransport("silly", "debug", ["http", "verbose", "debug", "silly"]) + ]; + + const winstonLogger = winston.createLogger({ + format: winston.format.json(), + transports: + process.env.NODE_ENV === "production" + ? getProductionTransports() + : [...getDevelopmentTransports(), ...getProductionTransports()] + }); + + if (isLocal) { + winstonLogger.debug( + `CloudWatch set to LocalStack end point: ${winstonCloudwatchTransportDefaults.awsOptions.endpoint}` + ); + } + + const log = (message, type, user, record, meta) => { + winstonLogger.log({ + level: normalizeLevel(type), + message, + user, + record, + hostname: internalHostname, + meta + }); + }; + + return { + log, + logger: winstonLogger + }; + } catch (e) { + console.error("Error setting up enhanced Logger, defaulting to console.: " + e?.message || ""); + return { + log: console.log, + logger: console.log, + LOG_LEVELS + }; + } +}; + +module.exports = createLogger(); diff --git a/server/utils/redisHelpers.js b/server/utils/redisHelpers.js index 99801000b..54d68773d 100644 --- a/server/utils/redisHelpers.js +++ b/server/utils/redisHelpers.js @@ -1,14 +1,14 @@ -const logger = require("./logger"); /** * Apply Redis helper functions * @param pubClient * @param app + * @param logger */ -const applyRedisHelpers = (pubClient, app, logger) => { +const applyRedisHelpers = ({ pubClient, app, logger }) => { // Store session data in Redis const setSessionData = async (socketId, key, value) => { try { - await pubClient.hSet(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient + await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient } catch (error) { logger.log(`Error Setting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } @@ -17,7 +17,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Retrieve session data from Redis const getSessionData = async (socketId, key) => { try { - const data = await pubClient.hGet(`socket:${socketId}`, key); + const data = await pubClient.hget(`socket:${socketId}`, key); return data ? JSON.parse(data) : null; } catch (error) { logger.log(`Error Getting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); @@ -38,7 +38,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { try { // keyValues is expected to be an object { key1: value1, key2: value2, ... } const entries = Object.entries(keyValues).map(([key, value]) => [key, JSON.stringify(value)]); - await pubClient.hSet(`socket:${socketId}`, ...entries.flat()); + await pubClient.hset(`socket:${socketId}`, ...entries.flat()); } catch (error) { logger.log(`Error Setting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } @@ -47,7 +47,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Retrieve multiple session data from Redis const getMultipleSessionData = async (socketId, keys) => { try { - const data = await pubClient.hmGet(`socket:${socketId}`, keys); + const data = await pubClient.hmget(`socket:${socketId}`, keys); // Redis returns an object with null values for missing keys, so we parse the non-null ones return Object.fromEntries(keys.map((key, index) => [key, data[index] ? JSON.parse(data[index]) : null])); } catch (error) { @@ -60,7 +60,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Use Redis multi/pipeline to batch the commands const multi = pubClient.multi(); keyValueArray.forEach(([key, value]) => { - multi.hSet(`socket:${socketId}`, key, JSON.stringify(value)); + multi.hset(`socket:${socketId}`, key, JSON.stringify(value)); }); await multi.exec(); // Execute all queued commands } catch (error) { @@ -71,7 +71,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Helper function to add an item to the end of the Redis list const addItemToEndOfList = async (socketId, key, newItem) => { try { - await pubClient.rPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); + await pubClient.rpush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { logger.log(`Error adding item to the end of the list for socket ${socketId}: ${error}`, "ERROR", "redis"); } @@ -80,7 +80,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Helper function to add an item to the beginning of the Redis list const addItemToBeginningOfList = async (socketId, key, newItem) => { try { - await pubClient.lPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); + await pubClient.lpush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { logger.log(`Error adding item to the beginning of the list for socket ${socketId}: ${error}`, "ERROR", "redis"); } @@ -98,7 +98,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { // Add methods to manage room users const addUserToRoom = async (room, user) => { try { - await pubClient.sAdd(room, JSON.stringify(user)); + await pubClient.sadd(room, JSON.stringify(user)); } catch (error) { logger.log(`Error adding user to room ${room}: ${error}`, "ERROR", "redis"); } @@ -106,7 +106,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { const removeUserFromRoom = async (room, user) => { try { - await pubClient.sRem(room, JSON.stringify(user)); + await pubClient.srem(room, JSON.stringify(user)); } catch (error) { logger.log(`Error removing user to room ${room}: ${error}`, "ERROR", "redis"); } @@ -114,7 +114,7 @@ const applyRedisHelpers = (pubClient, app, logger) => { const getUsersInRoom = async (room) => { try { - const users = await pubClient.sMembers(room); + const users = await pubClient.smembers(room); return users.map((user) => JSON.parse(user)); } catch (error) { logger.log(`Error getting users in room ${room}: ${error}`, "ERROR", "redis"); @@ -143,70 +143,87 @@ const applyRedisHelpers = (pubClient, app, logger) => { next(); }); - // // Demo to show how all the helper functions work + // Demo to show how all the helper functions work // const demoSessionData = async () => { // const socketId = "testSocketId"; // - // // Store session data using setSessionData - // await exports.setSessionData(socketId, "field1", "Hello, Redis!"); - // - // // Retrieve session data using getSessionData - // const field1Value = await exports.getSessionData(socketId, "field1"); + // // 1. Test setSessionData and getSessionData + // await setSessionData(socketId, "field1", "Hello, Redis!"); + // const field1Value = await getSessionData(socketId, "field1"); // console.log("Retrieved single field value:", field1Value); // - // // Store multiple session data using setMultipleSessionData - // await exports.setMultipleSessionData(socketId, { field2: "Second Value", field3: "Third Value" }); - // - // // Retrieve multiple session data using getMultipleSessionData - // const multipleFields = await exports.getMultipleSessionData(socketId, ["field2", "field3"]); + // // 2. Test setMultipleSessionData and getMultipleSessionData + // await setMultipleSessionData(socketId, { field2: "Second Value", field3: "Third Value" }); + // const multipleFields = await getMultipleSessionData(socketId, ["field2", "field3"]); // console.log("Retrieved multiple field values:", multipleFields); // - // // Store multiple session data using setMultipleFromArraySessionData - // await exports.setMultipleFromArraySessionData(socketId, [ + // // 3. Test setMultipleFromArraySessionData + // await setMultipleFromArraySessionData(socketId, [ // ["field4", "Fourth Value"], // ["field5", "Fifth Value"] // ]); // // // Retrieve and log all fields - // const allFields = await exports.getMultipleSessionData(socketId, [ - // "field1", - // "field2", - // "field3", - // "field4", - // "field5" - // ]); + // const allFields = await getMultipleSessionData(socketId, ["field1", "field2", "field3", "field4", "field5"]); // console.log("Retrieved all field values:", allFields); // + // // 4. Test list functions // // Add item to the end of a Redis list - // await exports.addItemToEndOfList(socketId, "logEvents", { event: "Log Event 1", timestamp: new Date() }); - // await exports.addItemToEndOfList(socketId, "logEvents", { event: "Log Event 2", timestamp: new Date() }); + // await addItemToEndOfList(socketId, "logEvents", { event: "Log Event 1", timestamp: new Date() }); + // await addItemToEndOfList(socketId, "logEvents", { event: "Log Event 2", timestamp: new Date() }); // // // Add item to the beginning of a Redis list - // await exports.addItemToBeginningOfList(socketId, "logEvents", { event: "First Log Event", timestamp: new Date() }); + // await addItemToBeginningOfList(socketId, "logEvents", { event: "First Log Event", timestamp: new Date() }); // - // // Retrieve the entire list (using lRange) - // const logEvents = await pubClient.lRange(`socket:${socketId}:logEvents`, 0, -1); - // console.log("Log Events List:", logEvents.map(JSON.parse)); + // // Retrieve the entire list + // const logEventsData = await pubClient.lrange(`socket:${socketId}:logEvents`, 0, -1); + // const logEvents = logEventsData.map((item) => JSON.parse(item)); + // console.log("Log Events List:", logEvents); // - // // **Add the new code below to test clearList** - // - // // Clear the list using clearList - // await exports.clearList(socketId, "logEvents"); + // // 5. Test clearList + // await clearList(socketId, "logEvents"); // console.log("Log Events List cleared."); // // // Retrieve the list after clearing to confirm it's empty - // const logEventsAfterClear = await pubClient.lRange(`socket:${socketId}:logEvents`, 0, -1); + // const logEventsAfterClear = await pubClient.lrange(`socket:${socketId}:logEvents`, 0, -1); // console.log("Log Events List after clearing:", logEventsAfterClear); // Should be an empty array // - // // Clear session data - // await exports.clearSessionData(socketId); + // // 6. Test clearSessionData + // await clearSessionData(socketId); // console.log("Session data cleared."); - // }; // + // // 7. Test room functions + // const roomName = "testRoom"; + // const user1 = { id: 1, name: "Alice" }; + // const user2 = { id: 2, name: "Bob" }; + // + // // Add users to room + // await addUserToRoom(roomName, user1); + // await addUserToRoom(roomName, user2); + // + // // Get users in room + // const usersInRoom = await getUsersInRoom(roomName); + // console.log(`Users in room ${roomName}:`, usersInRoom); + // + // // Remove a user from room + // await removeUserFromRoom(roomName, user1); + // + // // Get users in room after removal + // const usersInRoomAfterRemoval = await getUsersInRoom(roomName); + // console.log(`Users in room ${roomName} after removal:`, usersInRoomAfterRemoval); + // + // // Clean up: remove remaining users from room + // await removeUserFromRoom(roomName, user2); + // + // // Verify room is empty + // const usersInRoomAfterCleanup = await getUsersInRoom(roomName); + // console.log(`Users in room ${roomName} after cleanup:`, usersInRoomAfterCleanup); // Should be empty + // }; // if (process.env.NODE_ENV === "development") { // demoSessionData(); // } - // "th1s1sr3d1s" (BCrypt) + return api; }; -module.exports = applyRedisHelpers; + +module.exports = { applyRedisHelpers }; diff --git a/server/web-sockets/redisSocketEvents.js b/server/web-sockets/redisSocketEvents.js index 5cda88569..7ae8cd8c4 100644 --- a/server/web-sockets/redisSocketEvents.js +++ b/server/web-sockets/redisSocketEvents.js @@ -1,102 +1,17 @@ -const path = require("path"); -require("dotenv").config({ - path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) -}); -const logger = require("../utils/logger"); const { admin } = require("../firebase/firebase-handler"); -// Logging helper functions -function createLogEvent(socket, level, message) { - console.log(`[IOREDIS LOG EVENT] - ${socket.user.email} - ${socket.id} - ${message}`); - logger.log("ioredis-log-event", level, socket.user.email, null, { wsmessage: message }); -} - -const registerUpdateEvents = (socket) => { - socket.on("update-token", async (newToken) => { - try { - socket.user = await admin.auth().verifyIdToken(newToken); - createLogEvent(socket, "INFO", "Token updated successfully"); - socket.emit("token-updated", { success: true }); - } catch (error) { - createLogEvent(socket, "ERROR", `Token update failed: ${error.message}`); - socket.emit("token-updated", { success: false, error: error.message }); - // Optionally disconnect the socket if token verification fails - socket.disconnect(); - } - }); -}; - -const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRoom }, { getBodyshopRoom }) => { - // Room management and broadcasting events - function registerRoomAndBroadcastEvents(socket) { - socket.on("join-bodyshop-room", async (bodyshopUUID) => { - try { - const room = getBodyshopRoom(bodyshopUUID); - socket.join(room); - await addUserToRoom(room, { uid: socket.user.uid, email: socket.user.email, socket: socket.id }); - createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${room}`); - - // Notify all users in the room about the updated user list - const usersInRoom = await getUsersInRoom(room); - io.to(room).emit("room-users-updated", usersInRoom); - } catch (error) { - createLogEvent(socket, "ERROR", `Error joining room: ${error}`); - } - }); - - socket.on("leave-bodyshop-room", async (bodyshopUUID) => { - try { - const room = getBodyshopRoom(bodyshopUUID); - socket.leave(room); - createLogEvent(socket, "DEBUG", `Client left bodyshop room: ${room}`); - } catch (error) { - createLogEvent(socket, "ERROR", `Error joining room: ${error}`); - } - }); - - socket.on("get-room-users", async (bodyshopUUID, callback) => { - try { - const usersInRoom = await getUsersInRoom(getBodyshopRoom(bodyshopUUID)); - callback(usersInRoom); - } catch (error) { - createLogEvent(socket, "ERROR", `Error getting room: ${error}`); - } - }); - - socket.on("broadcast-to-bodyshop", async (bodyshopUUID, message) => { - try { - const room = getBodyshopRoom(bodyshopUUID); - io.to(room).emit("bodyshop-message", message); - createLogEvent(socket, "INFO", `Broadcast message to bodyshop ${room}`); - } catch (error) { - createLogEvent(socket, "ERROR", `Error getting room: ${error}`); - } - }); - - socket.on("disconnect", async () => { - try { - createLogEvent(socket, "DEBUG", `User disconnected.`); - - // Get all rooms the socket is part of - const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id); - for (const room of rooms) { - await removeUserFromRoom(room, { uid: socket.user.uid, email: socket.user.email, socket: socket.id }); - } - } catch (error) { - createLogEvent(socket, "ERROR", `Error getting room: ${error}`); - } - }); - } - - // Register all socket events for a given socket connection - function registerSocketEvents(socket) { - createLogEvent(socket, "DEBUG", `Registering RedisIO Socket Events.`); - - // Register room and broadcasting events - registerRoomAndBroadcastEvents(socket); - registerUpdateEvents(socket); - } +const redisSocketEvents = ({ + io, + redisHelpers: { setSessionData, clearSessionData }, // Note: Used if we persist user to Redis + ioHelpers: { getBodyshopRoom }, + logger +}) => { + // Logging helper functions + const createLogEvent = (socket, level, message) => { + logger.log("ioredis-log-event", level, socket?.user?.email, null, { wsmessage: message }); + }; + // Socket Auth Middleware const authMiddleware = (socket, next) => { try { if (socket.handshake.auth.token) { @@ -105,6 +20,9 @@ const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRo .verifyIdToken(socket.handshake.auth.token) .then((user) => { socket.user = user; + // Note: if we ever want to capture user data across sockets + // Uncomment the following line and then remove the next() to a second then() + // return setSessionData(socket.id, "user", user); next(); }) .catch((error) => { @@ -114,7 +32,6 @@ const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRo next(new Error("Authentication error - no authorization token.")); } } catch (error) { - console.log("Uncaught connection error:::", error); logger.log("websocket-connection-error", "error", null, null, { ...error }); @@ -122,7 +39,104 @@ const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRo } }; - // Socket.IO Middleware and Connection + // Register Socket Events + const registerSocketEvents = (socket) => { + // Uncomment for further testing + // createLogEvent(socket, "debug", `Registering RedisIO Socket Events.`); + + // Token Update Events + const registerUpdateEvents = (socket) => { + const updateToken = async (newToken) => { + try { + // noinspection UnnecessaryLocalVariableJS + const user = await admin.auth().verifyIdToken(newToken, true); + socket.user = user; + + // If We ever want to persist user Data across workers + // await setSessionData(socket.id, "user", user); + + // Uncomment for further testing + // createLogEvent(socket, "debug", "Token updated successfully"); + + socket.emit("token-updated", { success: true }); + } catch (error) { + if (error.code === "auth/id-token-expired") { + createLogEvent(socket, "warn", "Stale token received, waiting for new token"); + socket.emit("token-updated", { + success: false, + error: "Stale token." + }); + } else { + createLogEvent(socket, "error", `Token update failed: ${error.message}`); + socket.emit("token-updated", { success: false, error: error.message }); + // For any other errors, optionally disconnect the socket + socket.disconnect(); + } + } + }; + socket.on("update-token", updateToken); + }; + // Room Broadcast Events + const registerRoomAndBroadcastEvents = (socket) => { + const joinBodyshopRoom = (bodyshopUUID) => { + try { + const room = getBodyshopRoom(bodyshopUUID); + socket.join(room); + // createLogEvent(socket, "debug", `Client joined bodyshop room: ${room}`); + } catch (error) { + createLogEvent(socket, "error", `Error joining room: ${error}`); + } + }; + + const leaveBodyshopRoom = (bodyshopUUID) => { + try { + const room = getBodyshopRoom(bodyshopUUID); + socket.leave(room); + createLogEvent(socket, "debug", `Client left bodyshop room: ${room}`); + } catch (error) { + createLogEvent(socket, "error", `Error joining room: ${error}`); + } + }; + + const broadcastToBodyshopRoom = (bodyshopUUID, message) => { + try { + const room = getBodyshopRoom(bodyshopUUID); + io.to(room).emit("bodyshop-message", message); + // We do not need this as these can be debugged live + // createLogEvent(socket, "debug", `Broadcast message to bodyshop ${room}`); + } catch (error) { + createLogEvent(socket, "error", `Error getting room: ${error}`); + } + }; + + socket.on("join-bodyshop-room", joinBodyshopRoom); + socket.on("leave-bodyshop-room", leaveBodyshopRoom); + socket.on("broadcast-to-bodyshop", broadcastToBodyshopRoom); + }; + // Disconnect Events + const registerDisconnectEvents = (socket) => { + const disconnect = () => { + // Uncomment for further testing + // createLogEvent(socket, "debug", `User disconnected.`); + + const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id); + for (const room of rooms) { + socket.leave(room); + } + // If we ever want to persist the user across workers + // clearSessionData(socket.id); + }; + + socket.on("disconnect", disconnect); + }; + + // Call Handlers + registerRoomAndBroadcastEvents(socket); + registerUpdateEvents(socket); + registerDisconnectEvents(socket); + }; + + // Associate Middleware and Handlers io.use(authMiddleware); io.on("connection", registerSocketEvents); }; diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js index c19f77dcb..c5e5012c8 100644 --- a/server/web-sockets/web-socket.js +++ b/server/web-sockets/web-socket.js @@ -31,7 +31,7 @@ io.use(function (socket, next) { next(new Error("Authentication error - no authorization token.")); } } catch (error) { - console.log("Uncaught connection error:::", error); + //console.log("Uncaught connection error:::", error); logger.log("websocket-connection-error", "error", null, null, { token: socket.handshake.auth.token, ...error @@ -41,7 +41,7 @@ io.use(function (socket, next) { }); io.on("connection", (socket) => { - socket.log_level = "TRACE"; + socket.log_level = "DEBUG"; createLogEvent(socket, "DEBUG", `Connected and Authenticated.`); socket.on("set-log-level", (level) => { @@ -75,7 +75,7 @@ io.on("connection", (socket) => { socket.on("cdk-calculate-allocations", async (jobid, callback) => { const allocations = await CdkCalculateAllocations(socket, jobid); createLogEvent(socket, "DEBUG", `Allocations calculated.`); - createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); + createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); callback(allocations); }); @@ -85,7 +85,7 @@ io.on("connection", (socket) => { socket.on("pbs-calculate-allocations", async (jobid, callback) => { const allocations = await CdkCalculateAllocations(socket, jobid); createLogEvent(socket, "DEBUG", `Allocations calculated.`); - createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); + createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); callback(allocations); }); @@ -103,7 +103,7 @@ io.on("connection", (socket) => { socket.on("pbs-calculate-allocations-ap", async (billids, callback) => { const allocations = await PbsCalculateAllocationsAp(socket, billids); createLogEvent(socket, "DEBUG", `AP Allocations calculated.`); - createLogEvent(socket, "TRACE", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); + createLogEvent(socket, "DEBUG", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`); socket.apAllocations = allocations; callback(allocations); }); @@ -122,7 +122,7 @@ io.on("connection", (socket) => { function createLogEvent(socket, level, message) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) { - console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`); + // console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`); socket.emit("log-event", { timestamp: new Date(), level, @@ -148,7 +148,7 @@ function createLogEvent(socket, level, message) { function createJsonEvent(socket, level, message, json) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) { - console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`); + //console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`); socket.emit("log-event", { timestamp: new Date(), level, @@ -173,17 +173,17 @@ function createJsonEvent(socket, level, message, json) { } function createXmlEvent(socket, xml, message, isError = false) { - if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) { + if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("SILLY")) { socket.emit("log-event", { timestamp: new Date(), - level: isError ? "ERROR" : "TRACE", + level: isError ? "ERROR" : "SILLY", message: `${message}: ${xml}` }); } logger.log( isError ? "ws-log-event-xml-error" : "ws-log-event-xml", - isError ? "ERROR" : "TRACE", + isError ? "ERROR" : "SILLY", socket.user.email, socket.recordid, { @@ -195,7 +195,7 @@ function createXmlEvent(socket, xml, message, isError = false) { if (socket.logEvents && isArray(socket.logEvents)) { socket.logEvents.push({ timestamp: new Date(), - level: isError ? "ERROR" : "TRACE", + level: isError ? "ERROR" : "SILLY", message, xml }); @@ -206,13 +206,13 @@ function LogLevelHierarchy(level) { switch (level) { case "XML": return 5; - case "TRACE": + case "SILLY": return 5; case "DEBUG": return 4; case "INFO": return 3; - case "WARNING": + case "WARN": return 2; case "ERROR": return 1;