diff --git a/package-lock.json b/package-lock.json index a49ed6f09da391b4b2cd6382733cd3d2aa15e902..91f51d9546dc4352f1b478b92a3d0d96e437ce2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9186,9 +9186,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz", - "integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.6.tgz", + "integrity": "sha512-JRTlR8Bw+4BcmVTICa7tJsxqphAktakiLsAmibVLAWbu1lauFddY/tXeM6sAyl1cgkPuXtpnUgaCPhTdz1Qapg==", "dev": true, "license": "MIT", "dependencies": { @@ -9209,8 +9209,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.0.5", - "vitest": "3.0.5" + "@vitest/browser": "3.0.6", + "vitest": "3.0.6" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -9284,13 +9284,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", - "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.6.tgz", + "integrity": "sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", + "@vitest/spy": "3.0.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -9311,9 +9311,9 @@ } }, "node_modules/@vitest/mocker/node_modules/@vitest/spy": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", - "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.6.tgz", + "integrity": "sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -9357,23 +9357,23 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", - "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.6.tgz", + "integrity": "sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.0.5", - "pathe": "^2.0.2" + "@vitest/utils": "3.0.6", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner/node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.6.tgz", + "integrity": "sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==", "dev": true, "license": "MIT", "dependencies": { @@ -9384,14 +9384,14 @@ } }, "node_modules/@vitest/runner/node_modules/@vitest/utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", - "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.6.tgz", + "integrity": "sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", - "loupe": "^3.1.2", + "@vitest/pretty-format": "3.0.6", + "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, "funding": { @@ -9399,24 +9399,24 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", - "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.6.tgz", + "integrity": "sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", + "@vitest/pretty-format": "3.0.6", "magic-string": "^0.30.17", - "pathe": "^2.0.2" + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.6.tgz", + "integrity": "sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==", "dev": true, "license": "MIT", "dependencies": { @@ -10278,6 +10278,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, "license": "MIT" }, "node_modules/autoprefixer": { @@ -10345,9 +10346,10 @@ } }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -10802,15 +10804,6 @@ "node": ">=12.0.0" } }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -11848,6 +11841,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -12785,6 +12779,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -13437,6 +13432,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -14847,6 +14843,7 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, "funding": [ { "type": "individual", @@ -14900,6 +14897,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -15643,6 +15641,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -19172,15 +19171,6 @@ "node": ">=6" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -20634,21 +20624,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-mailjet": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/node-mailjet/-/node-mailjet-6.0.6.tgz", - "integrity": "sha512-cr8ciqtHuxyFd3+3bpDy+oKuNzctZfRQZtwRjurVAzE+DZLTfyxjgD+GTqQ1kr0ClAjDjSh3ERlZvd5MV0fKHg==", - "license": "MIT", - "dependencies": { - "axios": "1.7.4", - "json-bigint": "^1.0.0", - "url-join": "^4.0.0" - }, - "engines": { - "node": ">= 12.0.0", - "npm": ">= 6.9.0" - } - }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -25972,9 +25947,9 @@ "license": "ISC" }, "node_modules/preact": { - "version": "10.26.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.1.tgz", - "integrity": "sha512-K5aMG0NdGHZ8yV1GfGtGA4JwnWxe/HIDzyr9svdo2DeokLUJ/+W8MpeuPrfOytu5rHHgYQrvGxUoW83sapJZnw==", + "version": "10.26.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.2.tgz", + "integrity": "sha512-0gNmv4qpS9HaN3+40CLBAnKe0ZfyE4ZWo5xKlC1rVrr0ckkEvJvAQqKaHANdFKsGstoxrY4AItZ7kZSGVoVjgg==", "dev": true, "license": "MIT", "funding": { @@ -26204,6 +26179,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, "license": "MIT" }, "node_modules/prr": { @@ -29748,6 +29724,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, "license": "MIT" }, "node_modules/url-parse": { @@ -29918,16 +29895,16 @@ } }, "node_modules/vite-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", - "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.6.tgz", + "integrity": "sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { @@ -29941,31 +29918,31 @@ } }, "node_modules/vitest": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", - "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.6.tgz", + "integrity": "sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.0.5", - "@vitest/mocker": "3.0.5", - "@vitest/pretty-format": "^3.0.5", - "@vitest/runner": "3.0.5", - "@vitest/snapshot": "3.0.5", - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@vitest/expect": "3.0.6", + "@vitest/mocker": "3.0.6", + "@vitest/pretty-format": "^3.0.6", + "@vitest/runner": "3.0.6", + "@vitest/snapshot": "3.0.6", + "@vitest/spy": "3.0.6", + "@vitest/utils": "3.0.6", + "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.5", + "vite-node": "3.0.6", "why-is-node-running": "^2.3.0" }, "bin": { @@ -29981,8 +29958,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.5", - "@vitest/ui": "3.0.5", + "@vitest/browser": "3.0.6", + "@vitest/ui": "3.0.6", "happy-dom": "*", "jsdom": "*" }, @@ -30011,15 +29988,15 @@ } }, "node_modules/vitest/node_modules/@vitest/expect": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", - "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.6.tgz", + "integrity": "sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@vitest/spy": "3.0.6", + "@vitest/utils": "3.0.6", + "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, "funding": { @@ -30027,9 +30004,9 @@ } }, "node_modules/vitest/node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.6.tgz", + "integrity": "sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==", "dev": true, "license": "MIT", "dependencies": { @@ -30040,9 +30017,9 @@ } }, "node_modules/vitest/node_modules/@vitest/spy": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", - "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.6.tgz", + "integrity": "sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -30053,14 +30030,14 @@ } }, "node_modules/vitest/node_modules/@vitest/utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", - "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.6.tgz", + "integrity": "sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.5", - "loupe": "^3.1.2", + "@vitest/pretty-format": "3.0.6", + "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, "funding": { @@ -31083,7 +31060,6 @@ "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "knex": "^3.1.0", - "node-mailjet": "^6.0.6", "objection": "^3.1.5", "pg": "^8.13.3", "pg-large-object": "^2.0.0", diff --git a/packages/api/package.json b/packages/api/package.json index d6f59167b538d2772140a5112aac3e20a467c01f..5ddae96cfecb30a44bc548c46a88f057fede40f2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -84,7 +84,6 @@ "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "knex": "^3.1.0", - "node-mailjet": "^6.0.6", "objection": "^3.1.5", "pg": "^8.13.3", "pg-large-object": "^2.0.0", diff --git a/packages/api/src/business/entreprises-guyane.ts b/packages/api/src/business/entreprises-guyane.ts index d9f0f9f6f09cc387e7ab62884dd7dff62ec56b45..321f889f50992dcb4850b648b1195a39c6e95017 100644 --- a/packages/api/src/business/entreprises-guyane.ts +++ b/packages/api/src/business/entreprises-guyane.ts @@ -2,8 +2,8 @@ import { toDepartementId, Departements, CodePostal } from 'camino-common/src/sta import { PAYS_IDS } from 'camino-common/src/static/pays' import { Regions } from 'camino-common/src/static/region' import { knex } from '../knex' -import { exploitantsGuyaneSubscriberUpdate } from '../tools/api-mailjet/guyane' import { isNullOrUndefined } from 'camino-common/src/typescript-tools' +import { mailjetAddContactsToGuyaneList } from '../tools/api-mailjet' interface Result { id: string @@ -47,7 +47,7 @@ export const subscribeUsersToGuyaneExploitants = async (): Promise<ResultAggrega return acc }, {}) const users = Object.values(reduced).toSorted((a, b) => a.email.localeCompare(b.email)) - await exploitantsGuyaneSubscriberUpdate( + await mailjetAddContactsToGuyaneList( users.map(user => ({ email: user.email, nom: `${user.nomUtilisateur} ${user.prenom}`, diff --git a/packages/api/src/scripts/daily.ts b/packages/api/src/scripts/daily.ts index 655d4a5990863fb58513ecb046dce4f8df1ee2c3..ab1e8259429b2cb172397ec75f530785ba2d8518 100644 --- a/packages/api/src/scripts/daily.ts +++ b/packages/api/src/scripts/daily.ts @@ -117,7 +117,7 @@ const tasks = async () => { // TODO 2024-12-05 enlever le daily par email si il s'est bien envoyé via tchap await mailjetSend([config().ADMIN_EMAIL], { Subject: `[Camino][${config().ENV}] Résultats du daily`, - 'Text-part': emailBody, + TextPart: emailBody, }) } console.info('Tâches quotidiennes : terminé') diff --git a/packages/api/src/scripts/monthly.ts b/packages/api/src/scripts/monthly.ts index 8e8fba9cbe07d15e5df7757dcb73f37c68a14997..a85763910fb59e7071da0ed1f5336227bfe820ff 100644 --- a/packages/api/src/scripts/monthly.ts +++ b/packages/api/src/scripts/monthly.ts @@ -44,7 +44,7 @@ const tasks = async () => { const emailBody = `Résultats de ${config().ENV} \n${readFileSync(logFile).toString()}` await mailjetSend([config().ADMIN_EMAIL], { Subject: `[Camino][${config().ENV}] Résultats du monthly`, - 'Text-part': emailBody, + TextPart: emailBody, }) } console.info('Tâches mensuelles : terminé') diff --git a/packages/api/src/tools/api-mailjet/emails.ts b/packages/api/src/tools/api-mailjet/emails.ts index 36f37b3ebf4b4f7c0bbcf48836906f8912beaa24..a292a0a3e78d1c2873c6ad10529cdfa51fdafe82 100644 --- a/packages/api/src/tools/api-mailjet/emails.ts +++ b/packages/api/src/tools/api-mailjet/emails.ts @@ -1,16 +1,11 @@ import { convert } from 'html-to-text' -import { mailjet } from './index' +import { CaminoMailMessage, MailjetPostMessageRecipient, mailJetSendMail } from './index' import { EmailTemplateId } from './types' import { emailCheck } from '../email-check' import { config } from '../../config/index' -import { isNotNullNorUndefined, onlyUnique } from 'camino-common/src/typescript-tools' +import { isNotNullNorUndefined, OmitDistributive, onlyUnique } from 'camino-common/src/typescript-tools' -const from = { - email: config().API_MAILJET_EMAIL, - name: 'Camino - le cadastre minier', -} - -export const mailjetSend = async (emails: string[], options: Record<string, any>): Promise<void> => { +export const mailjetSend = async (emails: readonly string[], message: OmitDistributive<CaminoMailMessage, 'To'>): Promise<void> => { try { if (!Array.isArray(emails)) { throw new Error(`un tableau d'emails est attendu ${emails}`) @@ -26,27 +21,15 @@ export const mailjetSend = async (emails: string[], options: Record<string, any> // si on est pas sur le serveur de prod // l'adresse email du destinataire est remplacée if (config().NODE_ENV !== 'production' || config().ENV !== 'prod') { - emails = [config().ADMIN_EMAIL!] + emails = [config().ADMIN_EMAIL] } - const res = (await mailjet.post('send', { version: 'v3' }).request({ - SandboxMode: 'true', - Messages: [ - { - FromEmail: from.email, - FromName: from.name, - Recipients: emails.map(Email => ({ Email })), - ...options, - Headers: { 'Reply-To': config().API_MAILJET_REPLY_TO_EMAIL }, - }, - ], - })) as { - body: { - Sent: { Email: string; MessageID: string; MessageUUID: string }[] - } + const sendTo: MailjetPostMessageRecipient[] = emails.map(Email => ({ Email, Name: Email })) + const fullMessage: CaminoMailMessage = { + ...message, + To: sendTo, } - - console.info(`Messages envoyés: ${emails.join(', ')}, MessageIDs: ${res.body.Sent.map(m => m.MessageID).join(', ')}`) + await mailJetSendMail(fullMessage) } catch (e: any) { console.error('erreur: emailsSend', e) throw new Error(e) @@ -60,8 +43,8 @@ export const emailsSend = async (emails: string[], subject: string, html: string return mailjetSend(emails, { Subject: `[Camino] ${subject}`, - 'Html-part': html, - 'Text-part': convert(html, { + HTMLPart: html, + TextPart: convert(html, { wordwrap: 130, }), }) @@ -69,8 +52,8 @@ export const emailsSend = async (emails: string[], subject: string, html: string export const emailsWithTemplateSend = async (emails: string[], templateId: EmailTemplateId, params: Record<string, string>): Promise<void> => { return mailjetSend(emails, { - 'Mj-TemplateID': templateId, - 'Mj-TemplateLanguage': true, - Vars: params, + TemplateID: templateId, + TemplateLanguage: true, + Variables: params, }) } diff --git a/packages/api/src/tools/api-mailjet/guyane.ts b/packages/api/src/tools/api-mailjet/guyane.ts deleted file mode 100644 index 8d00207683c8dea680bdac97662d8a7c87a23f53..0000000000000000000000000000000000000000 --- a/packages/api/src/tools/api-mailjet/guyane.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { config } from '../../config/index' -import { mailjet } from './index' - -const exploitantsGuyaneContactListId = config().API_MAILJET_EXPLOITANTS_GUYANE_LIST_ID - -// TODO 2022-09-27 nettoyer la liste des mails déjà sur la liste mailjet. -export const exploitantsGuyaneSubscriberUpdate = async (users: { email: string; nom: string }[]): Promise<void> => { - console.info(`ajout de ${users.length} utilisateurs à la liste ${exploitantsGuyaneContactListId}`) - const contacts = users.map(user => ({ - Email: user.email, - Name: user.nom, - IsExcludedFromCampaigns: false, - Properties: 'object', - })) - await mailjet - .post('contact', { version: 'v3' }) - .action('managemanycontacts') - .request({ - Contacts: contacts, - ContactsLists: [{ Action: 'addforce', ListID: exploitantsGuyaneContactListId }], - }) -} diff --git a/packages/api/src/tools/api-mailjet/index.ts b/packages/api/src/tools/api-mailjet/index.ts index cb08d00d53900ae2bd5f54da13a6a434aa43c767..ace3034562d9991fb4c20f0b9737249fa93e3cf3 100644 --- a/packages/api/src/tools/api-mailjet/index.ts +++ b/packages/api/src/tools/api-mailjet/index.ts @@ -1,19 +1,122 @@ -import Mailjet from 'node-mailjet' import { config } from '../../config/index' -import { isNotNullNorUndefined } from 'camino-common/src/typescript-tools' - -const proxy = config().HTTPS_PROXY -export const mailjet = new Mailjet({ - apiKey: config().API_MAILJET_KEY, - apiSecret: config().API_MAILJET_SECRET, - - options: { - proxy: isNotNullNorUndefined(proxy) - ? { - host: proxy.host, - port: proxy.port, - protocol: proxy.protocol, - } - : undefined, - }, -}) +import { fetch } from 'undici' + +const basicCreds = Buffer.from(`${config().API_MAILJET_KEY}:${config().API_MAILJET_SECRET}`).toString('base64') + +export interface MailjetPostMessageRecipient { + Email: string + Name: string +} +interface MailjetPostMessageCommon { + From: MailjetPostMessageRecipient + ReplyTo: Omit<MailjetPostMessageRecipient, 'Name'> + To: MailjetPostMessageRecipient[] +} + +interface MailjetPostBodyMessage { + Subject: string + TextPart: string + HTMLPart?: string +} +interface MailjetPostTemplateMessage { + TemplateID: number + TemplateLanguage: true + Variables: Record<string, string> +} + +export type CaminoMailMessage = { To: MailjetPostMessageRecipient[] } & (MailjetPostBodyMessage | MailjetPostTemplateMessage) + +type MailjetPostMessage = MailjetPostMessageCommon & (MailjetPostBodyMessage | MailjetPostTemplateMessage) +interface MailjetPost { + Messages: MailjetPostMessage[] +} + +interface MailjetResponseMessageTo { + Email: string + MessageUUID: string + MessageID: string + MessageHref: string +} +interface MailjetResponseMessage { + Status: 'success' | string + To: MailjetResponseMessageTo[] +} +interface MailjetSendMailResponse { + Messages: MailjetResponseMessage[] +} + +const From: MailjetPostMessageRecipient = { + Email: config().API_MAILJET_EMAIL, + Name: 'Camino - le cadastre minier', +} + +const ReplyTo: Omit<MailjetPostMessageRecipient, 'Name'> = { + Email: config().API_MAILJET_REPLY_TO_EMAIL, +} + +const exploitantsGuyaneContactListId = config().API_MAILJET_EXPLOITANTS_GUYANE_LIST_ID +interface MailjetListUser { + Email: string + Name: string + IsExcludedFromCampaigns: false + Properties: 'object' +} +interface MailjetManageManyContacts { + Action: 'addnoforce' | 'addforce' + Contacts: MailjetListUser[] +} + +export const mailjetAddContactsToGuyaneList = async (mails: { email: string; nom: string }[]): Promise<void> => { + console.info(`ajout de ${mails.length} utilisateurs à la liste ${exploitantsGuyaneContactListId}`) + const body: MailjetManageManyContacts = { + Action: 'addforce', + Contacts: mails.map(user => ({ + Email: user.email, + Name: user.nom, + IsExcludedFromCampaigns: false, + Properties: 'object', + })), + } + const result = await fetch(`https://api.mailjet.com/v3/REST/contactslist/${exploitantsGuyaneContactListId}/managemanycontacts`, { + method: 'POST', + body: JSON.stringify(body), + headers: { + Authorization: `Basic ${basicCreds}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }) + + if (result.ok) { + console.info(`Liste mise à jour`) + } else { + console.error(`Une erreur est survenue lors de l'envoi des mails ${await result.text()}`) + } +} + +export const mailJetSendMail = async (post: CaminoMailMessage): Promise<void> => { + const body: MailjetPost = { + Messages: [{ ...post, From, ReplyTo }], + } + const result = await fetch('https://api.mailjet.com/v3.1/send', { + method: 'POST', + body: JSON.stringify(body), + headers: { + Authorization: `Basic ${basicCreds}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }) + + if (result.ok) { + const values: MailjetSendMailResponse = (await result.json()) as MailjetSendMailResponse + + if (values.Messages.find(message => message.Status !== 'success')) { + console.warn(`Quelque chose s'est mal passé durant l'envoi des mails, réponse: ${JSON.stringify(values)}`) + } else { + console.info(`Messages envoyés: ${post.To.map(({ Email }) => Email).join(', ')}, MessageIDs: ${values.Messages.flatMap(m => m.To.flatMap(to => to.MessageID)).join(', ')}`) + } + } else { + console.error(`Une erreur est survenue lors de l'envoi des mails ${await result.text()}`) + } +} diff --git a/packages/api/tests/vitestSetup.ts b/packages/api/tests/vitestSetup.ts index 9b8ca009275378b7f4c66e0b70a41bf74d63900f..536cfaa034a68ec9dd0bf232fdc8b025f5165fe2 100644 --- a/packages/api/tests/vitestSetup.ts +++ b/packages/api/tests/vitestSetup.ts @@ -1,13 +1,17 @@ import { Request, Response } from 'express' import { vi } from 'vitest' -const origEmails = await vi.importActual('../src/tools/api-mailjet/emails') vi.mock('../src/tools/api-mailjet/emails', () => ({ __esModule: true, - ...origEmails, emailsSend: vi.fn().mockImplementation(a => a), emailsWithTemplateSend: vi.fn().mockImplementation(a => a), })) +vi.mock('../src/tools/api-mailjet/index', () => ({ + __esModule: true, + mailjetAddContactsToGuyaneList: vi.fn().mockImplementation(a => a), + mailJetSendMail: vi.fn().mockImplementation(a => a), +})) + function assertObject(stuff: unknown): asserts stuff is object { if (typeof stuff !== 'object') { throw new Error(`${stuff} n'est pas un objet`)