@hartl-services/medusa-base 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +3 -1
- package/.medusa/server/src/admin/index.mjs +3 -1
- package/.medusa/server/src/api/customer-verification-request-throttle.js +50 -0
- package/.medusa/server/src/api/middlewares.js +46 -1
- package/.medusa/server/src/api/password-policy.js +23 -0
- package/.medusa/server/src/api/store/customer-verification/resend/route.js +17 -0
- package/.medusa/server/src/email/application/handle-customer-verification-event.js +341 -0
- package/.medusa/server/src/email/application/resend-customer-verification.js +65 -0
- package/.medusa/server/src/email/application/start-customer-verification-snapshot.js +21 -0
- package/.medusa/server/src/email/application/throttle-verification-request.js +49 -0
- package/.medusa/server/src/email/customer-verification-snapshot.js +144 -0
- package/.medusa/server/src/email/domain/business-keys.js +12 -2
- package/.medusa/server/src/email/domain/customer-verification-resend.js +37 -0
- package/.medusa/server/src/email/domain/event-schemas.js +18 -2
- package/.medusa/server/src/email/plugin-features.js +9 -2
- package/.medusa/server/src/email/rendering/default-templates.js +20 -1
- package/.medusa/server/src/email/rendering/resolve-shop-configuration.js +5 -1
- package/.medusa/server/src/email/types/email-kinds.js +14 -2
- package/.medusa/server/src/email/types/template-data.js +9 -1
- package/.medusa/server/src/modules/email/migrations/Migration20261001090000.js +16 -0
- package/.medusa/server/src/modules/email/service.js +30 -8
- package/.medusa/server/src/shop-config/build-food-shop-config.js +16 -1
- package/.medusa/server/src/subscribers/customer-verification.js +23 -0
- package/.medusa/server/src/workflows/email/dispatch-email-message.js +4 -2
- package/README.md +166 -15
- package/dist/email/customer-verification-snapshot.d.ts +53 -0
- package/dist/email/customer-verification-snapshot.js +143 -0
- package/dist/email/types/email-kinds.d.ts +9 -1
- package/dist/email/types/email-kinds.js +13 -1
- package/dist/email/types/shop-configuration.d.ts +8 -0
- package/dist/email/types/template-data.d.ts +9 -0
- package/dist/email/types/template-data.js +8 -0
- package/dist/shop-config/build-food-shop-config.js +15 -0
- package/package.json +2 -2
|
@@ -1665,6 +1665,7 @@ const KINDS = [
|
|
|
1665
1665
|
"order.return_received",
|
|
1666
1666
|
"order.refunded",
|
|
1667
1667
|
"auth.password_reset",
|
|
1668
|
+
"auth.verification",
|
|
1668
1669
|
"affiliate.consignment_report_due"
|
|
1669
1670
|
];
|
|
1670
1671
|
const STATUSES = [
|
|
@@ -1686,6 +1687,7 @@ const KIND_LABELS = {
|
|
|
1686
1687
|
"order.return_received": "Retoureneingang",
|
|
1687
1688
|
"order.refunded": "Rückerstattung",
|
|
1688
1689
|
"auth.password_reset": "Passwort zurücksetzen",
|
|
1690
|
+
"auth.verification": "Konto bestätigen",
|
|
1689
1691
|
"affiliate.consignment_report_due": "Affiliate-Meldung"
|
|
1690
1692
|
};
|
|
1691
1693
|
const STATUS_LABELS = {
|
|
@@ -1706,7 +1708,7 @@ const ATTEMPT_STATUS_LABELS = {
|
|
|
1706
1708
|
};
|
|
1707
1709
|
const isKindFilter = (value) => value === "all" || KINDS.includes(value);
|
|
1708
1710
|
const isStatusFilter = (value) => value === "all" || STATUSES.includes(value);
|
|
1709
|
-
const canResend = (message) => message.kind !== "auth.password_reset" && (message.status === "sent" || message.status === "unknown" || message.status === "failed");
|
|
1711
|
+
const canResend = (message) => message.kind !== "auth.password_reset" && message.kind !== "auth.verification" && (message.status === "sent" || message.status === "unknown" || message.status === "failed");
|
|
1710
1712
|
const formatDateTime$1 = (value) => {
|
|
1711
1713
|
if (!value) {
|
|
1712
1714
|
return "-";
|
|
@@ -1662,6 +1662,7 @@ const KINDS = [
|
|
|
1662
1662
|
"order.return_received",
|
|
1663
1663
|
"order.refunded",
|
|
1664
1664
|
"auth.password_reset",
|
|
1665
|
+
"auth.verification",
|
|
1665
1666
|
"affiliate.consignment_report_due"
|
|
1666
1667
|
];
|
|
1667
1668
|
const STATUSES = [
|
|
@@ -1683,6 +1684,7 @@ const KIND_LABELS = {
|
|
|
1683
1684
|
"order.return_received": "Retoureneingang",
|
|
1684
1685
|
"order.refunded": "Rückerstattung",
|
|
1685
1686
|
"auth.password_reset": "Passwort zurücksetzen",
|
|
1687
|
+
"auth.verification": "Konto bestätigen",
|
|
1686
1688
|
"affiliate.consignment_report_due": "Affiliate-Meldung"
|
|
1687
1689
|
};
|
|
1688
1690
|
const STATUS_LABELS = {
|
|
@@ -1703,7 +1705,7 @@ const ATTEMPT_STATUS_LABELS = {
|
|
|
1703
1705
|
};
|
|
1704
1706
|
const isKindFilter = (value) => value === "all" || KINDS.includes(value);
|
|
1705
1707
|
const isStatusFilter = (value) => value === "all" || STATUSES.includes(value);
|
|
1706
|
-
const canResend = (message) => message.kind !== "auth.password_reset" && (message.status === "sent" || message.status === "unknown" || message.status === "failed");
|
|
1708
|
+
const canResend = (message) => message.kind !== "auth.password_reset" && message.kind !== "auth.verification" && (message.status === "sent" || message.status === "unknown" || message.status === "failed");
|
|
1707
1709
|
const formatDateTime$1 = (value) => {
|
|
1708
1710
|
if (!value) {
|
|
1709
1711
|
return "-";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.throttleCustomerVerificationRequest = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const throttle_verification_request_1 = require("../email/application/throttle-verification-request");
|
|
6
|
+
const plugin_features_1 = require("../email/plugin-features");
|
|
7
|
+
const public_1 = require("../public");
|
|
8
|
+
/**
|
|
9
|
+
* Runs before Medusa's `POST /auth/verification/request`, the route the
|
|
10
|
+
* storefront calls at every blocked login (the resend route's own 60 s limit
|
|
11
|
+
* would not cover it). While `customer_verification` is on and the same
|
|
12
|
+
* customer account asked for its confirmation mail within the last minute,
|
|
13
|
+
* it answers like Medusa (201 `{ verification }`, without the token hash)
|
|
14
|
+
* and sends nothing, so repeated logins cannot mail-bomb an address. Every
|
|
15
|
+
* other request, and any failure of the check, goes on to Medusa unchanged.
|
|
16
|
+
* Needs `req.auth_context`: authenticate the same way Medusa does first.
|
|
17
|
+
*/
|
|
18
|
+
const throttleCustomerVerificationRequest = async (req, res, next) => {
|
|
19
|
+
try {
|
|
20
|
+
const body = req.body;
|
|
21
|
+
const authIdentityId = req.auth_context?.auth_identity_id;
|
|
22
|
+
if (body?.entity_type === "email" &&
|
|
23
|
+
typeof body.entity_id === "string" &&
|
|
24
|
+
authIdentityId &&
|
|
25
|
+
(await (0, public_1.isPluginFeatureEnabled)(req.scope, plugin_features_1.emailPluginFeatures, "account_mails", "customer_verification"))) {
|
|
26
|
+
const verification = await (0, throttle_verification_request_1.findThrottledVerificationRequest)(req.scope, {
|
|
27
|
+
authIdentityId,
|
|
28
|
+
entityId: body.entity_id,
|
|
29
|
+
});
|
|
30
|
+
if (verification) {
|
|
31
|
+
res.status(201).json({ verification });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Fail open with a fixed message: the error text can carry the address.
|
|
38
|
+
try {
|
|
39
|
+
req.scope
|
|
40
|
+
.resolve(utils_1.ContainerRegistrationKeys.LOGGER)
|
|
41
|
+
.warn("Customer verification request could not be rate limited.");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Logging must never block the login flow.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
next();
|
|
48
|
+
};
|
|
49
|
+
exports.throttleCustomerVerificationRequest = throttleCustomerVerificationRequest;
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItdmVyaWZpY2F0aW9uLXJlcXVlc3QtdGhyb3R0bGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL2N1c3RvbWVyLXZlcmlmaWNhdGlvbi1yZXF1ZXN0LXRocm90dGxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQU1BLHFEQUFxRTtBQUVyRSxzR0FBcUc7QUFDckcsOERBQThEO0FBQzlELHNDQUFrRDtBQUVsRDs7Ozs7Ozs7O0dBU0c7QUFDSSxNQUFNLG1DQUFtQyxHQUFHLEtBQUssRUFDdEQsR0FBa0IsRUFDbEIsR0FBbUIsRUFDbkIsSUFBd0IsRUFDVCxFQUFFO0lBQ2pCLElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHLEdBQUcsQ0FBQyxJQUFzRCxDQUFBO1FBQ3ZFLE1BQU0sY0FBYyxHQUNsQixHQUNELENBQUMsWUFBWSxFQUFFLGdCQUFnQixDQUFBO1FBQ2hDLElBQ0UsSUFBSSxFQUFFLFdBQVcsS0FBSyxPQUFPO1lBQzdCLE9BQU8sSUFBSSxDQUFDLFNBQVMsS0FBSyxRQUFRO1lBQ2xDLGNBQWM7WUFDZCxDQUFDLE1BQU0sSUFBQSwrQkFBc0IsRUFDM0IsR0FBRyxDQUFDLEtBQUssRUFDVCxxQ0FBbUIsRUFDbkIsZUFBZSxFQUNmLHVCQUF1QixDQUN4QixDQUFDLEVBQ0YsQ0FBQztZQUNELE1BQU0sWUFBWSxHQUFHLE1BQU0sSUFBQSxnRUFBZ0MsRUFBQyxHQUFHLENBQUMsS0FBSyxFQUFFO2dCQUNyRSxjQUFjO2dCQUNkLFFBQVEsRUFBRSxJQUFJLENBQUMsU0FBUzthQUN6QixDQUFDLENBQUE7WUFDRixJQUFJLFlBQVksRUFBRSxDQUFDO2dCQUNqQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUE7Z0JBQ3RDLE9BQU07WUFDUixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCx3RUFBd0U7UUFDeEUsSUFBSSxDQUFDO1lBQ0gsR0FBRyxDQUFDLEtBQUs7aUJBQ04sT0FBTyxDQUFTLGlDQUF5QixDQUFDLE1BQU0sQ0FBQztpQkFDakQsSUFBSSxDQUFDLDBEQUEwRCxDQUFDLENBQUE7UUFDckUsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLDJDQUEyQztRQUM3QyxDQUFDO0lBQ0gsQ0FBQztJQUNELElBQUksRUFBRSxDQUFBO0FBQ1IsQ0FBQyxDQUFBO0FBekNZLFFBQUEsbUNBQW1DLHVDQXlDL0MifQ==
|
|
@@ -4,9 +4,12 @@ const http_1 = require("@medusajs/framework/http");
|
|
|
4
4
|
const plugin_features_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/plugin-features");
|
|
5
5
|
const documents_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/documents");
|
|
6
6
|
const email_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/email");
|
|
7
|
+
const customer_verification_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-verification");
|
|
7
8
|
const files_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/files");
|
|
8
9
|
const plugin_features_2 = require("../documents/plugin-features");
|
|
9
10
|
const plugin_features_3 = require("../email/plugin-features");
|
|
11
|
+
const customer_verification_request_throttle_1 = require("./customer-verification-request-throttle");
|
|
12
|
+
const password_policy_1 = require("./password-policy");
|
|
10
13
|
const public_1 = require("../public");
|
|
11
14
|
/**
|
|
12
15
|
* Feature-toggle gate for the Documents routes. Without `methods` Medusa
|
|
@@ -108,6 +111,48 @@ exports.default = (0, http_1.defineMiddlewares)({
|
|
|
108
111
|
methods: ["GET"],
|
|
109
112
|
middlewares: [(0, http_1.authenticate)("user", ["session", "bearer"])],
|
|
110
113
|
},
|
|
114
|
+
// Account confirmation: the resend route answers 404 while the
|
|
115
|
+
// `customer_verification` feature (or Konto-Mails, its parent) is off.
|
|
116
|
+
// Public by design (the customer cannot log in yet); the 60 s limit per
|
|
117
|
+
// account and the uniform 202 answer live in the use case.
|
|
118
|
+
{
|
|
119
|
+
matcher: "/store/customer-verification",
|
|
120
|
+
middlewares: [
|
|
121
|
+
(0, public_1.requirePluginFeature)(plugin_features_3.emailPluginFeatures, "account_mails", "customer_verification"),
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
matcher: "/store/customer-verification/resend",
|
|
126
|
+
methods: ["POST"],
|
|
127
|
+
middlewares: [(0, http_1.validateAndTransformBody)(customer_verification_1.ResendCustomerVerificationSchema)],
|
|
128
|
+
},
|
|
129
|
+
// Medusa's own verification request (the storefront calls it at every
|
|
130
|
+
// blocked login) gets the same 60 s limit per account as the resend
|
|
131
|
+
// route. Medusa registers its entry for this matcher first (core routes
|
|
132
|
+
// load before plugin routes), so authentication has run; it is repeated
|
|
133
|
+
// here so the limit never depends on that order.
|
|
134
|
+
{
|
|
135
|
+
matcher: "/auth/verification/request",
|
|
136
|
+
methods: ["POST"],
|
|
137
|
+
middlewares: [
|
|
138
|
+
(0, http_1.authenticate)("*", ["session", "bearer"], { allowUnregistered: true }),
|
|
139
|
+
customer_verification_request_throttle_1.throttleCustomerVerificationRequest,
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
// Password policy for customers and Admin users: registration (sign-up
|
|
143
|
+
// and Admin invite) and the password reset. Without `methods` the entry
|
|
144
|
+
// is an `app.use` prefix match that runs before Medusa's method-specific
|
|
145
|
+
// entries, so a rejected password never consumes the single-use reset
|
|
146
|
+
// token (Medusa's `validateToken` deletes it on the update route). Both
|
|
147
|
+
// paths only exist for POST.
|
|
148
|
+
{
|
|
149
|
+
matcher: "/auth/:actor_type/emailpass/register",
|
|
150
|
+
middlewares: [password_policy_1.enforcePasswordPolicy],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
matcher: "/auth/:actor_type/emailpass/update",
|
|
154
|
+
middlewares: [password_policy_1.enforcePasswordPolicy],
|
|
155
|
+
},
|
|
111
156
|
// --- Dokumente ------------------------------------------------------
|
|
112
157
|
// Feature-toggle gates (see plugin-features.ts). They cover every
|
|
113
158
|
// Documents route and nothing else; subscribers and jobs check the
|
|
@@ -203,4 +248,4 @@ exports.default = (0, http_1.defineMiddlewares)({
|
|
|
203
248
|
},
|
|
204
249
|
],
|
|
205
250
|
});
|
|
206
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
251
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBS2lDO0FBQ2pDLCtHQUtrRjtBQUNsRixtR0FHNEU7QUFDNUUsMkZBS3dFO0FBQ3hFLDJIQUFrSTtBQUNsSSwyRkFBdUg7QUFFdkgsa0VBR3FDO0FBQ3JDLDhEQUE4RDtBQUM5RCxxR0FBOEY7QUFDOUYsdURBQXlEO0FBQ3pELHNDQUFnRDtBQUVoRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLGFBQWEsR0FBRyxDQUFDLE9BQWUsRUFBRSxHQUFHLElBQTJCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUUsT0FBTztJQUNQLFdBQVcsRUFBRSxDQUFDLElBQUEsNkJBQW9CLEVBQUMseUNBQXVCLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztDQUN0RSxDQUFDLENBQUE7QUFFRix5RUFBeUU7QUFDekUsbUVBQW1FO0FBQ25FLHVFQUF1RTtBQUN2RSwyRUFBMkU7QUFDM0UsMEVBQTBFO0FBQzFFLHVDQUF1QztBQUN2QyxrQkFBZSxJQUFBLHdCQUFpQixFQUFDO0lBQy9CLE1BQU0sRUFBRTtRQUNOO1lBQ0UsT0FBTyxFQUFFLHdCQUF3QjtZQUNqQyxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFLENBQUMsSUFBQSwrQkFBd0IsRUFBQyw0Q0FBMEIsQ0FBQyxDQUFDO1NBQ3BFO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsNkNBQTZDO1lBQ3RELE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQyxJQUFBLCtCQUF3QixFQUFDLDRDQUEwQixDQUFDLENBQUM7U0FDcEU7UUFDRDtZQUNFLE9BQU8sRUFBRSx3REFBd0Q7WUFDakUsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSxDQUFDLElBQUEsK0JBQXdCLEVBQUMsdUNBQXFCLENBQUMsQ0FBQztTQUMvRDtRQUNEO1lBQ0UsT0FBTyxFQUFFLDZEQUE2RDtZQUN0RSxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFO2dCQUNYLElBQUEsZ0NBQXlCLEVBQUMsaURBQStCLEVBQUU7b0JBQ3pELE1BQU0sRUFBRSxLQUFLO2lCQUNkLENBQUM7YUFDSDtTQUNGO1FBQ0QsdUVBQXVFO1FBQ3ZFLHlFQUF5RTtRQUN6RSxxRUFBcUU7UUFDckUsd0VBQXdFO1FBQ3hFLG1EQUFtRDtRQUNuRCxxRUFBcUU7UUFDckU7WUFDRSxPQUFPLEVBQUUsY0FBYztZQUN2QixXQUFXLEVBQUUsQ0FBQyxJQUFBLDZCQUFvQixFQUFDLHFDQUFtQixDQUFDLENBQUM7U0FDekQ7UUFDRDtZQUNFLE9BQU8sRUFBRSx1QkFBdUI7WUFDaEMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ2hCLFdBQVcsRUFBRTtnQkFDWCxJQUFBLG1CQUFZLEVBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUMzQyxJQUFBLGdDQUF5QixFQUFDLHlDQUFpQyxFQUFFO29CQUMzRCxNQUFNLEVBQUUsSUFBSTtpQkFDYixDQUFDO2FBQ0g7U0FDRjtRQUNEO1lBQ0UsT0FBTyxFQUFFLDJCQUEyQjtZQUNwQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFLENBQUMsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO1NBQzNEO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsbUNBQW1DO1lBQzVDLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUU7Z0JBQ1gsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDM0MsSUFBQSwrQkFBd0IsRUFBQyxxQ0FBNkIsQ0FBQzthQUN4RDtTQUNGO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsNkJBQTZCO1lBQ3RDLE9BQU8sRUFBRSxDQUFDLEtBQUssQ0FBQztZQUNoQixXQUFXLEVBQUU7Z0JBQ1gsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDM0MsSUFBQSxnQ0FBeUIsRUFBQyw4Q0FBc0MsRUFBRTtvQkFDaEUsTUFBTSxFQUFFLElBQUk7aUJBQ2IsQ0FBQzthQUNIO1NBQ0Y7UUFDRDtZQUNFLFVBQVUsRUFBRSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUU7WUFDaEMsT0FBTyxFQUFFLDZCQUE2QjtZQUN0QyxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFO2dCQUNYLElBQUEsbUJBQVksRUFBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQzNDLElBQUEsK0JBQXdCLEVBQUMsMENBQWtDLENBQUM7YUFDN0Q7U0FDRjtRQUNEO1lBQ0UsT0FBTyxFQUFFLDBDQUEwQztZQUNuRCxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFLENBQUMsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO1NBQzNEO1FBQ0QsK0RBQStEO1FBQy9ELHVFQUF1RTtRQUN2RSx3RUFBd0U7UUFDeEUsMkRBQTJEO1FBQzNEO1lBQ0UsT0FBTyxFQUFFLDhCQUE4QjtZQUN2QyxXQUFXLEVBQUU7Z0JBQ1gsSUFBQSw2QkFBb0IsRUFDbEIscUNBQW1CLEVBQ25CLGVBQWUsRUFDZix1QkFBdUIsQ0FDeEI7YUFDRjtTQUNGO1FBQ0Q7WUFDRSxPQUFPLEVBQUUscUNBQXFDO1lBQzlDLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQyxJQUFBLCtCQUF3QixFQUFDLHdEQUFnQyxDQUFDLENBQUM7U0FDMUU7UUFDRCxzRUFBc0U7UUFDdEUsb0VBQW9FO1FBQ3BFLHdFQUF3RTtRQUN4RSx3RUFBd0U7UUFDeEUsaURBQWlEO1FBQ2pEO1lBQ0UsT0FBTyxFQUFFLDRCQUE0QjtZQUNyQyxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFO2dCQUNYLElBQUEsbUJBQVksRUFBQyxHQUFHLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxJQUFJLEVBQUUsQ0FBQztnQkFDckUsNEVBQW1DO2FBQ3BDO1NBQ0Y7UUFDRCx1RUFBdUU7UUFDdkUsd0VBQXdFO1FBQ3hFLHlFQUF5RTtRQUN6RSxzRUFBc0U7UUFDdEUsd0VBQXdFO1FBQ3hFLDZCQUE2QjtRQUM3QjtZQUNFLE9BQU8sRUFBRSxzQ0FBc0M7WUFDL0MsV0FBVyxFQUFFLENBQUMsdUNBQXFCLENBQUM7U0FDckM7UUFDRDtZQUNFLE9BQU8sRUFBRSxvQ0FBb0M7WUFDN0MsV0FBVyxFQUFFLENBQUMsdUNBQXFCLENBQUM7U0FDckM7UUFDRCx1RUFBdUU7UUFDdkUsa0VBQWtFO1FBQ2xFLG1FQUFtRTtRQUNuRSxxRUFBcUU7UUFDckUsRUFBRTtRQUNGLGdCQUFnQjtRQUNoQixhQUFhLENBQUMsa0JBQWtCLENBQUM7UUFDakMsYUFBYSxDQUFDLDZCQUE2QixDQUFDO1FBQzVDLGFBQWEsQ0FBQyxpQ0FBaUMsQ0FBQztRQUNoRCx1QkFBdUI7UUFDdkIsYUFBYSxDQUFDLGtCQUFrQixFQUFFLFVBQVUsQ0FBQztRQUM3QyxhQUFhLENBQUMsNkJBQTZCLEVBQUUsVUFBVSxDQUFDO1FBQ3hELGFBQWE7UUFDYixhQUFhLENBQUMsaUNBQWlDLEVBQUUsZUFBZSxDQUFDO1FBQ2pFO1lBQ0UsT0FBTyxFQUFFLGtCQUFrQjtZQUMzQixPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFLENBQUMsSUFBQSxtQkFBWSxFQUFDLFVBQVUsRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO1NBQy9EO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsc0JBQXNCO1lBQy9CLE9BQU8sRUFBRSxDQUFDLEtBQUssQ0FBQztZQUNoQixXQUFXLEVBQUUsQ0FBQyxJQUFBLG1CQUFZLEVBQUMsVUFBVSxFQUFFLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7U0FDL0Q7UUFDRDtZQUNFLE9BQU8sRUFBRSwrQkFBK0I7WUFDeEMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ2hCLFdBQVcsRUFBRSxDQUFDLElBQUEsbUJBQVksRUFBQyxVQUFVLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztTQUMvRDtRQUNEO1lBQ0UsT0FBTyxFQUFFLDZCQUE2QjtZQUN0QyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDO1lBQ3hCLFdBQVcsRUFBRSxDQUFDLElBQUEsbUJBQVksRUFBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztTQUMzRDtRQUNEO1lBQ0UsT0FBTyxFQUFFLG1EQUFtRDtZQUM1RCxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFLENBQUMsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO1NBQzNEO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsd0NBQXdDO1lBQ2pELE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUU7Z0JBQ1gsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDM0MsSUFBQSwrQkFBd0IsRUFBQyxrREFBc0MsQ0FBQzthQUNqRTtTQUNGO1FBQ0Q7WUFDRSxPQUFPLEVBQUUscURBQXFEO1lBQzlELE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUU7Z0JBQ1gsSUFBQSxtQkFBWSxFQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDM0MsSUFBQSwrQkFBd0IsRUFBQyxrREFBc0MsQ0FBQzthQUNqRTtTQUNGO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsaUNBQWlDO1lBQzFDLE9BQU8sRUFBRSxDQUFDLEtBQUssQ0FBQztZQUNoQixXQUFXLEVBQUUsQ0FBQyxJQUFBLG1CQUFZLEVBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7U0FDM0Q7UUFDRDtZQUNFLE9BQU8sRUFBRSwwREFBMEQ7WUFDbkUsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ2hCLFdBQVcsRUFBRSxDQUFDLElBQUEsbUJBQVksRUFBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztTQUMzRDtRQUNEO1lBQ0UsT0FBTyxFQUFFLHVEQUF1RDtZQUNoRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFO2dCQUNYLElBQUEsbUJBQVksRUFBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQzNDLElBQUEsK0JBQXdCLEVBQUMsZ0RBQW9DLENBQUM7YUFDL0Q7U0FDRjtRQUNELHVFQUF1RTtRQUN2RTtZQUNFLE9BQU8sRUFBRSx1QkFBdUI7WUFDaEMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDO1lBQ2hCLFdBQVcsRUFBRTtnQkFDWCxJQUFBLGdDQUF5QixFQUFDLDZDQUFxQyxFQUFFO29CQUMvRCxNQUFNLEVBQUUsS0FBSztpQkFDZCxDQUFDO2FBQ0g7U0FDRjtRQUNEO1lBQ0UsVUFBVSxFQUFFLEtBQUs7WUFDakIsT0FBTyxFQUFFLHNCQUFzQjtZQUMvQixPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDaEIsV0FBVyxFQUFFO2dCQUNYLElBQUEsZ0NBQXlCLEVBQUMsNkNBQXFDLEVBQUU7b0JBQy9ELE1BQU0sRUFBRSxLQUFLO2lCQUNkLENBQUM7YUFDSDtTQUNGO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforcePasswordPolicy = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const password_policy_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/password-policy");
|
|
6
|
+
/**
|
|
7
|
+
* Runs before Medusa's `emailpass` register and password-update routes (every
|
|
8
|
+
* actor type, so customers and Admin users) and rejects a password shorter
|
|
9
|
+
* than `PASSWORD_MIN_LENGTH` with 400 `invalid_data`. A body without a string
|
|
10
|
+
* password goes on to Medusa unchanged, which answers it itself (register) or
|
|
11
|
+
* changes nothing (update). Login is never checked, so older, shorter
|
|
12
|
+
* passwords keep working.
|
|
13
|
+
*/
|
|
14
|
+
const enforcePasswordPolicy = (req, _res, next) => {
|
|
15
|
+
const password = req.body?.password;
|
|
16
|
+
if (typeof password === "string" && !(0, password_policy_1.isPasswordLongEnough)(password)) {
|
|
17
|
+
next(new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Das Passwort muss mindestens ${password_policy_1.PASSWORD_MIN_LENGTH} Zeichen lang sein.`));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
next();
|
|
21
|
+
};
|
|
22
|
+
exports.enforcePasswordPolicy = enforcePasswordPolicy;
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFzc3dvcmQtcG9saWN5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2FwaS9wYXNzd29yZC1wb2xpY3kudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBS0EscURBQXVEO0FBQ3ZELCtHQUdrRjtBQUVsRjs7Ozs7OztHQU9HO0FBQ0ksTUFBTSxxQkFBcUIsR0FBRyxDQUNuQyxHQUFrQixFQUNsQixJQUFvQixFQUNwQixJQUF3QixFQUNsQixFQUFFO0lBQ1IsTUFBTSxRQUFRLEdBQUksR0FBRyxDQUFDLElBQTJDLEVBQUUsUUFBUSxDQUFBO0lBQzNFLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxJQUFJLENBQUMsSUFBQSxzQ0FBb0IsRUFBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ3BFLElBQUksQ0FDRixJQUFJLG1CQUFXLENBQ2IsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixnQ0FBZ0MscUNBQW1CLHFCQUFxQixDQUN6RSxDQUNGLENBQUE7UUFDRCxPQUFNO0lBQ1IsQ0FBQztJQUNELElBQUksRUFBRSxDQUFBO0FBQ1IsQ0FBQyxDQUFBO0FBaEJZLFFBQUEscUJBQXFCLHlCQWdCakMifQ==
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
const resend_customer_verification_1 = require("../../../../email/application/resend-customer-verification");
|
|
5
|
+
/**
|
|
6
|
+
* Always 202 `{ accepted: true }`, whether or not the account exists, is
|
|
7
|
+
* confirmed already or was asked too recently. The answer goes out before any
|
|
8
|
+
* lookup, so existing and unknown addresses take equally long; the work
|
|
9
|
+
* runs detached and logs its own failures (it never rejects). The mail
|
|
10
|
+
* follows through the `auth.verification_requested` subscriber.
|
|
11
|
+
*/
|
|
12
|
+
const POST = async (req, res) => {
|
|
13
|
+
res.status(202).json({ accepted: true });
|
|
14
|
+
void (0, resend_customer_verification_1.resendCustomerVerification)(req.scope, req.validatedBody.email);
|
|
15
|
+
};
|
|
16
|
+
exports.POST = POST;
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVyLXZlcmlmaWNhdGlvbi9yZXNlbmQvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBTUEsNkdBQXVHO0FBRXZHOzs7Ozs7R0FNRztBQUNJLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBOEMsRUFDOUMsR0FBdUQsRUFDdkQsRUFBRTtJQUNGLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7SUFDeEMsS0FBSyxJQUFBLHlEQUEwQixFQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQUNyRSxDQUFDLENBQUE7QUFOWSxRQUFBLElBQUksUUFNaEIifQ==
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleCustomerVerificationEvent = exports.CUSTOMER_VERIFICATION_RETRY_SENTINEL = exports.CUSTOMER_VERIFICATION_MAX_ATTEMPTS = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const failure_classification_1 = require("../domain/failure-classification");
|
|
6
|
+
const business_keys_1 = require("../domain/business-keys");
|
|
7
|
+
const event_schemas_1 = require("../domain/event-schemas");
|
|
8
|
+
const email_1 = require("../../modules/email");
|
|
9
|
+
const load_email_shop_details_1 = require("../rendering/load-email-shop-details");
|
|
10
|
+
const render_email_1 = require("../rendering/render-email");
|
|
11
|
+
const dispatch_email_message_1 = require("../../workflows/email/dispatch-email-message");
|
|
12
|
+
exports.CUSTOMER_VERIFICATION_MAX_ATTEMPTS = 2;
|
|
13
|
+
exports.CUSTOMER_VERIFICATION_RETRY_SENTINEL = "EMAIL_CUSTOMER_VERIFICATION_RETRY";
|
|
14
|
+
const emailAddressPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
15
|
+
const retry = () => {
|
|
16
|
+
throw new Error(exports.CUSTOMER_VERIFICATION_RETRY_SENTINEL);
|
|
17
|
+
};
|
|
18
|
+
const resultForMessage = (message, code) => ({
|
|
19
|
+
...(code ? { code } : {}),
|
|
20
|
+
message_id: message.id,
|
|
21
|
+
status: message.status === "sent"
|
|
22
|
+
? "sent"
|
|
23
|
+
: message.status === "unknown"
|
|
24
|
+
? "unknown"
|
|
25
|
+
: message.status === "failed" || message.status === "suppressed"
|
|
26
|
+
? "failed"
|
|
27
|
+
: "in_progress",
|
|
28
|
+
});
|
|
29
|
+
const asNotificationRecord = (value) => {
|
|
30
|
+
if (!value || typeof value !== "object") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const record = value;
|
|
34
|
+
if (typeof record.id !== "string" ||
|
|
35
|
+
!record.id.trim() ||
|
|
36
|
+
(record.status !== "failure" &&
|
|
37
|
+
record.status !== "pending" &&
|
|
38
|
+
record.status !== "success")) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return { id: record.id, status: record.status };
|
|
42
|
+
};
|
|
43
|
+
const localeFromMetadata = (metadata) => {
|
|
44
|
+
const locale = metadata.locale;
|
|
45
|
+
return typeof locale === "string" &&
|
|
46
|
+
locale.trim() &&
|
|
47
|
+
locale.trim().length <= 35
|
|
48
|
+
? locale.trim()
|
|
49
|
+
: null;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The customer a confirmation mail may be sent for, or `null`: an identity
|
|
53
|
+
* without customer actor (an Admin user, or one no customer claimed) and an
|
|
54
|
+
* address that is not the identity's own emailpass address get no mail.
|
|
55
|
+
* Medusa's `POST /auth/verification/request` takes `entity_id` from the
|
|
56
|
+
* request body without comparing it, so without the address check any
|
|
57
|
+
* customer could make the shop mail a link to an arbitrary address. Medusa
|
|
58
|
+
* records the actor as `app_metadata.customer_id`.
|
|
59
|
+
*/
|
|
60
|
+
const resolveCustomerId = async (container, authIdentityId, entityId) => {
|
|
61
|
+
const auth = container.resolve(utils_1.Modules.AUTH);
|
|
62
|
+
const identity = await auth.retrieveAuthIdentity(authIdentityId, {
|
|
63
|
+
select: ["id", "app_metadata"],
|
|
64
|
+
relations: ["provider_identities"],
|
|
65
|
+
});
|
|
66
|
+
const ownsAddress = (identity.provider_identities ?? []).some((providerIdentity) => providerIdentity.provider === "emailpass" &&
|
|
67
|
+
providerIdentity.entity_id === entityId);
|
|
68
|
+
const customerId = identity.app_metadata
|
|
69
|
+
?.customer_id;
|
|
70
|
+
return ownsAddress && typeof customerId === "string" && customerId.trim()
|
|
71
|
+
? customerId
|
|
72
|
+
: null;
|
|
73
|
+
};
|
|
74
|
+
const reconcileSendingMessage = async (container, email, message, now) => {
|
|
75
|
+
const attempts = await email.listEmailAttempts({ message_id: message.id });
|
|
76
|
+
const current = [...attempts]
|
|
77
|
+
.sort((left, right) => right.attempt_number - left.attempt_number)
|
|
78
|
+
.find((attempt) => attempt.status === "processing");
|
|
79
|
+
if (!current?.attempt_token || !current.lease_expires_at) {
|
|
80
|
+
return retry();
|
|
81
|
+
}
|
|
82
|
+
const notification = await (0, dispatch_email_message_1.findAttemptNotification)(container, current.id);
|
|
83
|
+
if (notification?.status === "success") {
|
|
84
|
+
const completed = await email.markAttemptSent({
|
|
85
|
+
attempt_id: current.id,
|
|
86
|
+
attempt_token: current.attempt_token,
|
|
87
|
+
finished_at: now,
|
|
88
|
+
notification_id: notification.id,
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
attempt_id: current.id,
|
|
92
|
+
message_id: completed.message.id,
|
|
93
|
+
status: "sent",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (current.lease_expires_at.getTime() <= now.getTime()) {
|
|
97
|
+
const completed = await email.completeStaleAttempt({
|
|
98
|
+
attempt_id: current.id,
|
|
99
|
+
attempt_token: current.attempt_token,
|
|
100
|
+
now,
|
|
101
|
+
notification_id: notification?.id ?? null,
|
|
102
|
+
outcome: "unknown",
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
attempt_id: current.id,
|
|
106
|
+
code: "lease_expired",
|
|
107
|
+
message_id: completed.message.id,
|
|
108
|
+
status: "unknown",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return retry();
|
|
112
|
+
};
|
|
113
|
+
const failClaim = async (email, claim, code, now, notificationId = null) => {
|
|
114
|
+
const completed = await email.markAttemptFailed({
|
|
115
|
+
attempt_id: claim.attempt.id,
|
|
116
|
+
attempt_token: claim.attempt_token,
|
|
117
|
+
failure_code: code,
|
|
118
|
+
finished_at: now,
|
|
119
|
+
notification_id: notificationId,
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
attempt_id: claim.attempt.id,
|
|
123
|
+
code,
|
|
124
|
+
message_id: completed.message.id,
|
|
125
|
+
status: "failed",
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
const deliverCustomerVerification = async (container, email, claim, token, expiresAt, getNow) => {
|
|
129
|
+
const shop = await (0, load_email_shop_details_1.loadEmailShopDetails)(container);
|
|
130
|
+
let rendered;
|
|
131
|
+
try {
|
|
132
|
+
const configuration = email.getDispatchConfiguration();
|
|
133
|
+
const verificationData = claim.message.template_data;
|
|
134
|
+
if (!configuration.verifyAccountUrl) {
|
|
135
|
+
throw new Error("verifyAccountUrl is not configured.");
|
|
136
|
+
}
|
|
137
|
+
const verifyUrl = new URL(configuration.verifyAccountUrl({
|
|
138
|
+
locale: verificationData?.locale ?? null,
|
|
139
|
+
}));
|
|
140
|
+
verifyUrl.hash = new URLSearchParams({ token }).toString();
|
|
141
|
+
rendered = (0, render_email_1.renderEmail)(configuration, {
|
|
142
|
+
kind: "auth.verification",
|
|
143
|
+
data: {
|
|
144
|
+
locale: verificationData?.locale ?? null,
|
|
145
|
+
verify_url: verifyUrl.toString(),
|
|
146
|
+
},
|
|
147
|
+
}, shop);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return await failClaim(email, claim, "render_failed", getNow());
|
|
151
|
+
}
|
|
152
|
+
const beforeNotification = getNow();
|
|
153
|
+
if (expiresAt.getTime() <= beforeNotification.getTime()) {
|
|
154
|
+
return await failClaim(email, claim, "payload_expired", beforeNotification);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await email.renewAttemptLease({
|
|
158
|
+
attempt_id: claim.attempt.id,
|
|
159
|
+
attempt_token: claim.attempt_token,
|
|
160
|
+
now: beforeNotification,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return retry();
|
|
165
|
+
}
|
|
166
|
+
const notifications = container.resolve(utils_1.Modules.NOTIFICATION);
|
|
167
|
+
const safeData = {
|
|
168
|
+
attempt_id: claim.attempt.id,
|
|
169
|
+
kind: claim.message.kind,
|
|
170
|
+
message_id: claim.message.id,
|
|
171
|
+
};
|
|
172
|
+
let creationError;
|
|
173
|
+
let created = null;
|
|
174
|
+
try {
|
|
175
|
+
created = asNotificationRecord(await notifications.createNotifications({
|
|
176
|
+
channel: "email",
|
|
177
|
+
content: {
|
|
178
|
+
html: rendered.html,
|
|
179
|
+
subject: rendered.subject,
|
|
180
|
+
text: rendered.text,
|
|
181
|
+
},
|
|
182
|
+
data: safeData,
|
|
183
|
+
idempotency_key: (0, failure_classification_1.emailAttemptNotificationKey)(claim.attempt.id),
|
|
184
|
+
...(claim.previous_notification_id
|
|
185
|
+
? { original_notification_id: claim.previous_notification_id }
|
|
186
|
+
: {}),
|
|
187
|
+
provider_data: safeData,
|
|
188
|
+
...(claim.message.resource_id
|
|
189
|
+
? { resource_id: claim.message.resource_id }
|
|
190
|
+
: {}),
|
|
191
|
+
resource_type: "customer",
|
|
192
|
+
template: claim.message.kind,
|
|
193
|
+
to: claim.message.recipient,
|
|
194
|
+
trigger_type: "email.customer_verification.dispatch",
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
creationError = error;
|
|
199
|
+
}
|
|
200
|
+
let persisted = null;
|
|
201
|
+
let lookupFailed = false;
|
|
202
|
+
if (!created || creationError) {
|
|
203
|
+
try {
|
|
204
|
+
persisted = await (0, dispatch_email_message_1.findAttemptNotification)(container, claim.attempt.id);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
lookupFailed = true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const notification = created ?? persisted;
|
|
211
|
+
const finishedAt = getNow();
|
|
212
|
+
if (notification?.status === "success") {
|
|
213
|
+
const completed = await email.markAttemptSent({
|
|
214
|
+
attempt_id: claim.attempt.id,
|
|
215
|
+
attempt_token: claim.attempt_token,
|
|
216
|
+
finished_at: finishedAt,
|
|
217
|
+
notification_id: notification.id,
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
attempt_id: claim.attempt.id,
|
|
221
|
+
message_id: completed.message.id,
|
|
222
|
+
status: "sent",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const failure = creationError && notification?.status === "failure" && !lookupFailed
|
|
226
|
+
? (0, failure_classification_1.classifyNotificationFailure)(creationError)
|
|
227
|
+
: { code: "outcome_unknown", outcome: "unknown" };
|
|
228
|
+
if (failure.outcome === "retryable") {
|
|
229
|
+
const nextAttemptAt = new Date(finishedAt.getTime() + 1_000);
|
|
230
|
+
if (claim.attempt.attempt_number < exports.CUSTOMER_VERIFICATION_MAX_ATTEMPTS &&
|
|
231
|
+
nextAttemptAt.getTime() < expiresAt.getTime()) {
|
|
232
|
+
await email.markAttemptRetryable({
|
|
233
|
+
attempt_id: claim.attempt.id,
|
|
234
|
+
attempt_token: claim.attempt_token,
|
|
235
|
+
failure_code: failure.code,
|
|
236
|
+
finished_at: finishedAt,
|
|
237
|
+
next_attempt_at: nextAttemptAt,
|
|
238
|
+
notification_id: notification?.id ?? null,
|
|
239
|
+
});
|
|
240
|
+
return retry();
|
|
241
|
+
}
|
|
242
|
+
return await failClaim(email, claim, expiresAt.getTime() <= nextAttemptAt.getTime()
|
|
243
|
+
? "payload_expired"
|
|
244
|
+
: "retry_exhausted", finishedAt, notification?.id ?? null);
|
|
245
|
+
}
|
|
246
|
+
if (failure.outcome === "failed") {
|
|
247
|
+
return await failClaim(email, claim, failure.code, finishedAt, notification?.id ?? null);
|
|
248
|
+
}
|
|
249
|
+
const completed = await email.markAttemptUnknown({
|
|
250
|
+
attempt_id: claim.attempt.id,
|
|
251
|
+
attempt_token: claim.attempt_token,
|
|
252
|
+
failure_code: failure.code,
|
|
253
|
+
finished_at: finishedAt,
|
|
254
|
+
notification_id: notification?.id ?? null,
|
|
255
|
+
});
|
|
256
|
+
return {
|
|
257
|
+
attempt_id: claim.attempt.id,
|
|
258
|
+
code: failure.code,
|
|
259
|
+
message_id: completed.message.id,
|
|
260
|
+
status: "unknown",
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
const handleCustomerVerification = async (container, event, getNow) => {
|
|
264
|
+
const now = getNow();
|
|
265
|
+
const expiresAt = event.expires_at;
|
|
266
|
+
const email = container.resolve(email_1.EMAIL_MODULE);
|
|
267
|
+
const businessKey = (0, business_keys_1.customerVerificationBusinessKey)(event.code);
|
|
268
|
+
let message = await email.findMessageByBusinessKey(businessKey);
|
|
269
|
+
if (!message) {
|
|
270
|
+
// Checked only for a new token, so a replay of a terminal message never
|
|
271
|
+
// reads Auth.
|
|
272
|
+
const customerId = await resolveCustomerId(container, event.auth_identity_id, event.entity_id);
|
|
273
|
+
if (!customerId) {
|
|
274
|
+
return { code: "not_eligible", status: "ignored" };
|
|
275
|
+
}
|
|
276
|
+
message = await email.upsertMessageFact({
|
|
277
|
+
kind: "auth.verification",
|
|
278
|
+
business_key: businessKey,
|
|
279
|
+
resource_type: "customer",
|
|
280
|
+
resource_id: customerId,
|
|
281
|
+
recipient: event.entity_id,
|
|
282
|
+
required_facts: ["verification"],
|
|
283
|
+
fact: "verification",
|
|
284
|
+
template_data: { locale: localeFromMetadata(event.metadata ?? {}) },
|
|
285
|
+
payload_expires_at: expiresAt,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (message.status === "sent" ||
|
|
289
|
+
message.status === "failed" ||
|
|
290
|
+
message.status === "unknown" ||
|
|
291
|
+
message.status === "suppressed") {
|
|
292
|
+
return resultForMessage(message);
|
|
293
|
+
}
|
|
294
|
+
if (message.status === "sending") {
|
|
295
|
+
return await reconcileSendingMessage(container, email, message, now);
|
|
296
|
+
}
|
|
297
|
+
if (message.status === "retry_pending" &&
|
|
298
|
+
message.next_attempt_at &&
|
|
299
|
+
message.next_attempt_at.getTime() > now.getTime()) {
|
|
300
|
+
return retry();
|
|
301
|
+
}
|
|
302
|
+
const claim = await email.claimReadyMessage({
|
|
303
|
+
message_id: message.id,
|
|
304
|
+
now,
|
|
305
|
+
});
|
|
306
|
+
if (!claim) {
|
|
307
|
+
if (expiresAt.getTime() <= now.getTime()) {
|
|
308
|
+
return {
|
|
309
|
+
code: "payload_expired",
|
|
310
|
+
message_id: message.id,
|
|
311
|
+
status: "failed",
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return retry();
|
|
315
|
+
}
|
|
316
|
+
return await deliverCustomerVerification(container, email, claim, event.code, expiresAt, getNow);
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Sends the account-confirmation mail for Medusa's
|
|
320
|
+
* `auth.verification_requested`. The token (`code`) is only ever held in
|
|
321
|
+
* memory: the message stores its hash as business key, never the token or the
|
|
322
|
+
* link.
|
|
323
|
+
*/
|
|
324
|
+
const handleCustomerVerificationEvent = async (container, payload, clock = () => new Date()) => {
|
|
325
|
+
const event = event_schemas_1.NativeVerificationRequestedEventSchema.safeParse(payload);
|
|
326
|
+
if (!event.success ||
|
|
327
|
+
event.data.entity_type !== "email" ||
|
|
328
|
+
event.data.code_provider !== "token" ||
|
|
329
|
+
!emailAddressPattern.test(event.data.entity_id)) {
|
|
330
|
+
return { code: "invalid_event", status: "ignored" };
|
|
331
|
+
}
|
|
332
|
+
const getNow = clock instanceof Date ? () => clock : clock;
|
|
333
|
+
try {
|
|
334
|
+
return await handleCustomerVerification(container, event.data, getNow);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return retry();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
exports.handleCustomerVerificationEvent = handleCustomerVerificationEvent;
|
|
341
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlLWN1c3RvbWVyLXZlcmlmaWNhdGlvbi1ldmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9lbWFpbC9hcHBsaWNhdGlvbi9oYW5kbGUtY3VzdG9tZXItdmVyaWZpY2F0aW9uLWV2ZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUtBLHFEQUFvRDtBQUVwRCw2RUFHMEM7QUFDMUMsMkRBQTBFO0FBQzFFLDJEQUdpQztBQUNqQywrQ0FBbUQ7QUFNbkQsa0ZBQTRFO0FBQzVFLDREQUF3RDtBQUV4RCx5RkFBdUY7QUFFMUUsUUFBQSxrQ0FBa0MsR0FBRyxDQUFDLENBQUM7QUFDdkMsUUFBQSxvQ0FBb0MsR0FDL0MsbUNBQW1DLENBQUM7QUFFdEMsTUFBTSxtQkFBbUIsR0FBRyw0QkFBNEIsQ0FBQztBQWN6RCxNQUFNLEtBQUssR0FBRyxHQUFVLEVBQUU7SUFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBb0MsQ0FBQyxDQUFDO0FBQ3hELENBQUMsQ0FBQztBQUVGLE1BQU0sZ0JBQWdCLEdBQUcsQ0FDdkIsT0FBMkIsRUFDM0IsSUFBYSxFQUNlLEVBQUUsQ0FBQyxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUN6QixVQUFVLEVBQUUsT0FBTyxDQUFDLEVBQUU7SUFDdEIsTUFBTSxFQUNKLE9BQU8sQ0FBQyxNQUFNLEtBQUssTUFBTTtRQUN2QixDQUFDLENBQUMsTUFBTTtRQUNSLENBQUMsQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLFNBQVM7WUFDNUIsQ0FBQyxDQUFDLFNBQVM7WUFDWCxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxZQUFZO2dCQUM5RCxDQUFDLENBQUMsUUFBUTtnQkFDVixDQUFDLENBQUMsYUFBYTtDQUN4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
|