@hartl-services/medusa-base 2.6.1 → 2.7.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 +73 -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 +36 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +144 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +36 -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 +144 -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 +43 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +66 -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 +3118 -2020
- package/.medusa/server/src/admin/index.mjs +3121 -2023
- 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 +4 -1
- package/.medusa/server/src/modules/plugin-features/service.js +26 -1
- 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 +117 -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/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 +116 -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 +63 -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,25 @@
|
|
|
1
|
+
/** Reads the switches once; never rejects. Concurrent calls share one read. */
|
|
2
|
+
export declare const refreshCustomerAuthMethodsSnapshot: () => Promise<void>;
|
|
3
|
+
/** Stops the periodic refresh (application shutdown). The value stays. */
|
|
4
|
+
export declare const stopCustomerAuthMethodsSnapshot: () => void;
|
|
5
|
+
/**
|
|
6
|
+
* Registers the reader, loads the switches once and keeps them fresh every
|
|
7
|
+
* 30 s (application start). `read` returns the effective feature map of
|
|
8
|
+
* `customer_auth`; `warn` is the container logger.
|
|
9
|
+
*/
|
|
10
|
+
export declare const startCustomerAuthMethodsSnapshot: (input: Readonly<{
|
|
11
|
+
read: () => Promise<Readonly<Record<string, boolean>>>;
|
|
12
|
+
warn: (message: string) => void;
|
|
13
|
+
}>) => Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* The value of `authMethodsPerActor.customer`: the provider ids a customer
|
|
16
|
+
* may use. Synchronous; starts a background refresh when the copy is stale.
|
|
17
|
+
* A fresh array per call.
|
|
18
|
+
*/
|
|
19
|
+
export declare const customerAuthMethods: () => string[];
|
|
20
|
+
/**
|
|
21
|
+
* Test hook: sets the methods and counts as just refreshed, so they hold for
|
|
22
|
+
* 30 s without a reader; `null` forgets reader and value (back to the
|
|
23
|
+
* defaults).
|
|
24
|
+
*/
|
|
25
|
+
export declare const setCustomerAuthMethodsForTests: (methods: readonly string[] | null) => void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setCustomerAuthMethodsForTests = exports.customerAuthMethods = exports.startCustomerAuthMethodsSnapshot = exports.stopCustomerAuthMethodsSnapshot = exports.refreshCustomerAuthMethodsSnapshot = void 0;
|
|
4
|
+
const refreshing_snapshot_1 = require("../shared/lib/refreshing-snapshot");
|
|
5
|
+
const auth_methods_1 = require("./domain/auth-methods");
|
|
6
|
+
const plugin_features_1 = require("./plugin-features");
|
|
7
|
+
/**
|
|
8
|
+
* In-memory copy of the login methods customers may use, for the one place
|
|
9
|
+
* that cannot await them: Medusa reads
|
|
10
|
+
* `projectConfig.http.authMethodsPerActor.customer` synchronously on every
|
|
11
|
+
* request to an `/auth/customer/<provider>` route. The shop preset puts
|
|
12
|
+
* `customerAuthMethods()` behind a getter there, so a switch in the Admin
|
|
13
|
+
* takes effect without a restart. Loading, the 30 s refresh and the failure
|
|
14
|
+
* behavior are those of `createRefreshingSnapshot`.
|
|
15
|
+
*
|
|
16
|
+
* Start and fallback value are the descriptor defaults (`["emailpass"]`),
|
|
17
|
+
* never an empty list: an empty allowlist blocks every customer login, so a
|
|
18
|
+
* failed read must not produce one.
|
|
19
|
+
*/
|
|
20
|
+
const snapshot = (0, refreshing_snapshot_1.createRefreshingSnapshot)({
|
|
21
|
+
key: "@hartl-services/medusa-base/customer-auth-methods-snapshot",
|
|
22
|
+
initial: Object.freeze((0, auth_methods_1.defaultCustomerAuthMethods)(plugin_features_1.customerAuthPluginFeatures)),
|
|
23
|
+
describe: (methods) => `Customer login methods could not be read, keeping [${methods.join(", ")}]`,
|
|
24
|
+
});
|
|
25
|
+
/** Reads the switches once; never rejects. Concurrent calls share one read. */
|
|
26
|
+
const refreshCustomerAuthMethodsSnapshot = () => snapshot.refresh();
|
|
27
|
+
exports.refreshCustomerAuthMethodsSnapshot = refreshCustomerAuthMethodsSnapshot;
|
|
28
|
+
/** Stops the periodic refresh (application shutdown). The value stays. */
|
|
29
|
+
const stopCustomerAuthMethodsSnapshot = () => snapshot.stop();
|
|
30
|
+
exports.stopCustomerAuthMethodsSnapshot = stopCustomerAuthMethodsSnapshot;
|
|
31
|
+
/**
|
|
32
|
+
* Registers the reader, loads the switches once and keeps them fresh every
|
|
33
|
+
* 30 s (application start). `read` returns the effective feature map of
|
|
34
|
+
* `customer_auth`; `warn` is the container logger.
|
|
35
|
+
*/
|
|
36
|
+
const startCustomerAuthMethodsSnapshot = (input) => snapshot.start({
|
|
37
|
+
read: async () => Object.freeze((0, auth_methods_1.deriveCustomerAuthMethods)(await input.read())),
|
|
38
|
+
warn: input.warn,
|
|
39
|
+
});
|
|
40
|
+
exports.startCustomerAuthMethodsSnapshot = startCustomerAuthMethodsSnapshot;
|
|
41
|
+
/**
|
|
42
|
+
* The value of `authMethodsPerActor.customer`: the provider ids a customer
|
|
43
|
+
* may use. Synchronous; starts a background refresh when the copy is stale.
|
|
44
|
+
* A fresh array per call.
|
|
45
|
+
*/
|
|
46
|
+
const customerAuthMethods = () => [...snapshot.current()];
|
|
47
|
+
exports.customerAuthMethods = customerAuthMethods;
|
|
48
|
+
/**
|
|
49
|
+
* Test hook: sets the methods and counts as just refreshed, so they hold for
|
|
50
|
+
* 30 s without a reader; `null` forgets reader and value (back to the
|
|
51
|
+
* defaults).
|
|
52
|
+
*/
|
|
53
|
+
const setCustomerAuthMethodsForTests = (methods) => snapshot.setForTests(methods === null ? null : Object.freeze([...methods]));
|
|
54
|
+
exports.setCustomerAuthMethodsForTests = setCustomerAuthMethodsForTests;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PluginFeatureDescriptor } from "../../public/types";
|
|
2
|
+
/** Medusa's built-in e-mail + password provider. */
|
|
3
|
+
export declare const EMAIL_PASSWORD_AUTH_PROVIDER = "emailpass";
|
|
4
|
+
/**
|
|
5
|
+
* The auth providers a customer may use for the given effective switches:
|
|
6
|
+
* `emailpass` while `email_password` is on, `email-otp` while `email_otp` is
|
|
7
|
+
* on. Empty only when both are off, which blocks every customer login (Medusa
|
|
8
|
+
* treats an empty allowlist as "nothing allowed"). A fresh array per call.
|
|
9
|
+
*/
|
|
10
|
+
export declare const deriveCustomerAuthMethods: (features: Readonly<Record<string, boolean | undefined>>) => string[];
|
|
11
|
+
/**
|
|
12
|
+
* The methods of a shop whose switches were never stored: the defaults of
|
|
13
|
+
* the descriptor. This is the start value and the fallback of the snapshot,
|
|
14
|
+
* so an unreadable database never locks every customer out.
|
|
15
|
+
*/
|
|
16
|
+
export declare const defaultCustomerAuthMethods: (descriptor: PluginFeatureDescriptor) => string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultCustomerAuthMethods = exports.deriveCustomerAuthMethods = exports.EMAIL_PASSWORD_AUTH_PROVIDER = void 0;
|
|
4
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
5
|
+
/** Medusa's built-in e-mail + password provider. */
|
|
6
|
+
exports.EMAIL_PASSWORD_AUTH_PROVIDER = "emailpass";
|
|
7
|
+
/**
|
|
8
|
+
* The auth providers a customer may use for the given effective switches:
|
|
9
|
+
* `emailpass` while `email_password` is on, `email-otp` while `email_otp` is
|
|
10
|
+
* on. Empty only when both are off, which blocks every customer login (Medusa
|
|
11
|
+
* treats an empty allowlist as "nothing allowed"). A fresh array per call.
|
|
12
|
+
*/
|
|
13
|
+
const deriveCustomerAuthMethods = (features) => [
|
|
14
|
+
...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword] === true
|
|
15
|
+
? [exports.EMAIL_PASSWORD_AUTH_PROVIDER]
|
|
16
|
+
: []),
|
|
17
|
+
...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp] === true
|
|
18
|
+
? [customer_auth_1.EMAIL_OTP_AUTH_PROVIDER]
|
|
19
|
+
: []),
|
|
20
|
+
];
|
|
21
|
+
exports.deriveCustomerAuthMethods = deriveCustomerAuthMethods;
|
|
22
|
+
/**
|
|
23
|
+
* The methods of a shop whose switches were never stored: the defaults of
|
|
24
|
+
* the descriptor. This is the start value and the fallback of the snapshot,
|
|
25
|
+
* so an unreadable database never locks every customer out.
|
|
26
|
+
*/
|
|
27
|
+
const defaultCustomerAuthMethods = (descriptor) => (0, exports.deriveCustomerAuthMethods)(Object.fromEntries(descriptor.features.map((feature) => [
|
|
28
|
+
feature.key,
|
|
29
|
+
feature.default_enabled,
|
|
30
|
+
])));
|
|
31
|
+
exports.defaultCustomerAuthMethods = defaultCustomerAuthMethods;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const customerAuthPluginFeatures: import("../public").PluginFeatureDescriptor<"email_password" | "email_otp", "password_min_length" | "password_require_mixed_case" | "password_require_digit" | "password_require_symbol", never>;
|
|
2
|
+
export type CustomerAuthFeatureKey = (typeof customerAuthPluginFeatures)["features"][number]["key"];
|
|
@@ -0,0 +1,79 @@
|
|
|
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: "Welche Anmeldemethoden Kunden im Shop nutzen können. Mindestens eine Methode muss eingeschaltet bleiben, sonst kann sich niemand mehr anmelden; Admin-Benutzer melden sich unabhängig davon mit E-Mail und Passwort an. Eine Änderung wirkt innerhalb von etwa 30 Sekunden im Backend, die Storefront übernimmt sie nach ihrem Zwischenspeicher (etwa eine Minute). Die Passwort-Regeln gelten nur für Kunden, solange „E-Mail + Passwort“ eingeschaltet ist; Admin-Benutzer behalten mindestens 15 Zeichen, die bei der ersten Anmeldung eines noch nicht angelegten Admin-Benutzers (Einladung) geprüft werden.",
|
|
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. 30 Sekunden im Backend, in der Storefront 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. Aus: Das Backend lehnt diese Wege für Kunden ab; bestehende Konten melden sich dann nur noch mit den anderen eingeschalteten Methoden an.",
|
|
23
|
+
default_enabled: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp,
|
|
27
|
+
label: "Anmeldecode per E-Mail",
|
|
28
|
+
description: "Kunden fordern einen sechsstelligen Code an, der 10 Minuten gültig ist, und melden sich damit an; für neue Adressen entsteht dabei ein Kundenkonto. Ein bestehendes Passwort-Konto derselben Adresse wird nur verknüpft, wenn die Passwort-Anmeldung aus ist oder die Adresse bestätigt wurde. Die Code-Mail geht unabhängig von den Schaltern des E-Mail-Plugins hinaus. Aus: Code-Anfrage und Code-Anmeldung sind nicht möglich.",
|
|
29
|
+
default_enabled: false,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
setting_groups: [
|
|
33
|
+
{
|
|
34
|
+
name: PASSWORD_RULES,
|
|
35
|
+
description: "Gelten nur für Kunden mit „E-Mail + Passwort“, bei Registrierung und Passwort-Zurücksetzen. Admin-Benutzer behalten 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: "Wie viele Zeichen ein Kundenpasswort mindestens haben muss (8 bis 128). NIST SP 800-63B Rev. 4, die US-Richtlinie für digitale Identität, empfiehlt mindestens 15 Zeichen, wenn das Passwort der einzige Faktor ist. Die Regeln gelten nur, solange „E-Mail + Passwort“ eingeschaltet ist, und nur für Kunden; Admin-Benutzer behalten mindestens 15 Zeichen. Bestehende Passwörter bleiben gültig, die Regeln greifen bei Registrierung und Passwort-Zurücksetzen. Grenze: Wer sich über den Admin-Weg registriert, kann 15 Zeichen ohne Zusammensetzungsregeln wählen, auch wenn der Shop mehr verlangt; das schwächt nur das eigene Passwort.",
|
|
44
|
+
hint: "8 bis 128 Zeichen. Empfohlen: mindestens 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 Zusammensetzungsregeln ab: Sie führen zu vorhersehbaren Passwörtern wie „Passwort1!“. 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 (kein Buchstabe, keine Ziffer, kein Leerzeichen).",
|
|
74
|
+
group: PASSWORD_RULES,
|
|
75
|
+
type: "boolean",
|
|
76
|
+
default: false,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
@@ -4,21 +4,9 @@
|
|
|
4
4
|
* `projectConfig.http.authVerificationsPerActor.customer` synchronously on
|
|
5
5
|
* every login. The shop preset puts `customerVerificationActors()` behind a
|
|
6
6
|
* getter there, so a toggle change in the Admin takes effect without a
|
|
7
|
-
* restart.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - Afterwards an unref'd timer refreshes every 30 s, so a change reaches an
|
|
11
|
-
* idle instance too. A read that finds the copy older than 30 s (a blocked
|
|
12
|
-
* timer, say) also starts one refresh in the background
|
|
13
|
-
* (stale-while-revalidate) and still answers from the copy. A change thus
|
|
14
|
-
* reaches a running instance within about 30 s plus the duration of the
|
|
15
|
-
* read.
|
|
16
|
-
* - A failed read keeps the last value (initially "off") and is logged; the
|
|
17
|
-
* next attempt waits another 30 s.
|
|
18
|
-
*
|
|
19
|
-
* The state lives on `globalThis`: the preset (`dist`) and the Medusa-loaded
|
|
20
|
-
* plugin code (`.medusa/server`) are compiled separately and would otherwise
|
|
21
|
-
* each hold their own copy. Login is not on the cart path.
|
|
7
|
+
* restart. Loading, the 30 s refresh and the failure behavior (keep the last
|
|
8
|
+
* value, initially "off") are those of `createRefreshingSnapshot`. Login is
|
|
9
|
+
* not on the cart path.
|
|
22
10
|
*/
|
|
23
11
|
export declare const CUSTOMER_VERIFICATION_REFRESH_MS = 30000;
|
|
24
12
|
export type CustomerVerificationActor = Readonly<{
|
|
@@ -1,117 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.setCustomerVerificationEnabledForTests = exports.customerVerificationActors = exports.isCustomerVerificationEnabled = exports.startCustomerVerificationSnapshot = exports.stopCustomerVerificationSnapshot = exports.refreshCustomerVerificationSnapshot = exports.CUSTOMER_VERIFICATION_REFRESH_MS = void 0;
|
|
4
|
+
const refreshing_snapshot_1 = require("../shared/lib/refreshing-snapshot");
|
|
4
5
|
/**
|
|
5
6
|
* In-memory copy of the effective `customer_verification` toggle for the one
|
|
6
7
|
* place that cannot await it: Medusa reads
|
|
7
8
|
* `projectConfig.http.authVerificationsPerActor.customer` synchronously on
|
|
8
9
|
* every login. The shop preset puts `customerVerificationActors()` behind a
|
|
9
10
|
* getter there, so a toggle change in the Admin takes effect without a
|
|
10
|
-
* restart.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - Afterwards an unref'd timer refreshes every 30 s, so a change reaches an
|
|
14
|
-
* idle instance too. A read that finds the copy older than 30 s (a blocked
|
|
15
|
-
* timer, say) also starts one refresh in the background
|
|
16
|
-
* (stale-while-revalidate) and still answers from the copy. A change thus
|
|
17
|
-
* reaches a running instance within about 30 s plus the duration of the
|
|
18
|
-
* read.
|
|
19
|
-
* - A failed read keeps the last value (initially "off") and is logged; the
|
|
20
|
-
* next attempt waits another 30 s.
|
|
21
|
-
*
|
|
22
|
-
* The state lives on `globalThis`: the preset (`dist`) and the Medusa-loaded
|
|
23
|
-
* plugin code (`.medusa/server`) are compiled separately and would otherwise
|
|
24
|
-
* each hold their own copy. Login is not on the cart path.
|
|
11
|
+
* restart. Loading, the 30 s refresh and the failure behavior (keep the last
|
|
12
|
+
* value, initially "off") are those of `createRefreshingSnapshot`. Login is
|
|
13
|
+
* not on the cart path.
|
|
25
14
|
*/
|
|
26
|
-
exports.CUSTOMER_VERIFICATION_REFRESH_MS =
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
refreshing: null,
|
|
34
|
-
timer: null,
|
|
35
|
-
read: null,
|
|
36
|
-
warn: null,
|
|
37
|
-
};
|
|
38
|
-
return holder[STATE_KEY];
|
|
39
|
-
};
|
|
40
|
-
const logFailure = (current, error) => {
|
|
41
|
-
try {
|
|
42
|
-
current.warn?.(`Customer verification toggle could not be read, keeping "${current.enabled ? "on" : "off"}": ${error instanceof Error ? error.message : "unknown error"}`);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// A broken logger must not turn a failed refresh into a throw.
|
|
46
|
-
}
|
|
47
|
-
};
|
|
15
|
+
exports.CUSTOMER_VERIFICATION_REFRESH_MS = refreshing_snapshot_1.SNAPSHOT_REFRESH_MS;
|
|
16
|
+
// The initial value is "off" (no verification) until the toggle has been read.
|
|
17
|
+
const snapshot = (0, refreshing_snapshot_1.createRefreshingSnapshot)({
|
|
18
|
+
key: "@hartl-services/medusa-base/customer-verification-snapshot",
|
|
19
|
+
initial: false,
|
|
20
|
+
describe: (enabled) => `Customer verification toggle could not be read, keeping "${enabled ? "on" : "off"}"`,
|
|
21
|
+
});
|
|
48
22
|
/** Reads the toggle once; never rejects. Concurrent calls share one read. */
|
|
49
|
-
const refreshCustomerVerificationSnapshot = () =>
|
|
50
|
-
const current = state();
|
|
51
|
-
if (!current.read) {
|
|
52
|
-
return Promise.resolve();
|
|
53
|
-
}
|
|
54
|
-
if (current.refreshing) {
|
|
55
|
-
return current.refreshing;
|
|
56
|
-
}
|
|
57
|
-
const read = current.read;
|
|
58
|
-
// Started from a microtask so the assignment below always precedes `finally`.
|
|
59
|
-
const refreshing = Promise.resolve().then(async () => {
|
|
60
|
-
try {
|
|
61
|
-
current.enabled = (await read()) === true;
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
logFailure(current, error);
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
current.refreshedAt = Date.now();
|
|
68
|
-
current.refreshing = null;
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
current.refreshing = refreshing;
|
|
72
|
-
return refreshing;
|
|
73
|
-
};
|
|
23
|
+
const refreshCustomerVerificationSnapshot = () => snapshot.refresh();
|
|
74
24
|
exports.refreshCustomerVerificationSnapshot = refreshCustomerVerificationSnapshot;
|
|
75
|
-
const stopTimer = (current) => {
|
|
76
|
-
if (current.timer) {
|
|
77
|
-
clearInterval(current.timer);
|
|
78
|
-
current.timer = null;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
25
|
/** Stops the periodic refresh (application shutdown). The value stays. */
|
|
82
|
-
const stopCustomerVerificationSnapshot = () =>
|
|
83
|
-
stopTimer(state());
|
|
84
|
-
};
|
|
26
|
+
const stopCustomerVerificationSnapshot = () => snapshot.stop();
|
|
85
27
|
exports.stopCustomerVerificationSnapshot = stopCustomerVerificationSnapshot;
|
|
86
28
|
/**
|
|
87
29
|
* Registers the reader, loads the toggle once and keeps it fresh every 30 s
|
|
88
30
|
* (application start). `read` returns the effective toggle; `warn` is the
|
|
89
31
|
* container logger. Starting again replaces the reader and the timer.
|
|
90
32
|
*/
|
|
91
|
-
const startCustomerVerificationSnapshot = (input) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
stopTimer(current);
|
|
96
|
-
current.timer = setInterval(() => {
|
|
97
|
-
void (0, exports.refreshCustomerVerificationSnapshot)();
|
|
98
|
-
}, exports.CUSTOMER_VERIFICATION_REFRESH_MS);
|
|
99
|
-
// An idle timer must not keep the process (or a test run) alive.
|
|
100
|
-
current.timer.unref();
|
|
101
|
-
return (0, exports.refreshCustomerVerificationSnapshot)();
|
|
102
|
-
};
|
|
33
|
+
const startCustomerVerificationSnapshot = (input) => snapshot.start({
|
|
34
|
+
read: async () => (await input.read()) === true,
|
|
35
|
+
warn: input.warn,
|
|
36
|
+
});
|
|
103
37
|
exports.startCustomerVerificationSnapshot = startCustomerVerificationSnapshot;
|
|
104
38
|
/** Synchronous; starts a background refresh when the copy is stale. Never throws. */
|
|
105
|
-
const isCustomerVerificationEnabled = () =>
|
|
106
|
-
const current = state();
|
|
107
|
-
if (current.read &&
|
|
108
|
-
!current.refreshing &&
|
|
109
|
-
(current.refreshedAt === null ||
|
|
110
|
-
Date.now() - current.refreshedAt >= exports.CUSTOMER_VERIFICATION_REFRESH_MS)) {
|
|
111
|
-
void (0, exports.refreshCustomerVerificationSnapshot)();
|
|
112
|
-
}
|
|
113
|
-
return current.enabled;
|
|
114
|
-
};
|
|
39
|
+
const isCustomerVerificationEnabled = () => snapshot.current();
|
|
115
40
|
exports.isCustomerVerificationEnabled = isCustomerVerificationEnabled;
|
|
116
41
|
/**
|
|
117
42
|
* The value of `authVerificationsPerActor.customer`: Medusa's e-mail
|
|
@@ -126,18 +51,5 @@ exports.customerVerificationActors = customerVerificationActors;
|
|
|
126
51
|
* Test hook: sets the value and counts as just refreshed, so it holds for
|
|
127
52
|
* 30 s without a reader; `null` forgets reader and value.
|
|
128
53
|
*/
|
|
129
|
-
const setCustomerVerificationEnabledForTests = (enabled) =>
|
|
130
|
-
const current = state();
|
|
131
|
-
if (enabled === null) {
|
|
132
|
-
stopTimer(current);
|
|
133
|
-
current.enabled = false;
|
|
134
|
-
current.refreshedAt = null;
|
|
135
|
-
current.refreshing = null;
|
|
136
|
-
current.read = null;
|
|
137
|
-
current.warn = null;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
current.enabled = enabled;
|
|
141
|
-
current.refreshedAt = Date.now();
|
|
142
|
-
};
|
|
54
|
+
const setCustomerVerificationEnabledForTests = (enabled) => snapshot.setForTests(enabled);
|
|
143
55
|
exports.setCustomerVerificationEnabledForTests = setCustomerVerificationEnabledForTests;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "@medusajs/framework/zod";
|
|
2
|
-
export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification"];
|
|
2
|
+
export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification", "auth.login_code"];
|
|
3
3
|
export declare const EmailKindSchema: z.ZodEnum<{
|
|
4
4
|
"order.received": "order.received";
|
|
5
5
|
"order.received.admin": "order.received.admin";
|
|
@@ -13,12 +13,13 @@ export declare const EmailKindSchema: z.ZodEnum<{
|
|
|
13
13
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
14
14
|
"user.invite": "user.invite";
|
|
15
15
|
"auth.verification": "auth.verification";
|
|
16
|
+
"auth.login_code": "auth.login_code";
|
|
16
17
|
}>;
|
|
17
18
|
export type EmailKind = (typeof EMAIL_KINDS)[number];
|
|
18
19
|
/**
|
|
19
|
-
* Kinds whose link carries a one-time
|
|
20
|
+
* Kinds whose link or code carries a one-time secret that is never persisted: the
|
|
20
21
|
* subscriber builds the mail from the event, so neither the generic retry
|
|
21
22
|
* job, the automatic dispatch nor an Admin resend may touch them.
|
|
22
23
|
*/
|
|
23
|
-
export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.verification"];
|
|
24
|
+
export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.verification", "auth.login_code"];
|
|
24
25
|
export declare const isEphemeralEmailKind: (kind: EmailKind) => boolean;
|
|
@@ -15,16 +15,18 @@ exports.EMAIL_KINDS = [
|
|
|
15
15
|
"affiliate.application_submitted.admin",
|
|
16
16
|
"user.invite",
|
|
17
17
|
"auth.verification",
|
|
18
|
+
"auth.login_code",
|
|
18
19
|
];
|
|
19
20
|
exports.EmailKindSchema = zod_1.z.enum(exports.EMAIL_KINDS);
|
|
20
21
|
/**
|
|
21
|
-
* Kinds whose link carries a one-time
|
|
22
|
+
* Kinds whose link or code carries a one-time secret that is never persisted: the
|
|
22
23
|
* subscriber builds the mail from the event, so neither the generic retry
|
|
23
24
|
* job, the automatic dispatch nor an Admin resend may touch them.
|
|
24
25
|
*/
|
|
25
26
|
exports.EPHEMERAL_EMAIL_KINDS = [
|
|
26
27
|
"auth.password_reset",
|
|
27
28
|
"auth.verification",
|
|
29
|
+
"auth.login_code",
|
|
28
30
|
];
|
|
29
31
|
const isEphemeralEmailKind = (kind) => exports.EPHEMERAL_EMAIL_KINDS.includes(kind);
|
|
30
32
|
exports.isEphemeralEmailKind = isEphemeralEmailKind;
|
|
@@ -375,6 +375,7 @@ export declare const EmailPersistedDataSchemas: {
|
|
|
375
375
|
readonly "auth.verification": z.ZodObject<{
|
|
376
376
|
locale: z.ZodNullable<z.ZodString>;
|
|
377
377
|
}, z.core.$strict>;
|
|
378
|
+
readonly "auth.login_code": z.ZodObject<{}, z.core.$strict>;
|
|
378
379
|
};
|
|
379
380
|
export declare const EmailRenderDataSchemas: {
|
|
380
381
|
readonly "auth.password_reset": z.ZodObject<{
|
|
@@ -390,6 +391,10 @@ export declare const EmailRenderDataSchemas: {
|
|
|
390
391
|
locale: z.ZodNullable<z.ZodString>;
|
|
391
392
|
verify_url: z.ZodString;
|
|
392
393
|
}, z.core.$strict>;
|
|
394
|
+
readonly "auth.login_code": z.ZodObject<{
|
|
395
|
+
code: z.ZodString;
|
|
396
|
+
valid_minutes: z.ZodNumber;
|
|
397
|
+
}, z.core.$strict>;
|
|
393
398
|
readonly "order.received": z.ZodObject<{
|
|
394
399
|
order_id: z.ZodString;
|
|
395
400
|
summary: z.ZodOptional<z.ZodObject<{
|
|
@@ -628,6 +633,9 @@ export type EmailEphemeralRenderContextMap = {
|
|
|
628
633
|
expires_at?: string;
|
|
629
634
|
}> : Kind extends "auth.verification" ? Readonly<{
|
|
630
635
|
verify_url: string;
|
|
636
|
+
}> : Kind extends "auth.login_code" ? Readonly<{
|
|
637
|
+
code: string;
|
|
638
|
+
valid_minutes: number;
|
|
631
639
|
}> : EmptyRenderContext;
|
|
632
640
|
};
|
|
633
641
|
export type EmailRenderDataMap = {
|
|
@@ -231,6 +231,8 @@ exports.EmailPersistedDataSchemas = {
|
|
|
231
231
|
locale: NullableLocaleSchema,
|
|
232
232
|
})
|
|
233
233
|
.strict(),
|
|
234
|
+
// The code itself is never stored: it only exists in the render context.
|
|
235
|
+
"auth.login_code": zod_1.z.object({}).strict(),
|
|
234
236
|
};
|
|
235
237
|
exports.EmailRenderDataSchemas = {
|
|
236
238
|
...exports.EmailPersistedDataSchemas,
|
|
@@ -247,4 +249,10 @@ exports.EmailRenderDataSchemas = {
|
|
|
247
249
|
"auth.verification": exports.EmailPersistedDataSchemas["auth.verification"].extend({
|
|
248
250
|
verify_url: zod_1.z.string().url(),
|
|
249
251
|
}),
|
|
252
|
+
"auth.login_code": exports.EmailPersistedDataSchemas["auth.login_code"].extend({
|
|
253
|
+
/** The six digits; never persisted, only passed to rendering. */
|
|
254
|
+
code: zod_1.z.string().regex(/^[0-9]{6}$/),
|
|
255
|
+
/** How long the code is valid, in minutes. */
|
|
256
|
+
valid_minutes: zod_1.z.number().int().positive(),
|
|
257
|
+
}),
|
|
250
258
|
};
|