From 66fbcc68065cd1cc8a4ec22fb74cb8568b66d879 Mon Sep 17 00:00:00 2001 From: Scott Wilson Date: Thu, 1 May 2025 14:57:24 -0700 Subject: [PATCH] improvemet(email-templates): migrate email templates to react email --- backend/.eslintrc.js | 9 + backend/package-lock.json | 2493 ++++++++++++++++- backend/package.json | 8 +- .../secret-rotation-v2-fns.ts | 2 +- .../ssh/ssh-certificate-authority-service.ts | 4 +- .../src/services/auth/auth-login-service.ts | 6 +- backend/src/services/org/org-service.ts | 12 +- .../services/secret-sync/secret-sync-fns.ts | 2 +- .../services/secret-sync/secret-sync-queue.ts | 2 +- .../secret-v2-bridge/secret-v2-bridge-dal.ts | 2 +- backend/src/services/secret/secret-queue.ts | 2 +- .../emails/AccessApprovalRequestTemplate.tsx | 95 + .../services/smtp/emails/BaseEmailWrapper.tsx | 45 + .../services/smtp/emails/EmailMfaTemplate.tsx | 50 + .../smtp/emails/EmailVerificationTemplate.tsx | 53 + .../emails/ExternalImportFailedTemplate.tsx | 43 + .../emails/ExternalImportStartedTemplate.tsx | 30 + .../ExternalImportSucceededTemplate.tsx | 31 + .../emails/IntegrationSyncFailedTemplate.tsx | 65 + .../smtp/emails/NewDeviceLoginTemplate.tsx | 68 + .../OrgAdminBreakglassAccessTemplate.tsx | 57 + .../OrgAdminProjectGrantAccessTemplate.tsx | 40 + .../emails/OrganizationInvitationTemplate.tsx | 75 + .../smtp/emails/PasswordResetTemplate.tsx | 58 + .../smtp/emails/PasswordSetupTemplate.tsx | 57 + .../emails/PkiExpirationAlertTemplate.tsx | 68 + .../emails/ProjectAccessRequestTemplate.tsx | 68 + .../smtp/emails/ProjectInvitationTemplate.tsx | 50 + .../emails/ScimUserProvisionedTemplate.tsx | 56 + .../SecretApprovalRequestBypassedTemplate.tsx | 72 + ...cretApprovalRequestNeedsReviewTemplate.tsx | 57 + .../emails/SecretLeakIncidentTemplate.tsx | 82 + .../smtp/emails/SecretReminderTemplate.tsx | 45 + .../emails/SecretRequestCompletedTemplate.tsx | 53 + .../emails/SecretRotationFailedTemplate.tsx | 68 + .../smtp/emails/SecretSyncFailedTemplate.tsx | 80 + .../ServiceTokenExpiryNoticeTemplate.tsx | 53 + .../SignupEmailVerificationTemplate.tsx | 53 + .../smtp/emails/UnlockAccountTemplate.tsx | 46 + backend/src/services/smtp/emails/index.ts | 28 + backend/src/services/smtp/smtp-service.ts | 112 - backend/src/services/smtp/smtp-service.tsx | 179 ++ .../accessApprovalRequest.handlebars | 55 - .../accessSecretRequestBypassed.handlebars | 33 - .../smtp/templates/emailMfa.handlebars | 20 - .../templates/emailVerification.handlebars | 17 - .../historicalSecretLeakIncident.handlebars | 21 - .../integrationSyncFailed.handlebars | 33 - .../smtp/templates/newDevice.handlebars | 22 - .../orgAdminBreakglassAccess.handlebars | 20 - .../orgAdminProjectGrantAccess.handlebars | 16 - .../organizationInvitation.handlebars | 18 - .../smtp/templates/passwordReset.handlebars | 16 - .../smtp/templates/passwordSetup.handlebars | 17 - .../templates/pkiExpirationAlert.handlebars | 33 - .../smtp/templates/projectAccess.handlebars | 26 - .../templates/scimUserProvisioned.handlebars | 18 - ...ecretApprovalRequestNeedsReview.handlebars | 24 - .../templates/secretLeakIncident.handlebars | 27 - .../smtp/templates/secretReminder.handlebars | 20 - .../secretRequestCompleted.handlebars | 33 - .../templates/secretRotationFailed.handlebars | 31 - .../templates/secretSyncFailed.handlebars | 39 - .../templates/serviceTokenExpired.handlebars | 19 - .../signupEmailVerification.handlebars | 19 - .../smtp/templates/unlockAccount.handlebars | 18 - .../templates/workspaceInvitation.handlebars | 17 - .../super-admin/super-admin-service.ts | 4 +- backend/tsconfig.json | 3 +- 69 files changed, 4224 insertions(+), 874 deletions(-) create mode 100644 backend/src/services/smtp/emails/AccessApprovalRequestTemplate.tsx create mode 100644 backend/src/services/smtp/emails/BaseEmailWrapper.tsx create mode 100644 backend/src/services/smtp/emails/EmailMfaTemplate.tsx create mode 100644 backend/src/services/smtp/emails/EmailVerificationTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ExternalImportFailedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ExternalImportStartedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ExternalImportSucceededTemplate.tsx create mode 100644 backend/src/services/smtp/emails/IntegrationSyncFailedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx create mode 100644 backend/src/services/smtp/emails/OrgAdminBreakglassAccessTemplate.tsx create mode 100644 backend/src/services/smtp/emails/OrgAdminProjectGrantAccessTemplate.tsx create mode 100644 backend/src/services/smtp/emails/OrganizationInvitationTemplate.tsx create mode 100644 backend/src/services/smtp/emails/PasswordResetTemplate.tsx create mode 100644 backend/src/services/smtp/emails/PasswordSetupTemplate.tsx create mode 100644 backend/src/services/smtp/emails/PkiExpirationAlertTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ProjectAccessRequestTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ProjectInvitationTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ScimUserProvisionedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretApprovalRequestBypassedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretApprovalRequestNeedsReviewTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretLeakIncidentTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretReminderTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretRequestCompletedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretRotationFailedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SecretSyncFailedTemplate.tsx create mode 100644 backend/src/services/smtp/emails/ServiceTokenExpiryNoticeTemplate.tsx create mode 100644 backend/src/services/smtp/emails/SignupEmailVerificationTemplate.tsx create mode 100644 backend/src/services/smtp/emails/UnlockAccountTemplate.tsx create mode 100644 backend/src/services/smtp/emails/index.ts delete mode 100644 backend/src/services/smtp/smtp-service.ts create mode 100644 backend/src/services/smtp/smtp-service.tsx delete mode 100644 backend/src/services/smtp/templates/accessApprovalRequest.handlebars delete mode 100644 backend/src/services/smtp/templates/accessSecretRequestBypassed.handlebars delete mode 100644 backend/src/services/smtp/templates/emailMfa.handlebars delete mode 100644 backend/src/services/smtp/templates/emailVerification.handlebars delete mode 100644 backend/src/services/smtp/templates/historicalSecretLeakIncident.handlebars delete mode 100644 backend/src/services/smtp/templates/integrationSyncFailed.handlebars delete mode 100644 backend/src/services/smtp/templates/newDevice.handlebars delete mode 100644 backend/src/services/smtp/templates/orgAdminBreakglassAccess.handlebars delete mode 100644 backend/src/services/smtp/templates/orgAdminProjectGrantAccess.handlebars delete mode 100644 backend/src/services/smtp/templates/organizationInvitation.handlebars delete mode 100644 backend/src/services/smtp/templates/passwordReset.handlebars delete mode 100644 backend/src/services/smtp/templates/passwordSetup.handlebars delete mode 100644 backend/src/services/smtp/templates/pkiExpirationAlert.handlebars delete mode 100644 backend/src/services/smtp/templates/projectAccess.handlebars delete mode 100644 backend/src/services/smtp/templates/scimUserProvisioned.handlebars delete mode 100644 backend/src/services/smtp/templates/secretApprovalRequestNeedsReview.handlebars delete mode 100644 backend/src/services/smtp/templates/secretLeakIncident.handlebars delete mode 100644 backend/src/services/smtp/templates/secretReminder.handlebars delete mode 100644 backend/src/services/smtp/templates/secretRequestCompleted.handlebars delete mode 100644 backend/src/services/smtp/templates/secretRotationFailed.handlebars delete mode 100644 backend/src/services/smtp/templates/secretSyncFailed.handlebars delete mode 100644 backend/src/services/smtp/templates/serviceTokenExpired.handlebars delete mode 100644 backend/src/services/smtp/templates/signupEmailVerification.handlebars delete mode 100644 backend/src/services/smtp/templates/unlockAccount.handlebars delete mode 100644 backend/src/services/smtp/templates/workspaceInvitation.handlebars diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index b23cf05ae3..f901f0df9d 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -69,6 +69,15 @@ module.exports = { ["^\\."] ] } + ], + "import/extensions": [ + "error", + "ignorePackages", + { + "": "never", // this is required to get the .tsx to work... + ts: "never", + tsx: "never" + } ] } }; diff --git a/backend/package-lock.json b/backend/package-lock.json index 19e10942ee..93c28c9e29 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -48,6 +48,7 @@ "@opentelemetry/semantic-conventions": "^1.27.0", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/x509": "^1.12.1", + "@react-email/components": "0.0.36", "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "1.1.0", "@slack/oauth": "^3.0.2", @@ -107,6 +108,8 @@ "posthog-node": "^3.6.2", "probot": "^13.3.8", "re2": "^1.21.4", + "react": "19.1.0", + "react-dom": "19.1.0", "safe-regex": "^2.1.1", "scim-patch": "^0.8.3", "scim2-parse-filter": "^0.2.10", @@ -142,6 +145,7 @@ "@types/picomatch": "^2.3.3", "@types/pkcs11js": "^1.0.4", "@types/prompt-sync": "^4.2.3", + "@types/react": "^19.1.2", "@types/resolve": "^1.20.6", "@types/safe-regex": "^1.1.6", "@types/sjcl": "^1.0.34", @@ -161,6 +165,7 @@ "nodemon": "^3.0.2", "pino-pretty": "^10.2.3", "prompt-sync": "^4.2.0", + "react-email": "4.0.7", "rimraf": "^5.0.5", "ts-node": "^10.9.2", "tsc-alias": "^1.8.8", @@ -2902,13 +2907,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -2986,15 +2993,17 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" @@ -3010,6 +3019,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@babel/generator/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", @@ -3348,19 +3370,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -3402,76 +3426,15 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -4807,33 +4770,32 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz", + "integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -4874,14 +4836,14 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -5192,6 +5154,17 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -5465,6 +5438,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", @@ -5481,6 +5471,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", @@ -6222,6 +6229,386 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@infisical/quic": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@infisical/quic/-/quic-1.0.8.tgz", @@ -6759,6 +7146,149 @@ "win32" ] }, + "node_modules/@next/env": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -8519,6 +9049,286 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@react-email/body": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.11.tgz", + "integrity": "sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/button": { + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.19.tgz", + "integrity": "sha512-HYHrhyVGt7rdM/ls6FuuD6XE7fa7bjZTJqB2byn6/oGsfiEZaogY77OtoLL/mrQHjHjZiJadtAMSik9XLcm7+A==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-block": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.12.tgz", + "integrity": "sha512-Faw3Ij9+/Qwq6moWaeHnV8Hn7ekc/EqyAzPi6yUar21dhcqYugCC4Da1x4d9nA9zC0H9KU3lYVJczh8D3cA+Eg==", + "license": "MIT", + "dependencies": { + "prismjs": "1.30.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-inline": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.5.tgz", + "integrity": "sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/column": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.13.tgz", + "integrity": "sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/components": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.36.tgz", + "integrity": "sha512-VMh+OQplAnG8JMLlJjdnjt+ThJZ+JVkp0q2YMS2NEz+T88N22bLD2p7DZO0QgtNaKgumOhJI/0a2Q7VzCrwu5g==", + "license": "MIT", + "dependencies": { + "@react-email/body": "0.0.11", + "@react-email/button": "0.0.19", + "@react-email/code-block": "0.0.12", + "@react-email/code-inline": "0.0.5", + "@react-email/column": "0.0.13", + "@react-email/container": "0.0.15", + "@react-email/font": "0.0.9", + "@react-email/head": "0.0.12", + "@react-email/heading": "0.0.15", + "@react-email/hr": "0.0.11", + "@react-email/html": "0.0.11", + "@react-email/img": "0.0.11", + "@react-email/link": "0.0.12", + "@react-email/markdown": "0.0.14", + "@react-email/preview": "0.0.12", + "@react-email/render": "1.0.6", + "@react-email/row": "0.0.12", + "@react-email/section": "0.0.16", + "@react-email/tailwind": "1.0.4", + "@react-email/text": "0.1.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/container": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", + "integrity": "sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/font": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.9.tgz", + "integrity": "sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/head": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.12.tgz", + "integrity": "sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/heading": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.15.tgz", + "integrity": "sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/hr": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.11.tgz", + "integrity": "sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/html": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.11.tgz", + "integrity": "sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/img": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.11.tgz", + "integrity": "sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/link": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.12.tgz", + "integrity": "sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/markdown": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.14.tgz", + "integrity": "sha512-5IsobCyPkb4XwnQO8uFfGcNOxnsg3311GRXhJ3uKv51P7Jxme4ycC/MITnwIZ10w2zx7HIyTiqVzTj4XbuIHbg==", + "license": "MIT", + "dependencies": { + "md-to-react-email": "5.0.5" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/preview": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.12.tgz", + "integrity": "sha512-g/H5fa9PQPDK6WUEG7iTlC19sAktI23qyoiJtMLqQiXFCfWeQMhqjLGKeLSKkfzszqmfJCjZtpSiKtBoOdxp3Q==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/render": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", + "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", + "license": "MIT", + "dependencies": { + "html-to-text": "9.0.5", + "prettier": "3.5.3", + "react-promise-suspense": "0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/row": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.12.tgz", + "integrity": "sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/section": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.16.tgz", + "integrity": "sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/tailwind": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.0.4.tgz", + "integrity": "sha512-tJdcusncdqgvTUYZIuhNC6LYTfL9vNTSQpwWdTCQhQ1lsrNCEE4OKCSdzSV3S9F32pi0i0xQ+YPJHKIzGjdTSA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/text": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.1.tgz", + "integrity": "sha512-Zo9tSEzkO3fODLVH1yVhzVCiwETfeEL5wU93jXKWo2DHoMuiZ9Iabaso3T0D0UjhrCB1PBMeq2YiejqeToTyIQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -8743,6 +9553,19 @@ "win32" ] }, + "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/@sentry-internal/tracing": { "version": "7.119.2", "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.119.2.tgz", @@ -9659,6 +10482,13 @@ "node": ">=16.0.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, "node_modules/@swc/core": { "version": "1.3.107", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", @@ -9870,22 +10700,20 @@ } }, "node_modules/@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true, - "optional": true, - "peer": true + "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", - "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dev": true, - "optional": true, - "peer": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@swc/types": { @@ -9979,6 +10807,16 @@ "@types/node": "*" } }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -10287,6 +11125,16 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, "node_modules/@types/readable-stream": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", @@ -11914,6 +12762,16 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/base64url": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", @@ -12454,6 +13312,18 @@ "esbuild": ">=0.17" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -12794,6 +13664,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true, + "license": "MIT" + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -12863,6 +13740,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -13053,6 +13940,20 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -13130,6 +14031,13 @@ "node": ">=18" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -13262,6 +14170,19 @@ "node": ">= 8" } }, + "node_modules/debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.0.0.tgz", + "integrity": "sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -13354,6 +14275,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -13465,9 +14399,10 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -13697,6 +14632,77 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -16116,6 +17122,41 @@ ], "license": "MIT" }, + "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/html-to-text/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/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -16975,7 +18016,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -17487,6 +18529,15 @@ "integrity": "sha512-8s46m/r2lSFO2+DqMxqWiJ10iiL4tuR5LC/KndV+E5//OAOzOx5s3HS5O34PJ5+kyaCA+K2oCaEPaDRfXUnQow==", "license": "MIT" }, + "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/leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -17872,6 +18923,18 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/marked": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", + "integrity": "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -17880,6 +18943,18 @@ "node": ">= 0.4" } }, + "node_modules/md-to-react-email": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/md-to-react-email/-/md-to-react-email-5.0.5.tgz", + "integrity": "sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==", + "license": "MIT", + "dependencies": { + "marked": "7.0.4" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -18540,6 +19615,90 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/next": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "15.2.4", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", + "sharp": "^0.33.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-abi": { "version": "3.65.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", @@ -19557,6 +20716,19 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "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", @@ -19732,6 +20904,15 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "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/pg": { "version": "8.13.1", "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", @@ -20320,11 +21501,10 @@ } }, "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "dev": true, - "peer": true, + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -20373,6 +21553,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/probot": { "version": "13.3.8", "resolved": "https://registry.npmjs.org/probot/-/probot-13.3.8.tgz", @@ -20928,12 +22117,817 @@ "node-gyp": "^10.2.0" } }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-email": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.0.7.tgz", + "integrity": "sha512-XCXlfZLKv9gHd/ZwUEhCpRGc/FJLZGYczeuG1kVR/be2PlkwEB4gjX9ARBbRFv86ncbtpOu/wI6jD6kadRyAKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "7.24.5", + "@babel/traverse": "7.25.6", + "chalk": "4.1.2", + "chokidar": "4.0.3", + "commander": "11.1.0", + "debounce": "2.0.0", + "esbuild": "0.25.0", + "glob": "10.3.4", + "log-symbols": "4.1.0", + "mime-types": "2.1.35", + "next": "15.2.4", + "normalize-path": "3.0.0", + "ora": "5.4.1", + "socket.io": "4.8.1" + }, + "bin": { + "email": "dist/cli/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/react-email/node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-email/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/react-email/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/react-email/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/react-email/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "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.1.13" + } + }, + "node_modules/react-email/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-email/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/react-email/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/react-email/node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/react-email/node_modules/glob": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-email/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-email/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-email/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-email/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/react-email/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-email/node_modules/supports-color": { + "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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/react-promise-suspense": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", + "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "license": "MIT" + }, "node_modules/readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", @@ -21509,6 +23503,12 @@ "node": ">=v12.22.7" } }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, "node_modules/scim-patch": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/scim-patch/-/scim-patch-0.8.3.tgz", @@ -21537,18 +23537,28 @@ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, + "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/semifies": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -21708,6 +23718,62 @@ "sha.js": "bin.js" } }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp/node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -22080,6 +24146,126 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socks": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", @@ -22326,6 +24512,15 @@ "node": ">=4.0.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -22521,6 +24716,30 @@ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", "license": "MIT" }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -24923,6 +27142,16 @@ "node": ">=18" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index 58bc6a32d6..c19d304418 100644 --- a/backend/package.json +++ b/backend/package.json @@ -72,7 +72,8 @@ "seed:new": "tsx ./scripts/create-seed-file.ts", "seed": "knex --knexfile ./dist/db/knexfile.ts --client pg seed:run", "seed-dev": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run", - "db:reset": "npm run migration:rollback -- --all && npm run migration:latest" + "db:reset": "npm run migration:rollback -- --all && npm run migration:latest", + "email:dev": "email dev --dir src/services/smtp/emails" }, "keywords": [], "author": "", @@ -96,6 +97,7 @@ "@types/picomatch": "^2.3.3", "@types/pkcs11js": "^1.0.4", "@types/prompt-sync": "^4.2.3", + "@types/react": "^19.1.2", "@types/resolve": "^1.20.6", "@types/safe-regex": "^1.1.6", "@types/sjcl": "^1.0.34", @@ -115,6 +117,7 @@ "nodemon": "^3.0.2", "pino-pretty": "^10.2.3", "prompt-sync": "^4.2.0", + "react-email": "4.0.7", "rimraf": "^5.0.5", "ts-node": "^10.9.2", "tsc-alias": "^1.8.8", @@ -164,6 +167,7 @@ "@opentelemetry/semantic-conventions": "^1.27.0", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/x509": "^1.12.1", + "@react-email/components": "0.0.36", "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "1.1.0", "@slack/oauth": "^3.0.2", @@ -223,6 +227,8 @@ "posthog-node": "^3.6.2", "probot": "^13.3.8", "re2": "^1.21.4", + "react": "19.1.0", + "react-dom": "19.1.0", "safe-regex": "^2.1.1", "scim-patch": "^0.8.3", "scim2-parse-filter": "^0.2.10", diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts index 5c0d97ee88..a25482c8cc 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts @@ -219,7 +219,7 @@ export const parseRotationErrorMessage = (err: unknown): string => { if (err instanceof AxiosError) { errorMessage += err?.response?.data ? JSON.stringify(err?.response?.data) - : err?.message ?? "An unknown error occurred."; + : (err?.message ?? "An unknown error occurred."); } else { errorMessage += (err as Error)?.message || "An unknown error occurred."; } diff --git a/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts b/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts index 312b7966b7..d58644d905 100644 --- a/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts +++ b/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts @@ -282,7 +282,7 @@ export const sshCertificateAuthorityServiceFactory = ({ // set [keyId] depending on if [allowCustomKeyIds] is true or false const keyId = sshCertificateTemplate.allowCustomKeyIds - ? requestedKeyId ?? `${actor}-${actorId}` + ? (requestedKeyId ?? `${actor}-${actorId}`) : `${actor}-${actorId}`; const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId }); @@ -404,7 +404,7 @@ export const sshCertificateAuthorityServiceFactory = ({ // set [keyId] depending on if [allowCustomKeyIds] is true or false const keyId = sshCertificateTemplate.allowCustomKeyIds - ? requestedKeyId ?? `${actor}-${actorId}` + ? (requestedKeyId ?? `${actor}-${actorId}`) : `${actor}-${actorId}`; const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId }); diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index d1b0a550de..5e2b450bb5 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -397,8 +397,8 @@ export const authLoginServiceFactory = ({ } const shouldCheckMfa = selectedOrg.enforceMfa || user.isMfaEnabled; - const orgMfaMethod = selectedOrg.enforceMfa ? selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL : undefined; - const userMfaMethod = user.isMfaEnabled ? user.selectedMfaMethod ?? MfaMethod.EMAIL : undefined; + const orgMfaMethod = selectedOrg.enforceMfa ? (selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined; + const userMfaMethod = user.isMfaEnabled ? (user.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined; const mfaMethod = orgMfaMethod ?? userMfaMethod; if (shouldCheckMfa && (!decodedToken.isMfaVerified || decodedToken.mfaMethod !== mfaMethod)) { @@ -569,9 +569,9 @@ export const authLoginServiceFactory = ({ }: TVerifyMfaTokenDTO) => { const appCfg = getConfig(); const user = await userDAL.findById(userId); - enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd); try { + enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd); if (mfaMethod === MfaMethod.EMAIL) { await tokenService.validateTokenForUser({ type: TokenType.TOKEN_EMAIL_MFA, diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 060a016342..d794391c16 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -698,6 +698,8 @@ export const orgServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member); + const invitingUser = await userDAL.findOne({ id: actorId }); + const org = await orgDAL.findOrgById(orgId); const [inviteeOrgMembership] = await orgDAL.findMembership({ @@ -731,8 +733,8 @@ export const orgServiceFactory = ({ subjectLine: "Infisical organization invitation", recipients: [inviteeOrgMembership.email as string], substitutions: { - inviterFirstName: inviteeOrgMembership.firstName, - inviterUsername: inviteeOrgMembership.email, + inviterFirstName: invitingUser.firstName, + inviterUsername: invitingUser.email, organizationName: org?.name, email: inviteeOrgMembership.email, organizationId: org?.id.toString(), @@ -761,6 +763,8 @@ export const orgServiceFactory = ({ const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); + const invitingUser = await userDAL.findOne({ id: actorId }); + const org = await orgDAL.findOrgById(orgId); const isEmailInvalid = await isDisposableEmail(inviteeEmails); @@ -1179,8 +1183,8 @@ export const orgServiceFactory = ({ subjectLine: "Infisical organization invitation", recipients: [el.email], substitutions: { - inviterFirstName: el.firstName, - inviterUsername: el.email, + inviterFirstName: invitingUser.firstName, + inviterUsername: invitingUser.email, organizationName: org?.name, email: el.email, organizationId: org?.id.toString(), diff --git a/backend/src/services/secret-sync/secret-sync-fns.ts b/backend/src/services/secret-sync/secret-sync-fns.ts index 143fa46227..1d3e8888c3 100644 --- a/backend/src/services/secret-sync/secret-sync-fns.ts +++ b/backend/src/services/secret-sync/secret-sync-fns.ts @@ -282,7 +282,7 @@ export const parseSyncErrorMessage = (err: unknown): string => { } else if (err instanceof AxiosError) { errorMessage = err?.response?.data ? JSON.stringify(err?.response?.data) - : err?.message ?? "An unknown error occurred."; + : (err?.message ?? "An unknown error occurred."); } else { errorMessage = (err as Error)?.message || "An unknown error occurred."; } diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index 34ac84947d..62b4ba3cc4 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -834,7 +834,7 @@ export const secretSyncQueueFactory = ({ secretPath: folder?.path, environment: environment?.name, projectName: project.name, - syncUrl: `${appCfg.SITE_URL}/integrations/secret-syncs/${destination}/${secretSync.id}` + syncUrl: `${appCfg.SITE_URL}/secret-manager/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}` } }); }; diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index 6ab3485203..cd27731720 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -7,6 +7,7 @@ import { ProjectType, SecretsV2Schema, SecretType, TableName, TSecretsV2, TSecre import { TKeyStoreFactory } from "@app/keystore/keystore"; import { getConfig } from "@app/lib/config/env"; import { generateCacheKeyFromData } from "@app/lib/crypto/cache"; +import { applyJitter } from "@app/lib/dates"; import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; import { buildFindFilter, @@ -22,7 +23,6 @@ import type { TFindSecretsByFolderIdsFilter, TGetSecretsDTO } from "@app/services/secret-v2-bridge/secret-v2-bridge-types"; -import { applyJitter } from "@app/lib/dates"; export const SecretServiceCacheKeys = { get productKey() { diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 6a0868741e..714df0d3f8 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -740,7 +740,7 @@ export const secretQueueFactory = ({ environment: jobPayload.environmentName, count: jobPayload.count, projectName: project.name, - integrationUrl: `${appCfg.SITE_URL}/integrations/${project.id}` + integrationUrl: `${appCfg.SITE_URL}/secret-manager/${project.id}/integrations?selectedTab=native-integrations` } }); } diff --git a/backend/src/services/smtp/emails/AccessApprovalRequestTemplate.tsx b/backend/src/services/smtp/emails/AccessApprovalRequestTemplate.tsx new file mode 100644 index 0000000000..fef072546a --- /dev/null +++ b/backend/src/services/smtp/emails/AccessApprovalRequestTemplate.tsx @@ -0,0 +1,95 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface AccessApprovalRequestTemplateProps extends Omit { + projectName: string; + requesterFullName: string; + requesterEmail: string; + isTemporary: boolean; + secretPath: string; + environment: string; + expiresIn: string; + permissions: string[]; + note?: string; + approvalUrl: string; +} + +export const AccessApprovalRequestTemplate = ({ + projectName, + siteUrl, + requesterFullName, + requesterEmail, + isTemporary, + secretPath, + environment, + expiresIn, + permissions, + note, + approvalUrl +}: AccessApprovalRequestTemplateProps) => { + return ( + + + You have a new access approval request pending review for the project {projectName} + +
+ + {requesterFullName} ( + + {requesterEmail} + + ) has requested {isTemporary ? "temporary" : "permanent"} access to {secretPath} in the{" "} + {environment} environment. + + + {isTemporary && ( + + This access will expire {expiresIn} after approval. + + )} + + The following permissions are requested: + + {permissions.map((permission) => ( + + - {permission} + + ))} + {note && ( + + User Note: "{note}" + + )} +
+
+ +
+
+ ); +}; + +export default AccessApprovalRequestTemplate; + +AccessApprovalRequestTemplate.PreviewProps = { + requesterFullName: "Abigail Williams", + requesterEmail: "abigail@infisical.com", + isTemporary: true, + secretPath: "/api/secrets", + environment: "Production", + siteUrl: "https://infisical.com", + projectName: "Example Project", + expiresIn: "1 day", + permissions: ["Read Secret", "Delete Project", "Create Dynamic Secret"], + note: "I need access to these permissions for the new initiative for HR." +} as AccessApprovalRequestTemplateProps; diff --git a/backend/src/services/smtp/emails/BaseEmailWrapper.tsx b/backend/src/services/smtp/emails/BaseEmailWrapper.tsx new file mode 100644 index 0000000000..3d02fc793d --- /dev/null +++ b/backend/src/services/smtp/emails/BaseEmailWrapper.tsx @@ -0,0 +1,45 @@ +import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Tailwind, Text } from "@react-email/components"; +import React, { ReactNode } from "react"; + +export interface BaseEmailWrapperProps { + title: string; + preview: string; + siteUrl: string; + children?: ReactNode; +} + +export const BaseEmailWrapper = ({ title, preview, children, siteUrl }: BaseEmailWrapperProps) => { + return ( + + + + + {preview} + +
+
+
+ Infisical Logo +
+
+
{children}
+
+
+ + Email sent via{" "} + + Infisical + + +
+ + + + + ); +}; diff --git a/backend/src/services/smtp/emails/EmailMfaTemplate.tsx b/backend/src/services/smtp/emails/EmailMfaTemplate.tsx new file mode 100644 index 0000000000..8a35b011d1 --- /dev/null +++ b/backend/src/services/smtp/emails/EmailMfaTemplate.tsx @@ -0,0 +1,50 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface EmailMfaTemplateProps extends Omit { + code: string; + isCloud: boolean; +} + +export const EmailMfaTemplate = ({ code, siteUrl, isCloud }: EmailMfaTemplateProps) => { + return ( + + + MFA required + +
+ Enter the MFA code below in the browser where you started sign-in. + + {code} + +
+
+ + Not you?{" "} + {isCloud ? ( + <> + Contact us at{" "} + + support@infisical.com + {" "} + immediately + + ) : ( + "Contact your administrator immediately" + )} + . + +
+
+ ); +}; + +export default EmailMfaTemplate; + +EmailMfaTemplate.PreviewProps = { + code: "124356", + isCloud: true, + siteUrl: "https://infisical.com" +} as EmailMfaTemplateProps; diff --git a/backend/src/services/smtp/emails/EmailVerificationTemplate.tsx b/backend/src/services/smtp/emails/EmailVerificationTemplate.tsx new file mode 100644 index 0000000000..6c0f410b1c --- /dev/null +++ b/backend/src/services/smtp/emails/EmailVerificationTemplate.tsx @@ -0,0 +1,53 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface EmailVerificationTemplateProps extends Omit { + code: string; + isCloud: boolean; +} + +export const EmailVerificationTemplate = ({ code, siteUrl, isCloud }: EmailVerificationTemplateProps) => { + return ( + + + Confirm your email address + +
+ Enter the confirmation code below in the browser window requiring confirmation. + + {code} + +
+
+ + Questions about Infisical?{" "} + {isCloud ? ( + <> + Email us at{" "} + + support@infisical.com + + + ) : ( + "Contact your administrator" + )} + . + +
+
+ ); +}; + +export default EmailVerificationTemplate; + +EmailVerificationTemplate.PreviewProps = { + code: "124356", + isCloud: true, + siteUrl: "https://infisical.com" +} as EmailVerificationTemplateProps; diff --git a/backend/src/services/smtp/emails/ExternalImportFailedTemplate.tsx b/backend/src/services/smtp/emails/ExternalImportFailedTemplate.tsx new file mode 100644 index 0000000000..c283fdad93 --- /dev/null +++ b/backend/src/services/smtp/emails/ExternalImportFailedTemplate.tsx @@ -0,0 +1,43 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ExternalImportFailedTemplateProps extends Omit { + error: string; + provider: string; +} + +export const ExternalImportFailedTemplate = ({ error, siteUrl, provider }: ExternalImportFailedTemplateProps) => { + return ( + + + An import from {provider} to Infisical has failed + +
+ + An import from {provider} to Infisical has failed due to unforeseen circumstances. Please + re-try your import. + + + If your issue persists, you can contact the Infisical team at{" "} + + support@infisical.com + + . + + + Error: "{error}" + +
+
+ ); +}; + +export default ExternalImportFailedTemplate; + +ExternalImportFailedTemplate.PreviewProps = { + provider: "EnvKey", + error: "Something went wrong. Please try again.", + siteUrl: "https://infisical.com" +} as ExternalImportFailedTemplateProps; diff --git a/backend/src/services/smtp/emails/ExternalImportStartedTemplate.tsx b/backend/src/services/smtp/emails/ExternalImportStartedTemplate.tsx new file mode 100644 index 0000000000..fc24536b45 --- /dev/null +++ b/backend/src/services/smtp/emails/ExternalImportStartedTemplate.tsx @@ -0,0 +1,30 @@ +import { Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ExternalImportStartedTemplateProps extends Omit { + provider: string; +} + +export const ExternalImportStartedTemplate = ({ siteUrl, provider }: ExternalImportStartedTemplateProps) => { + return ( + + + An import from {provider} to Infisical has been started + +
+ + An import from {provider} to Infisical is in progress. The import process may take up to 30 + minutes. You will receive an email once the import has completed. + +
+
+ ); +}; + +export default ExternalImportStartedTemplate; + +ExternalImportStartedTemplate.PreviewProps = { + provider: "EnvKey" +} as ExternalImportStartedTemplateProps; diff --git a/backend/src/services/smtp/emails/ExternalImportSucceededTemplate.tsx b/backend/src/services/smtp/emails/ExternalImportSucceededTemplate.tsx new file mode 100644 index 0000000000..390d50fbca --- /dev/null +++ b/backend/src/services/smtp/emails/ExternalImportSucceededTemplate.tsx @@ -0,0 +1,31 @@ +import { Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ExternalImportSucceededTemplateProps extends Omit { + provider: string; +} + +export const ExternalImportSucceededTemplate = ({ siteUrl, provider }: ExternalImportSucceededTemplateProps) => { + return ( + + + An import from {provider} to Infisical has completed + +
+ + An import from {provider} to Infisical was successful. Your data is now available in + Infisical. + +
+
+ ); +}; + +export default ExternalImportSucceededTemplate; + +ExternalImportSucceededTemplate.PreviewProps = { + provider: "EnvKey", + siteUrl: "https://infisical.com" +} as ExternalImportSucceededTemplateProps; diff --git a/backend/src/services/smtp/emails/IntegrationSyncFailedTemplate.tsx b/backend/src/services/smtp/emails/IntegrationSyncFailedTemplate.tsx new file mode 100644 index 0000000000..9eeeb4c3f3 --- /dev/null +++ b/backend/src/services/smtp/emails/IntegrationSyncFailedTemplate.tsx @@ -0,0 +1,65 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface IntegrationSyncFailedTemplateProps extends Omit { + count: number; + projectName: string; + secretPath: string; + environment: string; + syncMessage: string; + integrationUrl: string; +} + +export const IntegrationSyncFailedTemplate = ({ + count, + siteUrl, + projectName, + secretPath, + environment, + syncMessage, + integrationUrl +}: IntegrationSyncFailedTemplateProps) => { + return ( + + + {count} integration(s) failed to sync + +
+ Project + {projectName} + Environment + {environment} + Secret Path + {secretPath} + Failure Reason: + "{syncMessage}" +
+
+ +
+
+ ); +}; + +export default IntegrationSyncFailedTemplate; + +IntegrationSyncFailedTemplate.PreviewProps = { + projectName: "Example Project", + secretPath: "/api/secrets", + environment: "Production", + siteUrl: "https://infisical.com", + integrationUrl: "https://infisical.com", + count: 2, + syncMessage: "Secret key cannot contain a colon (:)" +} as IntegrationSyncFailedTemplateProps; diff --git a/backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx b/backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx new file mode 100644 index 0000000000..3d301c0abb --- /dev/null +++ b/backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx @@ -0,0 +1,68 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface NewDeviceLoginTemplateProps extends Omit { + email: string; + timestamp: string; + ip: string; + userAgent: string; + isCloud: boolean; +} + +export const NewDeviceLoginTemplate = ({ + email, + timestamp, + ip, + userAgent, + siteUrl, + isCloud +}: NewDeviceLoginTemplateProps) => { + return ( + + + We're verifying a recent login for +
+ {email} +
+
+ Timestamp + {timestamp} + IP Address + {ip} + User Agent + {userAgent} +
+
+ + If you believe that this login is suspicious, please contact{" "} + {isCloud ? ( + + support@infisical.com + + ) : ( + "your administrator" + )}{" "} + or reset your password immediately. + +
+
+ ); +}; + +export default NewDeviceLoginTemplate; + +NewDeviceLoginTemplate.PreviewProps = { + email: "john@infisical.com", + ip: "127.0.0.1", + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15", + timestamp: "Tue Apr 29 2025 23:03:27 GMT+0000 (Coordinated Universal Time)", + isCloud: true, + siteUrl: "https://infisical.com" +} as NewDeviceLoginTemplateProps; diff --git a/backend/src/services/smtp/emails/OrgAdminBreakglassAccessTemplate.tsx b/backend/src/services/smtp/emails/OrgAdminBreakglassAccessTemplate.tsx new file mode 100644 index 0000000000..f3d0d01d77 --- /dev/null +++ b/backend/src/services/smtp/emails/OrgAdminBreakglassAccessTemplate.tsx @@ -0,0 +1,57 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface OrgAdminBreakglassAccessTemplateProps extends Omit { + email: string; + timestamp: string; + ip: string; + userAgent: string; +} + +export const OrgAdminBreakglassAccessTemplate = ({ + email, + siteUrl, + timestamp, + ip, + userAgent +}: OrgAdminBreakglassAccessTemplateProps) => { + return ( + + + The organization admin {email} has bypassed enforced SSO login. + +
+ Timestamp + {timestamp} + IP Address + {ip} + User Agent + {userAgent} + + If you'd like to disable Admin SSO Bypass, please visit{" "} + + Organization Security Settings + + . + +
+
+ ); +}; + +export default OrgAdminBreakglassAccessTemplate; + +OrgAdminBreakglassAccessTemplate.PreviewProps = { + ip: "127.0.0.1", + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15", + timestamp: "Tue Apr 29 2025 23:03:27 GMT+0000 (Coordinated Universal Time)", + siteUrl: "https://infisical.com", + email: "august@infiscal.com" +} as OrgAdminBreakglassAccessTemplateProps; diff --git a/backend/src/services/smtp/emails/OrgAdminProjectGrantAccessTemplate.tsx b/backend/src/services/smtp/emails/OrgAdminProjectGrantAccessTemplate.tsx new file mode 100644 index 0000000000..4a9eeae3e6 --- /dev/null +++ b/backend/src/services/smtp/emails/OrgAdminProjectGrantAccessTemplate.tsx @@ -0,0 +1,40 @@ +import { Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface OrgAdminProjectGrantAccessTemplateProps extends Omit { + email: string; + projectName: string; +} + +export const OrgAdminProjectGrantAccessTemplate = ({ + email, + siteUrl, + projectName +}: OrgAdminProjectGrantAccessTemplateProps) => { + return ( + + + An organization admin has joined the project {projectName} + +
+ + The organization admin {email} has self-issued direct access to the project{" "} + {projectName}. + +
+
+ ); +}; + +export default OrgAdminProjectGrantAccessTemplate; + +OrgAdminProjectGrantAccessTemplate.PreviewProps = { + email: "kevin@infisical.com", + projectName: "Example Project" +} as OrgAdminProjectGrantAccessTemplateProps; diff --git a/backend/src/services/smtp/emails/OrganizationInvitationTemplate.tsx b/backend/src/services/smtp/emails/OrganizationInvitationTemplate.tsx new file mode 100644 index 0000000000..6e27d1e9bb --- /dev/null +++ b/backend/src/services/smtp/emails/OrganizationInvitationTemplate.tsx @@ -0,0 +1,75 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface OrganizationInvitationTemplateProps extends Omit { + metadata?: string; + inviterFirstName: string; + inviterUsername: string; + organizationName: string; + email: string; + organizationId: string; + token: string; + callback_url: string; +} + +export const OrganizationInvitationTemplate = ({ + organizationName, + inviterFirstName, + inviterUsername, + token, + callback_url, + metadata, + email, + organizationId, + siteUrl +}: OrganizationInvitationTemplateProps) => { + return ( + + + You've been invited to join +
+ {organizationName} on Infisical +
+
+ + {inviterFirstName} ( + + {inviterUsername} + + ) has invited you to collaborate on {organizationName}. + +
+
+ +
+
+ + About Infisical: Infisical is an all-in-one platform to securely manage application secrets, + certificates, SSH keys, and configurations across your team and infrastructure. + +
+
+ ); +}; + +export default OrganizationInvitationTemplate; + +OrganizationInvitationTemplate.PreviewProps = { + organizationName: "Example Organization", + inviterFirstName: "Jane", + inviterUsername: "jane@infisical.com", + email: "john@infisical.com", + siteUrl: "https://infisical.com", + callback_url: "https://app.infisical.com" +} as OrganizationInvitationTemplateProps; diff --git a/backend/src/services/smtp/emails/PasswordResetTemplate.tsx b/backend/src/services/smtp/emails/PasswordResetTemplate.tsx new file mode 100644 index 0000000000..b4ac0d5a73 --- /dev/null +++ b/backend/src/services/smtp/emails/PasswordResetTemplate.tsx @@ -0,0 +1,58 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface PasswordResetTemplateProps extends Omit { + email: string; + callback_url: string; + token: string; + isCloud: boolean; +} + +export const PasswordResetTemplate = ({ email, isCloud, siteUrl, callback_url, token }: PasswordResetTemplateProps) => { + return ( + + + Account Recovery + +
+ A password reset was requested for your Infisical account. + + If you did not initiate this request, please contact{" "} + {isCloud ? ( + <> + us immediately at{" "} + + support@infisical.com + + + ) : ( + "your administrator immediately" + )} + . + +
+
+ +
+
+ ); +}; + +export default PasswordResetTemplate; + +PasswordResetTemplate.PreviewProps = { + email: "kevin@infisical.com", + callback_url: "https://app.infisical.com", + isCloud: true +} as PasswordResetTemplateProps; diff --git a/backend/src/services/smtp/emails/PasswordSetupTemplate.tsx b/backend/src/services/smtp/emails/PasswordSetupTemplate.tsx new file mode 100644 index 0000000000..a03724a39d --- /dev/null +++ b/backend/src/services/smtp/emails/PasswordSetupTemplate.tsx @@ -0,0 +1,57 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface PasswordSetupTemplateProps extends Omit { + email: string; + callback_url: string; + token: string; + isCloud: boolean; +} + +export const PasswordSetupTemplate = ({ email, isCloud, siteUrl, callback_url, token }: PasswordSetupTemplateProps) => { + return ( + + + Password Setup + +
+ Someone requested to set up a password for your Infisical account. + + Make sure you are already logged in to Infisical in the current browser before clicking the link below. + + + If you did not initiate this request, please contact{" "} + {isCloud ? ( + <> + us immediately at{" "} + + support@infisical.com + + + ) : ( + "your administrator immediately" + )} + . + +
+
+ +
+
+ ); +}; + +export default PasswordSetupTemplate; + +PasswordSetupTemplate.PreviewProps = { + email: "casey@infisical.com", + callback_url: "https://app.infisical.com", + isCloud: true +} as PasswordSetupTemplateProps; diff --git a/backend/src/services/smtp/emails/PkiExpirationAlertTemplate.tsx b/backend/src/services/smtp/emails/PkiExpirationAlertTemplate.tsx new file mode 100644 index 0000000000..db3b4816e5 --- /dev/null +++ b/backend/src/services/smtp/emails/PkiExpirationAlertTemplate.tsx @@ -0,0 +1,68 @@ +import { Heading, Hr, Section, Text } from "@react-email/components"; +import React, { Fragment } from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface PkiExpirationAlertTemplateProps extends Omit { + alertName: string; + alertBeforeDays: number; + items: { type: string; friendlyName: string; serialNumber: string; expiryDate: string }[]; +} + +export const PkiExpirationAlertTemplate = ({ + alertName, + siteUrl, + alertBeforeDays, + items +}: PkiExpirationAlertTemplateProps) => { + return ( + + + CA/Certificate Expiration Notice + +
+ Hello, + + This is an automated alert for {alertName} triggered for CAs/Certificates expiring in{" "} + {alertBeforeDays} days. + + + Expiring Items: + + {items.map((item) => ( + +
+ {item.type}: + {item.friendlyName} + Serial Number: + {item.serialNumber} + Expires On: + {item.expiryDate} +
+ ))} +
+ + Please take the necessary actions to renew these items before they expire. + + + For more details, please log in to your Infisical account and check your PKI management section. + +
+
+ ); +}; + +export default PkiExpirationAlertTemplate; + +PkiExpirationAlertTemplate.PreviewProps = { + alertBeforeDays: 5, + items: [ + { type: "CA", friendlyName: "Example CA", serialNumber: "1234567890", expiryDate: "2022-01-01" }, + { type: "Certificate", friendlyName: "Example Certificate", serialNumber: "1234567890", expiryDate: "2022-01-01" } + ], + alertName: "My PKI Alert" +} as PkiExpirationAlertTemplateProps; diff --git a/backend/src/services/smtp/emails/ProjectAccessRequestTemplate.tsx b/backend/src/services/smtp/emails/ProjectAccessRequestTemplate.tsx new file mode 100644 index 0000000000..28933b538a --- /dev/null +++ b/backend/src/services/smtp/emails/ProjectAccessRequestTemplate.tsx @@ -0,0 +1,68 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ProjectAccessRequestTemplateProps extends Omit { + projectName: string; + requesterName: string; + requesterEmail: string; + orgName: string; + environment: string; + note: string; + callback_url: string; +} + +export const ProjectAccessRequestTemplate = ({ + projectName, + siteUrl, + requesterName, + requesterEmail, + orgName, + note, + callback_url +}: ProjectAccessRequestTemplateProps) => { + return ( + + + A user has requested access to the project {projectName} + +
+ + {requesterName} ( + + {requesterEmail} + + ) has requested access to the project {projectName} in the organization{" "} + {orgName}. + + + User note: "{note}" + +
+
+ +
+
+ ); +}; + +export default ProjectAccessRequestTemplate; + +ProjectAccessRequestTemplate.PreviewProps = { + requesterName: "Abigail Williams", + requesterEmail: "abigail@infisical.com", + orgName: "Example Org", + siteUrl: "https://infisical.com", + projectName: "Example Project", + note: "I need access to the project for the new initiative for HR." +} as ProjectAccessRequestTemplateProps; diff --git a/backend/src/services/smtp/emails/ProjectInvitationTemplate.tsx b/backend/src/services/smtp/emails/ProjectInvitationTemplate.tsx new file mode 100644 index 0000000000..452b098a58 --- /dev/null +++ b/backend/src/services/smtp/emails/ProjectInvitationTemplate.tsx @@ -0,0 +1,50 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ProjectInvitationTemplateProps extends Omit { + callback_url: string; + workspaceName: string; +} + +export const ProjectInvitationTemplate = ({ callback_url, workspaceName, siteUrl }: ProjectInvitationTemplateProps) => { + return ( + + + You've been invited to join a new project on Infisical + +
+ + You've been invited to join the project {workspaceName}. + +
+
+ +
+
+ + About Infisical: Infisical is an all-in-one platform to securely manage application secrets, + certificates, SSH keys, and configurations across your team and infrastructure. + +
+
+ ); +}; + +export default ProjectInvitationTemplate; + +ProjectInvitationTemplate.PreviewProps = { + workspaceName: "Example Project", + siteUrl: "https://infisical.com", + callback_url: "https://app.infisical.com" +} as ProjectInvitationTemplateProps; diff --git a/backend/src/services/smtp/emails/ScimUserProvisionedTemplate.tsx b/backend/src/services/smtp/emails/ScimUserProvisionedTemplate.tsx new file mode 100644 index 0000000000..7228948354 --- /dev/null +++ b/backend/src/services/smtp/emails/ScimUserProvisionedTemplate.tsx @@ -0,0 +1,56 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ScimUserProvisionedTemplateProps extends Omit { + organizationName: string; + callback_url: string; +} + +export const ScimUserProvisionedTemplate = ({ + organizationName, + callback_url, + siteUrl +}: ScimUserProvisionedTemplateProps) => { + return ( + + + You've been invited to join +
+ {organizationName} on Infisical +
+
+ + You've been invited you to collaborate on {organizationName}. + +
+
+ +
+
+ + About Infisical: Infisical is an all-in-one platform to securely manage application secrets, + certificates, SSH keys, and configurations across your team and infrastructure. + +
+
+ ); +}; + +export default ScimUserProvisionedTemplate; + +ScimUserProvisionedTemplate.PreviewProps = { + organizationName: "Example Organization", + callback_url: "https://app.infisical.com", + siteUrl: "https://app.infisical.com" +} as ScimUserProvisionedTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretApprovalRequestBypassedTemplate.tsx b/backend/src/services/smtp/emails/SecretApprovalRequestBypassedTemplate.tsx new file mode 100644 index 0000000000..bad823bd34 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretApprovalRequestBypassedTemplate.tsx @@ -0,0 +1,72 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretApprovalRequestBypassedTemplateProps + extends Omit { + projectName: string; + requesterFullName: string; + requesterEmail: string; + secretPath: string; + environment: string; + bypassReason: string; + approvalUrl: string; +} + +export const SecretApprovalRequestBypassedTemplate = ({ + projectName, + siteUrl, + requesterFullName, + requesterEmail, + secretPath, + environment, + bypassReason, + approvalUrl +}: SecretApprovalRequestBypassedTemplateProps) => { + return ( + + + A secret approval request has been bypassed in the project {projectName} + +
+ + {requesterFullName} ( + + {requesterEmail} + + ) has merged a secret to {secretPath} in the {environment} environment + without obtaining the required approval. + + + The following reason was provided for bypassing the policy: " + {bypassReason}" + +
+
+ +
+
+ ); +}; + +export default SecretApprovalRequestBypassedTemplate; + +SecretApprovalRequestBypassedTemplate.PreviewProps = { + requesterFullName: "Abigail Williams", + requesterEmail: "abigail@infisical.com", + secretPath: "/api/secrets", + environment: "Production", + siteUrl: "https://infisical.com", + projectName: "Example Project", + bypassReason: "I needed urgent access for a production misconfiguration." +} as SecretApprovalRequestBypassedTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretApprovalRequestNeedsReviewTemplate.tsx b/backend/src/services/smtp/emails/SecretApprovalRequestNeedsReviewTemplate.tsx new file mode 100644 index 0000000000..faff34b582 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretApprovalRequestNeedsReviewTemplate.tsx @@ -0,0 +1,57 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretApprovalRequestNeedsReviewTemplateProps + extends Omit { + projectName: string; + firstName: string; + organizationName: string; + approvalUrl: string; +} + +export const SecretApprovalRequestNeedsReviewTemplate = ({ + projectName, + siteUrl, + firstName, + organizationName, + approvalUrl +}: SecretApprovalRequestNeedsReviewTemplateProps) => { + return ( + + + A secret approval request for the project {projectName} requires review. + +
+ Hello {firstName}, + + You have a new secret change request pending your review for the project {projectName} in the + organization {organizationName}. + +
+
+ +
+
+ ); +}; + +export default SecretApprovalRequestNeedsReviewTemplate; + +SecretApprovalRequestNeedsReviewTemplate.PreviewProps = { + firstName: "Gordon", + organizationName: "Example Org", + siteUrl: "https://infisical.com", + approvalUrl: "https://infisical.com", + projectName: "Example Project" +} as SecretApprovalRequestNeedsReviewTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretLeakIncidentTemplate.tsx b/backend/src/services/smtp/emails/SecretLeakIncidentTemplate.tsx new file mode 100644 index 0000000000..3ef10eb4e2 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretLeakIncidentTemplate.tsx @@ -0,0 +1,82 @@ +import { Button, Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretLeakIncidentTemplateProps extends Omit { + numberOfSecrets: number; + pusher_email: string; + pusher_name: string; +} + +export const SecretLeakIncidentTemplate = ({ + numberOfSecrets, + siteUrl, + pusher_name, + pusher_email +}: SecretLeakIncidentTemplateProps) => { + return ( + + + Infisical has uncovered {numberOfSecrets} secret(s) from a recent commit + +
+ + You are receiving this notification because one or more leaked secrets have been detected in a recent commit + {(pusher_email || pusher_name) && ( + <> + {" "} + pushed by {pusher_name ?? "Unknown Pusher"}{" "} + {pusher_email && ( + <> + ( + + {pusher_email} + + ) + + )} + + )} + . + + + If these are test secrets, please add `infisical-scan:ignore` at the end of the line containing the secret as + a comment in the given programming language. This will prevent future notifications from being sent out for + these secrets. + + + If these are production secrets, please rotate them immediately. + + + Once you have taken action, be sure to update the status of the risk in the{" "} + + Infisical Dashboard + + . + +
+
+ +
+
+ ); +}; + +export default SecretLeakIncidentTemplate; + +SecretLeakIncidentTemplate.PreviewProps = { + pusher_name: "Jim", + pusher_email: "jim@infisical.com", + numberOfSecrets: 3, + siteUrl: "https://infisical.com" +} as SecretLeakIncidentTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretReminderTemplate.tsx b/backend/src/services/smtp/emails/SecretReminderTemplate.tsx new file mode 100644 index 0000000000..753246db56 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretReminderTemplate.tsx @@ -0,0 +1,45 @@ +import { Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretReminderTemplateProps extends Omit { + projectName: string; + organizationName: string; + reminderNote?: string; +} + +export const SecretReminderTemplate = ({ + siteUrl, + reminderNote, + projectName, + organizationName +}: SecretReminderTemplateProps) => { + return ( + + + Secret Reminder + +
+ + You have a new secret reminder from the project {projectName} in the{" "} + {organizationName} organization. + + {reminderNote && ( + + Reminder Note: "{reminderNote}" + + )} +
+
+ ); +}; + +export default SecretReminderTemplate; + +SecretReminderTemplate.PreviewProps = { + reminderNote: "Remember to rotate secret.", + projectName: "Example Project", + organizationName: "Example Organization", + siteUrl: "https://infisical.com" +} as SecretReminderTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretRequestCompletedTemplate.tsx b/backend/src/services/smtp/emails/SecretRequestCompletedTemplate.tsx new file mode 100644 index 0000000000..75e2d1ba59 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretRequestCompletedTemplate.tsx @@ -0,0 +1,53 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ShareSecretTemplateProps extends Omit { + name?: string; + respondentUsername: string; + secretRequestUrl: string; +} + +export const SecretRequestCompletedTemplate = ({ + name, + siteUrl, + respondentUsername, + secretRequestUrl +}: ShareSecretTemplateProps) => { + return ( + + + A secret has been shared with you + +
+ + {respondentUsername ? {respondentUsername} : "Someone"} shared a secret{" "} + {name && ( + <> + {name}{" "} + + )}{" "} + with you. + +
+
+ +
+
+ ); +}; + +export default SecretRequestCompletedTemplate; + +SecretRequestCompletedTemplate.PreviewProps = { + respondentUsername: "Gracie", + siteUrl: "https://infisical.com", + secretRequestUrl: "https://infisical.com", + name: "API_TOKEN" +} as ShareSecretTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretRotationFailedTemplate.tsx b/backend/src/services/smtp/emails/SecretRotationFailedTemplate.tsx new file mode 100644 index 0000000000..6b149dcf38 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretRotationFailedTemplate.tsx @@ -0,0 +1,68 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretRotationFailedTemplateProps extends Omit { + rotationType: string; + rotationName: string; + rotationUrl: string; + projectName: string; + environment: string; + secretPath: string; + content: string; +} + +export const SecretRotationFailedTemplate = ({ + rotationType, + rotationName, + rotationUrl, + projectName, + siteUrl, + environment, + secretPath, + content +}: SecretRotationFailedTemplateProps) => { + return ( + + + Your {rotationType} Rotation {rotationName} failed to rotate + +
+ Name + {rotationName} + Type + {rotationType} + Project + {projectName} + Environment + {environment} + Secret Path + {secretPath} + Reason: + {content} +
+
+ +
+
+ ); +}; + +export default SecretRotationFailedTemplate; + +SecretRotationFailedTemplate.PreviewProps = { + rotationType: "Auth0 Client Secret", + rotationUrl: "https://infisical.com", + content: "See Rotation status for details", + projectName: "Example Project", + secretPath: "/api/secrets", + environment: "Production", + rotationName: "my-auth0-rotation", + siteUrl: "https://infisical.com" +} as SecretRotationFailedTemplateProps; diff --git a/backend/src/services/smtp/emails/SecretSyncFailedTemplate.tsx b/backend/src/services/smtp/emails/SecretSyncFailedTemplate.tsx new file mode 100644 index 0000000000..9cad012506 --- /dev/null +++ b/backend/src/services/smtp/emails/SecretSyncFailedTemplate.tsx @@ -0,0 +1,80 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SecretSyncFailedTemplateProps extends Omit { + syncDestination: string; + syncName: string; + syncUrl: string; + projectName: string; + environment: string; + secretPath: string; + failureMessage: string; +} + +export const SecretSyncFailedTemplate = ({ + syncDestination, + syncName, + syncUrl, + projectName, + siteUrl, + environment, + secretPath, + failureMessage +}: SecretSyncFailedTemplateProps) => { + return ( + + + Your {syncDestination} Sync {syncName} failed to sync + +
+ Name + {syncName} + Destination + {syncDestination} + Project + {projectName} + {environment && ( + <> + Environment + {environment} + + )} + {secretPath && ( + <> + Secret Path + {secretPath} + + )} + {failureMessage && ( + <> + Reason: + {failureMessage} + + )} +
+
+ +
+
+ ); +}; + +export default SecretSyncFailedTemplate; + +SecretSyncFailedTemplate.PreviewProps = { + syncDestination: "AWS Parameter Store", + syncUrl: "https://infisical.com", + failureMessage: "Key name cannot contain a colon (:) or a forward slash (/).", + projectName: "Example Project", + secretPath: "/api/secrets", + environment: "Production", + syncName: "my-aws-sync", + siteUrl: "https://infisical.com" +} as SecretSyncFailedTemplateProps; diff --git a/backend/src/services/smtp/emails/ServiceTokenExpiryNoticeTemplate.tsx b/backend/src/services/smtp/emails/ServiceTokenExpiryNoticeTemplate.tsx new file mode 100644 index 0000000000..a45b83d7de --- /dev/null +++ b/backend/src/services/smtp/emails/ServiceTokenExpiryNoticeTemplate.tsx @@ -0,0 +1,53 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface ServiceTokenExpiryNoticeTemplateProps extends Omit { + tokenName: string; + projectName: string; + url: string; +} + +export const ServiceTokenExpiryNoticeTemplate = ({ + tokenName, + siteUrl, + projectName, + url +}: ServiceTokenExpiryNoticeTemplateProps) => { + return ( + + + Service token expiry notice + +
+ + Your service token {tokenName} for the project {projectName} will expire + within 24 hours. + + If this token is still needed for your workflow, please create a new one before it expires. +
+
+ +
+
+ ); +}; + +export default ServiceTokenExpiryNoticeTemplate; + +ServiceTokenExpiryNoticeTemplate.PreviewProps = { + projectName: "Example Project", + siteUrl: "https://infisical.com", + url: "https://infisical.com", + tokenName: "Example Token" +} as ServiceTokenExpiryNoticeTemplateProps; diff --git a/backend/src/services/smtp/emails/SignupEmailVerificationTemplate.tsx b/backend/src/services/smtp/emails/SignupEmailVerificationTemplate.tsx new file mode 100644 index 0000000000..66bb8e6338 --- /dev/null +++ b/backend/src/services/smtp/emails/SignupEmailVerificationTemplate.tsx @@ -0,0 +1,53 @@ +import { Heading, Link, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface SignupEmailVerificationTemplateProps extends Omit { + code: string; + isCloud: boolean; +} + +export const SignupEmailVerificationTemplate = ({ code, siteUrl, isCloud }: SignupEmailVerificationTemplateProps) => { + return ( + + + Confirm your email address + +
+ Enter the confirmation code below in the browser where you started sign-up. + + {code} + +
+
+ + Questions about setting up Infisical?{" "} + {isCloud ? ( + <> + Email us at{" "} + + support@infisical.com + + + ) : ( + "Contact your administrator" + )} + . + +
+
+ ); +}; + +export default SignupEmailVerificationTemplate; + +SignupEmailVerificationTemplate.PreviewProps = { + code: "124356", + isCloud: true, + siteUrl: "https://infisical.com" +} as SignupEmailVerificationTemplateProps; diff --git a/backend/src/services/smtp/emails/UnlockAccountTemplate.tsx b/backend/src/services/smtp/emails/UnlockAccountTemplate.tsx new file mode 100644 index 0000000000..daeabf4fdc --- /dev/null +++ b/backend/src/services/smtp/emails/UnlockAccountTemplate.tsx @@ -0,0 +1,46 @@ +import { Button, Heading, Section, Text } from "@react-email/components"; +import React from "react"; + +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface UnlockAccountTemplateProps extends Omit { + token: string; + callback_url: string; +} + +export const UnlockAccountTemplate = ({ token, siteUrl, callback_url }: UnlockAccountTemplateProps) => { + return ( + + + Unlock your Infisical account + +
+ + Your account has been temporarily locked due to multiple failed login attempts. + + If these attempts were not made by you, reset your password immediately. +
+
+ +
+
+ ); +}; + +export default UnlockAccountTemplate; + +UnlockAccountTemplate.PreviewProps = { + callback_url: "Example Project", + siteUrl: "https://infisical.com", + url: "https://infisical.com", + token: "Example Token" +} as UnlockAccountTemplateProps; diff --git a/backend/src/services/smtp/emails/index.ts b/backend/src/services/smtp/emails/index.ts new file mode 100644 index 0000000000..324f7063e4 --- /dev/null +++ b/backend/src/services/smtp/emails/index.ts @@ -0,0 +1,28 @@ +export * from "./AccessApprovalRequestTemplate"; +export * from "./EmailMfaTemplate"; +export * from "./EmailVerificationTemplate"; +export * from "./ExternalImportFailedTemplate"; +export * from "./ExternalImportStartedTemplate"; +export * from "./ExternalImportSucceededTemplate"; +export * from "./IntegrationSyncFailedTemplate"; +export * from "./NewDeviceLoginTemplate"; +export * from "./OrgAdminBreakglassAccessTemplate"; +export * from "./OrgAdminProjectGrantAccessTemplate"; +export * from "./OrganizationInvitationTemplate"; +export * from "./OrganizationInvitationTemplate"; +export * from "./PasswordResetTemplate"; +export * from "./PasswordSetupTemplate"; +export * from "./PkiExpirationAlertTemplate"; +export * from "./ProjectAccessRequestTemplate"; +export * from "./ProjectInvitationTemplate"; +export * from "./ScimUserProvisionedTemplate"; +export * from "./SecretApprovalRequestBypassedTemplate"; +export * from "./SecretApprovalRequestNeedsReviewTemplate"; +export * from "./SecretLeakIncidentTemplate"; +export * from "./SecretReminderTemplate"; +export * from "./SecretRequestCompletedTemplate"; +export * from "./SecretRotationFailedTemplate"; +export * from "./SecretSyncFailedTemplate"; +export * from "./ServiceTokenExpiryNoticeTemplate"; +export * from "./SignupEmailVerificationTemplate"; +export * from "./UnlockAccountTemplate"; diff --git a/backend/src/services/smtp/smtp-service.ts b/backend/src/services/smtp/smtp-service.ts deleted file mode 100644 index 550e1bb076..0000000000 --- a/backend/src/services/smtp/smtp-service.ts +++ /dev/null @@ -1,112 +0,0 @@ -import fs from "node:fs/promises"; -import path from "node:path"; - -import handlebars from "handlebars"; -import { createTransport } from "nodemailer"; -import SMTPTransport from "nodemailer/lib/smtp-transport"; - -import { getConfig } from "@app/lib/config/env"; -import { logger } from "@app/lib/logger"; - -export type TSmtpConfig = SMTPTransport.Options; -export type TSmtpSendMail = { - template: SmtpTemplates; - subjectLine: string; - recipients: string[]; - substitutions: object; -}; -export type TSmtpService = ReturnType; - -export enum SmtpTemplates { - SignupEmailVerification = "signupEmailVerification.handlebars", - EmailVerification = "emailVerification.handlebars", - SecretReminder = "secretReminder.handlebars", - EmailMfa = "emailMfa.handlebars", - UnlockAccount = "unlockAccount.handlebars", - AccessApprovalRequest = "accessApprovalRequest.handlebars", - AccessSecretRequestBypassed = "accessSecretRequestBypassed.handlebars", - SecretApprovalRequestNeedsReview = "secretApprovalRequestNeedsReview.handlebars", - HistoricalSecretList = "historicalSecretLeakIncident.handlebars", - NewDeviceJoin = "newDevice.handlebars", - OrgInvite = "organizationInvitation.handlebars", - ResetPassword = "passwordReset.handlebars", - SetupPassword = "passwordSetup.handlebars", - SecretLeakIncident = "secretLeakIncident.handlebars", - WorkspaceInvite = "workspaceInvitation.handlebars", - ScimUserProvisioned = "scimUserProvisioned.handlebars", - PkiExpirationAlert = "pkiExpirationAlert.handlebars", - IntegrationSyncFailed = "integrationSyncFailed.handlebars", - SecretSyncFailed = "secretSyncFailed.handlebars", - ExternalImportSuccessful = "externalImportSuccessful.handlebars", - ExternalImportFailed = "externalImportFailed.handlebars", - ExternalImportStarted = "externalImportStarted.handlebars", - SecretRequestCompleted = "secretRequestCompleted.handlebars", - SecretRotationFailed = "secretRotationFailed.handlebars", - ProjectAccessRequest = "projectAccess.handlebars", - OrgAdminProjectDirectAccess = "orgAdminProjectGrantAccess.handlebars", - OrgAdminBreakglassAccess = "orgAdminBreakglassAccess.handlebars", - ServiceTokenExpired = "serviceTokenExpired.handlebars" -} - -export enum SmtpHost { - Sendgrid = "smtp.sendgrid.net", - Mailgun = "smtp.mailgun.org", - SocketLabs = "smtp.sockerlabs.com", - Zohomail = "smtp.zoho.com", - Gmail = "smtp.gmail.com", - Office365 = "smtp.office365.com" -} - -export const smtpServiceFactory = (cfg: TSmtpConfig) => { - const smtp = createTransport(cfg); - const isSmtpOn = Boolean(cfg.host); - - handlebars.registerHelper("emailFooter", () => { - const { SITE_URL } = getConfig(); - return new handlebars.SafeString( - `

Email sent via Infisical at ${SITE_URL}

` - ); - }); - - const sendMail = async ({ substitutions, recipients, template, subjectLine }: TSmtpSendMail) => { - const appCfg = getConfig(); - const html = await fs.readFile(path.resolve(__dirname, "./templates/", template), "utf8"); - const temp = handlebars.compile(html); - const htmlToSend = temp({ isCloud: appCfg.isCloud, siteUrl: appCfg.SITE_URL, ...substitutions }); - - if (isSmtpOn) { - await smtp.sendMail({ - from: cfg.from, - to: recipients.join(", "), - subject: subjectLine, - html: htmlToSend - }); - } else { - logger.info("SMTP is not configured. Outputting it in terminal"); - logger.info({ - from: cfg.from, - to: recipients.join(", "), - subject: subjectLine, - html: htmlToSend - }); - } - }; - - const verify = async () => { - const isConnected = smtp - .verify() - .then(async () => { - logger.info("SMTP connected"); - return true; - }) - .catch((err: Error) => { - logger.error("SMTP error"); - logger.error(err); - return false; - }); - - return isConnected; - }; - - return { sendMail, verify }; -}; diff --git a/backend/src/services/smtp/smtp-service.tsx b/backend/src/services/smtp/smtp-service.tsx new file mode 100644 index 0000000000..dd1755aafd --- /dev/null +++ b/backend/src/services/smtp/smtp-service.tsx @@ -0,0 +1,179 @@ +import { render } from "@react-email/components"; +import handlebars from "handlebars"; +import { createTransport } from "nodemailer"; +import SMTPTransport from "nodemailer/lib/smtp-transport"; +import React from "react"; +import { + AccessApprovalRequestTemplate, + EmailMfaTemplate, + EmailVerificationTemplate, + ExternalImportFailedTemplate, + ExternalImportStartedTemplate, + ExternalImportSucceededTemplate, + IntegrationSyncFailedTemplate, + NewDeviceLoginTemplate, + OrgAdminBreakglassAccessTemplate, + OrganizationInvitationTemplate, + PasswordResetTemplate, + PasswordSetupTemplate, + PkiExpirationAlertTemplate, + ProjectAccessRequestTemplate, + ProjectInvitationTemplate, + ScimUserProvisionedTemplate, + SecretApprovalRequestBypassedTemplate, + SecretApprovalRequestNeedsReviewTemplate, + SecretLeakIncidentTemplate, + SecretReminderTemplate, + SecretRequestCompletedTemplate, + SecretRotationFailedTemplate, + SecretSyncFailedTemplate, + ServiceTokenExpiryNoticeTemplate, + SignupEmailVerificationTemplate, + UnlockAccountTemplate +} from "src/services/smtp/emails"; + +import { getConfig } from "@app/lib/config/env"; +import { logger } from "@app/lib/logger"; + +import OrgAdminProjectGrantAccessTemplate from "./emails/OrgAdminProjectGrantAccessTemplate"; + +export type TSmtpConfig = SMTPTransport.Options; +export type TSmtpSendMail = { + template: SmtpTemplates; + subjectLine: string; + recipients: string[]; + substitutions: object; +}; +export type TSmtpService = ReturnType; + +export enum SmtpTemplates { + SignupEmailVerification = "signupEmailVerification", + EmailVerification = "emailVerification", + SecretReminder = "secretReminder", + EmailMfa = "emailMfa", + UnlockAccount = "unlockAccount", + AccessApprovalRequest = "accessApprovalRequest", + AccessSecretRequestBypassed = "accessSecretRequestBypassed", + SecretApprovalRequestNeedsReview = "secretApprovalRequestNeedsReview", + // HistoricalSecretList = "historicalSecretLeakIncident", not used anymore? + NewDeviceJoin = "newDevice", + OrgInvite = "organizationInvitation", + ResetPassword = "passwordReset", + SetupPassword = "passwordSetup", + SecretLeakIncident = "secretLeakIncident", + WorkspaceInvite = "workspaceInvitation", + ScimUserProvisioned = "scimUserProvisioned", + PkiExpirationAlert = "pkiExpirationAlert", + IntegrationSyncFailed = "integrationSyncFailed", + SecretSyncFailed = "secretSyncFailed", + ExternalImportSuccessful = "externalImportSuccessful", + ExternalImportFailed = "externalImportFailed", + ExternalImportStarted = "externalImportStarted", + SecretRequestCompleted = "secretRequestCompleted", + SecretRotationFailed = "secretRotationFailed", + ProjectAccessRequest = "projectAccess", + OrgAdminProjectDirectAccess = "orgAdminProjectGrantAccess", + OrgAdminBreakglassAccess = "orgAdminBreakglassAccess", + ServiceTokenExpired = "serviceTokenExpired" +} + +export enum SmtpHost { + Sendgrid = "smtp.sendgrid.net", + Mailgun = "smtp.mailgun.org", + SocketLabs = "smtp.sockerlabs.com", + Zohomail = "smtp.zoho.com", + Gmail = "smtp.gmail.com", + Office365 = "smtp.office365.com" +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const EmailTemplateMap: Record> = { + [SmtpTemplates.OrgInvite]: OrganizationInvitationTemplate, + [SmtpTemplates.NewDeviceJoin]: NewDeviceLoginTemplate, + [SmtpTemplates.SignupEmailVerification]: SignupEmailVerificationTemplate, + [SmtpTemplates.EmailMfa]: EmailMfaTemplate, + [SmtpTemplates.AccessApprovalRequest]: AccessApprovalRequestTemplate, + [SmtpTemplates.EmailVerification]: EmailVerificationTemplate, + [SmtpTemplates.ExternalImportFailed]: ExternalImportFailedTemplate, + [SmtpTemplates.ExternalImportStarted]: ExternalImportStartedTemplate, + [SmtpTemplates.ExternalImportSuccessful]: ExternalImportSucceededTemplate, + [SmtpTemplates.AccessSecretRequestBypassed]: SecretApprovalRequestBypassedTemplate, + [SmtpTemplates.IntegrationSyncFailed]: IntegrationSyncFailedTemplate, + [SmtpTemplates.OrgAdminBreakglassAccess]: OrgAdminBreakglassAccessTemplate, + [SmtpTemplates.SecretLeakIncident]: SecretLeakIncidentTemplate, + [SmtpTemplates.WorkspaceInvite]: ProjectInvitationTemplate, + [SmtpTemplates.ScimUserProvisioned]: ScimUserProvisionedTemplate, + [SmtpTemplates.SecretRequestCompleted]: SecretRequestCompletedTemplate, + [SmtpTemplates.UnlockAccount]: UnlockAccountTemplate, + [SmtpTemplates.ServiceTokenExpired]: ServiceTokenExpiryNoticeTemplate, + [SmtpTemplates.SecretReminder]: SecretReminderTemplate, + [SmtpTemplates.SecretRotationFailed]: SecretRotationFailedTemplate, + [SmtpTemplates.SecretSyncFailed]: SecretSyncFailedTemplate, + [SmtpTemplates.OrgAdminProjectDirectAccess]: OrgAdminProjectGrantAccessTemplate, + [SmtpTemplates.ProjectAccessRequest]: ProjectAccessRequestTemplate, + [SmtpTemplates.SecretApprovalRequestNeedsReview]: SecretApprovalRequestNeedsReviewTemplate, + [SmtpTemplates.ResetPassword]: PasswordResetTemplate, + [SmtpTemplates.SetupPassword]: PasswordSetupTemplate, + [SmtpTemplates.PkiExpirationAlert]: PkiExpirationAlertTemplate +}; + +export const smtpServiceFactory = (cfg: TSmtpConfig) => { + const smtp = createTransport(cfg); + const isSmtpOn = Boolean(cfg.host); + + handlebars.registerHelper("emailFooter", () => { + const { SITE_URL } = getConfig(); + return new handlebars.SafeString( + `

Email sent via Infisical at ${SITE_URL}

` + ); + }); + + const sendMail = async ({ substitutions, recipients, template, subjectLine }: TSmtpSendMail) => { + const appCfg = getConfig(); + + const EmailTemplate = EmailTemplateMap[template]; + + if (!EmailTemplate) { + throw new Error(`Email template ${template} not found`); + } + + const htmlToSend = await render( + + ); + + if (isSmtpOn) { + await smtp.sendMail({ + from: cfg.from, + to: recipients.join(", "), + subject: subjectLine, + html: htmlToSend + }); + } else { + logger.info("SMTP is not configured. Outputting it in terminal"); + logger.info({ + from: cfg.from, + to: recipients.join(", "), + subject: subjectLine, + html: htmlToSend + }); + } + }; + + const verify = async () => { + const isConnected = smtp + .verify() + .then(async () => { + logger.info("SMTP connected"); + return true; + }) + .catch((err: Error) => { + logger.error("SMTP error"); + logger.error(err); + return false; + }); + + return isConnected; + }; + + return { sendMail, verify }; +}; diff --git a/backend/src/services/smtp/templates/accessApprovalRequest.handlebars b/backend/src/services/smtp/templates/accessApprovalRequest.handlebars deleted file mode 100644 index 6813c12005..0000000000 --- a/backend/src/services/smtp/templates/accessApprovalRequest.handlebars +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Access Approval Request - - - -

Infisical

-

New access approval request pending your review

-

You have a new access approval request pending review in project "{{projectName}}".

- -

- {{requesterFullName}} - ({{requesterEmail}}) has requested - {{#if isTemporary}} - temporary - {{else}} - permanent - {{/if}} - access to - {{secretPath}} - in the - {{environment}} - environment. - - {{#if isTemporary}} -
- This access will expire - {{expiresIn}} - after it has been approved. - {{/if}} -

-

- The following permissions are requested: -

    - {{#each permissions}} -
  • {{this}}
  • - {{/each}} -
-

- {{#if note}} -

User Note: "{{note}}"

- {{/if}} - -

- View the request and approve or deny it - here. -

- - {{emailFooter}} - - - diff --git a/backend/src/services/smtp/templates/accessSecretRequestBypassed.handlebars b/backend/src/services/smtp/templates/accessSecretRequestBypassed.handlebars deleted file mode 100644 index 8c82df289c..0000000000 --- a/backend/src/services/smtp/templates/accessSecretRequestBypassed.handlebars +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Secret Approval Request Policy Bypassed - - - -

Infisical

-

Secret Approval Request Bypassed

-

A secret approval request has been bypassed in the project "{{projectName}}".

- -

- {{requesterFullName}} - ({{requesterEmail}}) has merged a secret to environment - {{environment}} - at secret path - {{secretPath}} - without obtaining the required approvals. -

-

- The following reason was provided for bypassing the policy: - {{bypassReason}} -

- -

- To review this action, please visit the request panel - here. -

- - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/emailMfa.handlebars b/backend/src/services/smtp/templates/emailMfa.handlebars deleted file mode 100644 index 4c948b08c0..0000000000 --- a/backend/src/services/smtp/templates/emailMfa.handlebars +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - MFA Code - - - -

Infisical

-

Sign in attempt requires further verification

-

Your MFA code is below — enter it where you started signing in to Infisical.

-

{{code}}

-

The MFA code will be valid for 2 minutes.

-

Not you? Contact {{#if isCloud}}Infisical{{else}}your administrator{{/if}} immediately.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/emailVerification.handlebars b/backend/src/services/smtp/templates/emailVerification.handlebars deleted file mode 100644 index 4a989626e4..0000000000 --- a/backend/src/services/smtp/templates/emailVerification.handlebars +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Code - - - -

Confirm your email address

-

Your confirmation code is below — enter it in the browser window where you've started confirming your email.

-

{{code}}

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/historicalSecretLeakIncident.handlebars b/backend/src/services/smtp/templates/historicalSecretLeakIncident.handlebars deleted file mode 100644 index 4a918ee0d6..0000000000 --- a/backend/src/services/smtp/templates/historicalSecretLeakIncident.handlebars +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - Incident alert: secrets potentially leaked - - - -

Infisical has uncovered {{numberOfSecrets}} secret(s) from historical commits to your repo

-

View leaked secrets

- -

If these are production secrets, please rotate them immediately.

- -

Once you have taken action, be sure to update the status of the risk in your - Infisical dashboard.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/integrationSyncFailed.handlebars b/backend/src/services/smtp/templates/integrationSyncFailed.handlebars deleted file mode 100644 index 2aff820fad..0000000000 --- a/backend/src/services/smtp/templates/integrationSyncFailed.handlebars +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - Integration Sync Failed - - - -

Infisical

- -
-

{{count}} integration(s) failed to sync.

- - View your project integrations. - -
- -
-
-

Project: {{projectName}}

-

Environment: {{environment}}

-

Secret Path: {{secretPath}}

-
- - {{#if syncMessage}} -

Reason: {{syncMessage}}

- {{/if}} - - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/newDevice.handlebars b/backend/src/services/smtp/templates/newDevice.handlebars deleted file mode 100644 index 197e0b7a7c..0000000000 --- a/backend/src/services/smtp/templates/newDevice.handlebars +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Successful login for {{email}} from new device - - - -

Infisical

-

We're verifying a recent login for {{email}}:

-

Timestamp: {{timestamp}}

-

IP address: {{ip}}

-

User agent: {{userAgent}}

-

If you believe that this login is suspicious, please contact - {{#if isCloud}}Infisical{{else}}your administrator{{/if}} - or reset your password immediately.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/orgAdminBreakglassAccess.handlebars b/backend/src/services/smtp/templates/orgAdminBreakglassAccess.handlebars deleted file mode 100644 index cc97ff2019..0000000000 --- a/backend/src/services/smtp/templates/orgAdminBreakglassAccess.handlebars +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Organization admin has bypassed SSO - - - -

Infisical

-

The organization admin {{email}} has bypassed enforced SSO login.

-

Timestamp: {{timestamp}}

-

IP address: {{ip}}

-

User agent: {{userAgent}}

-

If you'd like to disable Admin SSO Bypass, please visit Organization Settings > Security.

- - {{emailFooter}} - - - diff --git a/backend/src/services/smtp/templates/orgAdminProjectGrantAccess.handlebars b/backend/src/services/smtp/templates/orgAdminProjectGrantAccess.handlebars deleted file mode 100644 index ef8c6e6b42..0000000000 --- a/backend/src/services/smtp/templates/orgAdminProjectGrantAccess.handlebars +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Organization admin issued direct access to project - - - -

Infisical

-

The organization admin {{email}} has granted direct access to the project "{{projectName}}".

- - {{emailFooter}} - - - diff --git a/backend/src/services/smtp/templates/organizationInvitation.handlebars b/backend/src/services/smtp/templates/organizationInvitation.handlebars deleted file mode 100644 index da429477be..0000000000 --- a/backend/src/services/smtp/templates/organizationInvitation.handlebars +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - Organization Invitation - - -

Join your organization on Infisical

-

{{inviterFirstName}} ({{inviterUsername}}) has invited you to their Infisical organization named {{organizationName}}

- Click to join -

What is Infisical?

-

Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets and configs.

- - {{emailFooter}} - - diff --git a/backend/src/services/smtp/templates/passwordReset.handlebars b/backend/src/services/smtp/templates/passwordReset.handlebars deleted file mode 100644 index 1cb2ae8ce1..0000000000 --- a/backend/src/services/smtp/templates/passwordReset.handlebars +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Account Recovery - - -

Reset your password

-

Someone requested a password reset.

- Reset password -

If you didn't initiate this request, please contact - {{#if isCloud}}us immediately at team@infisical.com.{{else}}your administrator immediately.{{/if}}

- - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/passwordSetup.handlebars b/backend/src/services/smtp/templates/passwordSetup.handlebars deleted file mode 100644 index 1059a74460..0000000000 --- a/backend/src/services/smtp/templates/passwordSetup.handlebars +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Password Setup - - -

Setup your password

-

Someone requested to set up a password for your account.

-

Make sure you are already logged in to Infisical in the current browser before clicking the link below.

- Setup password -

If you didn't initiate this request, please contact - {{#if isCloud}}us immediately at team@infisical.com.{{else}}your administrator immediately.{{/if}}

- - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/pkiExpirationAlert.handlebars b/backend/src/services/smtp/templates/pkiExpirationAlert.handlebars deleted file mode 100644 index f9013e24db..0000000000 --- a/backend/src/services/smtp/templates/pkiExpirationAlert.handlebars +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Infisical CA/Certificate expiration notice - - -

Hello,

-

This is an automated alert for "{{alertName}}" triggered for CAs/Certificates expiring in - {{alertBeforeDays}} - days.

- -

Expiring Items:

-
    - {{#each items}} -
  • - {{type}}: - {{friendlyName}} -
    Serial Number: - {{serialNumber}} -
    Expires On: - {{expiryDate}} -
  • - {{/each}} -
- -

Please take necessary actions to renew these items before they expire.

- -

For more details, please log in to your Infisical account and check your PKI management section.

- - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/projectAccess.handlebars b/backend/src/services/smtp/templates/projectAccess.handlebars deleted file mode 100644 index 5ff1ca7ec4..0000000000 --- a/backend/src/services/smtp/templates/projectAccess.handlebars +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Project Access Request - - - -

Infisical

-

You have a new project access request!

-
    -
  • Requester Name: "{{requesterName}}"
  • -
  • Requester Email: "{{requesterEmail}}"
  • -
  • Project Name: "{{projectName}}"
  • -
  • Organization Name: "{{orgName}}"
  • -
  • User Note: "{{note}}"
  • -
-

- Please click on the link below to grant access -

- Grant Access - {{emailFooter}} - - - diff --git a/backend/src/services/smtp/templates/scimUserProvisioned.handlebars b/backend/src/services/smtp/templates/scimUserProvisioned.handlebars deleted file mode 100644 index ba04d72011..0000000000 --- a/backend/src/services/smtp/templates/scimUserProvisioned.handlebars +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Organization Invitation - - -

Join your organization on Infisical

-

You've been invited to join the Infisical organization — {{organizationName}}

- Join now -

What is Infisical?

-

Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets - and configs.

- - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretApprovalRequestNeedsReview.handlebars b/backend/src/services/smtp/templates/secretApprovalRequestNeedsReview.handlebars deleted file mode 100644 index c12c084607..0000000000 --- a/backend/src/services/smtp/templates/secretApprovalRequestNeedsReview.handlebars +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Secret Change Approval Request - - - -

Hi {{firstName}},

-

New secret change requests are pending review.

-
-

You have a secret change request pending your review in project "{{projectName}}", in the "{{organizationName}}" - organization.

- -

- View the request and approve or deny it - here. -

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretLeakIncident.handlebars b/backend/src/services/smtp/templates/secretLeakIncident.handlebars deleted file mode 100644 index d0d9a617ce..0000000000 --- a/backend/src/services/smtp/templates/secretLeakIncident.handlebars +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Incident alert: secret leaked - - - -

Infisical has uncovered {{numberOfSecrets}} secret(s) from your recent push

-

View leaked secrets

-

You are receiving this notification because one or more secret leaks have been detected in a recent commit pushed - by - {{pusher_name}} - ({{pusher_email}}). If these are test secrets, please add `infisical-scan:ignore` at the end of the line - containing the secret as comment in the given programming. This will prevent future notifications from being sent - out for those secret(s).

- -

If these are production secrets, please rotate them immediately.

- -

Once you have taken action, be sure to update the status of the risk in your - Infisical dashboard.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretReminder.handlebars b/backend/src/services/smtp/templates/secretReminder.handlebars deleted file mode 100644 index d64c4bf42f..0000000000 --- a/backend/src/services/smtp/templates/secretReminder.handlebars +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Secret Reminder - - - -

Infisical

-

You have a new secret reminder!

-

You have a new secret reminder from project "{{projectName}}", in {{organizationName}}

- {{#if reminderNote}} -

Here's the note included with the reminder: {{reminderNote}}

- {{/if}} - - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretRequestCompleted.handlebars b/backend/src/services/smtp/templates/secretRequestCompleted.handlebars deleted file mode 100644 index d2cefdd546..0000000000 --- a/backend/src/services/smtp/templates/secretRequestCompleted.handlebars +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - Secret Request Completed - - - -

Infisical

-

A secret has been shared with you

- - {{#if name}} -

Secret request name: {{name}}

- {{/if}} - {{#if respondentUsername}} -

Shared by: {{respondentUsername}}

- {{/if}} - -
-
- -

- You can access the secret by clicking the link below. -

-

- Access Secret -

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretRotationFailed.handlebars b/backend/src/services/smtp/templates/secretRotationFailed.handlebars deleted file mode 100644 index 728798ce85..0000000000 --- a/backend/src/services/smtp/templates/secretRotationFailed.handlebars +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Your {{rotationType}} Rotation "{{rotationName}}" Failed to Rotate - - - -

Infisical

- - - -
-
-

Name: {{rotationName}}

-

Type: {{rotationType}}

-

Project: {{projectName}}

-

Environment: {{environment}}

-

Secret Path: {{secretPath}}

-
- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/secretSyncFailed.handlebars b/backend/src/services/smtp/templates/secretSyncFailed.handlebars deleted file mode 100644 index 3e7ad7831d..0000000000 --- a/backend/src/services/smtp/templates/secretSyncFailed.handlebars +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - {{syncDestination}} Sync "{{syncName}}" Failed - - - -

Infisical

- -
-

{{content}}

- - View in Infisical. - -
- -
-
-

Name: {{syncName}}

-

Destination: {{syncDestination}}

-

Project: {{projectName}}

- {{#if environment}} -

Environment: {{environment}}

- {{/if}} - {{#if secretPath}} -

Secret Path: {{secretPath}}

- {{/if}} -
- - {{#if failureMessage}} -

Reason: {{failureMessage}}

- {{/if}} - - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/serviceTokenExpired.handlebars b/backend/src/services/smtp/templates/serviceTokenExpired.handlebars deleted file mode 100644 index 199150c051..0000000000 --- a/backend/src/services/smtp/templates/serviceTokenExpired.handlebars +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Service Token Expiring Soon - - - -

Service Token Expiry Notice

-

Your service token "{{tokenName}}" will expire within 24 hours.

- -

This token is currently being used on project "{{projectName}}". If this token is still needed for your workflow, please create a new one before it expires.

- - Create New Token - - {{emailFooter}} - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/signupEmailVerification.handlebars b/backend/src/services/smtp/templates/signupEmailVerification.handlebars deleted file mode 100644 index 39f47ae48b..0000000000 --- a/backend/src/services/smtp/templates/signupEmailVerification.handlebars +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Code - - - -

Confirm your email address

-

Your confirmation code is below — enter it in the browser window where you've started signing up for Infisical.

-

{{code}}

-

Questions about setting up Infisical? - {{#if isCloud}}Email us at support@infisical.com{{else}}Contact your administrator{{/if}}.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/unlockAccount.handlebars b/backend/src/services/smtp/templates/unlockAccount.handlebars deleted file mode 100644 index b65cb56259..0000000000 --- a/backend/src/services/smtp/templates/unlockAccount.handlebars +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Your Infisical account has been locked - - - -

Unlock your Infisical account

-

Your account has been temporarily locked due to multiple failed login attempts. - To unlock your account, follow the link here -

If these attempts were not made by you, reset your password immediately.

- - {{emailFooter}} - - - \ No newline at end of file diff --git a/backend/src/services/smtp/templates/workspaceInvitation.handlebars b/backend/src/services/smtp/templates/workspaceInvitation.handlebars deleted file mode 100644 index fde75a6d65..0000000000 --- a/backend/src/services/smtp/templates/workspaceInvitation.handlebars +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Project Invitation - - -

Join your team on Infisical

-

You have been invited to a new Infisical project named {{workspaceName}}

- Click to join -

What is Infisical?

-

Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets - and configs.

- - {{emailFooter}} - - diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 8c23a00c68..8687826c8d 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -172,8 +172,8 @@ export const superAdminServiceFactory = ({ const canServerAdminAccessAfterApply = data.enabledLoginMethods.some((loginMethod) => - loginMethodToAuthMethod[loginMethod as LoginMethod].some( - (authMethod) => superAdminUser.authMethods?.includes(authMethod) + loginMethodToAuthMethod[loginMethod as LoginMethod].some((authMethod) => + superAdminUser.authMethods?.includes(authMethod) ) ) || isUserSamlAccessEnabled || diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 90165acbe2..3d78132577 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -25,7 +25,8 @@ "baseUrl": ".", "paths": { "@app/*": ["./src/*"] - } + }, + "jsx": "react" }, "include": ["src/**/*", "scripts/**/*", "e2e-test/**/*", "./.eslintrc.js", "./tsup.config.js"], "exclude": ["node_modules"]