strapi-plugin-magic-link-v5 5.5.7 → 5.5.9
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-BZFGxKmY.mjs → index-5jsDgD-q.mjs} +1 -1
- package/dist/_chunks/{index-CddEy_As.js → index-BWuj-5WE.js} +2 -2
- package/dist/_chunks/{index-BFzW7Xex.js → index-Bj3H_Z4B.js} +4 -17
- package/dist/_chunks/{index-M5s7QN5m.js → index-CC4Eu9Dm.js} +1 -1
- package/dist/_chunks/{index-ivgYEX37.mjs → index-Csyi13vd.mjs} +9 -141
- package/dist/_chunks/{index-CuXnqVl4.js → index-DJqXiiP1.js} +9 -141
- package/dist/_chunks/{index-34ICAF9_.mjs → index-DQUYFsLT.mjs} +4 -17
- package/dist/_chunks/{index-B8urUtiz.mjs → index-Dufv-daf.mjs} +7 -36
- package/dist/_chunks/{index-CxCULraY.mjs → index-szeYJKpN.mjs} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +224 -773
- package/dist/server/index.mjs +224 -773
- 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 }) => {
|
|
@@ -1196,10 +1171,6 @@ var auth$1 = {
|
|
|
1196
1171
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1197
1172
|
const userService = strapi.plugin("users-permissions").service("user");
|
|
1198
1173
|
const jwtService = strapi.plugin("users-permissions").service("jwt");
|
|
1199
|
-
const isEnabled = await magicLink2.isEnabled();
|
|
1200
|
-
if (!isEnabled) {
|
|
1201
|
-
return i18n.sendError(ctx, "plugin.disabled", 400);
|
|
1202
|
-
}
|
|
1203
1174
|
if (_$1.isEmpty(loginToken)) {
|
|
1204
1175
|
return i18n.sendError(ctx, "token.invalid", 400);
|
|
1205
1176
|
}
|
|
@@ -1219,9 +1190,7 @@ var auth$1 = {
|
|
|
1219
1190
|
return i18n.sendError(ctx, "token.invalid", 400);
|
|
1220
1191
|
}
|
|
1221
1192
|
const settings = await magicLink2.settings();
|
|
1222
|
-
|
|
1223
|
-
const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
|
|
1224
|
-
if (settings.otp_enabled && hasOTPFeature) {
|
|
1193
|
+
if (settings.otp_enabled) {
|
|
1225
1194
|
await strapi.documents("plugin::magic-link.token").update({
|
|
1226
1195
|
documentId: token2.documentId,
|
|
1227
1196
|
data: {
|
|
@@ -1420,10 +1389,6 @@ var auth$1 = {
|
|
|
1420
1389
|
const magicLink2 = strapi.plugin("magic-link").service("magic-link");
|
|
1421
1390
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1422
1391
|
const whatsappService = strapi.plugin("magic-link").service("whatsapp");
|
|
1423
|
-
const isEnabled = await magicLink2.isEnabled();
|
|
1424
|
-
if (!isEnabled) {
|
|
1425
|
-
return i18n.sendError(ctx, "plugin.disabled", 400);
|
|
1426
|
-
}
|
|
1427
1392
|
const params = parseBody$1(sendLinkSchema, ctx.request.body);
|
|
1428
1393
|
const email2 = params.email || null;
|
|
1429
1394
|
const phoneNumber = params.phoneNumber || params.phone || null;
|
|
@@ -1688,7 +1653,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1688
1653
|
const { email: normalizedEmail, totpCode } = parseBody$1(loginWithTotpSchema, ctx.request.body);
|
|
1689
1654
|
const magicLink2 = strapi.plugin("magic-link").service("magic-link");
|
|
1690
1655
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
1691
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
1692
1656
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1693
1657
|
const userService = strapi.plugin("users-permissions").service("user");
|
|
1694
1658
|
const jwtService = strapi.plugin("users-permissions").service("jwt");
|
|
@@ -1707,10 +1671,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1707
1671
|
if (!settings.totp_as_primary_auth) {
|
|
1708
1672
|
return ctx.forbidden("TOTP login is not enabled");
|
|
1709
1673
|
}
|
|
1710
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-totp");
|
|
1711
|
-
if (!hasFeature) {
|
|
1712
|
-
return ctx.forbidden("TOTP login requires Advanced license");
|
|
1713
|
-
}
|
|
1714
1674
|
const GENERIC_INVALID = "Invalid credentials";
|
|
1715
1675
|
const users = await strapi.documents("plugin::users-permissions.user").findMany({
|
|
1716
1676
|
filters: { email: normalizedEmail },
|
|
@@ -2501,14 +2461,6 @@ var tokens$1 = {
|
|
|
2501
2461
|
if (token2.expires_at && new Date(token2.expires_at) < /* @__PURE__ */ new Date()) {
|
|
2502
2462
|
return ctx.badRequest("Token ist abgelaufen");
|
|
2503
2463
|
}
|
|
2504
|
-
const pluginStore = strapi.store({
|
|
2505
|
-
type: "plugin",
|
|
2506
|
-
name: "magic-link"
|
|
2507
|
-
});
|
|
2508
|
-
const settings = await pluginStore.get({ key: "settings" });
|
|
2509
|
-
if (!settings || !settings.enabled) {
|
|
2510
|
-
return ctx.badRequest("Magic Link ist nicht aktiviert");
|
|
2511
|
-
}
|
|
2512
2464
|
return ctx.badRequest("Token kann nicht erneut gesendet werden - der Original-Token ist gehashed. Bitte erstelle einen neuen Token für diese E-Mail.");
|
|
2513
2465
|
} catch (error) {
|
|
2514
2466
|
strapi.log.error("Fehler beim erneuten Senden:", error);
|
|
@@ -2675,7 +2627,7 @@ var tokens$1 = {
|
|
|
2675
2627
|
else if (jwtDays <= 30) jwtLifetimePoints = 5;
|
|
2676
2628
|
let configPoints = 0;
|
|
2677
2629
|
if (settings.createUserIfNotExists === false && settings.create_new_user === false) configPoints += 10;
|
|
2678
|
-
|
|
2630
|
+
configPoints += 5;
|
|
2679
2631
|
if (settings.remember_me === false) configPoints += 5;
|
|
2680
2632
|
if (settings.stays_valid === false) configPoints += 10;
|
|
2681
2633
|
let tokenStatusPoints = 0;
|
|
@@ -2883,49 +2835,43 @@ var jwt$1 = {
|
|
|
2883
2835
|
};
|
|
2884
2836
|
var license$1 = {
|
|
2885
2837
|
/**
|
|
2886
|
-
*
|
|
2838
|
+
* Returns the current license status. Always reports the plugin as
|
|
2839
|
+
* usable; `hasKey` indicates whether the admin has activated a key.
|
|
2840
|
+
*
|
|
2841
|
+
* @route GET /magic-link/license/status
|
|
2842
|
+
* @returns {{ success: true, valid: true, hasKey: boolean, data: object|null }}
|
|
2887
2843
|
*/
|
|
2888
2844
|
async getStatus(ctx) {
|
|
2889
2845
|
try {
|
|
2890
2846
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2891
|
-
const pluginStore = strapi.store({
|
|
2892
|
-
type: "plugin",
|
|
2893
|
-
name: "magic-link"
|
|
2894
|
-
});
|
|
2847
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
2895
2848
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
2896
2849
|
if (!licenseKey) {
|
|
2897
2850
|
return ctx.send({
|
|
2898
|
-
success:
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2851
|
+
success: true,
|
|
2852
|
+
valid: true,
|
|
2853
|
+
// plugin is always usable in the marketplace build
|
|
2854
|
+
hasKey: false,
|
|
2855
|
+
data: null,
|
|
2856
|
+
message: "Plugin is active. License key activation is optional."
|
|
2902
2857
|
});
|
|
2903
2858
|
}
|
|
2904
|
-
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
2905
2859
|
const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
|
|
2906
2860
|
return ctx.send({
|
|
2907
2861
|
success: true,
|
|
2908
|
-
valid:
|
|
2909
|
-
|
|
2862
|
+
valid: true,
|
|
2863
|
+
hasKey: true,
|
|
2910
2864
|
data: {
|
|
2911
2865
|
licenseKey,
|
|
2912
2866
|
email: license2?.email || null,
|
|
2913
2867
|
firstName: license2?.firstName || null,
|
|
2914
2868
|
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
|
-
},
|
|
2869
|
+
isActive: license2?.isActive ?? true,
|
|
2870
|
+
isExpired: license2?.isExpired ?? false,
|
|
2871
|
+
expiresAt: license2?.expiresAt || null,
|
|
2872
|
+
deviceName: license2?.deviceName || null,
|
|
2873
|
+
deviceId: license2?.deviceId || null,
|
|
2874
|
+
ipAddress: license2?.ipAddress || null,
|
|
2929
2875
|
maxDevices: license2?.maxDevices || 1,
|
|
2930
2876
|
currentDevices: license2?.currentDevices || 0
|
|
2931
2877
|
}
|
|
@@ -2936,7 +2882,11 @@ var license$1 = {
|
|
|
2936
2882
|
}
|
|
2937
2883
|
},
|
|
2938
2884
|
/**
|
|
2939
|
-
* Auto-create license
|
|
2885
|
+
* Auto-create a license using the currently logged-in admin user's
|
|
2886
|
+
* data. Best-effort: a remote failure simply leaves the install
|
|
2887
|
+
* un-keyed, the plugin keeps working.
|
|
2888
|
+
*
|
|
2889
|
+
* @route POST /magic-link/license/auto-create
|
|
2940
2890
|
*/
|
|
2941
2891
|
async autoCreate(ctx) {
|
|
2942
2892
|
try {
|
|
@@ -2951,15 +2901,9 @@ var license$1 = {
|
|
|
2951
2901
|
lastName: adminUser.lastname || "User"
|
|
2952
2902
|
});
|
|
2953
2903
|
if (!license2) {
|
|
2954
|
-
return ctx.badRequest("
|
|
2904
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2955
2905
|
}
|
|
2956
2906
|
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
2907
|
return ctx.send({
|
|
2964
2908
|
success: true,
|
|
2965
2909
|
message: "License automatically created and activated",
|
|
@@ -2971,26 +2915,22 @@ var license$1 = {
|
|
|
2971
2915
|
}
|
|
2972
2916
|
},
|
|
2973
2917
|
/**
|
|
2974
|
-
* Create and activate a new license
|
|
2918
|
+
* Create and activate a new license with explicit user details.
|
|
2919
|
+
*
|
|
2920
|
+
* @route POST /magic-link/license/create
|
|
2975
2921
|
*/
|
|
2976
2922
|
async createAndActivate(ctx) {
|
|
2977
2923
|
try {
|
|
2978
|
-
const { email: email2, firstName, lastName } = ctx.request.body;
|
|
2924
|
+
const { email: email2, firstName, lastName } = ctx.request.body || {};
|
|
2979
2925
|
if (!email2 || !firstName || !lastName) {
|
|
2980
2926
|
return ctx.badRequest("Email, firstName, and lastName are required");
|
|
2981
2927
|
}
|
|
2982
2928
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2983
2929
|
const license2 = await licenseGuard2.createLicense({ email: email2, firstName, lastName });
|
|
2984
2930
|
if (!license2) {
|
|
2985
|
-
return ctx.badRequest("
|
|
2931
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2986
2932
|
}
|
|
2987
2933
|
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
2934
|
return ctx.send({
|
|
2995
2935
|
success: true,
|
|
2996
2936
|
message: "License created and activated successfully",
|
|
@@ -3002,39 +2942,40 @@ var license$1 = {
|
|
|
3002
2942
|
}
|
|
3003
2943
|
},
|
|
3004
2944
|
/**
|
|
3005
|
-
*
|
|
2945
|
+
* Manual refresh: re-verifies the stored license once and returns the
|
|
2946
|
+
* latest details. Replaces the periodic-ping endpoint that used to run
|
|
2947
|
+
* every 15 minutes — admins can call this on demand from the UI to
|
|
2948
|
+
* pull updated info.
|
|
2949
|
+
*
|
|
2950
|
+
* @route POST /magic-link/license/ping
|
|
3006
2951
|
*/
|
|
3007
2952
|
async ping(ctx) {
|
|
3008
2953
|
try {
|
|
3009
|
-
const pluginStore = strapi.store({
|
|
3010
|
-
type: "plugin",
|
|
3011
|
-
name: "magic-link"
|
|
3012
|
-
});
|
|
2954
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
3013
2955
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
3014
2956
|
if (!licenseKey) {
|
|
3015
2957
|
return ctx.badRequest("No license key found");
|
|
3016
2958
|
}
|
|
3017
2959
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3018
|
-
const
|
|
3019
|
-
if (!pingResult) {
|
|
3020
|
-
return ctx.badRequest("Ping failed");
|
|
3021
|
-
}
|
|
2960
|
+
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
3022
2961
|
return ctx.send({
|
|
3023
2962
|
success: true,
|
|
3024
|
-
message: "License
|
|
3025
|
-
data:
|
|
2963
|
+
message: verification.valid ? "License refreshed successfully" : "License key could not be verified, but the plugin keeps working.",
|
|
2964
|
+
data: verification.data
|
|
3026
2965
|
});
|
|
3027
2966
|
} catch (error) {
|
|
3028
|
-
strapi.log.error("Error
|
|
3029
|
-
return ctx.badRequest("Error
|
|
2967
|
+
strapi.log.error("Error refreshing license:", error);
|
|
2968
|
+
return ctx.badRequest("Error refreshing license");
|
|
3030
2969
|
}
|
|
3031
2970
|
},
|
|
3032
2971
|
/**
|
|
3033
|
-
|
|
3034
|
-
|
|
2972
|
+
* Store and validate an existing license key.
|
|
2973
|
+
*
|
|
2974
|
+
* @route POST /magic-link/license/store-key
|
|
2975
|
+
*/
|
|
3035
2976
|
async storeKey(ctx) {
|
|
3036
2977
|
try {
|
|
3037
|
-
const { licenseKey, email: email2 } = ctx.request.body;
|
|
2978
|
+
const { licenseKey, email: email2 } = ctx.request.body || {};
|
|
3038
2979
|
if (!licenseKey || !licenseKey.trim()) {
|
|
3039
2980
|
return ctx.badRequest("License key is required");
|
|
3040
2981
|
}
|
|
@@ -3046,26 +2987,35 @@ var license$1 = {
|
|
|
3046
2987
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3047
2988
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
3048
2989
|
if (!verification.valid) {
|
|
3049
|
-
|
|
2990
|
+
if (verification.networkError) {
|
|
2991
|
+
strapi.log.warn(
|
|
2992
|
+
`[WARNING] License server unreachable during activation. Storing key anyway: ${trimmedKey.substring(0, 8)}...`
|
|
2993
|
+
);
|
|
2994
|
+
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
2995
|
+
return ctx.send({
|
|
2996
|
+
success: true,
|
|
2997
|
+
message: "License key stored (license server was unreachable; will retry on the next manual refresh).",
|
|
2998
|
+
data: { licenseKey: trimmedKey, email: trimmedEmail }
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
strapi.log.warn(`[WARNING] Invalid license key: ${trimmedKey.substring(0, 8)}...`);
|
|
3050
3002
|
return ctx.badRequest("Invalid or expired license key");
|
|
3051
3003
|
}
|
|
3052
3004
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
3053
3005
|
if (!license2) {
|
|
3054
|
-
strapi.log.warn(`[WARNING] License not found
|
|
3006
|
+
strapi.log.warn(`[WARNING] License not found: ${trimmedKey.substring(0, 8)}...`);
|
|
3055
3007
|
return ctx.badRequest("License not found");
|
|
3056
3008
|
}
|
|
3057
|
-
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
3058
|
-
strapi.log.warn(
|
|
3009
|
+
if (license2.email && license2.email.toLowerCase() !== trimmedEmail) {
|
|
3010
|
+
strapi.log.warn(
|
|
3011
|
+
`[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (attempted: ${trimmedEmail})`
|
|
3012
|
+
);
|
|
3059
3013
|
return ctx.badRequest("Email address does not match this license key");
|
|
3060
3014
|
}
|
|
3061
3015
|
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})`);
|
|
3016
|
+
strapi.log.info(
|
|
3017
|
+
`[SUCCESS] License key validated and stored: ${trimmedKey.substring(0, 8)}... (email: ${trimmedEmail})`
|
|
3018
|
+
);
|
|
3069
3019
|
return ctx.send({
|
|
3070
3020
|
success: true,
|
|
3071
3021
|
message: "License key validated and activated successfully",
|
|
@@ -3217,11 +3167,6 @@ var otp$3 = {
|
|
|
3217
3167
|
if (!otpSettings.enabled) {
|
|
3218
3168
|
return ctx.badRequest("OTP is not enabled");
|
|
3219
3169
|
}
|
|
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
3170
|
const rlFail = await enforceOtpRateLimits(ctx, normalizedEmail, "send");
|
|
3226
3171
|
if (rlFail) return ctx.tooManyRequests(rlFail.message);
|
|
3227
3172
|
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
@@ -3513,11 +3458,6 @@ var otp$3 = {
|
|
|
3513
3458
|
*/
|
|
3514
3459
|
async setupTOTP(ctx) {
|
|
3515
3460
|
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
3461
|
const { email: email2, id: userId } = ctx.state.user;
|
|
3522
3462
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3523
3463
|
const setupData = await otpService.setupTOTP(userId, email2);
|
|
@@ -3587,11 +3527,6 @@ var otp$3 = {
|
|
|
3587
3527
|
*/
|
|
3588
3528
|
async generateBackupCodes(ctx) {
|
|
3589
3529
|
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
3530
|
const { id: userId } = ctx.state.user;
|
|
3596
3531
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3597
3532
|
const backupCodes = await otpService.generateBackupCodes(userId);
|
|
@@ -3761,61 +3696,6 @@ const passwordlessCompat = passwordlessCompat$2;
|
|
|
3761
3696
|
var middlewares$1 = {
|
|
3762
3697
|
"passwordless-compat": passwordlessCompat
|
|
3763
3698
|
};
|
|
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
3699
|
var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
|
|
3820
3700
|
const pluginStore = strapi2.store({
|
|
3821
3701
|
type: "plugin",
|
|
@@ -3837,10 +3717,8 @@ var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) =
|
|
|
3837
3717
|
}
|
|
3838
3718
|
return true;
|
|
3839
3719
|
};
|
|
3840
|
-
const licenseCheck = licenseCheck$1;
|
|
3841
3720
|
const compatibilityCheck = compatibilityCheck$1;
|
|
3842
3721
|
var policies$1 = {
|
|
3843
|
-
"license-check": licenseCheck,
|
|
3844
3722
|
"compatibility-check": compatibilityCheck
|
|
3845
3723
|
};
|
|
3846
3724
|
var contentApi$1 = {
|
|
@@ -3852,7 +3730,7 @@ var contentApi$1 = {
|
|
|
3852
3730
|
handler: "auth.login",
|
|
3853
3731
|
config: {
|
|
3854
3732
|
auth: false,
|
|
3855
|
-
policies: [
|
|
3733
|
+
policies: []
|
|
3856
3734
|
}
|
|
3857
3735
|
},
|
|
3858
3736
|
{
|
|
@@ -3861,7 +3739,7 @@ var contentApi$1 = {
|
|
|
3861
3739
|
handler: "auth.sendLink",
|
|
3862
3740
|
config: {
|
|
3863
3741
|
auth: false,
|
|
3864
|
-
policies: [
|
|
3742
|
+
policies: []
|
|
3865
3743
|
}
|
|
3866
3744
|
},
|
|
3867
3745
|
// OTP Routes
|
|
@@ -3871,7 +3749,7 @@ var contentApi$1 = {
|
|
|
3871
3749
|
handler: "otp.send",
|
|
3872
3750
|
config: {
|
|
3873
3751
|
auth: false,
|
|
3874
|
-
policies: [
|
|
3752
|
+
policies: []
|
|
3875
3753
|
}
|
|
3876
3754
|
},
|
|
3877
3755
|
{
|
|
@@ -3880,7 +3758,7 @@ var contentApi$1 = {
|
|
|
3880
3758
|
handler: "otp.verify",
|
|
3881
3759
|
config: {
|
|
3882
3760
|
auth: false,
|
|
3883
|
-
policies: [
|
|
3761
|
+
policies: []
|
|
3884
3762
|
}
|
|
3885
3763
|
},
|
|
3886
3764
|
{
|
|
@@ -3889,7 +3767,7 @@ var contentApi$1 = {
|
|
|
3889
3767
|
handler: "otp.resend",
|
|
3890
3768
|
config: {
|
|
3891
3769
|
auth: false,
|
|
3892
|
-
policies: [
|
|
3770
|
+
policies: []
|
|
3893
3771
|
}
|
|
3894
3772
|
},
|
|
3895
3773
|
// MFA Routes
|
|
@@ -3899,7 +3777,7 @@ var contentApi$1 = {
|
|
|
3899
3777
|
handler: "auth.verifyMFATOTP",
|
|
3900
3778
|
config: {
|
|
3901
3779
|
auth: false,
|
|
3902
|
-
policies: [
|
|
3780
|
+
policies: []
|
|
3903
3781
|
}
|
|
3904
3782
|
},
|
|
3905
3783
|
{
|
|
@@ -3908,7 +3786,7 @@ var contentApi$1 = {
|
|
|
3908
3786
|
handler: "auth.loginWithTOTP",
|
|
3909
3787
|
config: {
|
|
3910
3788
|
auth: false,
|
|
3911
|
-
policies: [
|
|
3789
|
+
policies: []
|
|
3912
3790
|
}
|
|
3913
3791
|
}
|
|
3914
3792
|
]
|
|
@@ -4208,10 +4086,7 @@ var compatibility$1 = {
|
|
|
4208
4086
|
handler: "auth.login",
|
|
4209
4087
|
config: {
|
|
4210
4088
|
auth: false,
|
|
4211
|
-
policies: [
|
|
4212
|
-
"plugin::magic-link.compatibility-check",
|
|
4213
|
-
"plugin::magic-link.license-check"
|
|
4214
|
-
],
|
|
4089
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4215
4090
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4216
4091
|
}
|
|
4217
4092
|
},
|
|
@@ -4222,10 +4097,7 @@ var compatibility$1 = {
|
|
|
4222
4097
|
handler: "auth.sendLink",
|
|
4223
4098
|
config: {
|
|
4224
4099
|
auth: false,
|
|
4225
|
-
policies: [
|
|
4226
|
-
"plugin::magic-link.compatibility-check",
|
|
4227
|
-
"plugin::magic-link.license-check"
|
|
4228
|
-
],
|
|
4100
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4229
4101
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4230
4102
|
}
|
|
4231
4103
|
}
|
|
@@ -4279,9 +4151,17 @@ var magicLink$1 = ({ strapi: strapi2 }) => ({
|
|
|
4279
4151
|
});
|
|
4280
4152
|
return pluginStore.get({ key: "advanced" });
|
|
4281
4153
|
},
|
|
4154
|
+
/**
|
|
4155
|
+
* Returns whether the Magic Link plugin is active. In the marketplace
|
|
4156
|
+
* build the plugin is always-on once installed: it cannot be disabled
|
|
4157
|
+
* from the admin panel by mistake (that toggle was removed). The
|
|
4158
|
+
* function is kept as a stable export so any legacy caller continues
|
|
4159
|
+
* to compile.
|
|
4160
|
+
*
|
|
4161
|
+
* @returns {Promise<boolean>} always true
|
|
4162
|
+
*/
|
|
4282
4163
|
async isEnabled() {
|
|
4283
|
-
|
|
4284
|
-
return settings ? settings.enabled !== false : true;
|
|
4164
|
+
return true;
|
|
4285
4165
|
},
|
|
4286
4166
|
async createUser(user) {
|
|
4287
4167
|
const userSettings = await this.userSettings();
|
|
@@ -4867,265 +4747,8 @@ var store$1 = ({ strapi: strapi2 }) => ({
|
|
|
4867
4747
|
return pluginStore.set({ key: "settings", value: settings });
|
|
4868
4748
|
}
|
|
4869
4749
|
});
|
|
4870
|
-
var features$1 = {
|
|
4871
|
-
// FREE/DEMO Features
|
|
4872
|
-
free: {
|
|
4873
|
-
maxTokens: 100,
|
|
4874
|
-
// 100 aktive Tokens gleichzeitig
|
|
4875
|
-
maxSessions: 50,
|
|
4876
|
-
// 50 aktive JWT Sessions
|
|
4877
|
-
maxIPBans: 10,
|
|
4878
|
-
// 10 gebannte IPs
|
|
4879
|
-
features: [
|
|
4880
|
-
"basic-magic-link",
|
|
4881
|
-
// Basis Magic Link Auth
|
|
4882
|
-
"email-templates",
|
|
4883
|
-
// Basis Email Templates
|
|
4884
|
-
"jwt-sessions",
|
|
4885
|
-
// JWT Session Management
|
|
4886
|
-
"ip-tracking",
|
|
4887
|
-
// IP Tracking für Security
|
|
4888
|
-
"rate-limiting",
|
|
4889
|
-
// Basis Rate Limiting
|
|
4890
|
-
"auto-user-creation",
|
|
4891
|
-
// Auto User Creation
|
|
4892
|
-
"token-management",
|
|
4893
|
-
// Token Dashboard
|
|
4894
|
-
"ip-bans"
|
|
4895
|
-
// IP Ban Management
|
|
4896
|
-
]
|
|
4897
|
-
},
|
|
4898
|
-
// PREMIUM Features
|
|
4899
|
-
premium: {
|
|
4900
|
-
maxTokens: 1e3,
|
|
4901
|
-
// 1000 aktive Tokens
|
|
4902
|
-
maxSessions: 500,
|
|
4903
|
-
// 500 aktive Sessions
|
|
4904
|
-
maxIPBans: 100,
|
|
4905
|
-
// 100 gebannte IPs
|
|
4906
|
-
features: [
|
|
4907
|
-
"basic-magic-link",
|
|
4908
|
-
"email-templates",
|
|
4909
|
-
"jwt-sessions",
|
|
4910
|
-
"ip-tracking",
|
|
4911
|
-
"rate-limiting",
|
|
4912
|
-
"auto-user-creation",
|
|
4913
|
-
"token-management",
|
|
4914
|
-
"ip-bans",
|
|
4915
|
-
"otp-email",
|
|
4916
|
-
// Email OTP (6-digit code)
|
|
4917
|
-
"mfa-totp",
|
|
4918
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4919
|
-
"custom-email-templates",
|
|
4920
|
-
// Custom HTML/Text Templates
|
|
4921
|
-
"email-designer-integration",
|
|
4922
|
-
// Email Designer 5 Support
|
|
4923
|
-
"magic-mail-integration",
|
|
4924
|
-
// MagicMail Integration
|
|
4925
|
-
"advanced-rate-limiting",
|
|
4926
|
-
// Custom Rate Limits
|
|
4927
|
-
"session-management",
|
|
4928
|
-
// Erweiterte Session Verwaltung
|
|
4929
|
-
"bulk-operations",
|
|
4930
|
-
// Bulk Token/Session Operations
|
|
4931
|
-
"extended-logging",
|
|
4932
|
-
// Erweiterte Login-Info Logs
|
|
4933
|
-
"custom-redirect-urls"
|
|
4934
|
-
// Custom Redirect URLs per Token
|
|
4935
|
-
]
|
|
4936
|
-
},
|
|
4937
|
-
// ADVANCED Features
|
|
4938
|
-
advanced: {
|
|
4939
|
-
maxTokens: -1,
|
|
4940
|
-
// Unlimited
|
|
4941
|
-
maxSessions: -1,
|
|
4942
|
-
// Unlimited
|
|
4943
|
-
maxIPBans: -1,
|
|
4944
|
-
// Unlimited
|
|
4945
|
-
features: [
|
|
4946
|
-
"basic-magic-link",
|
|
4947
|
-
"email-templates",
|
|
4948
|
-
"jwt-sessions",
|
|
4949
|
-
"ip-tracking",
|
|
4950
|
-
"rate-limiting",
|
|
4951
|
-
"auto-user-creation",
|
|
4952
|
-
"token-management",
|
|
4953
|
-
"ip-bans",
|
|
4954
|
-
"otp-email",
|
|
4955
|
-
"mfa-totp",
|
|
4956
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4957
|
-
"otp-sms",
|
|
4958
|
-
// SMS OTP via Twilio/Vonage
|
|
4959
|
-
"otp-totp",
|
|
4960
|
-
// TOTP (Authenticator Apps)
|
|
4961
|
-
"totp-primary-login",
|
|
4962
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
4963
|
-
"custom-email-templates",
|
|
4964
|
-
"email-designer-integration",
|
|
4965
|
-
"magic-mail-integration",
|
|
4966
|
-
"advanced-rate-limiting",
|
|
4967
|
-
"session-management",
|
|
4968
|
-
"bulk-operations",
|
|
4969
|
-
"extended-logging",
|
|
4970
|
-
"custom-redirect-urls",
|
|
4971
|
-
"webhook-notifications",
|
|
4972
|
-
// Webhooks für Events
|
|
4973
|
-
"api-access-tokens",
|
|
4974
|
-
// API Tokens für Programmatic Access
|
|
4975
|
-
"advanced-analytics",
|
|
4976
|
-
// Analytics Dashboard
|
|
4977
|
-
"custom-token-context",
|
|
4978
|
-
// Custom Context Data in Tokens
|
|
4979
|
-
"geolocation-tracking",
|
|
4980
|
-
// GeoIP Tracking
|
|
4981
|
-
"device-fingerprinting",
|
|
4982
|
-
// Device Fingerprinting
|
|
4983
|
-
"suspicious-login-alerts"
|
|
4984
|
-
// Alerts für verdächtige Logins
|
|
4985
|
-
]
|
|
4986
|
-
},
|
|
4987
|
-
// ENTERPRISE Features
|
|
4988
|
-
enterprise: {
|
|
4989
|
-
maxTokens: -1,
|
|
4990
|
-
maxSessions: -1,
|
|
4991
|
-
maxIPBans: -1,
|
|
4992
|
-
features: [
|
|
4993
|
-
"basic-magic-link",
|
|
4994
|
-
"email-templates",
|
|
4995
|
-
"jwt-sessions",
|
|
4996
|
-
"ip-tracking",
|
|
4997
|
-
"rate-limiting",
|
|
4998
|
-
"auto-user-creation",
|
|
4999
|
-
"token-management",
|
|
5000
|
-
"ip-bans",
|
|
5001
|
-
"otp-email",
|
|
5002
|
-
"mfa-totp",
|
|
5003
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
5004
|
-
"otp-sms",
|
|
5005
|
-
"otp-totp",
|
|
5006
|
-
"totp-primary-login",
|
|
5007
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
5008
|
-
"otp-backup-codes",
|
|
5009
|
-
// Backup Recovery Codes
|
|
5010
|
-
"biometric-support",
|
|
5011
|
-
// WebAuthn/FIDO2
|
|
5012
|
-
"custom-email-templates",
|
|
5013
|
-
"email-designer-integration",
|
|
5014
|
-
"magic-mail-integration",
|
|
5015
|
-
"advanced-rate-limiting",
|
|
5016
|
-
"session-management",
|
|
5017
|
-
"bulk-operations",
|
|
5018
|
-
"extended-logging",
|
|
5019
|
-
"custom-redirect-urls",
|
|
5020
|
-
"webhook-notifications",
|
|
5021
|
-
"api-access-tokens",
|
|
5022
|
-
"advanced-analytics",
|
|
5023
|
-
"custom-token-context",
|
|
5024
|
-
"geolocation-tracking",
|
|
5025
|
-
"device-fingerprinting",
|
|
5026
|
-
"suspicious-login-alerts",
|
|
5027
|
-
"sso-integration",
|
|
5028
|
-
// SSO/SAML Integration
|
|
5029
|
-
"ldap-sync",
|
|
5030
|
-
// LDAP/AD Sync
|
|
5031
|
-
"audit-logs",
|
|
5032
|
-
// Vollständige Audit Logs
|
|
5033
|
-
"compliance-reports",
|
|
5034
|
-
// GDPR/SOC2 Reports
|
|
5035
|
-
"priority-support",
|
|
5036
|
-
// 24/7 Priority Support
|
|
5037
|
-
"custom-security-rules",
|
|
5038
|
-
// Custom Security Rules
|
|
5039
|
-
"multi-tenant",
|
|
5040
|
-
// Multi-Tenant Support
|
|
5041
|
-
"white-label"
|
|
5042
|
-
// White-Label Branding
|
|
5043
|
-
]
|
|
5044
|
-
},
|
|
5045
|
-
/**
|
|
5046
|
-
* Check if a feature is available for given license tier
|
|
5047
|
-
*/
|
|
5048
|
-
hasFeature(licenseData, featureName) {
|
|
5049
|
-
if (!licenseData) {
|
|
5050
|
-
return this.free.features.includes(featureName);
|
|
5051
|
-
}
|
|
5052
|
-
let isEnterprise = false;
|
|
5053
|
-
let isAdvanced = false;
|
|
5054
|
-
let isPremium = false;
|
|
5055
|
-
if (licenseData.tier) {
|
|
5056
|
-
isEnterprise = licenseData.tier === "enterprise";
|
|
5057
|
-
isAdvanced = licenseData.tier === "advanced";
|
|
5058
|
-
isPremium = licenseData.tier === "premium";
|
|
5059
|
-
}
|
|
5060
|
-
if (licenseData.features) {
|
|
5061
|
-
isEnterprise = isEnterprise || licenseData.features.enterprise === true;
|
|
5062
|
-
isAdvanced = isAdvanced || licenseData.features.advanced === true;
|
|
5063
|
-
isPremium = isPremium || licenseData.features.premium === true;
|
|
5064
|
-
}
|
|
5065
|
-
if (licenseData.featureEnterprise === true) {
|
|
5066
|
-
isEnterprise = true;
|
|
5067
|
-
}
|
|
5068
|
-
if (licenseData.featureAdvanced === true) {
|
|
5069
|
-
isAdvanced = true;
|
|
5070
|
-
}
|
|
5071
|
-
if (licenseData.featurePremium === true) {
|
|
5072
|
-
isPremium = true;
|
|
5073
|
-
}
|
|
5074
|
-
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
5075
|
-
return true;
|
|
5076
|
-
}
|
|
5077
|
-
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
5078
|
-
return true;
|
|
5079
|
-
}
|
|
5080
|
-
if (isPremium && this.premium.features.includes(featureName)) {
|
|
5081
|
-
return true;
|
|
5082
|
-
}
|
|
5083
|
-
return this.free.features.includes(featureName);
|
|
5084
|
-
},
|
|
5085
|
-
/**
|
|
5086
|
-
* Get max allowed tokens for license tier
|
|
5087
|
-
*/
|
|
5088
|
-
getMaxTokens(licenseData) {
|
|
5089
|
-
if (!licenseData) return this.free.maxTokens;
|
|
5090
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxTokens;
|
|
5091
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxTokens;
|
|
5092
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxTokens;
|
|
5093
|
-
return this.free.maxTokens;
|
|
5094
|
-
},
|
|
5095
|
-
/**
|
|
5096
|
-
* Get max allowed sessions for license tier
|
|
5097
|
-
*/
|
|
5098
|
-
getMaxSessions(licenseData) {
|
|
5099
|
-
if (!licenseData) return this.free.maxSessions;
|
|
5100
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxSessions;
|
|
5101
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxSessions;
|
|
5102
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxSessions;
|
|
5103
|
-
return this.free.maxSessions;
|
|
5104
|
-
},
|
|
5105
|
-
/**
|
|
5106
|
-
* Get max allowed IP bans for license tier
|
|
5107
|
-
*/
|
|
5108
|
-
getMaxIPBans(licenseData) {
|
|
5109
|
-
if (!licenseData) return this.free.maxIPBans;
|
|
5110
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxIPBans;
|
|
5111
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxIPBans;
|
|
5112
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxIPBans;
|
|
5113
|
-
return this.free.maxIPBans;
|
|
5114
|
-
},
|
|
5115
|
-
/**
|
|
5116
|
-
* Get tier name from license data
|
|
5117
|
-
*/
|
|
5118
|
-
getTierName(licenseData) {
|
|
5119
|
-
if (!licenseData) return "free";
|
|
5120
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return "enterprise";
|
|
5121
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return "advanced";
|
|
5122
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return "premium";
|
|
5123
|
-
return "free";
|
|
5124
|
-
}
|
|
5125
|
-
};
|
|
5126
4750
|
const crypto$1 = require$$0__default.default;
|
|
5127
4751
|
const os = require$$1__default$3.default;
|
|
5128
|
-
const features = features$1;
|
|
5129
4752
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
5130
4753
|
const envTimeout = Number(process.env.MAGIC_LICENSE_TIMEOUT_MS);
|
|
5131
4754
|
const DEFAULT_FETCH_TIMEOUT_MS = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 12e3;
|
|
@@ -5133,7 +4756,7 @@ const FETCH_RETRIES = 1;
|
|
|
5133
4756
|
const FETCH_RETRY_BACKOFF_MS = 750;
|
|
5134
4757
|
async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
5135
4758
|
let lastError;
|
|
5136
|
-
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt
|
|
4759
|
+
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt += 1) {
|
|
5137
4760
|
const controller2 = new AbortController();
|
|
5138
4761
|
const timer = setTimeout(() => controller2.abort(), timeoutMs);
|
|
5139
4762
|
try {
|
|
@@ -5152,15 +4775,22 @@ async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TI
|
|
|
5152
4775
|
throw lastError;
|
|
5153
4776
|
}
|
|
5154
4777
|
var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
4778
|
+
// ======================================================================
|
|
4779
|
+
// Network helpers (used only during admin-side key activation)
|
|
4780
|
+
// ======================================================================
|
|
5155
4781
|
/**
|
|
5156
|
-
*
|
|
5157
|
-
*
|
|
4782
|
+
* License server URL. Hard-coded — kept simple because the only call
|
|
4783
|
+
* site is the admin-side activation form. Not user-configurable.
|
|
4784
|
+
* @returns {string}
|
|
5158
4785
|
*/
|
|
5159
4786
|
getLicenseServerUrl() {
|
|
5160
4787
|
return LICENSE_SERVER_URL;
|
|
5161
4788
|
},
|
|
5162
4789
|
/**
|
|
5163
|
-
*
|
|
4790
|
+
* Hashed device identifier used by the upstream activation server to
|
|
4791
|
+
* de-duplicate seats. Hash of MAC addresses + hostname; the raw values
|
|
4792
|
+
* never leave the host.
|
|
4793
|
+
* @returns {string}
|
|
5164
4794
|
*/
|
|
5165
4795
|
generateDeviceId() {
|
|
5166
4796
|
try {
|
|
@@ -5177,11 +4807,12 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5177
4807
|
return crypto$1.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
|
|
5178
4808
|
} catch (error) {
|
|
5179
4809
|
strapi2.log.error("Error generating device ID:", error);
|
|
5180
|
-
return crypto$1.randomBytes(16).
|
|
4810
|
+
return crypto$1.randomBytes(16).toString("hex");
|
|
5181
4811
|
}
|
|
5182
4812
|
},
|
|
5183
4813
|
/**
|
|
5184
|
-
*
|
|
4814
|
+
* Friendly device name for the activation payload.
|
|
4815
|
+
* @returns {string}
|
|
5185
4816
|
*/
|
|
5186
4817
|
getDeviceName() {
|
|
5187
4818
|
try {
|
|
@@ -5191,7 +4822,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5191
4822
|
}
|
|
5192
4823
|
},
|
|
5193
4824
|
/**
|
|
5194
|
-
*
|
|
4825
|
+
* First non-internal IPv4 address. Best-effort, used in the activation
|
|
4826
|
+
* payload only.
|
|
4827
|
+
* @returns {string}
|
|
5195
4828
|
*/
|
|
5196
4829
|
getIpAddress() {
|
|
5197
4830
|
try {
|
|
@@ -5209,13 +4842,22 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5209
4842
|
}
|
|
5210
4843
|
},
|
|
5211
4844
|
/**
|
|
5212
|
-
*
|
|
4845
|
+
* Server-side user-agent string for the activation payload.
|
|
4846
|
+
* @returns {string}
|
|
5213
4847
|
*/
|
|
5214
4848
|
getUserAgent() {
|
|
5215
4849
|
return `Strapi/${strapi2.config.info.strapi} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
5216
4850
|
},
|
|
4851
|
+
// ======================================================================
|
|
4852
|
+
// License creation / verification (admin activation path only)
|
|
4853
|
+
// ======================================================================
|
|
5217
4854
|
/**
|
|
5218
|
-
* Create a license
|
|
4855
|
+
* Create a license on the upstream activation server. Used by the
|
|
4856
|
+
* admin "auto-create" / "create" flows. Failure to create is non-fatal
|
|
4857
|
+
* — the plugin keeps working, the user just doesn't get a key.
|
|
4858
|
+
*
|
|
4859
|
+
* @param {{ email: string, firstName?: string, lastName?: string }} args
|
|
4860
|
+
* @returns {Promise<object|null>} Upstream license payload, or null on failure
|
|
5219
4861
|
*/
|
|
5220
4862
|
async createLicense({ email: email2, firstName, lastName }) {
|
|
5221
4863
|
try {
|
|
@@ -5226,9 +4868,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5226
4868
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5227
4869
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/create`, {
|
|
5228
4870
|
method: "POST",
|
|
5229
|
-
headers: {
|
|
5230
|
-
"Content-Type": "application/json"
|
|
5231
|
-
},
|
|
4871
|
+
headers: { "Content-Type": "application/json" },
|
|
5232
4872
|
body: JSON.stringify({
|
|
5233
4873
|
email: email2,
|
|
5234
4874
|
firstName,
|
|
@@ -5243,390 +4883,201 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5243
4883
|
});
|
|
5244
4884
|
const data = await response.json();
|
|
5245
4885
|
if (data.success) {
|
|
5246
|
-
strapi2.log.info("[SUCCESS] License created
|
|
4886
|
+
strapi2.log.info("[SUCCESS] License created:", data.data.licenseKey);
|
|
5247
4887
|
return data.data;
|
|
5248
|
-
} else {
|
|
5249
|
-
strapi2.log.error("[ERROR] License creation failed:", data);
|
|
5250
|
-
return null;
|
|
5251
4888
|
}
|
|
4889
|
+
strapi2.log.warn("[WARNING] License creation rejected by server:", data.message || "unknown");
|
|
4890
|
+
return null;
|
|
5252
4891
|
} catch (error) {
|
|
5253
|
-
strapi2.log.error("[ERROR] Error creating license:", error);
|
|
4892
|
+
strapi2.log.error("[ERROR] Error creating license:", error.message);
|
|
5254
4893
|
return null;
|
|
5255
4894
|
}
|
|
5256
4895
|
},
|
|
5257
4896
|
/**
|
|
5258
|
-
* Verify a license
|
|
4897
|
+
* Verify a license against the upstream activation server.
|
|
4898
|
+
* Called once when the admin saves a license key — never during
|
|
4899
|
+
* runtime feature checks, never on a schedule.
|
|
4900
|
+
*
|
|
4901
|
+
* @param {string} licenseKey
|
|
4902
|
+
* @returns {Promise<{valid: boolean, data: object|null}>}
|
|
5259
4903
|
*/
|
|
5260
|
-
async verifyLicense(licenseKey
|
|
4904
|
+
async verifyLicense(licenseKey) {
|
|
5261
4905
|
try {
|
|
5262
|
-
const controller2 = new AbortController();
|
|
5263
|
-
const timeoutId = setTimeout(() => controller2.abort(), 5e3);
|
|
5264
4906
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5265
4907
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/verify`, {
|
|
5266
4908
|
method: "POST",
|
|
5267
|
-
headers: {
|
|
5268
|
-
"Content-Type": "application/json"
|
|
5269
|
-
},
|
|
4909
|
+
headers: { "Content-Type": "application/json" },
|
|
5270
4910
|
body: JSON.stringify({
|
|
5271
4911
|
licenseKey,
|
|
5272
4912
|
pluginName: "magic-link",
|
|
5273
4913
|
productName: "Magic Link - Passwordless Authentication"
|
|
5274
|
-
})
|
|
5275
|
-
signal: controller2.signal
|
|
4914
|
+
})
|
|
5276
4915
|
});
|
|
5277
|
-
clearTimeout(timeoutId);
|
|
5278
4916
|
const data = await response.json();
|
|
5279
4917
|
if (data.success) {
|
|
5280
|
-
const isValid = data.data.isActive && !data.data.isExpired;
|
|
4918
|
+
const isValid = !!(data.data && data.data.isActive && !data.data.isExpired);
|
|
5281
4919
|
if (isValid) {
|
|
5282
|
-
const pluginStore = strapi2.store({
|
|
5283
|
-
type: "plugin",
|
|
5284
|
-
name: "magic-link"
|
|
5285
|
-
});
|
|
4920
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5286
4921
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5287
4922
|
}
|
|
5288
|
-
return {
|
|
5289
|
-
valid: isValid,
|
|
5290
|
-
data: data.data
|
|
5291
|
-
};
|
|
5292
|
-
} else {
|
|
5293
|
-
strapi2.log.warn(`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5294
|
-
return { valid: false, data: null };
|
|
4923
|
+
return { valid: isValid, data: data.data };
|
|
5295
4924
|
}
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
strapi2.log.info(`[INFO] Grace period active - accepting stored license key`);
|
|
5300
|
-
return { valid: true, data: null, gracePeriod: true };
|
|
5301
|
-
}
|
|
5302
|
-
strapi2.log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
4925
|
+
strapi2.log.warn(
|
|
4926
|
+
`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`
|
|
4927
|
+
);
|
|
5303
4928
|
return { valid: false, data: null };
|
|
5304
|
-
}
|
|
5305
|
-
},
|
|
5306
|
-
/**
|
|
5307
|
-
* Ping a license (lightweight check)
|
|
5308
|
-
*/
|
|
5309
|
-
async pingLicense(licenseKey) {
|
|
5310
|
-
try {
|
|
5311
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5312
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/ping`, {
|
|
5313
|
-
method: "POST",
|
|
5314
|
-
headers: {
|
|
5315
|
-
"Content-Type": "application/json"
|
|
5316
|
-
},
|
|
5317
|
-
body: JSON.stringify({
|
|
5318
|
-
licenseKey,
|
|
5319
|
-
pluginName: "magic-link",
|
|
5320
|
-
productName: "Magic Link - Passwordless Authentication"
|
|
5321
|
-
})
|
|
5322
|
-
});
|
|
5323
|
-
const data = await response.json();
|
|
5324
|
-
if (data.success) {
|
|
5325
|
-
strapi2.log.debug(`[PING] License ping successful: ${data.data?.isActive ? "ACTIVE" : "INACTIVE"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5326
|
-
return data.data;
|
|
5327
|
-
} else {
|
|
5328
|
-
strapi2.log.debug(`[WARNING] License ping failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5329
|
-
return null;
|
|
5330
|
-
}
|
|
5331
|
-
} catch (error) {
|
|
5332
|
-
strapi2.log.debug(`License ping error: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5333
|
-
return null;
|
|
5334
|
-
}
|
|
5335
|
-
},
|
|
5336
|
-
/**
|
|
5337
|
-
* Upgrade a license
|
|
5338
|
-
*/
|
|
5339
|
-
async upgradeLicense(licenseId, features2) {
|
|
5340
|
-
try {
|
|
5341
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5342
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/${licenseId}/upgrade`, {
|
|
5343
|
-
method: "POST",
|
|
5344
|
-
headers: {
|
|
5345
|
-
"Content-Type": "application/json"
|
|
5346
|
-
},
|
|
5347
|
-
body: JSON.stringify(features2)
|
|
5348
|
-
});
|
|
5349
|
-
const data = await response.json();
|
|
5350
|
-
if (data.success) {
|
|
5351
|
-
strapi2.log.info("[SUCCESS] License upgraded:", licenseId);
|
|
5352
|
-
return data.data;
|
|
5353
|
-
} else {
|
|
5354
|
-
strapi2.log.error("[ERROR] License upgrade failed:", licenseId);
|
|
5355
|
-
return null;
|
|
5356
|
-
}
|
|
5357
4929
|
} catch (error) {
|
|
5358
|
-
strapi2.log.
|
|
5359
|
-
|
|
4930
|
+
strapi2.log.warn(
|
|
4931
|
+
`[WARNING] License server unreachable during activation: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...). Activation will not block this install.`
|
|
4932
|
+
);
|
|
4933
|
+
return { valid: false, data: null, networkError: true };
|
|
5360
4934
|
}
|
|
5361
4935
|
},
|
|
5362
4936
|
/**
|
|
5363
|
-
*
|
|
4937
|
+
* Look up a license by key on the upstream server. Used by the admin
|
|
4938
|
+
* License page to display details. Network failures yield null.
|
|
4939
|
+
*
|
|
4940
|
+
* @param {string} licenseKey
|
|
4941
|
+
* @returns {Promise<object|null>}
|
|
5364
4942
|
*/
|
|
5365
4943
|
async getLicenseByKey(licenseKey) {
|
|
5366
4944
|
try {
|
|
5367
4945
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5368
4946
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
|
|
5369
4947
|
const data = await response.json();
|
|
5370
|
-
if (data.success)
|
|
5371
|
-
return data.data;
|
|
5372
|
-
}
|
|
4948
|
+
if (data.success) return data.data;
|
|
5373
4949
|
return null;
|
|
5374
4950
|
} catch (error) {
|
|
5375
|
-
strapi2.log.
|
|
4951
|
+
strapi2.log.warn("[WARNING] Could not fetch license by key:", error.message);
|
|
5376
4952
|
return null;
|
|
5377
4953
|
}
|
|
5378
4954
|
},
|
|
5379
4955
|
/**
|
|
5380
|
-
*
|
|
4956
|
+
* Look up licenses by user email. Used by admin "auto-create" to
|
|
4957
|
+
* decide whether to reuse an existing key.
|
|
4958
|
+
*
|
|
4959
|
+
* @param {string} email
|
|
4960
|
+
* @returns {Promise<Array>}
|
|
5381
4961
|
*/
|
|
5382
4962
|
async getLicensesByEmail(email2) {
|
|
5383
4963
|
try {
|
|
5384
4964
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5385
|
-
const response = await fetchWithTimeout(
|
|
4965
|
+
const response = await fetchWithTimeout(
|
|
4966
|
+
`${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`
|
|
4967
|
+
);
|
|
5386
4968
|
const data = await response.json();
|
|
5387
|
-
if (data.success)
|
|
5388
|
-
return data.data;
|
|
5389
|
-
}
|
|
4969
|
+
if (data.success) return data.data;
|
|
5390
4970
|
return [];
|
|
5391
4971
|
} catch (error) {
|
|
5392
|
-
strapi2.log.
|
|
4972
|
+
strapi2.log.warn("[WARNING] Could not fetch licenses by email:", error.message);
|
|
5393
4973
|
return [];
|
|
5394
4974
|
}
|
|
5395
4975
|
},
|
|
4976
|
+
// ======================================================================
|
|
4977
|
+
// Storage (plugin store)
|
|
4978
|
+
// ======================================================================
|
|
5396
4979
|
/**
|
|
5397
|
-
*
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/stats/online`);
|
|
5403
|
-
const data = await response.json();
|
|
5404
|
-
if (data.success) {
|
|
5405
|
-
return data.data;
|
|
5406
|
-
}
|
|
5407
|
-
return null;
|
|
5408
|
-
} catch (error) {
|
|
5409
|
-
strapi2.log.error("[ERROR] Error fetching online stats:", error);
|
|
5410
|
-
return null;
|
|
5411
|
-
}
|
|
5412
|
-
},
|
|
5413
|
-
/**
|
|
5414
|
-
* Start periodic pinging for a license
|
|
5415
|
-
*/
|
|
5416
|
-
startPinging(licenseKey, intervalMinutes = 15) {
|
|
5417
|
-
const intervalMs = intervalMinutes * 60 * 1e3;
|
|
5418
|
-
this.pingLicense(licenseKey);
|
|
5419
|
-
const pingInterval = setInterval(async () => {
|
|
5420
|
-
await this.pingLicense(licenseKey);
|
|
5421
|
-
}, intervalMs);
|
|
5422
|
-
return pingInterval;
|
|
5423
|
-
},
|
|
5424
|
-
/**
|
|
5425
|
-
* Initialize license guard
|
|
5426
|
-
* Checks for existing license or prompts for creation
|
|
5427
|
-
*/
|
|
5428
|
-
async initialize() {
|
|
5429
|
-
try {
|
|
5430
|
-
const pluginStore = strapi2.store({
|
|
5431
|
-
type: "plugin",
|
|
5432
|
-
name: "magic-link"
|
|
5433
|
-
});
|
|
5434
|
-
let licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5435
|
-
const lastValidated = await pluginStore.get({ key: "lastValidated" });
|
|
5436
|
-
const now = /* @__PURE__ */ new Date();
|
|
5437
|
-
const gracePeriodHours = 24;
|
|
5438
|
-
let withinGracePeriod = false;
|
|
5439
|
-
if (lastValidated) {
|
|
5440
|
-
const lastValidatedDate = new Date(lastValidated);
|
|
5441
|
-
const hoursSinceValidation = (now - lastValidatedDate) / (1e3 * 60 * 60);
|
|
5442
|
-
withinGracePeriod = hoursSinceValidation < gracePeriodHours;
|
|
5443
|
-
}
|
|
5444
|
-
if (licenseKey) {
|
|
5445
|
-
const verification = await this.verifyLicense(licenseKey, true);
|
|
5446
|
-
if (verification.valid) {
|
|
5447
|
-
const pingInterval = this.startPinging(licenseKey, 15);
|
|
5448
|
-
strapi2.licenseGuard = {
|
|
5449
|
-
licenseKey,
|
|
5450
|
-
pingInterval,
|
|
5451
|
-
data: verification.data
|
|
5452
|
-
};
|
|
5453
|
-
return { valid: true, data: verification.data };
|
|
5454
|
-
} else {
|
|
5455
|
-
strapi2.log.warn("[WARNING] Stored license is invalid or expired");
|
|
5456
|
-
if (!withinGracePeriod) {
|
|
5457
|
-
await pluginStore.delete({ key: "licenseKey" });
|
|
5458
|
-
await pluginStore.delete({ key: "lastValidated" });
|
|
5459
|
-
}
|
|
5460
|
-
}
|
|
5461
|
-
}
|
|
5462
|
-
strapi2.log.warn("[WARNING] No valid license found. Plugin will run in demo mode.");
|
|
5463
|
-
return { valid: false, demo: true };
|
|
5464
|
-
} catch (error) {
|
|
5465
|
-
strapi2.log.error("[ERROR] Error initializing license guard:", error);
|
|
5466
|
-
return { valid: false, error: error.message };
|
|
5467
|
-
}
|
|
5468
|
-
},
|
|
5469
|
-
/**
|
|
5470
|
-
* Store license key after creation
|
|
4980
|
+
* Persist a license key to the plugin store. Used by the admin
|
|
4981
|
+
* activation form.
|
|
4982
|
+
*
|
|
4983
|
+
* @param {string} licenseKey
|
|
4984
|
+
* @returns {Promise<boolean>}
|
|
5471
4985
|
*/
|
|
5472
4986
|
async storeLicenseKey(licenseKey) {
|
|
5473
4987
|
try {
|
|
5474
|
-
strapi2.
|
|
5475
|
-
const pluginStore = strapi2.store({
|
|
5476
|
-
type: "plugin",
|
|
5477
|
-
name: "magic-link"
|
|
5478
|
-
});
|
|
4988
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5479
4989
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
5480
4990
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
strapi2.log.info("[SUCCESS] License key stored and verified successfully");
|
|
5484
|
-
return true;
|
|
5485
|
-
} else {
|
|
5486
|
-
strapi2.log.error("[ERROR] License key storage verification failed");
|
|
5487
|
-
return false;
|
|
5488
|
-
}
|
|
4991
|
+
strapi2.log.info("[SUCCESS] License key stored");
|
|
4992
|
+
return true;
|
|
5489
4993
|
} catch (error) {
|
|
5490
|
-
strapi2.log.error("[ERROR] Error storing license key:", error);
|
|
4994
|
+
strapi2.log.error("[ERROR] Error storing license key:", error.message);
|
|
5491
4995
|
return false;
|
|
5492
4996
|
}
|
|
5493
4997
|
},
|
|
4998
|
+
// ======================================================================
|
|
4999
|
+
// Lifecycle (no-ops in the marketplace build)
|
|
5000
|
+
// ======================================================================
|
|
5001
|
+
/**
|
|
5002
|
+
* Boot-time initializer. In the marketplace build this is a no-op:
|
|
5003
|
+
* we do not auto-verify the stored key and we do not start a periodic
|
|
5004
|
+
* ping. Kept as a stable export so bootstrap.js continues to work.
|
|
5005
|
+
*
|
|
5006
|
+
* @returns {Promise<{valid: boolean}>}
|
|
5007
|
+
*/
|
|
5008
|
+
async initialize() {
|
|
5009
|
+
return { valid: true };
|
|
5010
|
+
},
|
|
5494
5011
|
/**
|
|
5495
|
-
*
|
|
5012
|
+
* Plugin destroy hook. No-op (no interval to clear in the marketplace
|
|
5013
|
+
* build).
|
|
5496
5014
|
*/
|
|
5497
5015
|
cleanup() {
|
|
5498
|
-
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
5499
|
-
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
5500
|
-
strapi2.log.info("[INFO] License pinging stopped");
|
|
5501
|
-
}
|
|
5502
5016
|
},
|
|
5017
|
+
// ======================================================================
|
|
5018
|
+
// Permissive feature stubs (kept for backward compatibility with any
|
|
5019
|
+
// legacy caller — they always permit, never gate)
|
|
5020
|
+
// ======================================================================
|
|
5503
5021
|
/**
|
|
5504
|
-
*
|
|
5505
|
-
*
|
|
5506
|
-
*
|
|
5022
|
+
* Always-permissive feature check. Kept so any legacy `if (await
|
|
5023
|
+
* licenseGuard.hasFeature(...))` site continues to compile and runs
|
|
5024
|
+
* the gated branch.
|
|
5025
|
+
*
|
|
5026
|
+
* @returns {Promise<boolean>} always true
|
|
5507
5027
|
*/
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5512
|
-
if (!licenseKey) {
|
|
5513
|
-
return features.hasFeature(null, featureName);
|
|
5514
|
-
}
|
|
5515
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5516
|
-
return features.hasFeature(licenseData, featureName);
|
|
5517
|
-
} catch (error) {
|
|
5518
|
-
strapi2.log.error("Error checking feature:", error);
|
|
5519
|
-
return false;
|
|
5520
|
-
}
|
|
5028
|
+
// eslint-disable-next-line no-unused-vars
|
|
5029
|
+
async hasFeature(_featureName) {
|
|
5030
|
+
return true;
|
|
5521
5031
|
},
|
|
5522
5032
|
/**
|
|
5523
|
-
*
|
|
5524
|
-
* @returns {number} Max tokens (-1 = unlimited)
|
|
5033
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5525
5034
|
*/
|
|
5526
5035
|
async getMaxTokens() {
|
|
5527
|
-
|
|
5528
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5529
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5530
|
-
if (!licenseKey) {
|
|
5531
|
-
return features.getMaxTokens(null);
|
|
5532
|
-
}
|
|
5533
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5534
|
-
return features.getMaxTokens(licenseData);
|
|
5535
|
-
} catch (error) {
|
|
5536
|
-
strapi2.log.error("Error getting max tokens:", error);
|
|
5537
|
-
return features.free.maxTokens;
|
|
5538
|
-
}
|
|
5036
|
+
return -1;
|
|
5539
5037
|
},
|
|
5540
5038
|
/**
|
|
5541
|
-
*
|
|
5542
|
-
* @returns {number} Max sessions (-1 = unlimited)
|
|
5039
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5543
5040
|
*/
|
|
5544
5041
|
async getMaxSessions() {
|
|
5545
|
-
|
|
5546
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5547
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5548
|
-
if (!licenseKey) {
|
|
5549
|
-
return features.getMaxSessions(null);
|
|
5550
|
-
}
|
|
5551
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5552
|
-
return features.getMaxSessions(licenseData);
|
|
5553
|
-
} catch (error) {
|
|
5554
|
-
strapi2.log.error("Error getting max sessions:", error);
|
|
5555
|
-
return features.free.maxSessions;
|
|
5556
|
-
}
|
|
5042
|
+
return -1;
|
|
5557
5043
|
},
|
|
5558
5044
|
/**
|
|
5559
|
-
*
|
|
5560
|
-
* @returns {number} Max IP bans (-1 = unlimited)
|
|
5045
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5561
5046
|
*/
|
|
5562
5047
|
async getMaxIPBans() {
|
|
5563
|
-
|
|
5564
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5565
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5566
|
-
if (!licenseKey) {
|
|
5567
|
-
return features.getMaxIPBans(null);
|
|
5568
|
-
}
|
|
5569
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5570
|
-
return features.getMaxIPBans(licenseData);
|
|
5571
|
-
} catch (error) {
|
|
5572
|
-
strapi2.log.error("Error getting max IP bans:", error);
|
|
5573
|
-
return features.free.maxIPBans;
|
|
5574
|
-
}
|
|
5048
|
+
return -1;
|
|
5575
5049
|
},
|
|
5576
5050
|
/**
|
|
5577
|
-
*
|
|
5578
|
-
* @returns {Array} Available OTP types
|
|
5051
|
+
* @returns {Promise<string[]>} every supported OTP type
|
|
5579
5052
|
*/
|
|
5580
5053
|
async getAvailableOTPTypes() {
|
|
5581
|
-
|
|
5582
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5583
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5584
|
-
if (!licenseKey) {
|
|
5585
|
-
return features.getAvailableOTPTypes(null);
|
|
5586
|
-
}
|
|
5587
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5588
|
-
return features.getAvailableOTPTypes(licenseData);
|
|
5589
|
-
} catch (error) {
|
|
5590
|
-
strapi2.log.error("Error getting available OTP types:", error);
|
|
5591
|
-
return [];
|
|
5592
|
-
}
|
|
5054
|
+
return ["email", "totp", "backup-codes"];
|
|
5593
5055
|
},
|
|
5594
5056
|
/**
|
|
5595
|
-
*
|
|
5596
|
-
*
|
|
5057
|
+
* Tier info for UI display. Reports `pro` when a key is stored,
|
|
5058
|
+
* `free` otherwise. Pure information — no behavior depends on this.
|
|
5059
|
+
*
|
|
5060
|
+
* @returns {Promise<{tier: string, features: object, hasKey: boolean}>}
|
|
5597
5061
|
*/
|
|
5598
5062
|
async getLicenseTierInfo() {
|
|
5599
5063
|
try {
|
|
5600
5064
|
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5601
5065
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5602
|
-
|
|
5603
|
-
return {
|
|
5604
|
-
tier: "free",
|
|
5605
|
-
features: {
|
|
5606
|
-
premium: false,
|
|
5607
|
-
advanced: false,
|
|
5608
|
-
enterprise: false
|
|
5609
|
-
}
|
|
5610
|
-
};
|
|
5611
|
-
}
|
|
5612
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5066
|
+
const hasKey = !!licenseKey;
|
|
5613
5067
|
return {
|
|
5614
|
-
tier:
|
|
5068
|
+
tier: hasKey ? "pro" : "free",
|
|
5069
|
+
hasKey,
|
|
5615
5070
|
features: {
|
|
5616
|
-
premium:
|
|
5617
|
-
advanced:
|
|
5618
|
-
enterprise:
|
|
5071
|
+
premium: hasKey,
|
|
5072
|
+
advanced: hasKey,
|
|
5073
|
+
enterprise: hasKey
|
|
5619
5074
|
}
|
|
5620
5075
|
};
|
|
5621
5076
|
} catch (error) {
|
|
5622
|
-
strapi2.log.error("Error getting license tier info:", error);
|
|
5623
5077
|
return {
|
|
5624
5078
|
tier: "free",
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
advanced: false,
|
|
5628
|
-
enterprise: false
|
|
5629
|
-
}
|
|
5079
|
+
hasKey: false,
|
|
5080
|
+
features: { premium: false, advanced: false, enterprise: false }
|
|
5630
5081
|
};
|
|
5631
5082
|
}
|
|
5632
5083
|
}
|