@hartl-services/medusa-base 2.6.1 → 2.8.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/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +3 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginActionsSection.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.d.ts +16 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.js +25 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginDetailPage.js +1 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +5 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.js +8 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.js +24 -27
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +2 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +9 -9
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.js +11 -15
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.d.ts +10 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.js +10 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +0 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.js +77 -26
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -6
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.d.ts +11 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.js +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +40 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +160 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +40 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.js +66 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +160 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.d.ts +49 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.js +72 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.d.ts +1 -12
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.js +4 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.d.ts +47 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +70 -0
- package/.medusa/server/src/admin/admin-components/i18n/texts.d.ts +22 -2
- package/.medusa/server/src/admin/admin-components/i18n/texts.js +29 -7
- package/.medusa/server/src/admin/admin-components/index.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/index.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.d.ts +26 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.js +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.d.ts +23 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.js +20 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.d.ts +21 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.js +33 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.js +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.js +14 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.d.ts +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.js +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.js +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.js +16 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.js +29 -0
- package/.medusa/server/src/admin/index.js +3155 -2039
- package/.medusa/server/src/admin/index.mjs +3158 -2042
- package/.medusa/server/src/api/admin-first-login-password-policy.js +56 -0
- package/.medusa/server/src/api/answer-actor-type-casing-not-found.js +26 -0
- package/.medusa/server/src/api/answer-code-provider-route-not-found.js +34 -0
- package/.medusa/server/src/api/code-account-password-routes.js +92 -0
- package/.medusa/server/src/api/middlewares.js +82 -17
- package/.medusa/server/src/api/password-policy.js +55 -10
- package/.medusa/server/src/api/require-login-code-customer-email.js +40 -0
- package/.medusa/server/src/api/store/customer-auth/login-code/route.js +17 -0
- package/.medusa/server/src/api/store/customer-auth/password-policy/route.js +19 -0
- package/.medusa/server/src/customer-auth/application/prepare-code-account-password-reset.js +111 -0
- package/.medusa/server/src/customer-auth/application/request-login-code.js +145 -0
- package/.medusa/server/src/customer-auth/customer-auth-methods-snapshot.js +55 -0
- package/.medusa/server/src/customer-auth/customer-password-policy.js +40 -0
- package/.medusa/server/src/customer-auth/domain/account-linking.js +53 -0
- package/.medusa/server/src/customer-auth/domain/auth-methods.js +32 -0
- package/.medusa/server/src/customer-auth/domain/login-code.js +39 -0
- package/.medusa/server/src/customer-auth/login-code-accounts.js +146 -0
- package/.medusa/server/src/customer-auth/login-code-state.js +139 -0
- package/.medusa/server/src/customer-auth/plugin-features.js +80 -0
- package/.medusa/server/src/customer-auth/remove-provider-identity.js +18 -0
- package/.medusa/server/src/customer-auth/start-customer-auth-methods.js +20 -0
- package/.medusa/server/src/documents/plugin-features.js +8 -2
- package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +186 -0
- package/.medusa/server/src/email/application/deliver-login-code.js +89 -0
- package/.medusa/server/src/email/application/handle-customer-verification-event.js +29 -168
- package/.medusa/server/src/email/application/handle-password-reset-event.js +27 -166
- package/.medusa/server/src/email/customer-verification-snapshot.js +20 -108
- package/.medusa/server/src/email/domain/business-keys.js +15 -2
- package/.medusa/server/src/email/plugin-features.js +8 -3
- package/.medusa/server/src/email/rendering/default-templates.js +2 -1
- package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +19 -1
- package/.medusa/server/src/email/templates/defaults/documents.js +7 -1
- package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
- package/.medusa/server/src/email/templates/defaults/texts.js +5 -2
- package/.medusa/server/src/email/templates/render.js +2 -1
- package/.medusa/server/src/email/templates/sample-data.js +2 -1
- package/.medusa/server/src/email/templates/validate.js +8 -3
- package/.medusa/server/src/email/types/email-kinds.js +4 -2
- package/.medusa/server/src/email/types/template-data.js +9 -1
- package/.medusa/server/src/modules/customer-auth/index.js +14 -0
- package/.medusa/server/src/modules/customer-auth/service.js +35 -0
- package/.medusa/server/src/modules/email/migrations/Migration20261010090000.js +20 -0
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +30 -6
- package/.medusa/server/src/modules/plugin-features/service.js +29 -3
- package/.medusa/server/src/providers/email-otp/index.js +8 -0
- package/.medusa/server/src/providers/email-otp/service.js +96 -0
- package/.medusa/server/src/public/descriptor.js +138 -5
- package/.medusa/server/src/public/effective-features.js +22 -1
- package/.medusa/server/src/public/index.js +4 -2
- package/.medusa/server/src/public/plugin-icons.js +28 -0
- package/.medusa/server/src/public/shop-master-data.js +33 -14
- package/.medusa/server/src/shared/lib/dates.js +8 -2
- package/.medusa/server/src/shared/lib/index.js +2 -1
- package/.medusa/server/src/shared/lib/refreshing-snapshot.js +122 -0
- package/.medusa/server/src/shop-config/build-food-shop-config.js +38 -1
- package/README.md +436 -87
- package/dist/customer-auth/customer-auth-methods-snapshot.d.ts +25 -0
- package/dist/customer-auth/customer-auth-methods-snapshot.js +54 -0
- package/dist/customer-auth/domain/auth-methods.d.ts +16 -0
- package/dist/customer-auth/domain/auth-methods.js +31 -0
- package/dist/customer-auth/plugin-features.d.ts +2 -0
- package/dist/customer-auth/plugin-features.js +79 -0
- package/dist/email/customer-verification-snapshot.d.ts +3 -15
- package/dist/email/customer-verification-snapshot.js +19 -107
- package/dist/email/types/email-kinds.d.ts +4 -3
- package/dist/email/types/email-kinds.js +3 -1
- package/dist/email/types/template-data.d.ts +8 -0
- package/dist/email/types/template-data.js +8 -0
- package/dist/public/descriptor.js +137 -4
- package/dist/public/effective-features.d.ts +10 -0
- package/dist/public/effective-features.js +21 -0
- package/dist/public/index.d.ts +2 -1
- package/dist/public/index.js +3 -1
- package/dist/public/plugin-icons.d.ts +7 -0
- package/dist/public/plugin-icons.js +27 -0
- package/dist/public/shop-master-data.js +32 -13
- package/dist/public/types.d.ts +74 -2
- package/dist/shared/lib/dates.d.ts +2 -0
- package/dist/shared/lib/dates.js +7 -1
- package/dist/shared/lib/index.d.ts +1 -0
- package/dist/shared/lib/index.js +1 -0
- package/dist/shared/lib/refreshing-snapshot.d.ts +52 -0
- package/dist/shared/lib/refreshing-snapshot.js +121 -0
- package/dist/shop-config/build-food-shop-config.js +37 -0
- package/package.json +8 -3
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasCustomerAccountIdentity = exports.findCodeAccountAuthIdentityId = exports.findPasswordIdentities = exports.deleteUnconfirmedPasswordIdentities = exports.findEmailPasswordAccounts = exports.findOwnLoginIdentity = void 0;
|
|
4
|
+
/** The address's own code-login identity (provider `email-otp`), or null. */
|
|
5
|
+
const findOwnLoginIdentity = async (sql, entityId) => {
|
|
6
|
+
const result = await sql.raw(`SELECT pi.id AS provider_identity_id,
|
|
7
|
+
pi.auth_identity_id AS auth_identity_id,
|
|
8
|
+
NULLIF(ai.app_metadata->>'customer_id', '') AS customer_id
|
|
9
|
+
FROM provider_identity pi
|
|
10
|
+
JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
|
|
11
|
+
WHERE pi.provider = 'email-otp'
|
|
12
|
+
AND pi.entity_id = ?
|
|
13
|
+
AND pi.deleted_at IS NULL`, [entityId]);
|
|
14
|
+
const row = result.rows[0];
|
|
15
|
+
return row
|
|
16
|
+
? {
|
|
17
|
+
providerIdentityId: String(row.provider_identity_id),
|
|
18
|
+
authIdentityId: String(row.auth_identity_id),
|
|
19
|
+
customerId: typeof row.customer_id === "string" && row.customer_id
|
|
20
|
+
? row.customer_id
|
|
21
|
+
: null,
|
|
22
|
+
}
|
|
23
|
+
: null;
|
|
24
|
+
};
|
|
25
|
+
exports.findOwnLoginIdentity = findOwnLoginIdentity;
|
|
26
|
+
/**
|
|
27
|
+
* A verified Medusa auth verification of the provider identity `pi`'s very
|
|
28
|
+
* address (`entity_type` "email"): the one confirmation check of the linking
|
|
29
|
+
* rule and of the clean-up after a code login.
|
|
30
|
+
*/
|
|
31
|
+
const ADDRESS_CONFIRMED_SQL = `EXISTS (
|
|
32
|
+
SELECT 1 FROM auth_verification v
|
|
33
|
+
WHERE v.auth_identity_id = pi.auth_identity_id
|
|
34
|
+
AND v.entity_id = pi.entity_id
|
|
35
|
+
AND v.entity_type = 'email'
|
|
36
|
+
AND v.verified_at IS NOT NULL
|
|
37
|
+
AND v.deleted_at IS NULL
|
|
38
|
+
)`;
|
|
39
|
+
/**
|
|
40
|
+
* Customer accounts that registered with this address through `emailpass`.
|
|
41
|
+
* `emailpass` stores the address exactly as typed, so the match ignores
|
|
42
|
+
* letter case. Only identities whose auth identity carries a customer count
|
|
43
|
+
* (`app_metadata.customer_id`); `confirmed` is a verified Medusa auth
|
|
44
|
+
* verification of that very address. `customerEmailMatches` says the customer
|
|
45
|
+
* record itself (not deleted) has the same address, so a password identity
|
|
46
|
+
* pointing at a customer of another address is never linked.
|
|
47
|
+
*/
|
|
48
|
+
const findEmailPasswordAccounts = async (sql, normalizedEmail) => {
|
|
49
|
+
const result = await sql.raw(`SELECT pi.auth_identity_id AS auth_identity_id,
|
|
50
|
+
pi.entity_id AS entity_id,
|
|
51
|
+
${ADDRESS_CONFIRMED_SQL} AS confirmed,
|
|
52
|
+
(c.id IS NOT NULL AND lower(c.email) = ?) AS customer_email_matches
|
|
53
|
+
FROM provider_identity pi
|
|
54
|
+
JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
|
|
55
|
+
LEFT JOIN customer c
|
|
56
|
+
ON c.id = ai.app_metadata->>'customer_id' AND c.deleted_at IS NULL
|
|
57
|
+
WHERE pi.provider = 'emailpass'
|
|
58
|
+
AND lower(pi.entity_id) = ?
|
|
59
|
+
AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
|
|
60
|
+
AND pi.deleted_at IS NULL
|
|
61
|
+
AND NULLIF(ai.app_metadata->>'customer_id', '') IS NOT NULL`, [normalizedEmail, normalizedEmail]);
|
|
62
|
+
return result.rows.map((row) => ({
|
|
63
|
+
authIdentityId: String(row.auth_identity_id),
|
|
64
|
+
entityId: String(row.entity_id),
|
|
65
|
+
confirmed: row.confirmed === true,
|
|
66
|
+
customerEmailMatches: row.customer_email_matches === true,
|
|
67
|
+
}));
|
|
68
|
+
};
|
|
69
|
+
exports.findEmailPasswordAccounts = findEmailPasswordAccounts;
|
|
70
|
+
/**
|
|
71
|
+
* Deletes the `emailpass` identities of an auth identity whose address was
|
|
72
|
+
* never confirmed, for good (the unique index on `(entity_id, provider)` has no
|
|
73
|
+
* `deleted_at` filter, so a soft delete would block a new registration). A
|
|
74
|
+
* password somebody else may have planted for the address before the code
|
|
75
|
+
* login took over can then never be used. Returns how many were deleted.
|
|
76
|
+
*/
|
|
77
|
+
const deleteUnconfirmedPasswordIdentities = async (sql, authIdentityId) => {
|
|
78
|
+
const result = await sql.raw(`DELETE FROM provider_identity pi
|
|
79
|
+
WHERE pi.auth_identity_id = ?
|
|
80
|
+
AND pi.provider = 'emailpass'
|
|
81
|
+
AND NOT ${ADDRESS_CONFIRMED_SQL}
|
|
82
|
+
RETURNING pi.id`, [authIdentityId]);
|
|
83
|
+
return result.rows.length;
|
|
84
|
+
};
|
|
85
|
+
exports.deleteUnconfirmedPasswordIdentities = deleteUnconfirmedPasswordIdentities;
|
|
86
|
+
/**
|
|
87
|
+
* Every `emailpass` identity of the address, letter case ignored (`emailpass`
|
|
88
|
+
* stores the address as typed), with or without a customer.
|
|
89
|
+
*/
|
|
90
|
+
const findPasswordIdentities = async (sql, normalizedEmail) => {
|
|
91
|
+
const result = await sql.raw(`SELECT pi.id AS provider_identity_id,
|
|
92
|
+
pi.auth_identity_id AS auth_identity_id,
|
|
93
|
+
pi.entity_id AS entity_id,
|
|
94
|
+
NULLIF(ai.app_metadata->>'customer_id', '') AS customer_id
|
|
95
|
+
FROM provider_identity pi
|
|
96
|
+
JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
|
|
97
|
+
WHERE pi.provider = 'emailpass'
|
|
98
|
+
AND lower(pi.entity_id) = ?
|
|
99
|
+
AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
|
|
100
|
+
AND pi.deleted_at IS NULL`, [normalizedEmail]);
|
|
101
|
+
return result.rows.map((row) => ({
|
|
102
|
+
providerIdentityId: String(row.provider_identity_id),
|
|
103
|
+
authIdentityId: String(row.auth_identity_id),
|
|
104
|
+
entityId: String(row.entity_id),
|
|
105
|
+
customerId: typeof row.customer_id === "string" && row.customer_id
|
|
106
|
+
? row.customer_id
|
|
107
|
+
: null,
|
|
108
|
+
}));
|
|
109
|
+
};
|
|
110
|
+
exports.findPasswordIdentities = findPasswordIdentities;
|
|
111
|
+
/**
|
|
112
|
+
* The auth identity of the address's code login (`email-otp`) when it carries a
|
|
113
|
+
* customer (not deleted) with that very address: the account the address was
|
|
114
|
+
* proven for. Null otherwise.
|
|
115
|
+
*/
|
|
116
|
+
const findCodeAccountAuthIdentityId = async (sql, normalizedEmail) => {
|
|
117
|
+
const result = await sql.raw(`SELECT pi.auth_identity_id AS auth_identity_id
|
|
118
|
+
FROM provider_identity pi
|
|
119
|
+
JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
|
|
120
|
+
JOIN customer c
|
|
121
|
+
ON c.id = ai.app_metadata->>'customer_id' AND c.deleted_at IS NULL
|
|
122
|
+
WHERE pi.provider = 'email-otp'
|
|
123
|
+
AND pi.entity_id = ?
|
|
124
|
+
AND pi.deleted_at IS NULL
|
|
125
|
+
AND lower(c.email) = ?`, [normalizedEmail, normalizedEmail]);
|
|
126
|
+
const id = result.rows[0]?.auth_identity_id;
|
|
127
|
+
return typeof id === "string" && id ? id : null;
|
|
128
|
+
};
|
|
129
|
+
exports.findCodeAccountAuthIdentityId = findCodeAccountAuthIdentityId;
|
|
130
|
+
/**
|
|
131
|
+
* Whether an identity of any provider for the address (letter case ignored)
|
|
132
|
+
* belongs to an auth identity that carries a customer.
|
|
133
|
+
*/
|
|
134
|
+
const hasCustomerAccountIdentity = async (sql, normalizedEmail) => {
|
|
135
|
+
const result = await sql.raw(`SELECT 1 AS found
|
|
136
|
+
FROM provider_identity pi
|
|
137
|
+
JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
|
|
138
|
+
WHERE lower(pi.entity_id) = ?
|
|
139
|
+
AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
|
|
140
|
+
AND pi.deleted_at IS NULL
|
|
141
|
+
AND NULLIF(ai.app_metadata->>'customer_id', '') IS NOT NULL
|
|
142
|
+
LIMIT 1`, [normalizedEmail]);
|
|
143
|
+
return result.rows.length > 0;
|
|
144
|
+
};
|
|
145
|
+
exports.hasCustomerAccountIdentity = hasCustomerAccountIdentity;
|
|
146
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9naW4tY29kZS1hY2NvdW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jdXN0b21lci1hdXRoL2xvZ2luLWNvZGUtYWNjb3VudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBTUEsNkVBQTZFO0FBQ3RFLE1BQU0sb0JBQW9CLEdBQUcsS0FBSyxFQUN2QyxHQUFpQixFQUNqQixRQUFnQixFQUNrQixFQUFFO0lBQ3BDLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7Ozs7Ozs7aUNBTzZCLEVBQzdCLENBQUMsUUFBUSxDQUFDLENBQ1gsQ0FBQTtJQUNELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDMUIsT0FBTyxHQUFHO1FBQ1IsQ0FBQyxDQUFDO1lBQ0Usa0JBQWtCLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQztZQUNwRCxjQUFjLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQztZQUM1QyxVQUFVLEVBQ1IsT0FBTyxHQUFHLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsV0FBVztnQkFDcEQsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxXQUFXO2dCQUNqQixDQUFDLENBQUMsSUFBSTtTQUNYO1FBQ0gsQ0FBQyxDQUFDLElBQUksQ0FBQTtBQUNWLENBQUMsQ0FBQTtBQTFCWSxRQUFBLG9CQUFvQix3QkEwQmhDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0scUJBQXFCLEdBQUc7Ozs7Ozs7RUFPNUIsQ0FBQTtBQUVGOzs7Ozs7OztHQVFHO0FBQ0ksTUFBTSx5QkFBeUIsR0FBRyxLQUFLLEVBQzVDLEdBQWlCLEVBQ2pCLGVBQXVCLEVBQ2MsRUFBRTtJQUN2QyxNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQzFCOztjQUVVLHFCQUFxQjs7Ozs7Ozs7OzttRUFVZ0Ms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
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consumeLoginCode = exports.countLoginCodeAttempt = exports.storeLoginCode = exports.LOGIN_CODE_RESEND_SLACK_SECONDS = void 0;
|
|
4
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
5
|
+
/**
|
|
6
|
+
* The code state lives in `provider_identity.provider_metadata` of the
|
|
7
|
+
* `email-otp` provider identity: `login_code` is `{ hash, expires_at,
|
|
8
|
+
* attempts, sent_at }`, `login_code_budget` is `{ window_start, issued }`
|
|
9
|
+
* (codes issued in the current 24 hour window; issuing a new code does not
|
|
10
|
+
* reset it, a successful login does). Every transition below is one SQL
|
|
11
|
+
* statement whose WHERE clause carries the rule, so concurrent requests cannot
|
|
12
|
+
* both pass it. Time is the database's `now()`, never the application's clock.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The storefront starts its resend countdown when it receives the 202, which
|
|
16
|
+
* is before `sent_at` is written; the spacing check tolerates that head start.
|
|
17
|
+
*/
|
|
18
|
+
exports.LOGIN_CODE_RESEND_SLACK_SECONDS = 5;
|
|
19
|
+
/** Length of the issuance window (fixed, counted from its first code). */
|
|
20
|
+
const LOGIN_CODE_BUDGET_WINDOW_HOURS = 24;
|
|
21
|
+
/** The budget window of the row is still running (`window_start` < 24 h ago). */
|
|
22
|
+
const BUDGET_WINDOW_ACTIVE_SQL = `COALESCE(
|
|
23
|
+
jsonb_typeof(provider_metadata->'login_code_budget') = 'object'
|
|
24
|
+
AND (provider_metadata->'login_code_budget'->>'window_start')::timestamptz
|
|
25
|
+
> now() - make_interval(hours => ${LOGIN_CODE_BUDGET_WINDOW_HOURS}),
|
|
26
|
+
false
|
|
27
|
+
)`;
|
|
28
|
+
/**
|
|
29
|
+
* Stores a new code unless one was sent within the resend interval or the
|
|
30
|
+
* address already got `LOGIN_CODE_MAX_PER_DAY` codes in the running 24 hour
|
|
31
|
+
* window. Both rules are checked and the code and the budget are written by
|
|
32
|
+
* one conditional UPDATE. An expired window starts a new one with this code.
|
|
33
|
+
*/
|
|
34
|
+
const storeLoginCode = async (sql, providerIdentityId, hash) => {
|
|
35
|
+
const result = await sql.raw(`UPDATE provider_identity
|
|
36
|
+
SET provider_metadata = jsonb_set(
|
|
37
|
+
jsonb_set(
|
|
38
|
+
COALESCE(provider_metadata, '{}'::jsonb),
|
|
39
|
+
'{login_code}',
|
|
40
|
+
jsonb_build_object(
|
|
41
|
+
'hash', ?::text,
|
|
42
|
+
'expires_at', to_jsonb(now() + make_interval(mins => ?)),
|
|
43
|
+
'attempts', 0,
|
|
44
|
+
'sent_at', to_jsonb(now())
|
|
45
|
+
)
|
|
46
|
+
),
|
|
47
|
+
'{login_code_budget}',
|
|
48
|
+
jsonb_build_object(
|
|
49
|
+
'window_start', CASE
|
|
50
|
+
WHEN ${BUDGET_WINDOW_ACTIVE_SQL}
|
|
51
|
+
THEN provider_metadata->'login_code_budget'->'window_start'
|
|
52
|
+
ELSE to_jsonb(now())
|
|
53
|
+
END,
|
|
54
|
+
'issued', CASE
|
|
55
|
+
WHEN ${BUDGET_WINDOW_ACTIVE_SQL}
|
|
56
|
+
THEN to_jsonb((provider_metadata->'login_code_budget'->>'issued')::int + 1)
|
|
57
|
+
ELSE to_jsonb(1)
|
|
58
|
+
END
|
|
59
|
+
)
|
|
60
|
+
),
|
|
61
|
+
updated_at = now()
|
|
62
|
+
WHERE id = ?
|
|
63
|
+
AND provider = 'email-otp'
|
|
64
|
+
AND deleted_at IS NULL
|
|
65
|
+
AND (
|
|
66
|
+
jsonb_typeof(provider_metadata->'login_code') IS DISTINCT FROM 'object'
|
|
67
|
+
OR (provider_metadata->'login_code'->>'sent_at')::timestamptz
|
|
68
|
+
< now() - make_interval(secs => ?)
|
|
69
|
+
)
|
|
70
|
+
AND (
|
|
71
|
+
NOT ${BUDGET_WINDOW_ACTIVE_SQL}
|
|
72
|
+
OR (provider_metadata->'login_code_budget'->>'issued')::int < ?
|
|
73
|
+
)
|
|
74
|
+
RETURNING id`, [
|
|
75
|
+
hash,
|
|
76
|
+
customer_auth_1.LOGIN_CODE_TTL_MINUTES,
|
|
77
|
+
providerIdentityId,
|
|
78
|
+
customer_auth_1.LOGIN_CODE_RESEND_INTERVAL_SECONDS - exports.LOGIN_CODE_RESEND_SLACK_SECONDS,
|
|
79
|
+
customer_auth_1.LOGIN_CODE_MAX_PER_DAY,
|
|
80
|
+
]);
|
|
81
|
+
if (result.rows.length === 1) {
|
|
82
|
+
return "stored";
|
|
83
|
+
}
|
|
84
|
+
// Only to tell the reason apart for the caller's log; the decision above
|
|
85
|
+
// was already made atomically.
|
|
86
|
+
const capped = await sql.raw(`SELECT id FROM provider_identity
|
|
87
|
+
WHERE id = ?
|
|
88
|
+
AND provider = 'email-otp'
|
|
89
|
+
AND deleted_at IS NULL
|
|
90
|
+
AND ${BUDGET_WINDOW_ACTIVE_SQL}
|
|
91
|
+
AND (provider_metadata->'login_code_budget'->>'issued')::int >= ?`, [providerIdentityId, customer_auth_1.LOGIN_CODE_MAX_PER_DAY]);
|
|
92
|
+
return capped.rows.length === 1 ? "capped" : "throttled";
|
|
93
|
+
};
|
|
94
|
+
exports.storeLoginCode = storeLoginCode;
|
|
95
|
+
/**
|
|
96
|
+
* Counts one attempt against the address' live code and returns the stored
|
|
97
|
+
* hash; null when there is no code, it is expired or its attempts are used
|
|
98
|
+
* up. The count happens before the comparison, so parallel guesses share the
|
|
99
|
+
* limit instead of each seeing the same remaining budget.
|
|
100
|
+
*/
|
|
101
|
+
const countLoginCodeAttempt = async (sql, entityId) => {
|
|
102
|
+
const result = await sql.raw(`UPDATE provider_identity
|
|
103
|
+
SET provider_metadata = jsonb_set(
|
|
104
|
+
provider_metadata,
|
|
105
|
+
'{login_code,attempts}',
|
|
106
|
+
to_jsonb((provider_metadata->'login_code'->>'attempts')::int + 1)
|
|
107
|
+
),
|
|
108
|
+
updated_at = now()
|
|
109
|
+
WHERE provider = 'email-otp'
|
|
110
|
+
AND entity_id = ?
|
|
111
|
+
AND deleted_at IS NULL
|
|
112
|
+
AND jsonb_typeof(provider_metadata->'login_code') = 'object'
|
|
113
|
+
AND (provider_metadata->'login_code'->>'attempts')::int < ?
|
|
114
|
+
AND (provider_metadata->'login_code'->>'expires_at')::timestamptz > now()
|
|
115
|
+
RETURNING provider_metadata->'login_code'->>'hash' AS hash`, [entityId, customer_auth_1.LOGIN_CODE_MAX_ATTEMPTS]);
|
|
116
|
+
const hash = result.rows[0]?.hash;
|
|
117
|
+
return typeof hash === "string" && hash ? hash : null;
|
|
118
|
+
};
|
|
119
|
+
exports.countLoginCodeAttempt = countLoginCodeAttempt;
|
|
120
|
+
/**
|
|
121
|
+
* Uses the code up: removes it (and the issuance budget, a successful login
|
|
122
|
+
* proves the address' owner holds the mailbox) if it is still the one with
|
|
123
|
+
* `hash` and not expired. Exactly one of several parallel correct logins gets
|
|
124
|
+
* true.
|
|
125
|
+
*/
|
|
126
|
+
const consumeLoginCode = async (sql, entityId, hash) => {
|
|
127
|
+
const result = await sql.raw(`UPDATE provider_identity
|
|
128
|
+
SET provider_metadata = provider_metadata - 'login_code' - 'login_code_budget',
|
|
129
|
+
updated_at = now()
|
|
130
|
+
WHERE provider = 'email-otp'
|
|
131
|
+
AND entity_id = ?
|
|
132
|
+
AND deleted_at IS NULL
|
|
133
|
+
AND provider_metadata->'login_code'->>'hash' = ?
|
|
134
|
+
AND (provider_metadata->'login_code'->>'expires_at')::timestamptz > now()
|
|
135
|
+
RETURNING id`, [entityId, hash]);
|
|
136
|
+
return result.rows.length === 1;
|
|
137
|
+
};
|
|
138
|
+
exports.consumeLoginCode = consumeLoginCode;
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9naW4tY29kZS1zdGF0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jdXN0b21lci1hdXRoL2xvZ2luLWNvZGUtc3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkdBS2dGO0FBVWhGOzs7Ozs7OztHQVFHO0FBRUg7OztHQUdHO0FBQ1UsUUFBQSwrQkFBK0IsR0FBRyxDQUFDLENBQUE7QUFFaEQsMEVBQTBFO0FBQzFFLE1BQU0sOEJBQThCLEdBQUcsRUFBRSxDQUFBO0FBRXpDLGlGQUFpRjtBQUNqRixNQUFNLHdCQUF3QixHQUFHOzs7MkNBR1UsOEJBQThCOztFQUV2RSxDQUFBO0FBVUY7Ozs7O0dBS0c7QUFDSSxNQUFNLGNBQWMsR0FBRyxLQUFLLEVBQ2pDLEdBQWlCLEVBQ2pCLGtCQUEwQixFQUMxQixJQUFZLEVBQ21CLEVBQUU7SUFDakMsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7Ozs7Ozs7O3NCQWVrQix3QkFBd0I7Ozs7O3NCQUt4Qix3QkFBd0I7Ozs7Ozs7Ozs7Ozs7Ozs7ZUFnQi9CLHdCQUF3Qjs7O2tCQUdyQixFQUNkO1FBQ0UsSUFBSTtRQUNKLHNDQUFzQjtRQUN0QixrQkFBa0I7UUFDbEIsa0RBQWtDLEdBQUcsdUNBQStCO1FBQ3BFLHNDQUFzQjtLQUN2QixDQUNGLENBQUE7SUFDRCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzdCLE9BQU8sUUFBUSxDQUFBO0lBQ2pCLENBQUM7SUFDRCx5RUFBeUU7SUFDekUsK0JBQStCO0lBQy9CLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7Ozs7YUFJUyx3QkFBd0I7eUVBQ29DLEVBQ3JFLENBQUMsa0JBQWtCLEVBQUUsc0NBQXNCLENBQUMsQ0FDN0MsQ0FBQTtJQUNELE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQTtBQUMxRCxDQUFDLENBQUE7QUFyRVksUUFBQSxjQUFjLGtCQXFFMUI7QUFFRDs7Ozs7R0FLRztBQUNJLE1BQU0scUJBQXFCLEdBQUcsS0FBSyxFQUN4QyxHQUFpQixFQUNqQixRQUFnQixFQUNRLEVBQUU7SUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7Ozs7OztnRUFhNEQsRUFDNUQsQ0FBQyxRQUFRLEVBQUUsdUNBQXVCLENBQUMsQ0FDcEMsQ0FBQTtJQUNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFBO0lBQ2pDLE9BQU8sT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7QUFDdkQsQ0FBQyxDQUFBO0FBdkJZLFFBQUEscUJBQXFCLHlCQXVCakM7QUFFRDs7Ozs7R0FLRztBQUNJLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxFQUNuQyxHQUFpQixFQUNqQixRQUFnQixFQUNoQixJQUFZLEVBQ00sRUFBRTtJQUNwQixNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQzFCOzs7Ozs7OztrQkFRYyxFQUNkLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUNqQixDQUFBO0lBQ0QsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUE7QUFDakMsQ0FBQyxDQUFBO0FBbEJZLFFBQUEsZ0JBQWdCLG9CQWtCNUIifQ==
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.customerAuthPluginFeatures = void 0;
|
|
4
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
5
|
+
const descriptor_1 = require("../public/descriptor");
|
|
6
|
+
const PASSWORD_RULES = "Passwort-Regeln";
|
|
7
|
+
exports.customerAuthPluginFeatures = (0, descriptor_1.definePluginFeatures)({
|
|
8
|
+
plugin_key: customer_auth_1.CUSTOMER_AUTH_PLUGIN_KEY,
|
|
9
|
+
label: "Kunden-Login",
|
|
10
|
+
description: "Wie sich Kunden anmelden. Mindestens eine Methode bleibt eingeschaltet; Admin-Benutzer melden sich immer mit E-Mail und Passwort an.",
|
|
11
|
+
summary: "Anmeldemethoden und Passwort-Regeln für Kunden.",
|
|
12
|
+
icon: "ShieldCheck",
|
|
13
|
+
switchable: false,
|
|
14
|
+
features_heading: "Anmeldemethoden",
|
|
15
|
+
features_note: "Wirkt nach ca. 1 Minute.",
|
|
16
|
+
all_off_warning: "Keine Methode eingeschaltet: Kunden können sich nicht mehr anmelden.",
|
|
17
|
+
require_enabled_feature: true,
|
|
18
|
+
features: [
|
|
19
|
+
{
|
|
20
|
+
key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword,
|
|
21
|
+
label: "E-Mail + Passwort",
|
|
22
|
+
description: "Anmeldung, Registrierung und Passwort zurücksetzen.",
|
|
23
|
+
default_enabled: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp,
|
|
27
|
+
label: "Anmeldecode per E-Mail",
|
|
28
|
+
description: "Anmeldung mit einem 6-stelligen Code per E-Mail; für neue Adressen entsteht ein Konto.",
|
|
29
|
+
default_enabled: false,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
setting_groups: [
|
|
33
|
+
{
|
|
34
|
+
name: PASSWORD_RULES,
|
|
35
|
+
description: "Für Kunden mit „E-Mail + Passwort“. Admin-Benutzer: mindestens 15 Zeichen.",
|
|
36
|
+
requires_feature: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
settings: [
|
|
40
|
+
{
|
|
41
|
+
key: "password_min_length",
|
|
42
|
+
label: "Mindestlänge",
|
|
43
|
+
description: "Gilt bei Registrierung und Passwort-Zurücksetzen; bestehende Passwörter bleiben gültig.",
|
|
44
|
+
hint: "8 bis 128 Zeichen. Empfohlen: 15 (NIST SP 800-63B).",
|
|
45
|
+
group: PASSWORD_RULES,
|
|
46
|
+
width: "half",
|
|
47
|
+
type: "number",
|
|
48
|
+
min: 8,
|
|
49
|
+
max: 128,
|
|
50
|
+
integer: true,
|
|
51
|
+
default: 15,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: "password_require_mixed_case",
|
|
55
|
+
label: "Groß- und Kleinbuchstaben verlangen",
|
|
56
|
+
description: "Mindestens ein Groß- und ein Kleinbuchstabe.",
|
|
57
|
+
hint: "NIST rät von Zeichenregeln ab, Länge schützt besser.",
|
|
58
|
+
group: PASSWORD_RULES,
|
|
59
|
+
type: "boolean",
|
|
60
|
+
default: false,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "password_require_digit",
|
|
64
|
+
label: "Ziffer verlangen",
|
|
65
|
+
description: "Mindestens eine Ziffer.",
|
|
66
|
+
group: PASSWORD_RULES,
|
|
67
|
+
type: "boolean",
|
|
68
|
+
default: false,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "password_require_symbol",
|
|
72
|
+
label: "Sonderzeichen verlangen",
|
|
73
|
+
description: "Mindestens ein Sonderzeichen.",
|
|
74
|
+
group: PASSWORD_RULES,
|
|
75
|
+
type: "boolean",
|
|
76
|
+
default: false,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWZlYXR1cmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvcGx1Z2luLWZlYXR1cmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJHQUdnRjtBQUVoRixxREFBMkQ7QUFFM0QsTUFBTSxjQUFjLEdBQUcsaUJBQWlCLENBQUE7QUFFM0IsUUFBQSwwQkFBMEIsR0FBRyxJQUFBLGlDQUFvQixFQUFDO0lBQzdELFVBQVUsRUFBRSx3Q0FBd0I7SUFDcEMsS0FBSyxFQUFFLGNBQWM7SUFDckIsV0FBVyxFQUNULHNJQUFzSTtJQUN4SSxPQUFPLEVBQUUsaURBQWlEO0lBQzFELElBQUksRUFBRSxhQUFhO0lBQ25CLFVBQVUsRUFBRSxLQUFLO0lBQ2pCLGdCQUFnQixFQUFFLGlCQUFpQjtJQUNuQyxhQUFhLEVBQUUsMEJBQTBCO0lBQ3pDLGVBQWUsRUFDYixzRUFBc0U7SUFDeEUsdUJBQXVCLEVBQUUsSUFBSTtJQUM3QixRQUFRLEVBQUU7UUFDUjtZQUNFLEdBQUcsRUFBRSxzQ0FBc0IsQ0FBQyxhQUFhO1lBQ3pDLEtBQUssRUFBRSxtQkFBbUI7WUFDMUIsV0FBVyxFQUNULHFEQUFxRDtZQUN2RCxlQUFlLEVBQUUsSUFBSTtTQUN0QjtRQUNEO1lBQ0UsR0FBRyxFQUFFLHNDQUFzQixDQUFDLFFBQVE7WUFDcEMsS0FBSyxFQUFFLHdCQUF3QjtZQUMvQixXQUFXLEVBQ1Qsd0ZBQXdGO1lBQzFGLGVBQWUsRUFBRSxLQUFLO1NBQ3ZCO0tBQ0Y7SUFDRCxjQUFjLEVBQUU7UUFDZDtZQUNFLElBQUksRUFBRSxjQUFjO1lBQ3BCLFdBQVcsRUFDVCw0RUFBNEU7WUFDOUUsZ0JBQWdCLEVBQUUsc0NBQXNCLENBQUMsYUFBYTtTQUN2RDtLQUNGO0lBQ0QsUUFBUSxFQUFFO1FBQ1I7WUFDRSxHQUFHLEVBQUUscUJBQXFCO1lBQzFCLEtBQUssRUFBRSxjQUFjO1lBQ3JCLFdBQVcsRUFDVCx5RkFBeUY7WUFDM0YsSUFBSSxFQUFFLHFEQUFxRDtZQUMzRCxLQUFLLEVBQUUsY0FBYztZQUNyQixLQUFLLEVBQUUsTUFBTTtZQUNiLElBQUksRUFBRSxRQUFRO1lBQ2QsR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsR0FBRztZQUNSLE9BQU8sRUFBRSxJQUFJO1lBQ2IsT0FBTyxFQUFFLEVBQUU7U0FDWjtRQUNEO1lBQ0UsR0FBRyxFQUFFLDZCQUE2QjtZQUNsQyxLQUFLLEVBQUUscUNBQXFDO1lBQzVDLFdBQVcsRUFBRSw4Q0FBOEM7WUFDM0QsSUFBSSxFQUFFLHNEQUFzRDtZQUM1RCxLQUFLLEVBQUUsY0FBYztZQUNyQixJQUFJLEVBQUUsU0FBUztZQUNmLE9BQU8sRUFBRSxLQUFLO1NBQ2Y7UUFDRDtZQUNFLEdBQUcsRUFBRSx3QkFBd0I7WUFDN0IsS0FBSyxFQUFFLGtCQUFrQjtZQUN6QixXQUFXLEVBQUUseUJBQXlCO1lBQ3RDLEtBQUssRUFBRSxjQUFjO1lBQ3JCLElBQUksRUFBRSxTQUFTO1lBQ2YsT0FBTyxFQUFFLEtBQUs7U0FDZjtRQUNEO1lBQ0UsR0FBRyxFQUFFLHlCQUF5QjtZQUM5QixLQUFLLEVBQUUseUJBQXlCO1lBQ2hDLFdBQVcsRUFBRSwrQkFBK0I7WUFDNUMsS0FBSyxFQUFFLGNBQWM7WUFDckIsSUFBSSxFQUFFLFNBQVM7WUFDZixPQUFPLEVBQUUsS0FBSztTQUNmO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeProviderIdentity = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Deletes a provider identity for good and, when no provider identity is left
|
|
6
|
+
* on its auth identity, the auth identity too. The unique index on
|
|
7
|
+
* `(entity_id, provider)` has no `deleted_at` filter, so a soft delete would
|
|
8
|
+
* block the address for a new registration.
|
|
9
|
+
*/
|
|
10
|
+
const removeProviderIdentity = async (auth, identity) => {
|
|
11
|
+
await auth.deleteProviderIdentities([identity.providerIdentityId]);
|
|
12
|
+
const remaining = await auth.listProviderIdentities({ auth_identity_id: identity.authIdentityId }, { select: ["id"], take: 1 });
|
|
13
|
+
if (!remaining.length) {
|
|
14
|
+
await auth.deleteAuthIdentities([identity.authIdentityId]);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
exports.removeProviderIdentity = removeProviderIdentity;
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLXByb3ZpZGVyLWlkZW50aXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvcmVtb3ZlLXByb3ZpZGVyLWlkZW50aXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBOzs7OztHQUtHO0FBQ0ksTUFBTSxzQkFBc0IsR0FBRyxLQUFLLEVBQ3pDLElBQXdCLEVBQ3hCLFFBQTBFLEVBQzNELEVBQUU7SUFDakIsTUFBTSxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUNqRCxFQUFFLGdCQUFnQixFQUFFLFFBQVEsQ0FBQyxjQUFjLEVBQUUsRUFDN0MsRUFBRSxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQzVCLENBQUE7SUFDRCxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQVpZLFFBQUEsc0JBQXNCLDBCQVlsQyJ9
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stopCustomerAuthMethods = exports.startCustomerAuthMethods = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const module_access_1 = require("../public/module-access");
|
|
6
|
+
const customer_auth_methods_snapshot_1 = require("./customer-auth-methods-snapshot");
|
|
7
|
+
const plugin_features_1 = require("./plugin-features");
|
|
8
|
+
/**
|
|
9
|
+
* Hands the snapshot behind `authMethodsPerActor` its reader (the effective
|
|
10
|
+
* `customer_auth` switches) and loads it once. Called when the application
|
|
11
|
+
* has started, with the application container: the config getter that needs
|
|
12
|
+
* the switches has no container of its own.
|
|
13
|
+
*/
|
|
14
|
+
const startCustomerAuthMethods = (container) => (0, customer_auth_methods_snapshot_1.startCustomerAuthMethodsSnapshot)({
|
|
15
|
+
read: async () => (await (0, module_access_1.resolveBaseModule)(container).retrieveEffectiveFeatures(plugin_features_1.customerAuthPluginFeatures)).features,
|
|
16
|
+
warn: (message) => container.resolve(utils_1.ContainerRegistrationKeys.LOGGER).warn(message),
|
|
17
|
+
});
|
|
18
|
+
exports.startCustomerAuthMethods = startCustomerAuthMethods;
|
|
19
|
+
exports.stopCustomerAuthMethods = customer_auth_methods_snapshot_1.stopCustomerAuthMethodsSnapshot;
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnQtY3VzdG9tZXItYXV0aC1tZXRob2RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvc3RhcnQtY3VzdG9tZXItYXV0aC1tZXRob2RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUFxRTtBQUVyRSwyREFBMkQ7QUFFM0QscUZBR3lDO0FBQ3pDLHVEQUE4RDtBQVE5RDs7Ozs7R0FLRztBQUNJLE1BQU0sd0JBQXdCLEdBQUcsQ0FDdEMsU0FBMEIsRUFDWCxFQUFFLENBQ2pCLElBQUEsaUVBQWdDLEVBQUM7SUFDL0IsSUFBSSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQ2YsQ0FDRSxNQUFNLElBQUEsaUNBQWlCLEVBQ3JCLFNBQVMsQ0FDVixDQUFDLHlCQUF5QixDQUFDLDRDQUEwQixDQUFDLENBQ3hELENBQUMsUUFBUTtJQUNaLElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQ2hCLFNBQVMsQ0FBQyxPQUFPLENBQVMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztDQUM1RSxDQUFDLENBQUE7QUFaUyxRQUFBLHdCQUF3Qiw0QkFZakM7QUFFUyxRQUFBLHVCQUF1QixHQUFHLGdFQUErQixDQUFBIn0=
|
|
@@ -6,6 +6,10 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
|
|
|
6
6
|
plugin_key: "documents",
|
|
7
7
|
label: "Dokumente",
|
|
8
8
|
description: "Rechnungen, Stornos und Packlisten zu Bestellungen. Ausgeschaltet sind alle Dokument-Routen in Admin und Storefront nicht erreichbar und es werden keine neuen Dokumente erstellt; bereits ausgestellte Dokumente bleiben gespeichert. Checkout, Fulfillment und Versand laufen unverändert weiter.",
|
|
9
|
+
summary: "Rechnungen, Stornos und Packlisten zu Bestellungen.",
|
|
10
|
+
icon: "DocumentText",
|
|
11
|
+
disable_hint: "Aus: Dokument-Routen sind nicht erreichbar, es entstehen keine neuen Dokumente. Bestehende bleiben gespeichert.",
|
|
12
|
+
disable_confirm: "Alle Dokument-Routen in Admin und Storefront sind dann nicht erreichbar, und es werden keine neuen Dokumente erstellt. Bereits ausgestellte Dokumente bleiben gespeichert. Checkout, Fulfillment und Versand laufen unverändert weiter.",
|
|
9
13
|
features: [
|
|
10
14
|
{
|
|
11
15
|
key: "invoices",
|
|
@@ -16,7 +20,7 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
|
|
|
16
20
|
{
|
|
17
21
|
key: "packing_lists",
|
|
18
22
|
label: "Packlisten",
|
|
19
|
-
description: "
|
|
23
|
+
description: "Eine Packliste je Fulfillment, inklusive Chargenrevisionen und Abruf im Admin.",
|
|
20
24
|
default_enabled: true,
|
|
21
25
|
},
|
|
22
26
|
],
|
|
@@ -25,8 +29,10 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
|
|
|
25
29
|
key: "payment_terms",
|
|
26
30
|
label: "Zahlungskonditionen",
|
|
27
31
|
description: "Erscheint auf Rechnungen unter den Summen, z. B. „Der Betrag wurde bei der Bestellung per Kreditkarte bezahlt.“ Leer lassen, wenn nichts stehen soll. Gilt für neu erstellte und neu erzeugte Rechnungen.",
|
|
32
|
+
hint: "Erscheint auf Rechnungen unter den Summen.",
|
|
33
|
+
placeholder: "z. B. Der Betrag wurde bei der Bestellung per Kreditkarte bezahlt.",
|
|
28
34
|
type: "string",
|
|
29
35
|
},
|
|
30
36
|
],
|
|
31
37
|
});
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWZlYXR1cmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2RvY3VtZW50cy9wbHVnaW4tZmVhdHVyZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsc0NBQWdEO0FBRW5DLFFBQUEsdUJBQXVCLEdBQUcsSUFBQSw2QkFBb0IsRUFBQztJQUMxRCxVQUFVLEVBQUUsV0FBVztJQUN2QixLQUFLLEVBQUUsV0FBVztJQUNsQixXQUFXLEVBQ1QscVNBQXFTO0lBQ3ZTLE9BQU8sRUFBRSxxREFBcUQ7SUFDOUQsSUFBSSxFQUFFLGNBQWM7SUFDcEIsWUFBWSxFQUNWLGlIQUFpSDtJQUNuSCxlQUFlLEVBQ2IseU9BQXlPO0lBQzNPLFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLFVBQVU7WUFDZixLQUFLLEVBQUUsc0JBQXNCO1lBQzdCLFdBQVcsRUFDVCxzYkFBc2I7WUFDeGIsZUFBZSxFQUFFLElBQUk7U0FDdEI7UUFDRDtZQUNFLEdBQUcsRUFBRSxlQUFlO1lBQ3BCLEtBQUssRUFBRSxZQUFZO1lBQ25CLFdBQVcsRUFDVCxnRkFBZ0Y7WUFDbEYsZUFBZSxFQUFFLElBQUk7U0FDdEI7S0FDRjtJQUNELFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLGVBQWU7WUFDcEIsS0FBSyxFQUFFLHFCQUFxQjtZQUM1QixXQUFXLEVBQ1QsMk1BQTJNO1lBQzdNLElBQUksRUFBRSw0Q0FBNEM7WUFDbEQsV0FBVyxFQUFFLG9FQUFvRTtZQUNqRixJQUFJLEVBQUUsUUFBUTtTQUNmO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deliverEphemeralAuthMail = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const failure_classification_1 = require("../domain/failure-classification");
|
|
6
|
+
const load_email_shop_details_1 = require("../rendering/load-email-shop-details");
|
|
7
|
+
const load_template_1 = require("../templates/load-template");
|
|
8
|
+
const dispatch_email_message_1 = require("../../workflows/email/dispatch-email-message");
|
|
9
|
+
const asNotificationRecord = (value) => {
|
|
10
|
+
if (!value || typeof value !== "object") {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const record = value;
|
|
14
|
+
if (typeof record.id !== "string" ||
|
|
15
|
+
!record.id.trim() ||
|
|
16
|
+
(record.status !== "failure" &&
|
|
17
|
+
record.status !== "pending" &&
|
|
18
|
+
record.status !== "success")) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return { id: record.id, status: record.status };
|
|
22
|
+
};
|
|
23
|
+
const failClaim = async (email, claim, code, now, notificationId = null) => {
|
|
24
|
+
const completed = await email.markAttemptFailed({
|
|
25
|
+
attempt_id: claim.attempt.id,
|
|
26
|
+
attempt_token: claim.attempt_token,
|
|
27
|
+
failure_code: code,
|
|
28
|
+
finished_at: now,
|
|
29
|
+
notification_id: notificationId,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
kind: "done",
|
|
33
|
+
result: {
|
|
34
|
+
attempt_id: claim.attempt.id,
|
|
35
|
+
code,
|
|
36
|
+
message_id: completed.message.id,
|
|
37
|
+
status: "failed",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* One delivery attempt of a claimed message whose content carries a secret
|
|
43
|
+
* that lives only in the caller's `render` closure (password reset link,
|
|
44
|
+
* account confirmation link, login code): renders it, renews the lease, hands
|
|
45
|
+
* the content to the notification module and records the outcome on the
|
|
46
|
+
* attempt. The message stores no template data of the secret, the notification
|
|
47
|
+
* record keeps only ids, and nothing here is logged.
|
|
48
|
+
*
|
|
49
|
+
* Customer mails only (`resource_type` "customer").
|
|
50
|
+
*/
|
|
51
|
+
const deliverEphemeralAuthMail = async ({ claim, container, email, expiresAt, getNow, kind, maxAttempts, render, retryDelayMs, triggerType, }) => {
|
|
52
|
+
// A stored template never fails the mail: it falls back to the default.
|
|
53
|
+
const [shop, template] = await Promise.all([
|
|
54
|
+
(0, load_email_shop_details_1.loadEmailShopDetails)(container),
|
|
55
|
+
(0, load_template_1.loadEmailTemplate)(container, email, kind),
|
|
56
|
+
]);
|
|
57
|
+
let rendered;
|
|
58
|
+
try {
|
|
59
|
+
rendered = render({
|
|
60
|
+
configuration: email.getDispatchConfiguration(),
|
|
61
|
+
shop,
|
|
62
|
+
template,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return await failClaim(email, claim, "render_failed", getNow());
|
|
67
|
+
}
|
|
68
|
+
const beforeNotification = getNow();
|
|
69
|
+
if (expiresAt.getTime() <= beforeNotification.getTime()) {
|
|
70
|
+
return await failClaim(email, claim, "payload_expired", beforeNotification);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await email.renewAttemptLease({
|
|
74
|
+
attempt_id: claim.attempt.id,
|
|
75
|
+
attempt_token: claim.attempt_token,
|
|
76
|
+
now: beforeNotification,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return { kind: "retry" };
|
|
81
|
+
}
|
|
82
|
+
const notifications = container.resolve(utils_1.Modules.NOTIFICATION);
|
|
83
|
+
const safeData = {
|
|
84
|
+
attempt_id: claim.attempt.id,
|
|
85
|
+
kind: claim.message.kind,
|
|
86
|
+
message_id: claim.message.id,
|
|
87
|
+
};
|
|
88
|
+
let creationError;
|
|
89
|
+
let created = null;
|
|
90
|
+
try {
|
|
91
|
+
created = asNotificationRecord(await notifications.createNotifications({
|
|
92
|
+
channel: "email",
|
|
93
|
+
content: {
|
|
94
|
+
html: rendered.html,
|
|
95
|
+
subject: rendered.subject,
|
|
96
|
+
text: rendered.text,
|
|
97
|
+
},
|
|
98
|
+
data: safeData,
|
|
99
|
+
idempotency_key: (0, failure_classification_1.emailAttemptNotificationKey)(claim.attempt.id),
|
|
100
|
+
...(claim.previous_notification_id
|
|
101
|
+
? { original_notification_id: claim.previous_notification_id }
|
|
102
|
+
: {}),
|
|
103
|
+
provider_data: safeData,
|
|
104
|
+
...(claim.message.resource_id
|
|
105
|
+
? { resource_id: claim.message.resource_id }
|
|
106
|
+
: {}),
|
|
107
|
+
resource_type: "customer",
|
|
108
|
+
template: claim.message.kind,
|
|
109
|
+
to: claim.message.recipient,
|
|
110
|
+
trigger_type: triggerType,
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
creationError = error;
|
|
115
|
+
}
|
|
116
|
+
let persisted = null;
|
|
117
|
+
let lookupFailed = false;
|
|
118
|
+
if (!created || creationError) {
|
|
119
|
+
try {
|
|
120
|
+
persisted = await (0, dispatch_email_message_1.findAttemptNotification)(container, claim.attempt.id);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
lookupFailed = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const notification = created ?? persisted;
|
|
127
|
+
const finishedAt = getNow();
|
|
128
|
+
if (notification?.status === "success") {
|
|
129
|
+
const completed = await email.markAttemptSent({
|
|
130
|
+
attempt_id: claim.attempt.id,
|
|
131
|
+
attempt_token: claim.attempt_token,
|
|
132
|
+
finished_at: finishedAt,
|
|
133
|
+
notification_id: notification.id,
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
kind: "done",
|
|
137
|
+
result: {
|
|
138
|
+
attempt_id: claim.attempt.id,
|
|
139
|
+
message_id: completed.message.id,
|
|
140
|
+
status: "sent",
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const failure = creationError && notification?.status === "failure" && !lookupFailed
|
|
145
|
+
? (0, failure_classification_1.classifyNotificationFailure)(creationError)
|
|
146
|
+
: { code: "outcome_unknown", outcome: "unknown" };
|
|
147
|
+
if (failure.outcome === "retryable") {
|
|
148
|
+
const nextAttemptAt = new Date(finishedAt.getTime() + retryDelayMs);
|
|
149
|
+
if (claim.attempt.attempt_number < maxAttempts &&
|
|
150
|
+
nextAttemptAt.getTime() < expiresAt.getTime()) {
|
|
151
|
+
await email.markAttemptRetryable({
|
|
152
|
+
attempt_id: claim.attempt.id,
|
|
153
|
+
attempt_token: claim.attempt_token,
|
|
154
|
+
failure_code: failure.code,
|
|
155
|
+
finished_at: finishedAt,
|
|
156
|
+
next_attempt_at: nextAttemptAt,
|
|
157
|
+
notification_id: notification?.id ?? null,
|
|
158
|
+
});
|
|
159
|
+
return { kind: "retry" };
|
|
160
|
+
}
|
|
161
|
+
return await failClaim(email, claim, expiresAt.getTime() <= nextAttemptAt.getTime()
|
|
162
|
+
? "payload_expired"
|
|
163
|
+
: "retry_exhausted", finishedAt, notification?.id ?? null);
|
|
164
|
+
}
|
|
165
|
+
if (failure.outcome === "failed") {
|
|
166
|
+
return await failClaim(email, claim, failure.code, finishedAt, notification?.id ?? null);
|
|
167
|
+
}
|
|
168
|
+
const completed = await email.markAttemptUnknown({
|
|
169
|
+
attempt_id: claim.attempt.id,
|
|
170
|
+
attempt_token: claim.attempt_token,
|
|
171
|
+
failure_code: failure.code,
|
|
172
|
+
finished_at: finishedAt,
|
|
173
|
+
notification_id: notification?.id ?? null,
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
kind: "done",
|
|
177
|
+
result: {
|
|
178
|
+
attempt_id: claim.attempt.id,
|
|
179
|
+
code: failure.code,
|
|
180
|
+
message_id: completed.message.id,
|
|
181
|
+
status: "unknown",
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
exports.deliverEphemeralAuthMail = deliverEphemeralAuthMail;
|
|
186
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVsaXZlci1lcGhlbWVyYWwtYXV0aC1tYWlsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2VtYWlsL2FwcGxpY2F0aW9uL2RlbGl2ZXItZXBoZW1lcmFsLWF1dGgtbWFpbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFJQSxxREFBb0Q7QUFFcEQsNkVBRzBDO0FBRzFDLGtGQUE0RTtBQUU1RSw4REFBK0Q7QUFPL0QseUZBQXVGO0FBK0N2RixNQUFNLG9CQUFvQixHQUFHLENBQUMsS0FBYyxFQUE2QixFQUFFO0lBQ3pFLElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDeEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsS0FBZ0MsQ0FBQztJQUNoRCxJQUNFLE9BQU8sTUFBTSxDQUFDLEVBQUUsS0FBSyxRQUFRO1FBQzdCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFDakIsQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLFNBQVM7WUFDMUIsTUFBTSxDQUFDLE1BQU0sS0FBSyxTQUFTO1lBQzNCLE1BQU0sQ0FBQyxNQUFNLEtBQUssU0FBUyxDQUFDLEVBQzlCLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztBQUNsRCxDQUFDLENBQUM7QUFFRixNQUFNLFNBQVMsR0FBRyxLQUFLLEVBQ3JCLEtBQXlCLEVBQ3pCLEtBQXdCLEVBQ3hCLElBQVksRUFDWixHQUFTLEVBQ1QsaUJBQWdDLElBQUksRUFDRCxFQUFFO0lBQ3JDLE1BQU0sU0FBUyxHQUFHLE1BQU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDO1FBQzlDLFVBQVUsRUFBRSxLQUFLLENBQUMsT0FB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
|