@hartl-services/medusa-base 1.1.0 → 2.1.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 +31 -1
- package/.medusa/server/src/api/store/customer-verification/resend/route.js +17 -0
- package/.medusa/server/src/documents/utils/document-merchant.js +5 -5
- 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/documents/domain/merchant.js +2 -33
- package/.medusa/server/src/modules/documents/domain/options.js +1 -7
- 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/public/shop-master-data.js +6 -6
- package/.medusa/server/src/shop-config/build-food-shop-config.js +17 -6
- package/.medusa/server/src/shop-config/options.js +1 -15
- package/.medusa/server/src/subscribers/customer-verification.js +23 -0
- package/.medusa/server/src/workflows/documents/issue-cancellation-after-refund.js +2 -3
- package/.medusa/server/src/workflows/documents/issue-document.js +3 -4
- package/.medusa/server/src/workflows/documents/issue-packing-list.js +2 -2
- package/.medusa/server/src/workflows/email/dispatch-email-message.js +4 -2
- package/README.md +154 -49
- package/dist/documents/types/documents.d.ts +0 -22
- 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/modules/documents/domain/options.d.ts +1 -9
- package/dist/modules/documents/domain/options.js +0 -6
- package/dist/public/shop-master-data.d.ts +4 -5
- package/dist/public/shop-master-data.js +5 -5
- package/dist/shop-config/build-food-shop-config.js +16 -5
- package/dist/shop-config/options.d.ts +4 -6
- package/dist/shop-config/options.js +0 -14
- package/package.json +2 -2
- package/dist/modules/documents/domain/merchant.d.ts +0 -22
- package/dist/modules/documents/domain/merchant.js +0 -85
|
@@ -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,11 @@ 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");
|
|
10
12
|
const public_1 = require("../public");
|
|
11
13
|
/**
|
|
12
14
|
* Feature-toggle gate for the Documents routes. Without `methods` Medusa
|
|
@@ -108,6 +110,34 @@ exports.default = (0, http_1.defineMiddlewares)({
|
|
|
108
110
|
methods: ["GET"],
|
|
109
111
|
middlewares: [(0, http_1.authenticate)("user", ["session", "bearer"])],
|
|
110
112
|
},
|
|
113
|
+
// Account confirmation: the resend route answers 404 while the
|
|
114
|
+
// `customer_verification` feature (or Konto-Mails, its parent) is off.
|
|
115
|
+
// Public by design (the customer cannot log in yet); the 60 s limit per
|
|
116
|
+
// account and the uniform 202 answer live in the use case.
|
|
117
|
+
{
|
|
118
|
+
matcher: "/store/customer-verification",
|
|
119
|
+
middlewares: [
|
|
120
|
+
(0, public_1.requirePluginFeature)(plugin_features_3.emailPluginFeatures, "account_mails", "customer_verification"),
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
matcher: "/store/customer-verification/resend",
|
|
125
|
+
methods: ["POST"],
|
|
126
|
+
middlewares: [(0, http_1.validateAndTransformBody)(customer_verification_1.ResendCustomerVerificationSchema)],
|
|
127
|
+
},
|
|
128
|
+
// Medusa's own verification request (the storefront calls it at every
|
|
129
|
+
// blocked login) gets the same 60 s limit per account as the resend
|
|
130
|
+
// route. Medusa registers its entry for this matcher first (core routes
|
|
131
|
+
// load before plugin routes), so authentication has run; it is repeated
|
|
132
|
+
// here so the limit never depends on that order.
|
|
133
|
+
{
|
|
134
|
+
matcher: "/auth/verification/request",
|
|
135
|
+
methods: ["POST"],
|
|
136
|
+
middlewares: [
|
|
137
|
+
(0, http_1.authenticate)("*", ["session", "bearer"], { allowUnregistered: true }),
|
|
138
|
+
customer_verification_request_throttle_1.throttleCustomerVerificationRequest,
|
|
139
|
+
],
|
|
140
|
+
},
|
|
111
141
|
// --- Dokumente ------------------------------------------------------
|
|
112
142
|
// Feature-toggle gates (see plugin-features.ts). They cover every
|
|
113
143
|
// Documents route and nothing else; subscribers and jobs check the
|
|
@@ -203,4 +233,4 @@ exports.default = (0, http_1.defineMiddlewares)({
|
|
|
203
233
|
},
|
|
204
234
|
],
|
|
205
235
|
});
|
|
206
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
236
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBS2lDO0FBQ2pDLCtHQUtrRjtBQUNsRixtR0FHNEU7QUFDNUUsMkZBS3dFO0FBQ3hFLDJIQUFrSTtBQUNsSSwyRkFBdUg7QUFFdkgsa0VBR3FDO0FBQ3JDLDhEQUE4RDtBQUM5RCxxR0FBOEY7QUFDOUYsc0NBQWdEO0FBRWhEOzs7Ozs7R0FNRztBQUNILE1BQU0sYUFBYSxHQUFHLENBQUMsT0FBZSxFQUFFLEdBQUcsSUFBMkIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMxRSxPQUFPO0lBQ1AsV0FBVyxFQUFFLENBQUMsSUFBQSw2QkFBb0IsRUFBQyx5Q0FBdUIsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO0NBQ3RFLENBQUMsQ0FBQTtBQUVGLHlFQUF5RTtBQUN6RSxtRUFBbUU7QUFDbkUsdUVBQXVFO0FBQ3ZFLDJFQUEyRTtBQUMzRSwwRUFBMEU7QUFDMUUsdUNBQXVDO0FBQ3ZDLGtCQUFlLElBQUEsd0JBQWlCLEVBQUM7SUFDL0IsTUFBTSxFQUFFO1FBQ047WUFDRSxPQUFPLEVBQUUsd0JBQXdCO1lBQ2pDLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQyxJQUFBLCtCQUF3QixFQUFDLDRDQUEwQixDQUFDLENBQUM7U0FDcEU7UUFDRDtZQUNFLE9BQU8sRUFBRSw2Q0FBNkM7WUFDdEQsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSxDQUFDLElBQUEsK0JBQXdCLEVBQUMsNENBQTBCLENBQUMsQ0FBQztTQUNwRTtRQUNEO1lBQ0UsT0FBTyxFQUFFLHdEQUF3RDtZQUNqRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFLENBQUMsSUFBQSwrQkFBd0IsRUFBQyx1Q0FBcUIsQ0FB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
|
|
@@ -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==
|
|
@@ -5,13 +5,13 @@ const public_1 = require("../../public");
|
|
|
5
5
|
const utils_1 = require("@medusajs/framework/utils");
|
|
6
6
|
const merchant_1 = require("../../modules/documents/domain/merchant");
|
|
7
7
|
/**
|
|
8
|
-
* The shop's sender facts for one render
|
|
9
|
-
*
|
|
8
|
+
* The shop's sender facts for one render, from the base plugin's shop master
|
|
9
|
+
* data (stored in the Admin; there is no config fallback). Fails with
|
|
10
10
|
* `UNEXPECTED_STATE` naming the missing fields, so the document is marked
|
|
11
11
|
* failed and issued again once the data is complete. Read failures propagate.
|
|
12
12
|
*/
|
|
13
|
-
const resolveDocumentMerchant = async (container
|
|
14
|
-
const { values } = await (0, public_1.getShopMasterData)(container
|
|
13
|
+
const resolveDocumentMerchant = async (container) => {
|
|
14
|
+
const { values } = await (0, public_1.getShopMasterData)(container);
|
|
15
15
|
const resolution = (0, merchant_1.toDocumentMerchant)(values);
|
|
16
16
|
if (!resolution.merchant) {
|
|
17
17
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Shop-Stammdaten unvollständig: ${resolution.missing.join(", ")}. Bitte unter Einstellungen → Hartl Services Plugins → Shop-Stammdaten ergänzen.`);
|
|
@@ -19,4 +19,4 @@ const resolveDocumentMerchant = async (container, fallback) => {
|
|
|
19
19
|
return resolution.merchant;
|
|
20
20
|
};
|
|
21
21
|
exports.resolveDocumentMerchant = resolveDocumentMerchant;
|
|
22
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jdW1lbnQtbWVyY2hhbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvZG9jdW1lbnRzL3V0aWxzL2RvY3VtZW50LW1lcmNoYW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlDQUFnRDtBQUVoRCxxREFBdUQ7QUFFdkQsc0VBQTRFO0FBRzVFOzs7OztHQUtHO0FBQ0ksTUFBTSx1QkFBdUIsR0FBRyxLQUFLLEVBQzFDLFNBQTBCLEVBQ0MsRUFBRTtJQUM3QixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLDBCQUFpQixFQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQ3JELE1BQU0sVUFBVSxHQUFHLElBQUEsNkJBQWtCLEVBQUMsTUFBTSxDQUFDLENBQUE7SUFDN0MsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN6QixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQ2xDLGtDQUFrQyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsa0ZBQWtGLENBQ2xKLENBQUE7SUFDSCxDQUFDO0lBQ0QsT0FBTyxVQUFVLENBQUMsUUFBUSxDQUFBO0FBQzVCLENBQUMsQ0FBQTtBQVpZLFFBQUEsdUJBQXVCLDJCQVluQyJ9
|
|
@@ -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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
|