strapi-plugin-magic-link-v5 5.5.6 → 5.5.8
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/CHANGELOG.md +14 -0
- package/README.md +28 -47
- package/dist/_chunks/LicenseGuard-CiyjFnrw.js +54 -0
- package/dist/_chunks/LicenseGuard-DJ5fq2UZ.mjs +55 -0
- package/dist/_chunks/{index-PgHpSdc8.js → index-01GHNxSr.js} +6 -35
- package/dist/_chunks/{index-CxCULraY.mjs → index-B9Ch8Jpe.mjs} +2 -2
- package/dist/_chunks/{index-34ICAF9_.mjs → index-Bj-erY7g.mjs} +4 -17
- package/dist/_chunks/{index-BFzW7Xex.js → index-CGxbhJfg.js} +4 -17
- package/dist/_chunks/{index-BZFGxKmY.mjs → index-DF3Edydl.mjs} +1 -1
- package/dist/_chunks/{index-B8urUtiz.mjs → index-Dufv-daf.mjs} +7 -36
- package/dist/_chunks/{index-M5s7QN5m.js → index-I72rBTkL.js} +1 -1
- package/dist/_chunks/{index-ivgYEX37.mjs → index-SCxv154G.mjs} +9 -128
- package/dist/_chunks/{index-CddEy_As.js → index-XYWLmmEs.js} +2 -2
- package/dist/_chunks/{index-CuXnqVl4.js → index-zzoovbM0.js} +9 -128
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +257 -754
- package/dist/server/index.mjs +257 -754
- package/package.json +3 -3
- package/dist/_chunks/LicenseGuard-5sMwgS6j.mjs +0 -646
- package/dist/_chunks/LicenseGuard-DqyOwTlf.js +0 -647
- package/dist/_chunks/LicensePage-2M-vOJX8.js +0 -377
- package/dist/_chunks/LicensePage-CCNW2hT6.mjs +0 -375
package/dist/server/index.js
CHANGED
|
@@ -370,38 +370,14 @@ _Falls du diesen Link nicht angefordert hast, ignoriere diese Nachricht._`,
|
|
|
370
370
|
try {
|
|
371
371
|
const licenseGuardService = strapi2.plugin("magic-link").service("license-guard");
|
|
372
372
|
setTimeout(async () => {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
strapi2.log.
|
|
377
|
-
strapi2.log.warn("║ ║");
|
|
378
|
-
strapi2.log.warn("║ To activate, create a license via Admin UI: ║");
|
|
379
|
-
strapi2.log.warn("║ Go to Settings → Magic Link → License ║");
|
|
380
|
-
strapi2.log.warn("╚════════════════════════════════════════════════════════════════╝");
|
|
381
|
-
} else if (licenseStatus.valid) {
|
|
382
|
-
const pluginStore2 = strapi2.store({
|
|
383
|
-
type: "plugin",
|
|
384
|
-
name: "magic-link"
|
|
385
|
-
});
|
|
386
|
-
const storedKey = await pluginStore2.get({ key: "licenseKey" });
|
|
387
|
-
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
388
|
-
strapi2.log.info("║ [SUCCESS] MAGIC LINK PLUGIN LICENSE ACTIVE ║");
|
|
389
|
-
strapi2.log.info("║ ║");
|
|
390
|
-
if (licenseStatus.data) {
|
|
391
|
-
strapi2.log.info(`║ License: ${licenseStatus.data.licenseKey} ║`);
|
|
392
|
-
strapi2.log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + "║");
|
|
393
|
-
strapi2.log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + "║");
|
|
394
|
-
} else if (storedKey) {
|
|
395
|
-
strapi2.log.info(`║ License: ${storedKey} (Offline Mode) ║`);
|
|
396
|
-
strapi2.log.info(`║ Status: Grace Period Active ║`);
|
|
397
|
-
}
|
|
398
|
-
strapi2.log.info("║ ║");
|
|
399
|
-
strapi2.log.info("║ [AUTO] Pinging every 15 minutes ║");
|
|
400
|
-
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
373
|
+
try {
|
|
374
|
+
await licenseGuardService.initialize();
|
|
375
|
+
} catch (initErr) {
|
|
376
|
+
strapi2.log.debug("[magic-link] license-guard initialize() warning:", initErr.message);
|
|
401
377
|
}
|
|
402
378
|
}, 3e3);
|
|
403
379
|
} catch (error) {
|
|
404
|
-
strapi2.log.
|
|
380
|
+
strapi2.log.debug("[magic-link] license-guard service not available at bootstrap:", error.message);
|
|
405
381
|
}
|
|
406
382
|
try {
|
|
407
383
|
setTimeout(async () => {
|
|
@@ -429,12 +405,11 @@ _Falls du diesen Link nicht angefordert hast, ignoriere diese Nachricht._`,
|
|
|
429
405
|
var destroy$1 = ({ strapi: strapi2 }) => {
|
|
430
406
|
try {
|
|
431
407
|
const licenseGuardService = strapi2.plugin("magic-link")?.service("license-guard");
|
|
432
|
-
if (licenseGuardService) {
|
|
408
|
+
if (licenseGuardService && typeof licenseGuardService.cleanup === "function") {
|
|
433
409
|
licenseGuardService.cleanup();
|
|
434
|
-
strapi2.log.info("[SUCCESS] License Guard cleanup completed");
|
|
435
410
|
}
|
|
436
411
|
} catch (error) {
|
|
437
|
-
strapi2.log.
|
|
412
|
+
strapi2.log.debug("[magic-link] license-guard cleanup warning:", error.message);
|
|
438
413
|
}
|
|
439
414
|
};
|
|
440
415
|
var passwordlessCompat$2 = (config2, { strapi: strapi2 }) => {
|
|
@@ -1219,9 +1194,7 @@ var auth$1 = {
|
|
|
1219
1194
|
return i18n.sendError(ctx, "token.invalid", 400);
|
|
1220
1195
|
}
|
|
1221
1196
|
const settings = await magicLink2.settings();
|
|
1222
|
-
|
|
1223
|
-
const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
|
|
1224
|
-
if (settings.otp_enabled && hasOTPFeature) {
|
|
1197
|
+
if (settings.otp_enabled) {
|
|
1225
1198
|
await strapi.documents("plugin::magic-link.token").update({
|
|
1226
1199
|
documentId: token2.documentId,
|
|
1227
1200
|
data: {
|
|
@@ -1688,7 +1661,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1688
1661
|
const { email: normalizedEmail, totpCode } = parseBody$1(loginWithTotpSchema, ctx.request.body);
|
|
1689
1662
|
const magicLink2 = strapi.plugin("magic-link").service("magic-link");
|
|
1690
1663
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
1691
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
1692
1664
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1693
1665
|
const userService = strapi.plugin("users-permissions").service("user");
|
|
1694
1666
|
const jwtService = strapi.plugin("users-permissions").service("jwt");
|
|
@@ -1707,10 +1679,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1707
1679
|
if (!settings.totp_as_primary_auth) {
|
|
1708
1680
|
return ctx.forbidden("TOTP login is not enabled");
|
|
1709
1681
|
}
|
|
1710
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-totp");
|
|
1711
|
-
if (!hasFeature) {
|
|
1712
|
-
return ctx.forbidden("TOTP login requires Advanced license");
|
|
1713
|
-
}
|
|
1714
1682
|
const GENERIC_INVALID = "Invalid credentials";
|
|
1715
1683
|
const users = await strapi.documents("plugin::users-permissions.user").findMany({
|
|
1716
1684
|
filters: { email: normalizedEmail },
|
|
@@ -2883,49 +2851,43 @@ var jwt$1 = {
|
|
|
2883
2851
|
};
|
|
2884
2852
|
var license$1 = {
|
|
2885
2853
|
/**
|
|
2886
|
-
*
|
|
2854
|
+
* Returns the current license status. Always reports the plugin as
|
|
2855
|
+
* usable; `hasKey` indicates whether the admin has activated a key.
|
|
2856
|
+
*
|
|
2857
|
+
* @route GET /magic-link/license/status
|
|
2858
|
+
* @returns {{ success: true, valid: true, hasKey: boolean, data: object|null }}
|
|
2887
2859
|
*/
|
|
2888
2860
|
async getStatus(ctx) {
|
|
2889
2861
|
try {
|
|
2890
2862
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2891
|
-
const pluginStore = strapi.store({
|
|
2892
|
-
type: "plugin",
|
|
2893
|
-
name: "magic-link"
|
|
2894
|
-
});
|
|
2863
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
2895
2864
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
2896
2865
|
if (!licenseKey) {
|
|
2897
2866
|
return ctx.send({
|
|
2898
|
-
success:
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2867
|
+
success: true,
|
|
2868
|
+
valid: true,
|
|
2869
|
+
// plugin is always usable in the marketplace build
|
|
2870
|
+
hasKey: false,
|
|
2871
|
+
data: null,
|
|
2872
|
+
message: "Plugin is active. License key activation is optional."
|
|
2902
2873
|
});
|
|
2903
2874
|
}
|
|
2904
|
-
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
2905
2875
|
const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
|
|
2906
2876
|
return ctx.send({
|
|
2907
2877
|
success: true,
|
|
2908
|
-
valid:
|
|
2909
|
-
|
|
2878
|
+
valid: true,
|
|
2879
|
+
hasKey: true,
|
|
2910
2880
|
data: {
|
|
2911
2881
|
licenseKey,
|
|
2912
2882
|
email: license2?.email || null,
|
|
2913
2883
|
firstName: license2?.firstName || null,
|
|
2914
2884
|
lastName: license2?.lastName || null,
|
|
2915
|
-
isActive: license2?.isActive
|
|
2916
|
-
isExpired: license2?.isExpired
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
deviceId: license2?.deviceId,
|
|
2922
|
-
ipAddress: license2?.ipAddress,
|
|
2923
|
-
features: {
|
|
2924
|
-
premium: license2?.featurePremium || false,
|
|
2925
|
-
advanced: license2?.featureAdvanced || false,
|
|
2926
|
-
enterprise: license2?.featureEnterprise || false,
|
|
2927
|
-
custom: license2?.featureCustom || false
|
|
2928
|
-
},
|
|
2885
|
+
isActive: license2?.isActive ?? true,
|
|
2886
|
+
isExpired: license2?.isExpired ?? false,
|
|
2887
|
+
expiresAt: license2?.expiresAt || null,
|
|
2888
|
+
deviceName: license2?.deviceName || null,
|
|
2889
|
+
deviceId: license2?.deviceId || null,
|
|
2890
|
+
ipAddress: license2?.ipAddress || null,
|
|
2929
2891
|
maxDevices: license2?.maxDevices || 1,
|
|
2930
2892
|
currentDevices: license2?.currentDevices || 0
|
|
2931
2893
|
}
|
|
@@ -2936,7 +2898,11 @@ var license$1 = {
|
|
|
2936
2898
|
}
|
|
2937
2899
|
},
|
|
2938
2900
|
/**
|
|
2939
|
-
* Auto-create license
|
|
2901
|
+
* Auto-create a license using the currently logged-in admin user's
|
|
2902
|
+
* data. Best-effort: a remote failure simply leaves the install
|
|
2903
|
+
* un-keyed, the plugin keeps working.
|
|
2904
|
+
*
|
|
2905
|
+
* @route POST /magic-link/license/auto-create
|
|
2940
2906
|
*/
|
|
2941
2907
|
async autoCreate(ctx) {
|
|
2942
2908
|
try {
|
|
@@ -2951,15 +2917,9 @@ var license$1 = {
|
|
|
2951
2917
|
lastName: adminUser.lastname || "User"
|
|
2952
2918
|
});
|
|
2953
2919
|
if (!license2) {
|
|
2954
|
-
return ctx.badRequest("
|
|
2920
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2955
2921
|
}
|
|
2956
2922
|
await licenseGuard2.storeLicenseKey(license2.licenseKey);
|
|
2957
|
-
const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
|
|
2958
|
-
strapi.licenseGuard = {
|
|
2959
|
-
licenseKey: license2.licenseKey,
|
|
2960
|
-
pingInterval,
|
|
2961
|
-
data: license2
|
|
2962
|
-
};
|
|
2963
2923
|
return ctx.send({
|
|
2964
2924
|
success: true,
|
|
2965
2925
|
message: "License automatically created and activated",
|
|
@@ -2971,26 +2931,22 @@ var license$1 = {
|
|
|
2971
2931
|
}
|
|
2972
2932
|
},
|
|
2973
2933
|
/**
|
|
2974
|
-
* Create and activate a new license
|
|
2934
|
+
* Create and activate a new license with explicit user details.
|
|
2935
|
+
*
|
|
2936
|
+
* @route POST /magic-link/license/create
|
|
2975
2937
|
*/
|
|
2976
2938
|
async createAndActivate(ctx) {
|
|
2977
2939
|
try {
|
|
2978
|
-
const { email: email2, firstName, lastName } = ctx.request.body;
|
|
2940
|
+
const { email: email2, firstName, lastName } = ctx.request.body || {};
|
|
2979
2941
|
if (!email2 || !firstName || !lastName) {
|
|
2980
2942
|
return ctx.badRequest("Email, firstName, and lastName are required");
|
|
2981
2943
|
}
|
|
2982
2944
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2983
2945
|
const license2 = await licenseGuard2.createLicense({ email: email2, firstName, lastName });
|
|
2984
2946
|
if (!license2) {
|
|
2985
|
-
return ctx.badRequest("
|
|
2947
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2986
2948
|
}
|
|
2987
2949
|
await licenseGuard2.storeLicenseKey(license2.licenseKey);
|
|
2988
|
-
const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
|
|
2989
|
-
strapi.licenseGuard = {
|
|
2990
|
-
licenseKey: license2.licenseKey,
|
|
2991
|
-
pingInterval,
|
|
2992
|
-
data: license2
|
|
2993
|
-
};
|
|
2994
2950
|
return ctx.send({
|
|
2995
2951
|
success: true,
|
|
2996
2952
|
message: "License created and activated successfully",
|
|
@@ -3002,39 +2958,40 @@ var license$1 = {
|
|
|
3002
2958
|
}
|
|
3003
2959
|
},
|
|
3004
2960
|
/**
|
|
3005
|
-
*
|
|
2961
|
+
* Manual refresh: re-verifies the stored license once and returns the
|
|
2962
|
+
* latest details. Replaces the periodic-ping endpoint that used to run
|
|
2963
|
+
* every 15 minutes — admins can call this on demand from the UI to
|
|
2964
|
+
* pull updated info.
|
|
2965
|
+
*
|
|
2966
|
+
* @route POST /magic-link/license/ping
|
|
3006
2967
|
*/
|
|
3007
2968
|
async ping(ctx) {
|
|
3008
2969
|
try {
|
|
3009
|
-
const pluginStore = strapi.store({
|
|
3010
|
-
type: "plugin",
|
|
3011
|
-
name: "magic-link"
|
|
3012
|
-
});
|
|
2970
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
3013
2971
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
3014
2972
|
if (!licenseKey) {
|
|
3015
2973
|
return ctx.badRequest("No license key found");
|
|
3016
2974
|
}
|
|
3017
2975
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3018
|
-
const
|
|
3019
|
-
if (!pingResult) {
|
|
3020
|
-
return ctx.badRequest("Ping failed");
|
|
3021
|
-
}
|
|
2976
|
+
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
3022
2977
|
return ctx.send({
|
|
3023
2978
|
success: true,
|
|
3024
|
-
message: "License
|
|
3025
|
-
data:
|
|
2979
|
+
message: verification.valid ? "License refreshed successfully" : "License key could not be verified, but the plugin keeps working.",
|
|
2980
|
+
data: verification.data
|
|
3026
2981
|
});
|
|
3027
2982
|
} catch (error) {
|
|
3028
|
-
strapi.log.error("Error
|
|
3029
|
-
return ctx.badRequest("Error
|
|
2983
|
+
strapi.log.error("Error refreshing license:", error);
|
|
2984
|
+
return ctx.badRequest("Error refreshing license");
|
|
3030
2985
|
}
|
|
3031
2986
|
},
|
|
3032
2987
|
/**
|
|
3033
|
-
|
|
3034
|
-
|
|
2988
|
+
* Store and validate an existing license key.
|
|
2989
|
+
*
|
|
2990
|
+
* @route POST /magic-link/license/store-key
|
|
2991
|
+
*/
|
|
3035
2992
|
async storeKey(ctx) {
|
|
3036
2993
|
try {
|
|
3037
|
-
const { licenseKey, email: email2 } = ctx.request.body;
|
|
2994
|
+
const { licenseKey, email: email2 } = ctx.request.body || {};
|
|
3038
2995
|
if (!licenseKey || !licenseKey.trim()) {
|
|
3039
2996
|
return ctx.badRequest("License key is required");
|
|
3040
2997
|
}
|
|
@@ -3046,26 +3003,35 @@ var license$1 = {
|
|
|
3046
3003
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3047
3004
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
3048
3005
|
if (!verification.valid) {
|
|
3049
|
-
|
|
3006
|
+
if (verification.networkError) {
|
|
3007
|
+
strapi.log.warn(
|
|
3008
|
+
`[WARNING] License server unreachable during activation. Storing key anyway: ${trimmedKey.substring(0, 8)}...`
|
|
3009
|
+
);
|
|
3010
|
+
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
3011
|
+
return ctx.send({
|
|
3012
|
+
success: true,
|
|
3013
|
+
message: "License key stored (license server was unreachable; will retry on the next manual refresh).",
|
|
3014
|
+
data: { licenseKey: trimmedKey, email: trimmedEmail }
|
|
3015
|
+
});
|
|
3016
|
+
}
|
|
3017
|
+
strapi.log.warn(`[WARNING] Invalid license key: ${trimmedKey.substring(0, 8)}...`);
|
|
3050
3018
|
return ctx.badRequest("Invalid or expired license key");
|
|
3051
3019
|
}
|
|
3052
3020
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
3053
3021
|
if (!license2) {
|
|
3054
|
-
strapi.log.warn(`[WARNING] License not found
|
|
3022
|
+
strapi.log.warn(`[WARNING] License not found: ${trimmedKey.substring(0, 8)}...`);
|
|
3055
3023
|
return ctx.badRequest("License not found");
|
|
3056
3024
|
}
|
|
3057
|
-
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
3058
|
-
strapi.log.warn(
|
|
3025
|
+
if (license2.email && license2.email.toLowerCase() !== trimmedEmail) {
|
|
3026
|
+
strapi.log.warn(
|
|
3027
|
+
`[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (attempted: ${trimmedEmail})`
|
|
3028
|
+
);
|
|
3059
3029
|
return ctx.badRequest("Email address does not match this license key");
|
|
3060
3030
|
}
|
|
3061
3031
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
pingInterval,
|
|
3066
|
-
data: verification.data
|
|
3067
|
-
};
|
|
3068
|
-
strapi.log.info(`[SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
|
|
3032
|
+
strapi.log.info(
|
|
3033
|
+
`[SUCCESS] License key validated and stored: ${trimmedKey.substring(0, 8)}... (email: ${trimmedEmail})`
|
|
3034
|
+
);
|
|
3069
3035
|
return ctx.send({
|
|
3070
3036
|
success: true,
|
|
3071
3037
|
message: "License key validated and activated successfully",
|
|
@@ -3217,11 +3183,6 @@ var otp$3 = {
|
|
|
3217
3183
|
if (!otpSettings.enabled) {
|
|
3218
3184
|
return ctx.badRequest("OTP is not enabled");
|
|
3219
3185
|
}
|
|
3220
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3221
|
-
const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
|
|
3222
|
-
if (!hasOTPFeature) {
|
|
3223
|
-
return ctx.forbidden("OTP feature requires Premium license or higher");
|
|
3224
|
-
}
|
|
3225
3186
|
const rlFail = await enforceOtpRateLimits(ctx, normalizedEmail, "send");
|
|
3226
3187
|
if (rlFail) return ctx.tooManyRequests(rlFail.message);
|
|
3227
3188
|
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
@@ -3513,11 +3474,6 @@ var otp$3 = {
|
|
|
3513
3474
|
*/
|
|
3514
3475
|
async setupTOTP(ctx) {
|
|
3515
3476
|
try {
|
|
3516
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3517
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-totp");
|
|
3518
|
-
if (!hasFeature) {
|
|
3519
|
-
return ctx.forbidden("TOTP feature requires Advanced or Enterprise license");
|
|
3520
|
-
}
|
|
3521
3477
|
const { email: email2, id: userId } = ctx.state.user;
|
|
3522
3478
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3523
3479
|
const setupData = await otpService.setupTOTP(userId, email2);
|
|
@@ -3587,11 +3543,6 @@ var otp$3 = {
|
|
|
3587
3543
|
*/
|
|
3588
3544
|
async generateBackupCodes(ctx) {
|
|
3589
3545
|
try {
|
|
3590
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3591
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-backup-codes");
|
|
3592
|
-
if (!hasFeature) {
|
|
3593
|
-
return ctx.forbidden("Backup codes feature requires Enterprise license");
|
|
3594
|
-
}
|
|
3595
3546
|
const { id: userId } = ctx.state.user;
|
|
3596
3547
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3597
3548
|
const backupCodes = await otpService.generateBackupCodes(userId);
|
|
@@ -3761,61 +3712,6 @@ const passwordlessCompat = passwordlessCompat$2;
|
|
|
3761
3712
|
var middlewares$1 = {
|
|
3762
3713
|
"passwordless-compat": passwordlessCompat
|
|
3763
3714
|
};
|
|
3764
|
-
const { PolicyError } = require$$1__default$1.default.errors;
|
|
3765
|
-
var licenseCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
|
|
3766
|
-
try {
|
|
3767
|
-
const licenseGuard2 = strapi2.plugin("magic-link").service("license-guard");
|
|
3768
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
3769
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
3770
|
-
if (!licenseKey) {
|
|
3771
|
-
strapi2.log.warn("[ACCESS-DENIED] No license key found");
|
|
3772
|
-
throw new PolicyError("No license found. Please activate the plugin first.", {
|
|
3773
|
-
policy: "license-check",
|
|
3774
|
-
errCode: "LICENSE_MISSING"
|
|
3775
|
-
});
|
|
3776
|
-
}
|
|
3777
|
-
const verification = await licenseGuard2.verifyLicense(licenseKey, true);
|
|
3778
|
-
if (!verification.valid) {
|
|
3779
|
-
strapi2.log.warn("[ACCESS-DENIED] Invalid license");
|
|
3780
|
-
throw new PolicyError("Invalid or expired license.", {
|
|
3781
|
-
policy: "license-check",
|
|
3782
|
-
errCode: "LICENSE_INVALID"
|
|
3783
|
-
});
|
|
3784
|
-
}
|
|
3785
|
-
const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
|
|
3786
|
-
if (!license2) {
|
|
3787
|
-
strapi2.log.warn("[ACCESS-DENIED] License not found in database");
|
|
3788
|
-
throw new PolicyError("License not found. Please contact support.", {
|
|
3789
|
-
policy: "license-check",
|
|
3790
|
-
errCode: "LICENSE_NOT_FOUND"
|
|
3791
|
-
});
|
|
3792
|
-
}
|
|
3793
|
-
if (!license2.isActive) {
|
|
3794
|
-
strapi2.log.warn("[ACCESS-DENIED] License is inactive");
|
|
3795
|
-
throw new PolicyError("License is inactive. Please activate your license.", {
|
|
3796
|
-
policy: "license-check",
|
|
3797
|
-
errCode: "LICENSE_INACTIVE"
|
|
3798
|
-
});
|
|
3799
|
-
}
|
|
3800
|
-
if (license2.isExpired) {
|
|
3801
|
-
strapi2.log.warn("[ACCESS-DENIED] License has expired");
|
|
3802
|
-
throw new PolicyError("License has expired. Please renew your license.", {
|
|
3803
|
-
policy: "license-check",
|
|
3804
|
-
errCode: "LICENSE_EXPIRED"
|
|
3805
|
-
});
|
|
3806
|
-
}
|
|
3807
|
-
return true;
|
|
3808
|
-
} catch (error) {
|
|
3809
|
-
if (error instanceof PolicyError) {
|
|
3810
|
-
throw error;
|
|
3811
|
-
}
|
|
3812
|
-
strapi2.log.error("Error checking license:", error);
|
|
3813
|
-
throw new PolicyError("Error verifying license. Please try again.", {
|
|
3814
|
-
policy: "license-check",
|
|
3815
|
-
errCode: "LICENSE_CHECK_ERROR"
|
|
3816
|
-
});
|
|
3817
|
-
}
|
|
3818
|
-
};
|
|
3819
3715
|
var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
|
|
3820
3716
|
const pluginStore = strapi2.store({
|
|
3821
3717
|
type: "plugin",
|
|
@@ -3837,10 +3733,8 @@ var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) =
|
|
|
3837
3733
|
}
|
|
3838
3734
|
return true;
|
|
3839
3735
|
};
|
|
3840
|
-
const licenseCheck = licenseCheck$1;
|
|
3841
3736
|
const compatibilityCheck = compatibilityCheck$1;
|
|
3842
3737
|
var policies$1 = {
|
|
3843
|
-
"license-check": licenseCheck,
|
|
3844
3738
|
"compatibility-check": compatibilityCheck
|
|
3845
3739
|
};
|
|
3846
3740
|
var contentApi$1 = {
|
|
@@ -3852,7 +3746,7 @@ var contentApi$1 = {
|
|
|
3852
3746
|
handler: "auth.login",
|
|
3853
3747
|
config: {
|
|
3854
3748
|
auth: false,
|
|
3855
|
-
policies: [
|
|
3749
|
+
policies: []
|
|
3856
3750
|
}
|
|
3857
3751
|
},
|
|
3858
3752
|
{
|
|
@@ -3861,7 +3755,7 @@ var contentApi$1 = {
|
|
|
3861
3755
|
handler: "auth.sendLink",
|
|
3862
3756
|
config: {
|
|
3863
3757
|
auth: false,
|
|
3864
|
-
policies: [
|
|
3758
|
+
policies: []
|
|
3865
3759
|
}
|
|
3866
3760
|
},
|
|
3867
3761
|
// OTP Routes
|
|
@@ -3871,7 +3765,7 @@ var contentApi$1 = {
|
|
|
3871
3765
|
handler: "otp.send",
|
|
3872
3766
|
config: {
|
|
3873
3767
|
auth: false,
|
|
3874
|
-
policies: [
|
|
3768
|
+
policies: []
|
|
3875
3769
|
}
|
|
3876
3770
|
},
|
|
3877
3771
|
{
|
|
@@ -3880,7 +3774,7 @@ var contentApi$1 = {
|
|
|
3880
3774
|
handler: "otp.verify",
|
|
3881
3775
|
config: {
|
|
3882
3776
|
auth: false,
|
|
3883
|
-
policies: [
|
|
3777
|
+
policies: []
|
|
3884
3778
|
}
|
|
3885
3779
|
},
|
|
3886
3780
|
{
|
|
@@ -3889,7 +3783,7 @@ var contentApi$1 = {
|
|
|
3889
3783
|
handler: "otp.resend",
|
|
3890
3784
|
config: {
|
|
3891
3785
|
auth: false,
|
|
3892
|
-
policies: [
|
|
3786
|
+
policies: []
|
|
3893
3787
|
}
|
|
3894
3788
|
},
|
|
3895
3789
|
// MFA Routes
|
|
@@ -3899,7 +3793,7 @@ var contentApi$1 = {
|
|
|
3899
3793
|
handler: "auth.verifyMFATOTP",
|
|
3900
3794
|
config: {
|
|
3901
3795
|
auth: false,
|
|
3902
|
-
policies: [
|
|
3796
|
+
policies: []
|
|
3903
3797
|
}
|
|
3904
3798
|
},
|
|
3905
3799
|
{
|
|
@@ -3908,7 +3802,7 @@ var contentApi$1 = {
|
|
|
3908
3802
|
handler: "auth.loginWithTOTP",
|
|
3909
3803
|
config: {
|
|
3910
3804
|
auth: false,
|
|
3911
|
-
policies: [
|
|
3805
|
+
policies: []
|
|
3912
3806
|
}
|
|
3913
3807
|
}
|
|
3914
3808
|
]
|
|
@@ -4208,10 +4102,7 @@ var compatibility$1 = {
|
|
|
4208
4102
|
handler: "auth.login",
|
|
4209
4103
|
config: {
|
|
4210
4104
|
auth: false,
|
|
4211
|
-
policies: [
|
|
4212
|
-
"plugin::magic-link.compatibility-check",
|
|
4213
|
-
"plugin::magic-link.license-check"
|
|
4214
|
-
],
|
|
4105
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4215
4106
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4216
4107
|
}
|
|
4217
4108
|
},
|
|
@@ -4222,10 +4113,7 @@ var compatibility$1 = {
|
|
|
4222
4113
|
handler: "auth.sendLink",
|
|
4223
4114
|
config: {
|
|
4224
4115
|
auth: false,
|
|
4225
|
-
policies: [
|
|
4226
|
-
"plugin::magic-link.compatibility-check",
|
|
4227
|
-
"plugin::magic-link.license-check"
|
|
4228
|
-
],
|
|
4116
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4229
4117
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4230
4118
|
}
|
|
4231
4119
|
}
|
|
@@ -4641,6 +4529,50 @@ var magicLink$1 = ({ strapi: strapi2 }) => ({
|
|
|
4641
4529
|
strapi2.log.error("MagicMail send failed, falling back to default provider:", error);
|
|
4642
4530
|
}
|
|
4643
4531
|
}
|
|
4532
|
+
const wantsEmailDesigner = settings?.use_email_designer && settings?.email_designer_template_id;
|
|
4533
|
+
const hasEmailDesignerPlugin = Boolean(strapi2.plugin("email-designer-5"));
|
|
4534
|
+
if (wantsEmailDesigner && hasEmailDesignerPlugin) {
|
|
4535
|
+
const templateId = parseInt(settings.email_designer_template_id, 10);
|
|
4536
|
+
if (!Number.isNaN(templateId) && templateId > 0) {
|
|
4537
|
+
try {
|
|
4538
|
+
await strapi2.plugin("email-designer-5").service("email").sendTemplatedEmail(
|
|
4539
|
+
{
|
|
4540
|
+
to: token2.email,
|
|
4541
|
+
from: settings.from_email ? `${settings.from_name || "Magic Link"} <${settings.from_email}>` : void 0,
|
|
4542
|
+
replyTo: settings.response_email || void 0
|
|
4543
|
+
},
|
|
4544
|
+
{
|
|
4545
|
+
templateReferenceId: templateId,
|
|
4546
|
+
subject: settings.object || emailConfig.object
|
|
4547
|
+
},
|
|
4548
|
+
{
|
|
4549
|
+
...templatePayload,
|
|
4550
|
+
user: { email: token2.email },
|
|
4551
|
+
magicLink: loginUrl,
|
|
4552
|
+
expiresAt: token2.expires_at ? new Date(token2.expires_at).toISOString() : void 0,
|
|
4553
|
+
expiryText: expiryTextForTemplate
|
|
4554
|
+
}
|
|
4555
|
+
);
|
|
4556
|
+
strapi2.log.info(
|
|
4557
|
+
`Magic Link email sent via Email Designer 5 (template ${templateId}) to ${token2.email}`
|
|
4558
|
+
);
|
|
4559
|
+
return token2;
|
|
4560
|
+
} catch (emailDesignerError) {
|
|
4561
|
+
strapi2.log.error(
|
|
4562
|
+
"Email Designer 5 send failed, falling back to default provider:",
|
|
4563
|
+
emailDesignerError
|
|
4564
|
+
);
|
|
4565
|
+
}
|
|
4566
|
+
} else {
|
|
4567
|
+
strapi2.log.warn(
|
|
4568
|
+
`Invalid Email Designer template ID: '${settings.email_designer_template_id}', falling back to default email`
|
|
4569
|
+
);
|
|
4570
|
+
}
|
|
4571
|
+
} else if (wantsEmailDesigner && !hasEmailDesignerPlugin) {
|
|
4572
|
+
strapi2.log.debug(
|
|
4573
|
+
"Email Designer enabled but plugin not installed — falling back to default email"
|
|
4574
|
+
);
|
|
4575
|
+
}
|
|
4644
4576
|
await strapi2.plugin("email").service("email").send({
|
|
4645
4577
|
to: token2.email,
|
|
4646
4578
|
from: emailConfig.from,
|
|
@@ -4823,265 +4755,8 @@ var store$1 = ({ strapi: strapi2 }) => ({
|
|
|
4823
4755
|
return pluginStore.set({ key: "settings", value: settings });
|
|
4824
4756
|
}
|
|
4825
4757
|
});
|
|
4826
|
-
var features$1 = {
|
|
4827
|
-
// FREE/DEMO Features
|
|
4828
|
-
free: {
|
|
4829
|
-
maxTokens: 100,
|
|
4830
|
-
// 100 aktive Tokens gleichzeitig
|
|
4831
|
-
maxSessions: 50,
|
|
4832
|
-
// 50 aktive JWT Sessions
|
|
4833
|
-
maxIPBans: 10,
|
|
4834
|
-
// 10 gebannte IPs
|
|
4835
|
-
features: [
|
|
4836
|
-
"basic-magic-link",
|
|
4837
|
-
// Basis Magic Link Auth
|
|
4838
|
-
"email-templates",
|
|
4839
|
-
// Basis Email Templates
|
|
4840
|
-
"jwt-sessions",
|
|
4841
|
-
// JWT Session Management
|
|
4842
|
-
"ip-tracking",
|
|
4843
|
-
// IP Tracking für Security
|
|
4844
|
-
"rate-limiting",
|
|
4845
|
-
// Basis Rate Limiting
|
|
4846
|
-
"auto-user-creation",
|
|
4847
|
-
// Auto User Creation
|
|
4848
|
-
"token-management",
|
|
4849
|
-
// Token Dashboard
|
|
4850
|
-
"ip-bans"
|
|
4851
|
-
// IP Ban Management
|
|
4852
|
-
]
|
|
4853
|
-
},
|
|
4854
|
-
// PREMIUM Features
|
|
4855
|
-
premium: {
|
|
4856
|
-
maxTokens: 1e3,
|
|
4857
|
-
// 1000 aktive Tokens
|
|
4858
|
-
maxSessions: 500,
|
|
4859
|
-
// 500 aktive Sessions
|
|
4860
|
-
maxIPBans: 100,
|
|
4861
|
-
// 100 gebannte IPs
|
|
4862
|
-
features: [
|
|
4863
|
-
"basic-magic-link",
|
|
4864
|
-
"email-templates",
|
|
4865
|
-
"jwt-sessions",
|
|
4866
|
-
"ip-tracking",
|
|
4867
|
-
"rate-limiting",
|
|
4868
|
-
"auto-user-creation",
|
|
4869
|
-
"token-management",
|
|
4870
|
-
"ip-bans",
|
|
4871
|
-
"otp-email",
|
|
4872
|
-
// Email OTP (6-digit code)
|
|
4873
|
-
"mfa-totp",
|
|
4874
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4875
|
-
"custom-email-templates",
|
|
4876
|
-
// Custom HTML/Text Templates
|
|
4877
|
-
"email-designer-integration",
|
|
4878
|
-
// Email Designer 5 Support
|
|
4879
|
-
"magic-mail-integration",
|
|
4880
|
-
// MagicMail Integration
|
|
4881
|
-
"advanced-rate-limiting",
|
|
4882
|
-
// Custom Rate Limits
|
|
4883
|
-
"session-management",
|
|
4884
|
-
// Erweiterte Session Verwaltung
|
|
4885
|
-
"bulk-operations",
|
|
4886
|
-
// Bulk Token/Session Operations
|
|
4887
|
-
"extended-logging",
|
|
4888
|
-
// Erweiterte Login-Info Logs
|
|
4889
|
-
"custom-redirect-urls"
|
|
4890
|
-
// Custom Redirect URLs per Token
|
|
4891
|
-
]
|
|
4892
|
-
},
|
|
4893
|
-
// ADVANCED Features
|
|
4894
|
-
advanced: {
|
|
4895
|
-
maxTokens: -1,
|
|
4896
|
-
// Unlimited
|
|
4897
|
-
maxSessions: -1,
|
|
4898
|
-
// Unlimited
|
|
4899
|
-
maxIPBans: -1,
|
|
4900
|
-
// Unlimited
|
|
4901
|
-
features: [
|
|
4902
|
-
"basic-magic-link",
|
|
4903
|
-
"email-templates",
|
|
4904
|
-
"jwt-sessions",
|
|
4905
|
-
"ip-tracking",
|
|
4906
|
-
"rate-limiting",
|
|
4907
|
-
"auto-user-creation",
|
|
4908
|
-
"token-management",
|
|
4909
|
-
"ip-bans",
|
|
4910
|
-
"otp-email",
|
|
4911
|
-
"mfa-totp",
|
|
4912
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4913
|
-
"otp-sms",
|
|
4914
|
-
// SMS OTP via Twilio/Vonage
|
|
4915
|
-
"otp-totp",
|
|
4916
|
-
// TOTP (Authenticator Apps)
|
|
4917
|
-
"totp-primary-login",
|
|
4918
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
4919
|
-
"custom-email-templates",
|
|
4920
|
-
"email-designer-integration",
|
|
4921
|
-
"magic-mail-integration",
|
|
4922
|
-
"advanced-rate-limiting",
|
|
4923
|
-
"session-management",
|
|
4924
|
-
"bulk-operations",
|
|
4925
|
-
"extended-logging",
|
|
4926
|
-
"custom-redirect-urls",
|
|
4927
|
-
"webhook-notifications",
|
|
4928
|
-
// Webhooks für Events
|
|
4929
|
-
"api-access-tokens",
|
|
4930
|
-
// API Tokens für Programmatic Access
|
|
4931
|
-
"advanced-analytics",
|
|
4932
|
-
// Analytics Dashboard
|
|
4933
|
-
"custom-token-context",
|
|
4934
|
-
// Custom Context Data in Tokens
|
|
4935
|
-
"geolocation-tracking",
|
|
4936
|
-
// GeoIP Tracking
|
|
4937
|
-
"device-fingerprinting",
|
|
4938
|
-
// Device Fingerprinting
|
|
4939
|
-
"suspicious-login-alerts"
|
|
4940
|
-
// Alerts für verdächtige Logins
|
|
4941
|
-
]
|
|
4942
|
-
},
|
|
4943
|
-
// ENTERPRISE Features
|
|
4944
|
-
enterprise: {
|
|
4945
|
-
maxTokens: -1,
|
|
4946
|
-
maxSessions: -1,
|
|
4947
|
-
maxIPBans: -1,
|
|
4948
|
-
features: [
|
|
4949
|
-
"basic-magic-link",
|
|
4950
|
-
"email-templates",
|
|
4951
|
-
"jwt-sessions",
|
|
4952
|
-
"ip-tracking",
|
|
4953
|
-
"rate-limiting",
|
|
4954
|
-
"auto-user-creation",
|
|
4955
|
-
"token-management",
|
|
4956
|
-
"ip-bans",
|
|
4957
|
-
"otp-email",
|
|
4958
|
-
"mfa-totp",
|
|
4959
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4960
|
-
"otp-sms",
|
|
4961
|
-
"otp-totp",
|
|
4962
|
-
"totp-primary-login",
|
|
4963
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
4964
|
-
"otp-backup-codes",
|
|
4965
|
-
// Backup Recovery Codes
|
|
4966
|
-
"biometric-support",
|
|
4967
|
-
// WebAuthn/FIDO2
|
|
4968
|
-
"custom-email-templates",
|
|
4969
|
-
"email-designer-integration",
|
|
4970
|
-
"magic-mail-integration",
|
|
4971
|
-
"advanced-rate-limiting",
|
|
4972
|
-
"session-management",
|
|
4973
|
-
"bulk-operations",
|
|
4974
|
-
"extended-logging",
|
|
4975
|
-
"custom-redirect-urls",
|
|
4976
|
-
"webhook-notifications",
|
|
4977
|
-
"api-access-tokens",
|
|
4978
|
-
"advanced-analytics",
|
|
4979
|
-
"custom-token-context",
|
|
4980
|
-
"geolocation-tracking",
|
|
4981
|
-
"device-fingerprinting",
|
|
4982
|
-
"suspicious-login-alerts",
|
|
4983
|
-
"sso-integration",
|
|
4984
|
-
// SSO/SAML Integration
|
|
4985
|
-
"ldap-sync",
|
|
4986
|
-
// LDAP/AD Sync
|
|
4987
|
-
"audit-logs",
|
|
4988
|
-
// Vollständige Audit Logs
|
|
4989
|
-
"compliance-reports",
|
|
4990
|
-
// GDPR/SOC2 Reports
|
|
4991
|
-
"priority-support",
|
|
4992
|
-
// 24/7 Priority Support
|
|
4993
|
-
"custom-security-rules",
|
|
4994
|
-
// Custom Security Rules
|
|
4995
|
-
"multi-tenant",
|
|
4996
|
-
// Multi-Tenant Support
|
|
4997
|
-
"white-label"
|
|
4998
|
-
// White-Label Branding
|
|
4999
|
-
]
|
|
5000
|
-
},
|
|
5001
|
-
/**
|
|
5002
|
-
* Check if a feature is available for given license tier
|
|
5003
|
-
*/
|
|
5004
|
-
hasFeature(licenseData, featureName) {
|
|
5005
|
-
if (!licenseData) {
|
|
5006
|
-
return this.free.features.includes(featureName);
|
|
5007
|
-
}
|
|
5008
|
-
let isEnterprise = false;
|
|
5009
|
-
let isAdvanced = false;
|
|
5010
|
-
let isPremium = false;
|
|
5011
|
-
if (licenseData.tier) {
|
|
5012
|
-
isEnterprise = licenseData.tier === "enterprise";
|
|
5013
|
-
isAdvanced = licenseData.tier === "advanced";
|
|
5014
|
-
isPremium = licenseData.tier === "premium";
|
|
5015
|
-
}
|
|
5016
|
-
if (licenseData.features) {
|
|
5017
|
-
isEnterprise = isEnterprise || licenseData.features.enterprise === true;
|
|
5018
|
-
isAdvanced = isAdvanced || licenseData.features.advanced === true;
|
|
5019
|
-
isPremium = isPremium || licenseData.features.premium === true;
|
|
5020
|
-
}
|
|
5021
|
-
if (licenseData.featureEnterprise === true) {
|
|
5022
|
-
isEnterprise = true;
|
|
5023
|
-
}
|
|
5024
|
-
if (licenseData.featureAdvanced === true) {
|
|
5025
|
-
isAdvanced = true;
|
|
5026
|
-
}
|
|
5027
|
-
if (licenseData.featurePremium === true) {
|
|
5028
|
-
isPremium = true;
|
|
5029
|
-
}
|
|
5030
|
-
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
5031
|
-
return true;
|
|
5032
|
-
}
|
|
5033
|
-
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
5034
|
-
return true;
|
|
5035
|
-
}
|
|
5036
|
-
if (isPremium && this.premium.features.includes(featureName)) {
|
|
5037
|
-
return true;
|
|
5038
|
-
}
|
|
5039
|
-
return this.free.features.includes(featureName);
|
|
5040
|
-
},
|
|
5041
|
-
/**
|
|
5042
|
-
* Get max allowed tokens for license tier
|
|
5043
|
-
*/
|
|
5044
|
-
getMaxTokens(licenseData) {
|
|
5045
|
-
if (!licenseData) return this.free.maxTokens;
|
|
5046
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxTokens;
|
|
5047
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxTokens;
|
|
5048
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxTokens;
|
|
5049
|
-
return this.free.maxTokens;
|
|
5050
|
-
},
|
|
5051
|
-
/**
|
|
5052
|
-
* Get max allowed sessions for license tier
|
|
5053
|
-
*/
|
|
5054
|
-
getMaxSessions(licenseData) {
|
|
5055
|
-
if (!licenseData) return this.free.maxSessions;
|
|
5056
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxSessions;
|
|
5057
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxSessions;
|
|
5058
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxSessions;
|
|
5059
|
-
return this.free.maxSessions;
|
|
5060
|
-
},
|
|
5061
|
-
/**
|
|
5062
|
-
* Get max allowed IP bans for license tier
|
|
5063
|
-
*/
|
|
5064
|
-
getMaxIPBans(licenseData) {
|
|
5065
|
-
if (!licenseData) return this.free.maxIPBans;
|
|
5066
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxIPBans;
|
|
5067
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxIPBans;
|
|
5068
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxIPBans;
|
|
5069
|
-
return this.free.maxIPBans;
|
|
5070
|
-
},
|
|
5071
|
-
/**
|
|
5072
|
-
* Get tier name from license data
|
|
5073
|
-
*/
|
|
5074
|
-
getTierName(licenseData) {
|
|
5075
|
-
if (!licenseData) return "free";
|
|
5076
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return "enterprise";
|
|
5077
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return "advanced";
|
|
5078
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return "premium";
|
|
5079
|
-
return "free";
|
|
5080
|
-
}
|
|
5081
|
-
};
|
|
5082
4758
|
const crypto$1 = require$$0__default.default;
|
|
5083
4759
|
const os = require$$1__default$3.default;
|
|
5084
|
-
const features = features$1;
|
|
5085
4760
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
5086
4761
|
const envTimeout = Number(process.env.MAGIC_LICENSE_TIMEOUT_MS);
|
|
5087
4762
|
const DEFAULT_FETCH_TIMEOUT_MS = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 12e3;
|
|
@@ -5089,7 +4764,7 @@ const FETCH_RETRIES = 1;
|
|
|
5089
4764
|
const FETCH_RETRY_BACKOFF_MS = 750;
|
|
5090
4765
|
async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
5091
4766
|
let lastError;
|
|
5092
|
-
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt
|
|
4767
|
+
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt += 1) {
|
|
5093
4768
|
const controller2 = new AbortController();
|
|
5094
4769
|
const timer = setTimeout(() => controller2.abort(), timeoutMs);
|
|
5095
4770
|
try {
|
|
@@ -5108,15 +4783,22 @@ async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TI
|
|
|
5108
4783
|
throw lastError;
|
|
5109
4784
|
}
|
|
5110
4785
|
var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
4786
|
+
// ======================================================================
|
|
4787
|
+
// Network helpers (used only during admin-side key activation)
|
|
4788
|
+
// ======================================================================
|
|
5111
4789
|
/**
|
|
5112
|
-
*
|
|
5113
|
-
*
|
|
4790
|
+
* License server URL. Hard-coded — kept simple because the only call
|
|
4791
|
+
* site is the admin-side activation form. Not user-configurable.
|
|
4792
|
+
* @returns {string}
|
|
5114
4793
|
*/
|
|
5115
4794
|
getLicenseServerUrl() {
|
|
5116
4795
|
return LICENSE_SERVER_URL;
|
|
5117
4796
|
},
|
|
5118
4797
|
/**
|
|
5119
|
-
*
|
|
4798
|
+
* Hashed device identifier used by the upstream activation server to
|
|
4799
|
+
* de-duplicate seats. Hash of MAC addresses + hostname; the raw values
|
|
4800
|
+
* never leave the host.
|
|
4801
|
+
* @returns {string}
|
|
5120
4802
|
*/
|
|
5121
4803
|
generateDeviceId() {
|
|
5122
4804
|
try {
|
|
@@ -5133,11 +4815,12 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5133
4815
|
return crypto$1.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
|
|
5134
4816
|
} catch (error) {
|
|
5135
4817
|
strapi2.log.error("Error generating device ID:", error);
|
|
5136
|
-
return crypto$1.randomBytes(16).
|
|
4818
|
+
return crypto$1.randomBytes(16).toString("hex");
|
|
5137
4819
|
}
|
|
5138
4820
|
},
|
|
5139
4821
|
/**
|
|
5140
|
-
*
|
|
4822
|
+
* Friendly device name for the activation payload.
|
|
4823
|
+
* @returns {string}
|
|
5141
4824
|
*/
|
|
5142
4825
|
getDeviceName() {
|
|
5143
4826
|
try {
|
|
@@ -5147,7 +4830,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5147
4830
|
}
|
|
5148
4831
|
},
|
|
5149
4832
|
/**
|
|
5150
|
-
*
|
|
4833
|
+
* First non-internal IPv4 address. Best-effort, used in the activation
|
|
4834
|
+
* payload only.
|
|
4835
|
+
* @returns {string}
|
|
5151
4836
|
*/
|
|
5152
4837
|
getIpAddress() {
|
|
5153
4838
|
try {
|
|
@@ -5165,13 +4850,22 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5165
4850
|
}
|
|
5166
4851
|
},
|
|
5167
4852
|
/**
|
|
5168
|
-
*
|
|
4853
|
+
* Server-side user-agent string for the activation payload.
|
|
4854
|
+
* @returns {string}
|
|
5169
4855
|
*/
|
|
5170
4856
|
getUserAgent() {
|
|
5171
4857
|
return `Strapi/${strapi2.config.info.strapi} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
5172
4858
|
},
|
|
4859
|
+
// ======================================================================
|
|
4860
|
+
// License creation / verification (admin activation path only)
|
|
4861
|
+
// ======================================================================
|
|
5173
4862
|
/**
|
|
5174
|
-
* Create a license
|
|
4863
|
+
* Create a license on the upstream activation server. Used by the
|
|
4864
|
+
* admin "auto-create" / "create" flows. Failure to create is non-fatal
|
|
4865
|
+
* — the plugin keeps working, the user just doesn't get a key.
|
|
4866
|
+
*
|
|
4867
|
+
* @param {{ email: string, firstName?: string, lastName?: string }} args
|
|
4868
|
+
* @returns {Promise<object|null>} Upstream license payload, or null on failure
|
|
5175
4869
|
*/
|
|
5176
4870
|
async createLicense({ email: email2, firstName, lastName }) {
|
|
5177
4871
|
try {
|
|
@@ -5182,9 +4876,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5182
4876
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5183
4877
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/create`, {
|
|
5184
4878
|
method: "POST",
|
|
5185
|
-
headers: {
|
|
5186
|
-
"Content-Type": "application/json"
|
|
5187
|
-
},
|
|
4879
|
+
headers: { "Content-Type": "application/json" },
|
|
5188
4880
|
body: JSON.stringify({
|
|
5189
4881
|
email: email2,
|
|
5190
4882
|
firstName,
|
|
@@ -5199,390 +4891,201 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5199
4891
|
});
|
|
5200
4892
|
const data = await response.json();
|
|
5201
4893
|
if (data.success) {
|
|
5202
|
-
strapi2.log.info("[SUCCESS] License created
|
|
4894
|
+
strapi2.log.info("[SUCCESS] License created:", data.data.licenseKey);
|
|
5203
4895
|
return data.data;
|
|
5204
|
-
} else {
|
|
5205
|
-
strapi2.log.error("[ERROR] License creation failed:", data);
|
|
5206
|
-
return null;
|
|
5207
4896
|
}
|
|
4897
|
+
strapi2.log.warn("[WARNING] License creation rejected by server:", data.message || "unknown");
|
|
4898
|
+
return null;
|
|
5208
4899
|
} catch (error) {
|
|
5209
|
-
strapi2.log.error("[ERROR] Error creating license:", error);
|
|
4900
|
+
strapi2.log.error("[ERROR] Error creating license:", error.message);
|
|
5210
4901
|
return null;
|
|
5211
4902
|
}
|
|
5212
4903
|
},
|
|
5213
4904
|
/**
|
|
5214
|
-
* Verify a license
|
|
4905
|
+
* Verify a license against the upstream activation server.
|
|
4906
|
+
* Called once when the admin saves a license key — never during
|
|
4907
|
+
* runtime feature checks, never on a schedule.
|
|
4908
|
+
*
|
|
4909
|
+
* @param {string} licenseKey
|
|
4910
|
+
* @returns {Promise<{valid: boolean, data: object|null}>}
|
|
5215
4911
|
*/
|
|
5216
|
-
async verifyLicense(licenseKey
|
|
4912
|
+
async verifyLicense(licenseKey) {
|
|
5217
4913
|
try {
|
|
5218
|
-
const controller2 = new AbortController();
|
|
5219
|
-
const timeoutId = setTimeout(() => controller2.abort(), 5e3);
|
|
5220
4914
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5221
4915
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/verify`, {
|
|
5222
4916
|
method: "POST",
|
|
5223
|
-
headers: {
|
|
5224
|
-
"Content-Type": "application/json"
|
|
5225
|
-
},
|
|
4917
|
+
headers: { "Content-Type": "application/json" },
|
|
5226
4918
|
body: JSON.stringify({
|
|
5227
4919
|
licenseKey,
|
|
5228
4920
|
pluginName: "magic-link",
|
|
5229
4921
|
productName: "Magic Link - Passwordless Authentication"
|
|
5230
|
-
})
|
|
5231
|
-
signal: controller2.signal
|
|
4922
|
+
})
|
|
5232
4923
|
});
|
|
5233
|
-
clearTimeout(timeoutId);
|
|
5234
4924
|
const data = await response.json();
|
|
5235
4925
|
if (data.success) {
|
|
5236
|
-
const isValid = data.data.isActive && !data.data.isExpired;
|
|
4926
|
+
const isValid = !!(data.data && data.data.isActive && !data.data.isExpired);
|
|
5237
4927
|
if (isValid) {
|
|
5238
|
-
const pluginStore = strapi2.store({
|
|
5239
|
-
type: "plugin",
|
|
5240
|
-
name: "magic-link"
|
|
5241
|
-
});
|
|
4928
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5242
4929
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5243
4930
|
}
|
|
5244
|
-
return {
|
|
5245
|
-
valid: isValid,
|
|
5246
|
-
data: data.data
|
|
5247
|
-
};
|
|
5248
|
-
} else {
|
|
5249
|
-
strapi2.log.warn(`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5250
|
-
return { valid: false, data: null };
|
|
5251
|
-
}
|
|
5252
|
-
} catch (error) {
|
|
5253
|
-
if (allowGracePeriod) {
|
|
5254
|
-
strapi2.log.warn(`[WARNING] Cannot verify license online: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5255
|
-
strapi2.log.info(`[INFO] Grace period active - accepting stored license key`);
|
|
5256
|
-
return { valid: true, data: null, gracePeriod: true };
|
|
4931
|
+
return { valid: isValid, data: data.data };
|
|
5257
4932
|
}
|
|
5258
|
-
strapi2.log.
|
|
4933
|
+
strapi2.log.warn(
|
|
4934
|
+
`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`
|
|
4935
|
+
);
|
|
5259
4936
|
return { valid: false, data: null };
|
|
5260
|
-
}
|
|
5261
|
-
},
|
|
5262
|
-
/**
|
|
5263
|
-
* Ping a license (lightweight check)
|
|
5264
|
-
*/
|
|
5265
|
-
async pingLicense(licenseKey) {
|
|
5266
|
-
try {
|
|
5267
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5268
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/ping`, {
|
|
5269
|
-
method: "POST",
|
|
5270
|
-
headers: {
|
|
5271
|
-
"Content-Type": "application/json"
|
|
5272
|
-
},
|
|
5273
|
-
body: JSON.stringify({
|
|
5274
|
-
licenseKey,
|
|
5275
|
-
pluginName: "magic-link",
|
|
5276
|
-
productName: "Magic Link - Passwordless Authentication"
|
|
5277
|
-
})
|
|
5278
|
-
});
|
|
5279
|
-
const data = await response.json();
|
|
5280
|
-
if (data.success) {
|
|
5281
|
-
strapi2.log.debug(`[PING] License ping successful: ${data.data?.isActive ? "ACTIVE" : "INACTIVE"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5282
|
-
return data.data;
|
|
5283
|
-
} else {
|
|
5284
|
-
strapi2.log.debug(`[WARNING] License ping failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5285
|
-
return null;
|
|
5286
|
-
}
|
|
5287
|
-
} catch (error) {
|
|
5288
|
-
strapi2.log.debug(`License ping error: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5289
|
-
return null;
|
|
5290
|
-
}
|
|
5291
|
-
},
|
|
5292
|
-
/**
|
|
5293
|
-
* Upgrade a license
|
|
5294
|
-
*/
|
|
5295
|
-
async upgradeLicense(licenseId, features2) {
|
|
5296
|
-
try {
|
|
5297
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5298
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/${licenseId}/upgrade`, {
|
|
5299
|
-
method: "POST",
|
|
5300
|
-
headers: {
|
|
5301
|
-
"Content-Type": "application/json"
|
|
5302
|
-
},
|
|
5303
|
-
body: JSON.stringify(features2)
|
|
5304
|
-
});
|
|
5305
|
-
const data = await response.json();
|
|
5306
|
-
if (data.success) {
|
|
5307
|
-
strapi2.log.info("[SUCCESS] License upgraded:", licenseId);
|
|
5308
|
-
return data.data;
|
|
5309
|
-
} else {
|
|
5310
|
-
strapi2.log.error("[ERROR] License upgrade failed:", licenseId);
|
|
5311
|
-
return null;
|
|
5312
|
-
}
|
|
5313
4937
|
} catch (error) {
|
|
5314
|
-
strapi2.log.
|
|
5315
|
-
|
|
4938
|
+
strapi2.log.warn(
|
|
4939
|
+
`[WARNING] License server unreachable during activation: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...). Activation will not block this install.`
|
|
4940
|
+
);
|
|
4941
|
+
return { valid: false, data: null, networkError: true };
|
|
5316
4942
|
}
|
|
5317
4943
|
},
|
|
5318
4944
|
/**
|
|
5319
|
-
*
|
|
4945
|
+
* Look up a license by key on the upstream server. Used by the admin
|
|
4946
|
+
* License page to display details. Network failures yield null.
|
|
4947
|
+
*
|
|
4948
|
+
* @param {string} licenseKey
|
|
4949
|
+
* @returns {Promise<object|null>}
|
|
5320
4950
|
*/
|
|
5321
4951
|
async getLicenseByKey(licenseKey) {
|
|
5322
4952
|
try {
|
|
5323
4953
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5324
4954
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
|
|
5325
4955
|
const data = await response.json();
|
|
5326
|
-
if (data.success)
|
|
5327
|
-
return data.data;
|
|
5328
|
-
}
|
|
4956
|
+
if (data.success) return data.data;
|
|
5329
4957
|
return null;
|
|
5330
4958
|
} catch (error) {
|
|
5331
|
-
strapi2.log.
|
|
4959
|
+
strapi2.log.warn("[WARNING] Could not fetch license by key:", error.message);
|
|
5332
4960
|
return null;
|
|
5333
4961
|
}
|
|
5334
4962
|
},
|
|
5335
4963
|
/**
|
|
5336
|
-
*
|
|
4964
|
+
* Look up licenses by user email. Used by admin "auto-create" to
|
|
4965
|
+
* decide whether to reuse an existing key.
|
|
4966
|
+
*
|
|
4967
|
+
* @param {string} email
|
|
4968
|
+
* @returns {Promise<Array>}
|
|
5337
4969
|
*/
|
|
5338
4970
|
async getLicensesByEmail(email2) {
|
|
5339
4971
|
try {
|
|
5340
4972
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5341
|
-
const response = await fetchWithTimeout(
|
|
4973
|
+
const response = await fetchWithTimeout(
|
|
4974
|
+
`${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`
|
|
4975
|
+
);
|
|
5342
4976
|
const data = await response.json();
|
|
5343
|
-
if (data.success)
|
|
5344
|
-
return data.data;
|
|
5345
|
-
}
|
|
4977
|
+
if (data.success) return data.data;
|
|
5346
4978
|
return [];
|
|
5347
4979
|
} catch (error) {
|
|
5348
|
-
strapi2.log.
|
|
4980
|
+
strapi2.log.warn("[WARNING] Could not fetch licenses by email:", error.message);
|
|
5349
4981
|
return [];
|
|
5350
4982
|
}
|
|
5351
4983
|
},
|
|
4984
|
+
// ======================================================================
|
|
4985
|
+
// Storage (plugin store)
|
|
4986
|
+
// ======================================================================
|
|
5352
4987
|
/**
|
|
5353
|
-
*
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/stats/online`);
|
|
5359
|
-
const data = await response.json();
|
|
5360
|
-
if (data.success) {
|
|
5361
|
-
return data.data;
|
|
5362
|
-
}
|
|
5363
|
-
return null;
|
|
5364
|
-
} catch (error) {
|
|
5365
|
-
strapi2.log.error("[ERROR] Error fetching online stats:", error);
|
|
5366
|
-
return null;
|
|
5367
|
-
}
|
|
5368
|
-
},
|
|
5369
|
-
/**
|
|
5370
|
-
* Start periodic pinging for a license
|
|
5371
|
-
*/
|
|
5372
|
-
startPinging(licenseKey, intervalMinutes = 15) {
|
|
5373
|
-
const intervalMs = intervalMinutes * 60 * 1e3;
|
|
5374
|
-
this.pingLicense(licenseKey);
|
|
5375
|
-
const pingInterval = setInterval(async () => {
|
|
5376
|
-
await this.pingLicense(licenseKey);
|
|
5377
|
-
}, intervalMs);
|
|
5378
|
-
return pingInterval;
|
|
5379
|
-
},
|
|
5380
|
-
/**
|
|
5381
|
-
* Initialize license guard
|
|
5382
|
-
* Checks for existing license or prompts for creation
|
|
5383
|
-
*/
|
|
5384
|
-
async initialize() {
|
|
5385
|
-
try {
|
|
5386
|
-
const pluginStore = strapi2.store({
|
|
5387
|
-
type: "plugin",
|
|
5388
|
-
name: "magic-link"
|
|
5389
|
-
});
|
|
5390
|
-
let licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5391
|
-
const lastValidated = await pluginStore.get({ key: "lastValidated" });
|
|
5392
|
-
const now = /* @__PURE__ */ new Date();
|
|
5393
|
-
const gracePeriodHours = 24;
|
|
5394
|
-
let withinGracePeriod = false;
|
|
5395
|
-
if (lastValidated) {
|
|
5396
|
-
const lastValidatedDate = new Date(lastValidated);
|
|
5397
|
-
const hoursSinceValidation = (now - lastValidatedDate) / (1e3 * 60 * 60);
|
|
5398
|
-
withinGracePeriod = hoursSinceValidation < gracePeriodHours;
|
|
5399
|
-
}
|
|
5400
|
-
if (licenseKey) {
|
|
5401
|
-
const verification = await this.verifyLicense(licenseKey, true);
|
|
5402
|
-
if (verification.valid) {
|
|
5403
|
-
const pingInterval = this.startPinging(licenseKey, 15);
|
|
5404
|
-
strapi2.licenseGuard = {
|
|
5405
|
-
licenseKey,
|
|
5406
|
-
pingInterval,
|
|
5407
|
-
data: verification.data
|
|
5408
|
-
};
|
|
5409
|
-
return { valid: true, data: verification.data };
|
|
5410
|
-
} else {
|
|
5411
|
-
strapi2.log.warn("[WARNING] Stored license is invalid or expired");
|
|
5412
|
-
if (!withinGracePeriod) {
|
|
5413
|
-
await pluginStore.delete({ key: "licenseKey" });
|
|
5414
|
-
await pluginStore.delete({ key: "lastValidated" });
|
|
5415
|
-
}
|
|
5416
|
-
}
|
|
5417
|
-
}
|
|
5418
|
-
strapi2.log.warn("[WARNING] No valid license found. Plugin will run in demo mode.");
|
|
5419
|
-
return { valid: false, demo: true };
|
|
5420
|
-
} catch (error) {
|
|
5421
|
-
strapi2.log.error("[ERROR] Error initializing license guard:", error);
|
|
5422
|
-
return { valid: false, error: error.message };
|
|
5423
|
-
}
|
|
5424
|
-
},
|
|
5425
|
-
/**
|
|
5426
|
-
* Store license key after creation
|
|
4988
|
+
* Persist a license key to the plugin store. Used by the admin
|
|
4989
|
+
* activation form.
|
|
4990
|
+
*
|
|
4991
|
+
* @param {string} licenseKey
|
|
4992
|
+
* @returns {Promise<boolean>}
|
|
5427
4993
|
*/
|
|
5428
4994
|
async storeLicenseKey(licenseKey) {
|
|
5429
4995
|
try {
|
|
5430
|
-
strapi2.
|
|
5431
|
-
const pluginStore = strapi2.store({
|
|
5432
|
-
type: "plugin",
|
|
5433
|
-
name: "magic-link"
|
|
5434
|
-
});
|
|
4996
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5435
4997
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
5436
4998
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
strapi2.log.info("[SUCCESS] License key stored and verified successfully");
|
|
5440
|
-
return true;
|
|
5441
|
-
} else {
|
|
5442
|
-
strapi2.log.error("[ERROR] License key storage verification failed");
|
|
5443
|
-
return false;
|
|
5444
|
-
}
|
|
4999
|
+
strapi2.log.info("[SUCCESS] License key stored");
|
|
5000
|
+
return true;
|
|
5445
5001
|
} catch (error) {
|
|
5446
|
-
strapi2.log.error("[ERROR] Error storing license key:", error);
|
|
5002
|
+
strapi2.log.error("[ERROR] Error storing license key:", error.message);
|
|
5447
5003
|
return false;
|
|
5448
5004
|
}
|
|
5449
5005
|
},
|
|
5006
|
+
// ======================================================================
|
|
5007
|
+
// Lifecycle (no-ops in the marketplace build)
|
|
5008
|
+
// ======================================================================
|
|
5009
|
+
/**
|
|
5010
|
+
* Boot-time initializer. In the marketplace build this is a no-op:
|
|
5011
|
+
* we do not auto-verify the stored key and we do not start a periodic
|
|
5012
|
+
* ping. Kept as a stable export so bootstrap.js continues to work.
|
|
5013
|
+
*
|
|
5014
|
+
* @returns {Promise<{valid: boolean}>}
|
|
5015
|
+
*/
|
|
5016
|
+
async initialize() {
|
|
5017
|
+
return { valid: true };
|
|
5018
|
+
},
|
|
5450
5019
|
/**
|
|
5451
|
-
*
|
|
5020
|
+
* Plugin destroy hook. No-op (no interval to clear in the marketplace
|
|
5021
|
+
* build).
|
|
5452
5022
|
*/
|
|
5453
5023
|
cleanup() {
|
|
5454
|
-
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
5455
|
-
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
5456
|
-
strapi2.log.info("[INFO] License pinging stopped");
|
|
5457
|
-
}
|
|
5458
5024
|
},
|
|
5025
|
+
// ======================================================================
|
|
5026
|
+
// Permissive feature stubs (kept for backward compatibility with any
|
|
5027
|
+
// legacy caller — they always permit, never gate)
|
|
5028
|
+
// ======================================================================
|
|
5459
5029
|
/**
|
|
5460
|
-
*
|
|
5461
|
-
*
|
|
5462
|
-
*
|
|
5030
|
+
* Always-permissive feature check. Kept so any legacy `if (await
|
|
5031
|
+
* licenseGuard.hasFeature(...))` site continues to compile and runs
|
|
5032
|
+
* the gated branch.
|
|
5033
|
+
*
|
|
5034
|
+
* @returns {Promise<boolean>} always true
|
|
5463
5035
|
*/
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5468
|
-
if (!licenseKey) {
|
|
5469
|
-
return features.hasFeature(null, featureName);
|
|
5470
|
-
}
|
|
5471
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5472
|
-
return features.hasFeature(licenseData, featureName);
|
|
5473
|
-
} catch (error) {
|
|
5474
|
-
strapi2.log.error("Error checking feature:", error);
|
|
5475
|
-
return false;
|
|
5476
|
-
}
|
|
5036
|
+
// eslint-disable-next-line no-unused-vars
|
|
5037
|
+
async hasFeature(_featureName) {
|
|
5038
|
+
return true;
|
|
5477
5039
|
},
|
|
5478
5040
|
/**
|
|
5479
|
-
*
|
|
5480
|
-
* @returns {number} Max tokens (-1 = unlimited)
|
|
5041
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5481
5042
|
*/
|
|
5482
5043
|
async getMaxTokens() {
|
|
5483
|
-
|
|
5484
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5485
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5486
|
-
if (!licenseKey) {
|
|
5487
|
-
return features.getMaxTokens(null);
|
|
5488
|
-
}
|
|
5489
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5490
|
-
return features.getMaxTokens(licenseData);
|
|
5491
|
-
} catch (error) {
|
|
5492
|
-
strapi2.log.error("Error getting max tokens:", error);
|
|
5493
|
-
return features.free.maxTokens;
|
|
5494
|
-
}
|
|
5044
|
+
return -1;
|
|
5495
5045
|
},
|
|
5496
5046
|
/**
|
|
5497
|
-
*
|
|
5498
|
-
* @returns {number} Max sessions (-1 = unlimited)
|
|
5047
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5499
5048
|
*/
|
|
5500
5049
|
async getMaxSessions() {
|
|
5501
|
-
|
|
5502
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5503
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5504
|
-
if (!licenseKey) {
|
|
5505
|
-
return features.getMaxSessions(null);
|
|
5506
|
-
}
|
|
5507
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5508
|
-
return features.getMaxSessions(licenseData);
|
|
5509
|
-
} catch (error) {
|
|
5510
|
-
strapi2.log.error("Error getting max sessions:", error);
|
|
5511
|
-
return features.free.maxSessions;
|
|
5512
|
-
}
|
|
5050
|
+
return -1;
|
|
5513
5051
|
},
|
|
5514
5052
|
/**
|
|
5515
|
-
*
|
|
5516
|
-
* @returns {number} Max IP bans (-1 = unlimited)
|
|
5053
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5517
5054
|
*/
|
|
5518
5055
|
async getMaxIPBans() {
|
|
5519
|
-
|
|
5520
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5521
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5522
|
-
if (!licenseKey) {
|
|
5523
|
-
return features.getMaxIPBans(null);
|
|
5524
|
-
}
|
|
5525
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5526
|
-
return features.getMaxIPBans(licenseData);
|
|
5527
|
-
} catch (error) {
|
|
5528
|
-
strapi2.log.error("Error getting max IP bans:", error);
|
|
5529
|
-
return features.free.maxIPBans;
|
|
5530
|
-
}
|
|
5056
|
+
return -1;
|
|
5531
5057
|
},
|
|
5532
5058
|
/**
|
|
5533
|
-
*
|
|
5534
|
-
* @returns {Array} Available OTP types
|
|
5059
|
+
* @returns {Promise<string[]>} every supported OTP type
|
|
5535
5060
|
*/
|
|
5536
5061
|
async getAvailableOTPTypes() {
|
|
5537
|
-
|
|
5538
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5539
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5540
|
-
if (!licenseKey) {
|
|
5541
|
-
return features.getAvailableOTPTypes(null);
|
|
5542
|
-
}
|
|
5543
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5544
|
-
return features.getAvailableOTPTypes(licenseData);
|
|
5545
|
-
} catch (error) {
|
|
5546
|
-
strapi2.log.error("Error getting available OTP types:", error);
|
|
5547
|
-
return [];
|
|
5548
|
-
}
|
|
5062
|
+
return ["email", "totp", "backup-codes"];
|
|
5549
5063
|
},
|
|
5550
5064
|
/**
|
|
5551
|
-
*
|
|
5552
|
-
*
|
|
5065
|
+
* Tier info for UI display. Reports `pro` when a key is stored,
|
|
5066
|
+
* `free` otherwise. Pure information — no behavior depends on this.
|
|
5067
|
+
*
|
|
5068
|
+
* @returns {Promise<{tier: string, features: object, hasKey: boolean}>}
|
|
5553
5069
|
*/
|
|
5554
5070
|
async getLicenseTierInfo() {
|
|
5555
5071
|
try {
|
|
5556
5072
|
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5557
5073
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5558
|
-
|
|
5559
|
-
return {
|
|
5560
|
-
tier: "free",
|
|
5561
|
-
features: {
|
|
5562
|
-
premium: false,
|
|
5563
|
-
advanced: false,
|
|
5564
|
-
enterprise: false
|
|
5565
|
-
}
|
|
5566
|
-
};
|
|
5567
|
-
}
|
|
5568
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5074
|
+
const hasKey = !!licenseKey;
|
|
5569
5075
|
return {
|
|
5570
|
-
tier:
|
|
5076
|
+
tier: hasKey ? "pro" : "free",
|
|
5077
|
+
hasKey,
|
|
5571
5078
|
features: {
|
|
5572
|
-
premium:
|
|
5573
|
-
advanced:
|
|
5574
|
-
enterprise:
|
|
5079
|
+
premium: hasKey,
|
|
5080
|
+
advanced: hasKey,
|
|
5081
|
+
enterprise: hasKey
|
|
5575
5082
|
}
|
|
5576
5083
|
};
|
|
5577
5084
|
} catch (error) {
|
|
5578
|
-
strapi2.log.error("Error getting license tier info:", error);
|
|
5579
5085
|
return {
|
|
5580
5086
|
tier: "free",
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
advanced: false,
|
|
5584
|
-
enterprise: false
|
|
5585
|
-
}
|
|
5087
|
+
hasKey: false,
|
|
5088
|
+
features: { premium: false, advanced: false, enterprise: false }
|
|
5586
5089
|
};
|
|
5587
5090
|
}
|
|
5588
5091
|
}
|