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.mjs
CHANGED
|
@@ -357,38 +357,14 @@ _Falls du diesen Link nicht angefordert hast, ignoriere diese Nachricht._`,
|
|
|
357
357
|
try {
|
|
358
358
|
const licenseGuardService = strapi2.plugin("magic-link").service("license-guard");
|
|
359
359
|
setTimeout(async () => {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
strapi2.log.
|
|
364
|
-
strapi2.log.warn("║ ║");
|
|
365
|
-
strapi2.log.warn("║ To activate, create a license via Admin UI: ║");
|
|
366
|
-
strapi2.log.warn("║ Go to Settings → Magic Link → License ║");
|
|
367
|
-
strapi2.log.warn("╚════════════════════════════════════════════════════════════════╝");
|
|
368
|
-
} else if (licenseStatus.valid) {
|
|
369
|
-
const pluginStore2 = strapi2.store({
|
|
370
|
-
type: "plugin",
|
|
371
|
-
name: "magic-link"
|
|
372
|
-
});
|
|
373
|
-
const storedKey = await pluginStore2.get({ key: "licenseKey" });
|
|
374
|
-
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
375
|
-
strapi2.log.info("║ [SUCCESS] MAGIC LINK PLUGIN LICENSE ACTIVE ║");
|
|
376
|
-
strapi2.log.info("║ ║");
|
|
377
|
-
if (licenseStatus.data) {
|
|
378
|
-
strapi2.log.info(`║ License: ${licenseStatus.data.licenseKey} ║`);
|
|
379
|
-
strapi2.log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + "║");
|
|
380
|
-
strapi2.log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + "║");
|
|
381
|
-
} else if (storedKey) {
|
|
382
|
-
strapi2.log.info(`║ License: ${storedKey} (Offline Mode) ║`);
|
|
383
|
-
strapi2.log.info(`║ Status: Grace Period Active ║`);
|
|
384
|
-
}
|
|
385
|
-
strapi2.log.info("║ ║");
|
|
386
|
-
strapi2.log.info("║ [AUTO] Pinging every 15 minutes ║");
|
|
387
|
-
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
360
|
+
try {
|
|
361
|
+
await licenseGuardService.initialize();
|
|
362
|
+
} catch (initErr) {
|
|
363
|
+
strapi2.log.debug("[magic-link] license-guard initialize() warning:", initErr.message);
|
|
388
364
|
}
|
|
389
365
|
}, 3e3);
|
|
390
366
|
} catch (error) {
|
|
391
|
-
strapi2.log.
|
|
367
|
+
strapi2.log.debug("[magic-link] license-guard service not available at bootstrap:", error.message);
|
|
392
368
|
}
|
|
393
369
|
try {
|
|
394
370
|
setTimeout(async () => {
|
|
@@ -416,12 +392,11 @@ _Falls du diesen Link nicht angefordert hast, ignoriere diese Nachricht._`,
|
|
|
416
392
|
var destroy$1 = ({ strapi: strapi2 }) => {
|
|
417
393
|
try {
|
|
418
394
|
const licenseGuardService = strapi2.plugin("magic-link")?.service("license-guard");
|
|
419
|
-
if (licenseGuardService) {
|
|
395
|
+
if (licenseGuardService && typeof licenseGuardService.cleanup === "function") {
|
|
420
396
|
licenseGuardService.cleanup();
|
|
421
|
-
strapi2.log.info("[SUCCESS] License Guard cleanup completed");
|
|
422
397
|
}
|
|
423
398
|
} catch (error) {
|
|
424
|
-
strapi2.log.
|
|
399
|
+
strapi2.log.debug("[magic-link] license-guard cleanup warning:", error.message);
|
|
425
400
|
}
|
|
426
401
|
};
|
|
427
402
|
var passwordlessCompat$2 = (config2, { strapi: strapi2 }) => {
|
|
@@ -1183,10 +1158,6 @@ var auth$1 = {
|
|
|
1183
1158
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1184
1159
|
const userService = strapi.plugin("users-permissions").service("user");
|
|
1185
1160
|
const jwtService = strapi.plugin("users-permissions").service("jwt");
|
|
1186
|
-
const isEnabled = await magicLink2.isEnabled();
|
|
1187
|
-
if (!isEnabled) {
|
|
1188
|
-
return i18n.sendError(ctx, "plugin.disabled", 400);
|
|
1189
|
-
}
|
|
1190
1161
|
if (_$1.isEmpty(loginToken)) {
|
|
1191
1162
|
return i18n.sendError(ctx, "token.invalid", 400);
|
|
1192
1163
|
}
|
|
@@ -1206,9 +1177,7 @@ var auth$1 = {
|
|
|
1206
1177
|
return i18n.sendError(ctx, "token.invalid", 400);
|
|
1207
1178
|
}
|
|
1208
1179
|
const settings = await magicLink2.settings();
|
|
1209
|
-
|
|
1210
|
-
const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
|
|
1211
|
-
if (settings.otp_enabled && hasOTPFeature) {
|
|
1180
|
+
if (settings.otp_enabled) {
|
|
1212
1181
|
await strapi.documents("plugin::magic-link.token").update({
|
|
1213
1182
|
documentId: token2.documentId,
|
|
1214
1183
|
data: {
|
|
@@ -1407,10 +1376,6 @@ var auth$1 = {
|
|
|
1407
1376
|
const magicLink2 = strapi.plugin("magic-link").service("magic-link");
|
|
1408
1377
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1409
1378
|
const whatsappService = strapi.plugin("magic-link").service("whatsapp");
|
|
1410
|
-
const isEnabled = await magicLink2.isEnabled();
|
|
1411
|
-
if (!isEnabled) {
|
|
1412
|
-
return i18n.sendError(ctx, "plugin.disabled", 400);
|
|
1413
|
-
}
|
|
1414
1379
|
const params = parseBody$1(sendLinkSchema, ctx.request.body);
|
|
1415
1380
|
const email2 = params.email || null;
|
|
1416
1381
|
const phoneNumber = params.phoneNumber || params.phone || null;
|
|
@@ -1675,7 +1640,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1675
1640
|
const { email: normalizedEmail, totpCode } = parseBody$1(loginWithTotpSchema, ctx.request.body);
|
|
1676
1641
|
const magicLink2 = strapi.plugin("magic-link").service("magic-link");
|
|
1677
1642
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
1678
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
1679
1643
|
const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
|
|
1680
1644
|
const userService = strapi.plugin("users-permissions").service("user");
|
|
1681
1645
|
const jwtService = strapi.plugin("users-permissions").service("jwt");
|
|
@@ -1694,10 +1658,6 @@ Thanks for joining. Your Magic Link is on the way.`,
|
|
|
1694
1658
|
if (!settings.totp_as_primary_auth) {
|
|
1695
1659
|
return ctx.forbidden("TOTP login is not enabled");
|
|
1696
1660
|
}
|
|
1697
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-totp");
|
|
1698
|
-
if (!hasFeature) {
|
|
1699
|
-
return ctx.forbidden("TOTP login requires Advanced license");
|
|
1700
|
-
}
|
|
1701
1661
|
const GENERIC_INVALID = "Invalid credentials";
|
|
1702
1662
|
const users = await strapi.documents("plugin::users-permissions.user").findMany({
|
|
1703
1663
|
filters: { email: normalizedEmail },
|
|
@@ -2488,14 +2448,6 @@ var tokens$1 = {
|
|
|
2488
2448
|
if (token2.expires_at && new Date(token2.expires_at) < /* @__PURE__ */ new Date()) {
|
|
2489
2449
|
return ctx.badRequest("Token ist abgelaufen");
|
|
2490
2450
|
}
|
|
2491
|
-
const pluginStore = strapi.store({
|
|
2492
|
-
type: "plugin",
|
|
2493
|
-
name: "magic-link"
|
|
2494
|
-
});
|
|
2495
|
-
const settings = await pluginStore.get({ key: "settings" });
|
|
2496
|
-
if (!settings || !settings.enabled) {
|
|
2497
|
-
return ctx.badRequest("Magic Link ist nicht aktiviert");
|
|
2498
|
-
}
|
|
2499
2451
|
return ctx.badRequest("Token kann nicht erneut gesendet werden - der Original-Token ist gehashed. Bitte erstelle einen neuen Token für diese E-Mail.");
|
|
2500
2452
|
} catch (error) {
|
|
2501
2453
|
strapi.log.error("Fehler beim erneuten Senden:", error);
|
|
@@ -2662,7 +2614,7 @@ var tokens$1 = {
|
|
|
2662
2614
|
else if (jwtDays <= 30) jwtLifetimePoints = 5;
|
|
2663
2615
|
let configPoints = 0;
|
|
2664
2616
|
if (settings.createUserIfNotExists === false && settings.create_new_user === false) configPoints += 10;
|
|
2665
|
-
|
|
2617
|
+
configPoints += 5;
|
|
2666
2618
|
if (settings.remember_me === false) configPoints += 5;
|
|
2667
2619
|
if (settings.stays_valid === false) configPoints += 10;
|
|
2668
2620
|
let tokenStatusPoints = 0;
|
|
@@ -2870,49 +2822,43 @@ var jwt$1 = {
|
|
|
2870
2822
|
};
|
|
2871
2823
|
var license$1 = {
|
|
2872
2824
|
/**
|
|
2873
|
-
*
|
|
2825
|
+
* Returns the current license status. Always reports the plugin as
|
|
2826
|
+
* usable; `hasKey` indicates whether the admin has activated a key.
|
|
2827
|
+
*
|
|
2828
|
+
* @route GET /magic-link/license/status
|
|
2829
|
+
* @returns {{ success: true, valid: true, hasKey: boolean, data: object|null }}
|
|
2874
2830
|
*/
|
|
2875
2831
|
async getStatus(ctx) {
|
|
2876
2832
|
try {
|
|
2877
2833
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2878
|
-
const pluginStore = strapi.store({
|
|
2879
|
-
type: "plugin",
|
|
2880
|
-
name: "magic-link"
|
|
2881
|
-
});
|
|
2834
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
2882
2835
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
2883
2836
|
if (!licenseKey) {
|
|
2884
2837
|
return ctx.send({
|
|
2885
|
-
success:
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2838
|
+
success: true,
|
|
2839
|
+
valid: true,
|
|
2840
|
+
// plugin is always usable in the marketplace build
|
|
2841
|
+
hasKey: false,
|
|
2842
|
+
data: null,
|
|
2843
|
+
message: "Plugin is active. License key activation is optional."
|
|
2889
2844
|
});
|
|
2890
2845
|
}
|
|
2891
|
-
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
2892
2846
|
const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
|
|
2893
2847
|
return ctx.send({
|
|
2894
2848
|
success: true,
|
|
2895
|
-
valid:
|
|
2896
|
-
|
|
2849
|
+
valid: true,
|
|
2850
|
+
hasKey: true,
|
|
2897
2851
|
data: {
|
|
2898
2852
|
licenseKey,
|
|
2899
2853
|
email: license2?.email || null,
|
|
2900
2854
|
firstName: license2?.firstName || null,
|
|
2901
2855
|
lastName: license2?.lastName || null,
|
|
2902
|
-
isActive: license2?.isActive
|
|
2903
|
-
isExpired: license2?.isExpired
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
deviceId: license2?.deviceId,
|
|
2909
|
-
ipAddress: license2?.ipAddress,
|
|
2910
|
-
features: {
|
|
2911
|
-
premium: license2?.featurePremium || false,
|
|
2912
|
-
advanced: license2?.featureAdvanced || false,
|
|
2913
|
-
enterprise: license2?.featureEnterprise || false,
|
|
2914
|
-
custom: license2?.featureCustom || false
|
|
2915
|
-
},
|
|
2856
|
+
isActive: license2?.isActive ?? true,
|
|
2857
|
+
isExpired: license2?.isExpired ?? false,
|
|
2858
|
+
expiresAt: license2?.expiresAt || null,
|
|
2859
|
+
deviceName: license2?.deviceName || null,
|
|
2860
|
+
deviceId: license2?.deviceId || null,
|
|
2861
|
+
ipAddress: license2?.ipAddress || null,
|
|
2916
2862
|
maxDevices: license2?.maxDevices || 1,
|
|
2917
2863
|
currentDevices: license2?.currentDevices || 0
|
|
2918
2864
|
}
|
|
@@ -2923,7 +2869,11 @@ var license$1 = {
|
|
|
2923
2869
|
}
|
|
2924
2870
|
},
|
|
2925
2871
|
/**
|
|
2926
|
-
* Auto-create license
|
|
2872
|
+
* Auto-create a license using the currently logged-in admin user's
|
|
2873
|
+
* data. Best-effort: a remote failure simply leaves the install
|
|
2874
|
+
* un-keyed, the plugin keeps working.
|
|
2875
|
+
*
|
|
2876
|
+
* @route POST /magic-link/license/auto-create
|
|
2927
2877
|
*/
|
|
2928
2878
|
async autoCreate(ctx) {
|
|
2929
2879
|
try {
|
|
@@ -2938,15 +2888,9 @@ var license$1 = {
|
|
|
2938
2888
|
lastName: adminUser.lastname || "User"
|
|
2939
2889
|
});
|
|
2940
2890
|
if (!license2) {
|
|
2941
|
-
return ctx.badRequest("
|
|
2891
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2942
2892
|
}
|
|
2943
2893
|
await licenseGuard2.storeLicenseKey(license2.licenseKey);
|
|
2944
|
-
const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
|
|
2945
|
-
strapi.licenseGuard = {
|
|
2946
|
-
licenseKey: license2.licenseKey,
|
|
2947
|
-
pingInterval,
|
|
2948
|
-
data: license2
|
|
2949
|
-
};
|
|
2950
2894
|
return ctx.send({
|
|
2951
2895
|
success: true,
|
|
2952
2896
|
message: "License automatically created and activated",
|
|
@@ -2958,26 +2902,22 @@ var license$1 = {
|
|
|
2958
2902
|
}
|
|
2959
2903
|
},
|
|
2960
2904
|
/**
|
|
2961
|
-
* Create and activate a new license
|
|
2905
|
+
* Create and activate a new license with explicit user details.
|
|
2906
|
+
*
|
|
2907
|
+
* @route POST /magic-link/license/create
|
|
2962
2908
|
*/
|
|
2963
2909
|
async createAndActivate(ctx) {
|
|
2964
2910
|
try {
|
|
2965
|
-
const { email: email2, firstName, lastName } = ctx.request.body;
|
|
2911
|
+
const { email: email2, firstName, lastName } = ctx.request.body || {};
|
|
2966
2912
|
if (!email2 || !firstName || !lastName) {
|
|
2967
2913
|
return ctx.badRequest("Email, firstName, and lastName are required");
|
|
2968
2914
|
}
|
|
2969
2915
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
2970
2916
|
const license2 = await licenseGuard2.createLicense({ email: email2, firstName, lastName });
|
|
2971
2917
|
if (!license2) {
|
|
2972
|
-
return ctx.badRequest("
|
|
2918
|
+
return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
|
|
2973
2919
|
}
|
|
2974
2920
|
await licenseGuard2.storeLicenseKey(license2.licenseKey);
|
|
2975
|
-
const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
|
|
2976
|
-
strapi.licenseGuard = {
|
|
2977
|
-
licenseKey: license2.licenseKey,
|
|
2978
|
-
pingInterval,
|
|
2979
|
-
data: license2
|
|
2980
|
-
};
|
|
2981
2921
|
return ctx.send({
|
|
2982
2922
|
success: true,
|
|
2983
2923
|
message: "License created and activated successfully",
|
|
@@ -2989,39 +2929,40 @@ var license$1 = {
|
|
|
2989
2929
|
}
|
|
2990
2930
|
},
|
|
2991
2931
|
/**
|
|
2992
|
-
*
|
|
2932
|
+
* Manual refresh: re-verifies the stored license once and returns the
|
|
2933
|
+
* latest details. Replaces the periodic-ping endpoint that used to run
|
|
2934
|
+
* every 15 minutes — admins can call this on demand from the UI to
|
|
2935
|
+
* pull updated info.
|
|
2936
|
+
*
|
|
2937
|
+
* @route POST /magic-link/license/ping
|
|
2993
2938
|
*/
|
|
2994
2939
|
async ping(ctx) {
|
|
2995
2940
|
try {
|
|
2996
|
-
const pluginStore = strapi.store({
|
|
2997
|
-
type: "plugin",
|
|
2998
|
-
name: "magic-link"
|
|
2999
|
-
});
|
|
2941
|
+
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
3000
2942
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
3001
2943
|
if (!licenseKey) {
|
|
3002
2944
|
return ctx.badRequest("No license key found");
|
|
3003
2945
|
}
|
|
3004
2946
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3005
|
-
const
|
|
3006
|
-
if (!pingResult) {
|
|
3007
|
-
return ctx.badRequest("Ping failed");
|
|
3008
|
-
}
|
|
2947
|
+
const verification = await licenseGuard2.verifyLicense(licenseKey);
|
|
3009
2948
|
return ctx.send({
|
|
3010
2949
|
success: true,
|
|
3011
|
-
message: "License
|
|
3012
|
-
data:
|
|
2950
|
+
message: verification.valid ? "License refreshed successfully" : "License key could not be verified, but the plugin keeps working.",
|
|
2951
|
+
data: verification.data
|
|
3013
2952
|
});
|
|
3014
2953
|
} catch (error) {
|
|
3015
|
-
strapi.log.error("Error
|
|
3016
|
-
return ctx.badRequest("Error
|
|
2954
|
+
strapi.log.error("Error refreshing license:", error);
|
|
2955
|
+
return ctx.badRequest("Error refreshing license");
|
|
3017
2956
|
}
|
|
3018
2957
|
},
|
|
3019
2958
|
/**
|
|
3020
|
-
|
|
3021
|
-
|
|
2959
|
+
* Store and validate an existing license key.
|
|
2960
|
+
*
|
|
2961
|
+
* @route POST /magic-link/license/store-key
|
|
2962
|
+
*/
|
|
3022
2963
|
async storeKey(ctx) {
|
|
3023
2964
|
try {
|
|
3024
|
-
const { licenseKey, email: email2 } = ctx.request.body;
|
|
2965
|
+
const { licenseKey, email: email2 } = ctx.request.body || {};
|
|
3025
2966
|
if (!licenseKey || !licenseKey.trim()) {
|
|
3026
2967
|
return ctx.badRequest("License key is required");
|
|
3027
2968
|
}
|
|
@@ -3033,26 +2974,35 @@ var license$1 = {
|
|
|
3033
2974
|
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3034
2975
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
3035
2976
|
if (!verification.valid) {
|
|
3036
|
-
|
|
2977
|
+
if (verification.networkError) {
|
|
2978
|
+
strapi.log.warn(
|
|
2979
|
+
`[WARNING] License server unreachable during activation. Storing key anyway: ${trimmedKey.substring(0, 8)}...`
|
|
2980
|
+
);
|
|
2981
|
+
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
2982
|
+
return ctx.send({
|
|
2983
|
+
success: true,
|
|
2984
|
+
message: "License key stored (license server was unreachable; will retry on the next manual refresh).",
|
|
2985
|
+
data: { licenseKey: trimmedKey, email: trimmedEmail }
|
|
2986
|
+
});
|
|
2987
|
+
}
|
|
2988
|
+
strapi.log.warn(`[WARNING] Invalid license key: ${trimmedKey.substring(0, 8)}...`);
|
|
3037
2989
|
return ctx.badRequest("Invalid or expired license key");
|
|
3038
2990
|
}
|
|
3039
2991
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
3040
2992
|
if (!license2) {
|
|
3041
|
-
strapi.log.warn(`[WARNING] License not found
|
|
2993
|
+
strapi.log.warn(`[WARNING] License not found: ${trimmedKey.substring(0, 8)}...`);
|
|
3042
2994
|
return ctx.badRequest("License not found");
|
|
3043
2995
|
}
|
|
3044
|
-
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
3045
|
-
strapi.log.warn(
|
|
2996
|
+
if (license2.email && license2.email.toLowerCase() !== trimmedEmail) {
|
|
2997
|
+
strapi.log.warn(
|
|
2998
|
+
`[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (attempted: ${trimmedEmail})`
|
|
2999
|
+
);
|
|
3046
3000
|
return ctx.badRequest("Email address does not match this license key");
|
|
3047
3001
|
}
|
|
3048
3002
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
pingInterval,
|
|
3053
|
-
data: verification.data
|
|
3054
|
-
};
|
|
3055
|
-
strapi.log.info(`[SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
|
|
3003
|
+
strapi.log.info(
|
|
3004
|
+
`[SUCCESS] License key validated and stored: ${trimmedKey.substring(0, 8)}... (email: ${trimmedEmail})`
|
|
3005
|
+
);
|
|
3056
3006
|
return ctx.send({
|
|
3057
3007
|
success: true,
|
|
3058
3008
|
message: "License key validated and activated successfully",
|
|
@@ -3204,11 +3154,6 @@ var otp$3 = {
|
|
|
3204
3154
|
if (!otpSettings.enabled) {
|
|
3205
3155
|
return ctx.badRequest("OTP is not enabled");
|
|
3206
3156
|
}
|
|
3207
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3208
|
-
const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
|
|
3209
|
-
if (!hasOTPFeature) {
|
|
3210
|
-
return ctx.forbidden("OTP feature requires Premium license or higher");
|
|
3211
|
-
}
|
|
3212
3157
|
const rlFail = await enforceOtpRateLimits(ctx, normalizedEmail, "send");
|
|
3213
3158
|
if (rlFail) return ctx.tooManyRequests(rlFail.message);
|
|
3214
3159
|
const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
|
|
@@ -3500,11 +3445,6 @@ var otp$3 = {
|
|
|
3500
3445
|
*/
|
|
3501
3446
|
async setupTOTP(ctx) {
|
|
3502
3447
|
try {
|
|
3503
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3504
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-totp");
|
|
3505
|
-
if (!hasFeature) {
|
|
3506
|
-
return ctx.forbidden("TOTP feature requires Advanced or Enterprise license");
|
|
3507
|
-
}
|
|
3508
3448
|
const { email: email2, id: userId } = ctx.state.user;
|
|
3509
3449
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3510
3450
|
const setupData = await otpService.setupTOTP(userId, email2);
|
|
@@ -3574,11 +3514,6 @@ var otp$3 = {
|
|
|
3574
3514
|
*/
|
|
3575
3515
|
async generateBackupCodes(ctx) {
|
|
3576
3516
|
try {
|
|
3577
|
-
const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
|
|
3578
|
-
const hasFeature = await licenseGuard2.hasFeature("otp-backup-codes");
|
|
3579
|
-
if (!hasFeature) {
|
|
3580
|
-
return ctx.forbidden("Backup codes feature requires Enterprise license");
|
|
3581
|
-
}
|
|
3582
3517
|
const { id: userId } = ctx.state.user;
|
|
3583
3518
|
const otpService = strapi.plugin("magic-link").service("otp");
|
|
3584
3519
|
const backupCodes = await otpService.generateBackupCodes(userId);
|
|
@@ -3748,61 +3683,6 @@ const passwordlessCompat = passwordlessCompat$2;
|
|
|
3748
3683
|
var middlewares$1 = {
|
|
3749
3684
|
"passwordless-compat": passwordlessCompat
|
|
3750
3685
|
};
|
|
3751
|
-
const { PolicyError } = require$$1$1.errors;
|
|
3752
|
-
var licenseCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
|
|
3753
|
-
try {
|
|
3754
|
-
const licenseGuard2 = strapi2.plugin("magic-link").service("license-guard");
|
|
3755
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
3756
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
3757
|
-
if (!licenseKey) {
|
|
3758
|
-
strapi2.log.warn("[ACCESS-DENIED] No license key found");
|
|
3759
|
-
throw new PolicyError("No license found. Please activate the plugin first.", {
|
|
3760
|
-
policy: "license-check",
|
|
3761
|
-
errCode: "LICENSE_MISSING"
|
|
3762
|
-
});
|
|
3763
|
-
}
|
|
3764
|
-
const verification = await licenseGuard2.verifyLicense(licenseKey, true);
|
|
3765
|
-
if (!verification.valid) {
|
|
3766
|
-
strapi2.log.warn("[ACCESS-DENIED] Invalid license");
|
|
3767
|
-
throw new PolicyError("Invalid or expired license.", {
|
|
3768
|
-
policy: "license-check",
|
|
3769
|
-
errCode: "LICENSE_INVALID"
|
|
3770
|
-
});
|
|
3771
|
-
}
|
|
3772
|
-
const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
|
|
3773
|
-
if (!license2) {
|
|
3774
|
-
strapi2.log.warn("[ACCESS-DENIED] License not found in database");
|
|
3775
|
-
throw new PolicyError("License not found. Please contact support.", {
|
|
3776
|
-
policy: "license-check",
|
|
3777
|
-
errCode: "LICENSE_NOT_FOUND"
|
|
3778
|
-
});
|
|
3779
|
-
}
|
|
3780
|
-
if (!license2.isActive) {
|
|
3781
|
-
strapi2.log.warn("[ACCESS-DENIED] License is inactive");
|
|
3782
|
-
throw new PolicyError("License is inactive. Please activate your license.", {
|
|
3783
|
-
policy: "license-check",
|
|
3784
|
-
errCode: "LICENSE_INACTIVE"
|
|
3785
|
-
});
|
|
3786
|
-
}
|
|
3787
|
-
if (license2.isExpired) {
|
|
3788
|
-
strapi2.log.warn("[ACCESS-DENIED] License has expired");
|
|
3789
|
-
throw new PolicyError("License has expired. Please renew your license.", {
|
|
3790
|
-
policy: "license-check",
|
|
3791
|
-
errCode: "LICENSE_EXPIRED"
|
|
3792
|
-
});
|
|
3793
|
-
}
|
|
3794
|
-
return true;
|
|
3795
|
-
} catch (error) {
|
|
3796
|
-
if (error instanceof PolicyError) {
|
|
3797
|
-
throw error;
|
|
3798
|
-
}
|
|
3799
|
-
strapi2.log.error("Error checking license:", error);
|
|
3800
|
-
throw new PolicyError("Error verifying license. Please try again.", {
|
|
3801
|
-
policy: "license-check",
|
|
3802
|
-
errCode: "LICENSE_CHECK_ERROR"
|
|
3803
|
-
});
|
|
3804
|
-
}
|
|
3805
|
-
};
|
|
3806
3686
|
var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
|
|
3807
3687
|
const pluginStore = strapi2.store({
|
|
3808
3688
|
type: "plugin",
|
|
@@ -3824,10 +3704,8 @@ var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) =
|
|
|
3824
3704
|
}
|
|
3825
3705
|
return true;
|
|
3826
3706
|
};
|
|
3827
|
-
const licenseCheck = licenseCheck$1;
|
|
3828
3707
|
const compatibilityCheck = compatibilityCheck$1;
|
|
3829
3708
|
var policies$1 = {
|
|
3830
|
-
"license-check": licenseCheck,
|
|
3831
3709
|
"compatibility-check": compatibilityCheck
|
|
3832
3710
|
};
|
|
3833
3711
|
var contentApi$1 = {
|
|
@@ -3839,7 +3717,7 @@ var contentApi$1 = {
|
|
|
3839
3717
|
handler: "auth.login",
|
|
3840
3718
|
config: {
|
|
3841
3719
|
auth: false,
|
|
3842
|
-
policies: [
|
|
3720
|
+
policies: []
|
|
3843
3721
|
}
|
|
3844
3722
|
},
|
|
3845
3723
|
{
|
|
@@ -3848,7 +3726,7 @@ var contentApi$1 = {
|
|
|
3848
3726
|
handler: "auth.sendLink",
|
|
3849
3727
|
config: {
|
|
3850
3728
|
auth: false,
|
|
3851
|
-
policies: [
|
|
3729
|
+
policies: []
|
|
3852
3730
|
}
|
|
3853
3731
|
},
|
|
3854
3732
|
// OTP Routes
|
|
@@ -3858,7 +3736,7 @@ var contentApi$1 = {
|
|
|
3858
3736
|
handler: "otp.send",
|
|
3859
3737
|
config: {
|
|
3860
3738
|
auth: false,
|
|
3861
|
-
policies: [
|
|
3739
|
+
policies: []
|
|
3862
3740
|
}
|
|
3863
3741
|
},
|
|
3864
3742
|
{
|
|
@@ -3867,7 +3745,7 @@ var contentApi$1 = {
|
|
|
3867
3745
|
handler: "otp.verify",
|
|
3868
3746
|
config: {
|
|
3869
3747
|
auth: false,
|
|
3870
|
-
policies: [
|
|
3748
|
+
policies: []
|
|
3871
3749
|
}
|
|
3872
3750
|
},
|
|
3873
3751
|
{
|
|
@@ -3876,7 +3754,7 @@ var contentApi$1 = {
|
|
|
3876
3754
|
handler: "otp.resend",
|
|
3877
3755
|
config: {
|
|
3878
3756
|
auth: false,
|
|
3879
|
-
policies: [
|
|
3757
|
+
policies: []
|
|
3880
3758
|
}
|
|
3881
3759
|
},
|
|
3882
3760
|
// MFA Routes
|
|
@@ -3886,7 +3764,7 @@ var contentApi$1 = {
|
|
|
3886
3764
|
handler: "auth.verifyMFATOTP",
|
|
3887
3765
|
config: {
|
|
3888
3766
|
auth: false,
|
|
3889
|
-
policies: [
|
|
3767
|
+
policies: []
|
|
3890
3768
|
}
|
|
3891
3769
|
},
|
|
3892
3770
|
{
|
|
@@ -3895,7 +3773,7 @@ var contentApi$1 = {
|
|
|
3895
3773
|
handler: "auth.loginWithTOTP",
|
|
3896
3774
|
config: {
|
|
3897
3775
|
auth: false,
|
|
3898
|
-
policies: [
|
|
3776
|
+
policies: []
|
|
3899
3777
|
}
|
|
3900
3778
|
}
|
|
3901
3779
|
]
|
|
@@ -4195,10 +4073,7 @@ var compatibility$1 = {
|
|
|
4195
4073
|
handler: "auth.login",
|
|
4196
4074
|
config: {
|
|
4197
4075
|
auth: false,
|
|
4198
|
-
policies: [
|
|
4199
|
-
"plugin::magic-link.compatibility-check",
|
|
4200
|
-
"plugin::magic-link.license-check"
|
|
4201
|
-
],
|
|
4076
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4202
4077
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4203
4078
|
}
|
|
4204
4079
|
},
|
|
@@ -4209,10 +4084,7 @@ var compatibility$1 = {
|
|
|
4209
4084
|
handler: "auth.sendLink",
|
|
4210
4085
|
config: {
|
|
4211
4086
|
auth: false,
|
|
4212
|
-
policies: [
|
|
4213
|
-
"plugin::magic-link.compatibility-check",
|
|
4214
|
-
"plugin::magic-link.license-check"
|
|
4215
|
-
],
|
|
4087
|
+
policies: ["plugin::magic-link.compatibility-check"],
|
|
4216
4088
|
description: "Compatibility route for strapi-plugin-passwordless"
|
|
4217
4089
|
}
|
|
4218
4090
|
}
|
|
@@ -4266,9 +4138,17 @@ var magicLink$1 = ({ strapi: strapi2 }) => ({
|
|
|
4266
4138
|
});
|
|
4267
4139
|
return pluginStore.get({ key: "advanced" });
|
|
4268
4140
|
},
|
|
4141
|
+
/**
|
|
4142
|
+
* Returns whether the Magic Link plugin is active. In the marketplace
|
|
4143
|
+
* build the plugin is always-on once installed: it cannot be disabled
|
|
4144
|
+
* from the admin panel by mistake (that toggle was removed). The
|
|
4145
|
+
* function is kept as a stable export so any legacy caller continues
|
|
4146
|
+
* to compile.
|
|
4147
|
+
*
|
|
4148
|
+
* @returns {Promise<boolean>} always true
|
|
4149
|
+
*/
|
|
4269
4150
|
async isEnabled() {
|
|
4270
|
-
|
|
4271
|
-
return settings ? settings.enabled !== false : true;
|
|
4151
|
+
return true;
|
|
4272
4152
|
},
|
|
4273
4153
|
async createUser(user) {
|
|
4274
4154
|
const userSettings = await this.userSettings();
|
|
@@ -4854,265 +4734,8 @@ var store$1 = ({ strapi: strapi2 }) => ({
|
|
|
4854
4734
|
return pluginStore.set({ key: "settings", value: settings });
|
|
4855
4735
|
}
|
|
4856
4736
|
});
|
|
4857
|
-
var features$1 = {
|
|
4858
|
-
// FREE/DEMO Features
|
|
4859
|
-
free: {
|
|
4860
|
-
maxTokens: 100,
|
|
4861
|
-
// 100 aktive Tokens gleichzeitig
|
|
4862
|
-
maxSessions: 50,
|
|
4863
|
-
// 50 aktive JWT Sessions
|
|
4864
|
-
maxIPBans: 10,
|
|
4865
|
-
// 10 gebannte IPs
|
|
4866
|
-
features: [
|
|
4867
|
-
"basic-magic-link",
|
|
4868
|
-
// Basis Magic Link Auth
|
|
4869
|
-
"email-templates",
|
|
4870
|
-
// Basis Email Templates
|
|
4871
|
-
"jwt-sessions",
|
|
4872
|
-
// JWT Session Management
|
|
4873
|
-
"ip-tracking",
|
|
4874
|
-
// IP Tracking für Security
|
|
4875
|
-
"rate-limiting",
|
|
4876
|
-
// Basis Rate Limiting
|
|
4877
|
-
"auto-user-creation",
|
|
4878
|
-
// Auto User Creation
|
|
4879
|
-
"token-management",
|
|
4880
|
-
// Token Dashboard
|
|
4881
|
-
"ip-bans"
|
|
4882
|
-
// IP Ban Management
|
|
4883
|
-
]
|
|
4884
|
-
},
|
|
4885
|
-
// PREMIUM Features
|
|
4886
|
-
premium: {
|
|
4887
|
-
maxTokens: 1e3,
|
|
4888
|
-
// 1000 aktive Tokens
|
|
4889
|
-
maxSessions: 500,
|
|
4890
|
-
// 500 aktive Sessions
|
|
4891
|
-
maxIPBans: 100,
|
|
4892
|
-
// 100 gebannte IPs
|
|
4893
|
-
features: [
|
|
4894
|
-
"basic-magic-link",
|
|
4895
|
-
"email-templates",
|
|
4896
|
-
"jwt-sessions",
|
|
4897
|
-
"ip-tracking",
|
|
4898
|
-
"rate-limiting",
|
|
4899
|
-
"auto-user-creation",
|
|
4900
|
-
"token-management",
|
|
4901
|
-
"ip-bans",
|
|
4902
|
-
"otp-email",
|
|
4903
|
-
// Email OTP (6-digit code)
|
|
4904
|
-
"mfa-totp",
|
|
4905
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4906
|
-
"custom-email-templates",
|
|
4907
|
-
// Custom HTML/Text Templates
|
|
4908
|
-
"email-designer-integration",
|
|
4909
|
-
// Email Designer 5 Support
|
|
4910
|
-
"magic-mail-integration",
|
|
4911
|
-
// MagicMail Integration
|
|
4912
|
-
"advanced-rate-limiting",
|
|
4913
|
-
// Custom Rate Limits
|
|
4914
|
-
"session-management",
|
|
4915
|
-
// Erweiterte Session Verwaltung
|
|
4916
|
-
"bulk-operations",
|
|
4917
|
-
// Bulk Token/Session Operations
|
|
4918
|
-
"extended-logging",
|
|
4919
|
-
// Erweiterte Login-Info Logs
|
|
4920
|
-
"custom-redirect-urls"
|
|
4921
|
-
// Custom Redirect URLs per Token
|
|
4922
|
-
]
|
|
4923
|
-
},
|
|
4924
|
-
// ADVANCED Features
|
|
4925
|
-
advanced: {
|
|
4926
|
-
maxTokens: -1,
|
|
4927
|
-
// Unlimited
|
|
4928
|
-
maxSessions: -1,
|
|
4929
|
-
// Unlimited
|
|
4930
|
-
maxIPBans: -1,
|
|
4931
|
-
// Unlimited
|
|
4932
|
-
features: [
|
|
4933
|
-
"basic-magic-link",
|
|
4934
|
-
"email-templates",
|
|
4935
|
-
"jwt-sessions",
|
|
4936
|
-
"ip-tracking",
|
|
4937
|
-
"rate-limiting",
|
|
4938
|
-
"auto-user-creation",
|
|
4939
|
-
"token-management",
|
|
4940
|
-
"ip-bans",
|
|
4941
|
-
"otp-email",
|
|
4942
|
-
"mfa-totp",
|
|
4943
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4944
|
-
"otp-sms",
|
|
4945
|
-
// SMS OTP via Twilio/Vonage
|
|
4946
|
-
"otp-totp",
|
|
4947
|
-
// TOTP (Authenticator Apps)
|
|
4948
|
-
"totp-primary-login",
|
|
4949
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
4950
|
-
"custom-email-templates",
|
|
4951
|
-
"email-designer-integration",
|
|
4952
|
-
"magic-mail-integration",
|
|
4953
|
-
"advanced-rate-limiting",
|
|
4954
|
-
"session-management",
|
|
4955
|
-
"bulk-operations",
|
|
4956
|
-
"extended-logging",
|
|
4957
|
-
"custom-redirect-urls",
|
|
4958
|
-
"webhook-notifications",
|
|
4959
|
-
// Webhooks für Events
|
|
4960
|
-
"api-access-tokens",
|
|
4961
|
-
// API Tokens für Programmatic Access
|
|
4962
|
-
"advanced-analytics",
|
|
4963
|
-
// Analytics Dashboard
|
|
4964
|
-
"custom-token-context",
|
|
4965
|
-
// Custom Context Data in Tokens
|
|
4966
|
-
"geolocation-tracking",
|
|
4967
|
-
// GeoIP Tracking
|
|
4968
|
-
"device-fingerprinting",
|
|
4969
|
-
// Device Fingerprinting
|
|
4970
|
-
"suspicious-login-alerts"
|
|
4971
|
-
// Alerts für verdächtige Logins
|
|
4972
|
-
]
|
|
4973
|
-
},
|
|
4974
|
-
// ENTERPRISE Features
|
|
4975
|
-
enterprise: {
|
|
4976
|
-
maxTokens: -1,
|
|
4977
|
-
maxSessions: -1,
|
|
4978
|
-
maxIPBans: -1,
|
|
4979
|
-
features: [
|
|
4980
|
-
"basic-magic-link",
|
|
4981
|
-
"email-templates",
|
|
4982
|
-
"jwt-sessions",
|
|
4983
|
-
"ip-tracking",
|
|
4984
|
-
"rate-limiting",
|
|
4985
|
-
"auto-user-creation",
|
|
4986
|
-
"token-management",
|
|
4987
|
-
"ip-bans",
|
|
4988
|
-
"otp-email",
|
|
4989
|
-
"mfa-totp",
|
|
4990
|
-
// MFA: Magic Link + TOTP (Szenario 1)
|
|
4991
|
-
"otp-sms",
|
|
4992
|
-
"otp-totp",
|
|
4993
|
-
"totp-primary-login",
|
|
4994
|
-
// Login with Email + TOTP only (Szenario 2)
|
|
4995
|
-
"otp-backup-codes",
|
|
4996
|
-
// Backup Recovery Codes
|
|
4997
|
-
"biometric-support",
|
|
4998
|
-
// WebAuthn/FIDO2
|
|
4999
|
-
"custom-email-templates",
|
|
5000
|
-
"email-designer-integration",
|
|
5001
|
-
"magic-mail-integration",
|
|
5002
|
-
"advanced-rate-limiting",
|
|
5003
|
-
"session-management",
|
|
5004
|
-
"bulk-operations",
|
|
5005
|
-
"extended-logging",
|
|
5006
|
-
"custom-redirect-urls",
|
|
5007
|
-
"webhook-notifications",
|
|
5008
|
-
"api-access-tokens",
|
|
5009
|
-
"advanced-analytics",
|
|
5010
|
-
"custom-token-context",
|
|
5011
|
-
"geolocation-tracking",
|
|
5012
|
-
"device-fingerprinting",
|
|
5013
|
-
"suspicious-login-alerts",
|
|
5014
|
-
"sso-integration",
|
|
5015
|
-
// SSO/SAML Integration
|
|
5016
|
-
"ldap-sync",
|
|
5017
|
-
// LDAP/AD Sync
|
|
5018
|
-
"audit-logs",
|
|
5019
|
-
// Vollständige Audit Logs
|
|
5020
|
-
"compliance-reports",
|
|
5021
|
-
// GDPR/SOC2 Reports
|
|
5022
|
-
"priority-support",
|
|
5023
|
-
// 24/7 Priority Support
|
|
5024
|
-
"custom-security-rules",
|
|
5025
|
-
// Custom Security Rules
|
|
5026
|
-
"multi-tenant",
|
|
5027
|
-
// Multi-Tenant Support
|
|
5028
|
-
"white-label"
|
|
5029
|
-
// White-Label Branding
|
|
5030
|
-
]
|
|
5031
|
-
},
|
|
5032
|
-
/**
|
|
5033
|
-
* Check if a feature is available for given license tier
|
|
5034
|
-
*/
|
|
5035
|
-
hasFeature(licenseData, featureName) {
|
|
5036
|
-
if (!licenseData) {
|
|
5037
|
-
return this.free.features.includes(featureName);
|
|
5038
|
-
}
|
|
5039
|
-
let isEnterprise = false;
|
|
5040
|
-
let isAdvanced = false;
|
|
5041
|
-
let isPremium = false;
|
|
5042
|
-
if (licenseData.tier) {
|
|
5043
|
-
isEnterprise = licenseData.tier === "enterprise";
|
|
5044
|
-
isAdvanced = licenseData.tier === "advanced";
|
|
5045
|
-
isPremium = licenseData.tier === "premium";
|
|
5046
|
-
}
|
|
5047
|
-
if (licenseData.features) {
|
|
5048
|
-
isEnterprise = isEnterprise || licenseData.features.enterprise === true;
|
|
5049
|
-
isAdvanced = isAdvanced || licenseData.features.advanced === true;
|
|
5050
|
-
isPremium = isPremium || licenseData.features.premium === true;
|
|
5051
|
-
}
|
|
5052
|
-
if (licenseData.featureEnterprise === true) {
|
|
5053
|
-
isEnterprise = true;
|
|
5054
|
-
}
|
|
5055
|
-
if (licenseData.featureAdvanced === true) {
|
|
5056
|
-
isAdvanced = true;
|
|
5057
|
-
}
|
|
5058
|
-
if (licenseData.featurePremium === true) {
|
|
5059
|
-
isPremium = true;
|
|
5060
|
-
}
|
|
5061
|
-
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
5062
|
-
return true;
|
|
5063
|
-
}
|
|
5064
|
-
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
5065
|
-
return true;
|
|
5066
|
-
}
|
|
5067
|
-
if (isPremium && this.premium.features.includes(featureName)) {
|
|
5068
|
-
return true;
|
|
5069
|
-
}
|
|
5070
|
-
return this.free.features.includes(featureName);
|
|
5071
|
-
},
|
|
5072
|
-
/**
|
|
5073
|
-
* Get max allowed tokens for license tier
|
|
5074
|
-
*/
|
|
5075
|
-
getMaxTokens(licenseData) {
|
|
5076
|
-
if (!licenseData) return this.free.maxTokens;
|
|
5077
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxTokens;
|
|
5078
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxTokens;
|
|
5079
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxTokens;
|
|
5080
|
-
return this.free.maxTokens;
|
|
5081
|
-
},
|
|
5082
|
-
/**
|
|
5083
|
-
* Get max allowed sessions for license tier
|
|
5084
|
-
*/
|
|
5085
|
-
getMaxSessions(licenseData) {
|
|
5086
|
-
if (!licenseData) return this.free.maxSessions;
|
|
5087
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxSessions;
|
|
5088
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxSessions;
|
|
5089
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxSessions;
|
|
5090
|
-
return this.free.maxSessions;
|
|
5091
|
-
},
|
|
5092
|
-
/**
|
|
5093
|
-
* Get max allowed IP bans for license tier
|
|
5094
|
-
*/
|
|
5095
|
-
getMaxIPBans(licenseData) {
|
|
5096
|
-
if (!licenseData) return this.free.maxIPBans;
|
|
5097
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxIPBans;
|
|
5098
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxIPBans;
|
|
5099
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxIPBans;
|
|
5100
|
-
return this.free.maxIPBans;
|
|
5101
|
-
},
|
|
5102
|
-
/**
|
|
5103
|
-
* Get tier name from license data
|
|
5104
|
-
*/
|
|
5105
|
-
getTierName(licenseData) {
|
|
5106
|
-
if (!licenseData) return "free";
|
|
5107
|
-
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return "enterprise";
|
|
5108
|
-
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return "advanced";
|
|
5109
|
-
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return "premium";
|
|
5110
|
-
return "free";
|
|
5111
|
-
}
|
|
5112
|
-
};
|
|
5113
4737
|
const crypto$1 = require$$0$3;
|
|
5114
4738
|
const os = require$$1$3;
|
|
5115
|
-
const features = features$1;
|
|
5116
4739
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
5117
4740
|
const envTimeout = Number(process.env.MAGIC_LICENSE_TIMEOUT_MS);
|
|
5118
4741
|
const DEFAULT_FETCH_TIMEOUT_MS = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 12e3;
|
|
@@ -5120,7 +4743,7 @@ const FETCH_RETRIES = 1;
|
|
|
5120
4743
|
const FETCH_RETRY_BACKOFF_MS = 750;
|
|
5121
4744
|
async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
5122
4745
|
let lastError;
|
|
5123
|
-
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt
|
|
4746
|
+
for (let attempt = 0; attempt <= FETCH_RETRIES; attempt += 1) {
|
|
5124
4747
|
const controller2 = new AbortController();
|
|
5125
4748
|
const timer = setTimeout(() => controller2.abort(), timeoutMs);
|
|
5126
4749
|
try {
|
|
@@ -5139,15 +4762,22 @@ async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TI
|
|
|
5139
4762
|
throw lastError;
|
|
5140
4763
|
}
|
|
5141
4764
|
var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
4765
|
+
// ======================================================================
|
|
4766
|
+
// Network helpers (used only during admin-side key activation)
|
|
4767
|
+
// ======================================================================
|
|
5142
4768
|
/**
|
|
5143
|
-
*
|
|
5144
|
-
*
|
|
4769
|
+
* License server URL. Hard-coded — kept simple because the only call
|
|
4770
|
+
* site is the admin-side activation form. Not user-configurable.
|
|
4771
|
+
* @returns {string}
|
|
5145
4772
|
*/
|
|
5146
4773
|
getLicenseServerUrl() {
|
|
5147
4774
|
return LICENSE_SERVER_URL;
|
|
5148
4775
|
},
|
|
5149
4776
|
/**
|
|
5150
|
-
*
|
|
4777
|
+
* Hashed device identifier used by the upstream activation server to
|
|
4778
|
+
* de-duplicate seats. Hash of MAC addresses + hostname; the raw values
|
|
4779
|
+
* never leave the host.
|
|
4780
|
+
* @returns {string}
|
|
5151
4781
|
*/
|
|
5152
4782
|
generateDeviceId() {
|
|
5153
4783
|
try {
|
|
@@ -5164,11 +4794,12 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5164
4794
|
return crypto$1.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
|
|
5165
4795
|
} catch (error) {
|
|
5166
4796
|
strapi2.log.error("Error generating device ID:", error);
|
|
5167
|
-
return crypto$1.randomBytes(16).
|
|
4797
|
+
return crypto$1.randomBytes(16).toString("hex");
|
|
5168
4798
|
}
|
|
5169
4799
|
},
|
|
5170
4800
|
/**
|
|
5171
|
-
*
|
|
4801
|
+
* Friendly device name for the activation payload.
|
|
4802
|
+
* @returns {string}
|
|
5172
4803
|
*/
|
|
5173
4804
|
getDeviceName() {
|
|
5174
4805
|
try {
|
|
@@ -5178,7 +4809,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5178
4809
|
}
|
|
5179
4810
|
},
|
|
5180
4811
|
/**
|
|
5181
|
-
*
|
|
4812
|
+
* First non-internal IPv4 address. Best-effort, used in the activation
|
|
4813
|
+
* payload only.
|
|
4814
|
+
* @returns {string}
|
|
5182
4815
|
*/
|
|
5183
4816
|
getIpAddress() {
|
|
5184
4817
|
try {
|
|
@@ -5196,13 +4829,22 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5196
4829
|
}
|
|
5197
4830
|
},
|
|
5198
4831
|
/**
|
|
5199
|
-
*
|
|
4832
|
+
* Server-side user-agent string for the activation payload.
|
|
4833
|
+
* @returns {string}
|
|
5200
4834
|
*/
|
|
5201
4835
|
getUserAgent() {
|
|
5202
4836
|
return `Strapi/${strapi2.config.info.strapi} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
5203
4837
|
},
|
|
4838
|
+
// ======================================================================
|
|
4839
|
+
// License creation / verification (admin activation path only)
|
|
4840
|
+
// ======================================================================
|
|
5204
4841
|
/**
|
|
5205
|
-
* Create a license
|
|
4842
|
+
* Create a license on the upstream activation server. Used by the
|
|
4843
|
+
* admin "auto-create" / "create" flows. Failure to create is non-fatal
|
|
4844
|
+
* — the plugin keeps working, the user just doesn't get a key.
|
|
4845
|
+
*
|
|
4846
|
+
* @param {{ email: string, firstName?: string, lastName?: string }} args
|
|
4847
|
+
* @returns {Promise<object|null>} Upstream license payload, or null on failure
|
|
5206
4848
|
*/
|
|
5207
4849
|
async createLicense({ email: email2, firstName, lastName }) {
|
|
5208
4850
|
try {
|
|
@@ -5213,9 +4855,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5213
4855
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5214
4856
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/create`, {
|
|
5215
4857
|
method: "POST",
|
|
5216
|
-
headers: {
|
|
5217
|
-
"Content-Type": "application/json"
|
|
5218
|
-
},
|
|
4858
|
+
headers: { "Content-Type": "application/json" },
|
|
5219
4859
|
body: JSON.stringify({
|
|
5220
4860
|
email: email2,
|
|
5221
4861
|
firstName,
|
|
@@ -5230,390 +4870,201 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
5230
4870
|
});
|
|
5231
4871
|
const data = await response.json();
|
|
5232
4872
|
if (data.success) {
|
|
5233
|
-
strapi2.log.info("[SUCCESS] License created
|
|
4873
|
+
strapi2.log.info("[SUCCESS] License created:", data.data.licenseKey);
|
|
5234
4874
|
return data.data;
|
|
5235
|
-
} else {
|
|
5236
|
-
strapi2.log.error("[ERROR] License creation failed:", data);
|
|
5237
|
-
return null;
|
|
5238
4875
|
}
|
|
4876
|
+
strapi2.log.warn("[WARNING] License creation rejected by server:", data.message || "unknown");
|
|
4877
|
+
return null;
|
|
5239
4878
|
} catch (error) {
|
|
5240
|
-
strapi2.log.error("[ERROR] Error creating license:", error);
|
|
4879
|
+
strapi2.log.error("[ERROR] Error creating license:", error.message);
|
|
5241
4880
|
return null;
|
|
5242
4881
|
}
|
|
5243
4882
|
},
|
|
5244
4883
|
/**
|
|
5245
|
-
* Verify a license
|
|
4884
|
+
* Verify a license against the upstream activation server.
|
|
4885
|
+
* Called once when the admin saves a license key — never during
|
|
4886
|
+
* runtime feature checks, never on a schedule.
|
|
4887
|
+
*
|
|
4888
|
+
* @param {string} licenseKey
|
|
4889
|
+
* @returns {Promise<{valid: boolean, data: object|null}>}
|
|
5246
4890
|
*/
|
|
5247
|
-
async verifyLicense(licenseKey
|
|
4891
|
+
async verifyLicense(licenseKey) {
|
|
5248
4892
|
try {
|
|
5249
|
-
const controller2 = new AbortController();
|
|
5250
|
-
const timeoutId = setTimeout(() => controller2.abort(), 5e3);
|
|
5251
4893
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5252
4894
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/verify`, {
|
|
5253
4895
|
method: "POST",
|
|
5254
|
-
headers: {
|
|
5255
|
-
"Content-Type": "application/json"
|
|
5256
|
-
},
|
|
4896
|
+
headers: { "Content-Type": "application/json" },
|
|
5257
4897
|
body: JSON.stringify({
|
|
5258
4898
|
licenseKey,
|
|
5259
4899
|
pluginName: "magic-link",
|
|
5260
4900
|
productName: "Magic Link - Passwordless Authentication"
|
|
5261
|
-
})
|
|
5262
|
-
signal: controller2.signal
|
|
4901
|
+
})
|
|
5263
4902
|
});
|
|
5264
|
-
clearTimeout(timeoutId);
|
|
5265
4903
|
const data = await response.json();
|
|
5266
4904
|
if (data.success) {
|
|
5267
|
-
const isValid = data.data.isActive && !data.data.isExpired;
|
|
4905
|
+
const isValid = !!(data.data && data.data.isActive && !data.data.isExpired);
|
|
5268
4906
|
if (isValid) {
|
|
5269
|
-
const pluginStore = strapi2.store({
|
|
5270
|
-
type: "plugin",
|
|
5271
|
-
name: "magic-link"
|
|
5272
|
-
});
|
|
4907
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5273
4908
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5274
4909
|
}
|
|
5275
|
-
return {
|
|
5276
|
-
valid: isValid,
|
|
5277
|
-
data: data.data
|
|
5278
|
-
};
|
|
5279
|
-
} else {
|
|
5280
|
-
strapi2.log.warn(`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5281
|
-
return { valid: false, data: null };
|
|
4910
|
+
return { valid: isValid, data: data.data };
|
|
5282
4911
|
}
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
strapi2.log.info(`[INFO] Grace period active - accepting stored license key`);
|
|
5287
|
-
return { valid: true, data: null, gracePeriod: true };
|
|
5288
|
-
}
|
|
5289
|
-
strapi2.log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
4912
|
+
strapi2.log.warn(
|
|
4913
|
+
`[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`
|
|
4914
|
+
);
|
|
5290
4915
|
return { valid: false, data: null };
|
|
5291
|
-
}
|
|
5292
|
-
},
|
|
5293
|
-
/**
|
|
5294
|
-
* Ping a license (lightweight check)
|
|
5295
|
-
*/
|
|
5296
|
-
async pingLicense(licenseKey) {
|
|
5297
|
-
try {
|
|
5298
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5299
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/ping`, {
|
|
5300
|
-
method: "POST",
|
|
5301
|
-
headers: {
|
|
5302
|
-
"Content-Type": "application/json"
|
|
5303
|
-
},
|
|
5304
|
-
body: JSON.stringify({
|
|
5305
|
-
licenseKey,
|
|
5306
|
-
pluginName: "magic-link",
|
|
5307
|
-
productName: "Magic Link - Passwordless Authentication"
|
|
5308
|
-
})
|
|
5309
|
-
});
|
|
5310
|
-
const data = await response.json();
|
|
5311
|
-
if (data.success) {
|
|
5312
|
-
strapi2.log.debug(`[PING] License ping successful: ${data.data?.isActive ? "ACTIVE" : "INACTIVE"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5313
|
-
return data.data;
|
|
5314
|
-
} else {
|
|
5315
|
-
strapi2.log.debug(`[WARNING] License ping failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5316
|
-
return null;
|
|
5317
|
-
}
|
|
5318
|
-
} catch (error) {
|
|
5319
|
-
strapi2.log.debug(`License ping error: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
|
|
5320
|
-
return null;
|
|
5321
|
-
}
|
|
5322
|
-
},
|
|
5323
|
-
/**
|
|
5324
|
-
* Upgrade a license
|
|
5325
|
-
*/
|
|
5326
|
-
async upgradeLicense(licenseId, features2) {
|
|
5327
|
-
try {
|
|
5328
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5329
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/${licenseId}/upgrade`, {
|
|
5330
|
-
method: "POST",
|
|
5331
|
-
headers: {
|
|
5332
|
-
"Content-Type": "application/json"
|
|
5333
|
-
},
|
|
5334
|
-
body: JSON.stringify(features2)
|
|
5335
|
-
});
|
|
5336
|
-
const data = await response.json();
|
|
5337
|
-
if (data.success) {
|
|
5338
|
-
strapi2.log.info("[SUCCESS] License upgraded:", licenseId);
|
|
5339
|
-
return data.data;
|
|
5340
|
-
} else {
|
|
5341
|
-
strapi2.log.error("[ERROR] License upgrade failed:", licenseId);
|
|
5342
|
-
return null;
|
|
5343
|
-
}
|
|
5344
4916
|
} catch (error) {
|
|
5345
|
-
strapi2.log.
|
|
5346
|
-
|
|
4917
|
+
strapi2.log.warn(
|
|
4918
|
+
`[WARNING] License server unreachable during activation: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...). Activation will not block this install.`
|
|
4919
|
+
);
|
|
4920
|
+
return { valid: false, data: null, networkError: true };
|
|
5347
4921
|
}
|
|
5348
4922
|
},
|
|
5349
4923
|
/**
|
|
5350
|
-
*
|
|
4924
|
+
* Look up a license by key on the upstream server. Used by the admin
|
|
4925
|
+
* License page to display details. Network failures yield null.
|
|
4926
|
+
*
|
|
4927
|
+
* @param {string} licenseKey
|
|
4928
|
+
* @returns {Promise<object|null>}
|
|
5351
4929
|
*/
|
|
5352
4930
|
async getLicenseByKey(licenseKey) {
|
|
5353
4931
|
try {
|
|
5354
4932
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5355
4933
|
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
|
|
5356
4934
|
const data = await response.json();
|
|
5357
|
-
if (data.success)
|
|
5358
|
-
return data.data;
|
|
5359
|
-
}
|
|
4935
|
+
if (data.success) return data.data;
|
|
5360
4936
|
return null;
|
|
5361
4937
|
} catch (error) {
|
|
5362
|
-
strapi2.log.
|
|
4938
|
+
strapi2.log.warn("[WARNING] Could not fetch license by key:", error.message);
|
|
5363
4939
|
return null;
|
|
5364
4940
|
}
|
|
5365
4941
|
},
|
|
5366
4942
|
/**
|
|
5367
|
-
*
|
|
4943
|
+
* Look up licenses by user email. Used by admin "auto-create" to
|
|
4944
|
+
* decide whether to reuse an existing key.
|
|
4945
|
+
*
|
|
4946
|
+
* @param {string} email
|
|
4947
|
+
* @returns {Promise<Array>}
|
|
5368
4948
|
*/
|
|
5369
4949
|
async getLicensesByEmail(email2) {
|
|
5370
4950
|
try {
|
|
5371
4951
|
const licenseServerUrl = this.getLicenseServerUrl();
|
|
5372
|
-
const response = await fetchWithTimeout(
|
|
4952
|
+
const response = await fetchWithTimeout(
|
|
4953
|
+
`${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`
|
|
4954
|
+
);
|
|
5373
4955
|
const data = await response.json();
|
|
5374
|
-
if (data.success)
|
|
5375
|
-
return data.data;
|
|
5376
|
-
}
|
|
4956
|
+
if (data.success) return data.data;
|
|
5377
4957
|
return [];
|
|
5378
4958
|
} catch (error) {
|
|
5379
|
-
strapi2.log.
|
|
4959
|
+
strapi2.log.warn("[WARNING] Could not fetch licenses by email:", error.message);
|
|
5380
4960
|
return [];
|
|
5381
4961
|
}
|
|
5382
4962
|
},
|
|
4963
|
+
// ======================================================================
|
|
4964
|
+
// Storage (plugin store)
|
|
4965
|
+
// ======================================================================
|
|
5383
4966
|
/**
|
|
5384
|
-
*
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/stats/online`);
|
|
5390
|
-
const data = await response.json();
|
|
5391
|
-
if (data.success) {
|
|
5392
|
-
return data.data;
|
|
5393
|
-
}
|
|
5394
|
-
return null;
|
|
5395
|
-
} catch (error) {
|
|
5396
|
-
strapi2.log.error("[ERROR] Error fetching online stats:", error);
|
|
5397
|
-
return null;
|
|
5398
|
-
}
|
|
5399
|
-
},
|
|
5400
|
-
/**
|
|
5401
|
-
* Start periodic pinging for a license
|
|
5402
|
-
*/
|
|
5403
|
-
startPinging(licenseKey, intervalMinutes = 15) {
|
|
5404
|
-
const intervalMs = intervalMinutes * 60 * 1e3;
|
|
5405
|
-
this.pingLicense(licenseKey);
|
|
5406
|
-
const pingInterval = setInterval(async () => {
|
|
5407
|
-
await this.pingLicense(licenseKey);
|
|
5408
|
-
}, intervalMs);
|
|
5409
|
-
return pingInterval;
|
|
5410
|
-
},
|
|
5411
|
-
/**
|
|
5412
|
-
* Initialize license guard
|
|
5413
|
-
* Checks for existing license or prompts for creation
|
|
5414
|
-
*/
|
|
5415
|
-
async initialize() {
|
|
5416
|
-
try {
|
|
5417
|
-
const pluginStore = strapi2.store({
|
|
5418
|
-
type: "plugin",
|
|
5419
|
-
name: "magic-link"
|
|
5420
|
-
});
|
|
5421
|
-
let licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5422
|
-
const lastValidated = await pluginStore.get({ key: "lastValidated" });
|
|
5423
|
-
const now = /* @__PURE__ */ new Date();
|
|
5424
|
-
const gracePeriodHours = 24;
|
|
5425
|
-
let withinGracePeriod = false;
|
|
5426
|
-
if (lastValidated) {
|
|
5427
|
-
const lastValidatedDate = new Date(lastValidated);
|
|
5428
|
-
const hoursSinceValidation = (now - lastValidatedDate) / (1e3 * 60 * 60);
|
|
5429
|
-
withinGracePeriod = hoursSinceValidation < gracePeriodHours;
|
|
5430
|
-
}
|
|
5431
|
-
if (licenseKey) {
|
|
5432
|
-
const verification = await this.verifyLicense(licenseKey, true);
|
|
5433
|
-
if (verification.valid) {
|
|
5434
|
-
const pingInterval = this.startPinging(licenseKey, 15);
|
|
5435
|
-
strapi2.licenseGuard = {
|
|
5436
|
-
licenseKey,
|
|
5437
|
-
pingInterval,
|
|
5438
|
-
data: verification.data
|
|
5439
|
-
};
|
|
5440
|
-
return { valid: true, data: verification.data };
|
|
5441
|
-
} else {
|
|
5442
|
-
strapi2.log.warn("[WARNING] Stored license is invalid or expired");
|
|
5443
|
-
if (!withinGracePeriod) {
|
|
5444
|
-
await pluginStore.delete({ key: "licenseKey" });
|
|
5445
|
-
await pluginStore.delete({ key: "lastValidated" });
|
|
5446
|
-
}
|
|
5447
|
-
}
|
|
5448
|
-
}
|
|
5449
|
-
strapi2.log.warn("[WARNING] No valid license found. Plugin will run in demo mode.");
|
|
5450
|
-
return { valid: false, demo: true };
|
|
5451
|
-
} catch (error) {
|
|
5452
|
-
strapi2.log.error("[ERROR] Error initializing license guard:", error);
|
|
5453
|
-
return { valid: false, error: error.message };
|
|
5454
|
-
}
|
|
5455
|
-
},
|
|
5456
|
-
/**
|
|
5457
|
-
* Store license key after creation
|
|
4967
|
+
* Persist a license key to the plugin store. Used by the admin
|
|
4968
|
+
* activation form.
|
|
4969
|
+
*
|
|
4970
|
+
* @param {string} licenseKey
|
|
4971
|
+
* @returns {Promise<boolean>}
|
|
5458
4972
|
*/
|
|
5459
4973
|
async storeLicenseKey(licenseKey) {
|
|
5460
4974
|
try {
|
|
5461
|
-
strapi2.
|
|
5462
|
-
const pluginStore = strapi2.store({
|
|
5463
|
-
type: "plugin",
|
|
5464
|
-
name: "magic-link"
|
|
5465
|
-
});
|
|
4975
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5466
4976
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
5467
4977
|
await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
strapi2.log.info("[SUCCESS] License key stored and verified successfully");
|
|
5471
|
-
return true;
|
|
5472
|
-
} else {
|
|
5473
|
-
strapi2.log.error("[ERROR] License key storage verification failed");
|
|
5474
|
-
return false;
|
|
5475
|
-
}
|
|
4978
|
+
strapi2.log.info("[SUCCESS] License key stored");
|
|
4979
|
+
return true;
|
|
5476
4980
|
} catch (error) {
|
|
5477
|
-
strapi2.log.error("[ERROR] Error storing license key:", error);
|
|
4981
|
+
strapi2.log.error("[ERROR] Error storing license key:", error.message);
|
|
5478
4982
|
return false;
|
|
5479
4983
|
}
|
|
5480
4984
|
},
|
|
4985
|
+
// ======================================================================
|
|
4986
|
+
// Lifecycle (no-ops in the marketplace build)
|
|
4987
|
+
// ======================================================================
|
|
4988
|
+
/**
|
|
4989
|
+
* Boot-time initializer. In the marketplace build this is a no-op:
|
|
4990
|
+
* we do not auto-verify the stored key and we do not start a periodic
|
|
4991
|
+
* ping. Kept as a stable export so bootstrap.js continues to work.
|
|
4992
|
+
*
|
|
4993
|
+
* @returns {Promise<{valid: boolean}>}
|
|
4994
|
+
*/
|
|
4995
|
+
async initialize() {
|
|
4996
|
+
return { valid: true };
|
|
4997
|
+
},
|
|
5481
4998
|
/**
|
|
5482
|
-
*
|
|
4999
|
+
* Plugin destroy hook. No-op (no interval to clear in the marketplace
|
|
5000
|
+
* build).
|
|
5483
5001
|
*/
|
|
5484
5002
|
cleanup() {
|
|
5485
|
-
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
5486
|
-
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
5487
|
-
strapi2.log.info("[INFO] License pinging stopped");
|
|
5488
|
-
}
|
|
5489
5003
|
},
|
|
5004
|
+
// ======================================================================
|
|
5005
|
+
// Permissive feature stubs (kept for backward compatibility with any
|
|
5006
|
+
// legacy caller — they always permit, never gate)
|
|
5007
|
+
// ======================================================================
|
|
5490
5008
|
/**
|
|
5491
|
-
*
|
|
5492
|
-
*
|
|
5493
|
-
*
|
|
5009
|
+
* Always-permissive feature check. Kept so any legacy `if (await
|
|
5010
|
+
* licenseGuard.hasFeature(...))` site continues to compile and runs
|
|
5011
|
+
* the gated branch.
|
|
5012
|
+
*
|
|
5013
|
+
* @returns {Promise<boolean>} always true
|
|
5494
5014
|
*/
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5499
|
-
if (!licenseKey) {
|
|
5500
|
-
return features.hasFeature(null, featureName);
|
|
5501
|
-
}
|
|
5502
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5503
|
-
return features.hasFeature(licenseData, featureName);
|
|
5504
|
-
} catch (error) {
|
|
5505
|
-
strapi2.log.error("Error checking feature:", error);
|
|
5506
|
-
return false;
|
|
5507
|
-
}
|
|
5015
|
+
// eslint-disable-next-line no-unused-vars
|
|
5016
|
+
async hasFeature(_featureName) {
|
|
5017
|
+
return true;
|
|
5508
5018
|
},
|
|
5509
5019
|
/**
|
|
5510
|
-
*
|
|
5511
|
-
* @returns {number} Max tokens (-1 = unlimited)
|
|
5020
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5512
5021
|
*/
|
|
5513
5022
|
async getMaxTokens() {
|
|
5514
|
-
|
|
5515
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5516
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5517
|
-
if (!licenseKey) {
|
|
5518
|
-
return features.getMaxTokens(null);
|
|
5519
|
-
}
|
|
5520
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5521
|
-
return features.getMaxTokens(licenseData);
|
|
5522
|
-
} catch (error) {
|
|
5523
|
-
strapi2.log.error("Error getting max tokens:", error);
|
|
5524
|
-
return features.free.maxTokens;
|
|
5525
|
-
}
|
|
5023
|
+
return -1;
|
|
5526
5024
|
},
|
|
5527
5025
|
/**
|
|
5528
|
-
*
|
|
5529
|
-
* @returns {number} Max sessions (-1 = unlimited)
|
|
5026
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5530
5027
|
*/
|
|
5531
5028
|
async getMaxSessions() {
|
|
5532
|
-
|
|
5533
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5534
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5535
|
-
if (!licenseKey) {
|
|
5536
|
-
return features.getMaxSessions(null);
|
|
5537
|
-
}
|
|
5538
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5539
|
-
return features.getMaxSessions(licenseData);
|
|
5540
|
-
} catch (error) {
|
|
5541
|
-
strapi2.log.error("Error getting max sessions:", error);
|
|
5542
|
-
return features.free.maxSessions;
|
|
5543
|
-
}
|
|
5029
|
+
return -1;
|
|
5544
5030
|
},
|
|
5545
5031
|
/**
|
|
5546
|
-
*
|
|
5547
|
-
* @returns {number} Max IP bans (-1 = unlimited)
|
|
5032
|
+
* @returns {Promise<number>} -1 (unlimited)
|
|
5548
5033
|
*/
|
|
5549
5034
|
async getMaxIPBans() {
|
|
5550
|
-
|
|
5551
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5552
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5553
|
-
if (!licenseKey) {
|
|
5554
|
-
return features.getMaxIPBans(null);
|
|
5555
|
-
}
|
|
5556
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5557
|
-
return features.getMaxIPBans(licenseData);
|
|
5558
|
-
} catch (error) {
|
|
5559
|
-
strapi2.log.error("Error getting max IP bans:", error);
|
|
5560
|
-
return features.free.maxIPBans;
|
|
5561
|
-
}
|
|
5035
|
+
return -1;
|
|
5562
5036
|
},
|
|
5563
5037
|
/**
|
|
5564
|
-
*
|
|
5565
|
-
* @returns {Array} Available OTP types
|
|
5038
|
+
* @returns {Promise<string[]>} every supported OTP type
|
|
5566
5039
|
*/
|
|
5567
5040
|
async getAvailableOTPTypes() {
|
|
5568
|
-
|
|
5569
|
-
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5570
|
-
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5571
|
-
if (!licenseKey) {
|
|
5572
|
-
return features.getAvailableOTPTypes(null);
|
|
5573
|
-
}
|
|
5574
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5575
|
-
return features.getAvailableOTPTypes(licenseData);
|
|
5576
|
-
} catch (error) {
|
|
5577
|
-
strapi2.log.error("Error getting available OTP types:", error);
|
|
5578
|
-
return [];
|
|
5579
|
-
}
|
|
5041
|
+
return ["email", "totp", "backup-codes"];
|
|
5580
5042
|
},
|
|
5581
5043
|
/**
|
|
5582
|
-
*
|
|
5583
|
-
*
|
|
5044
|
+
* Tier info for UI display. Reports `pro` when a key is stored,
|
|
5045
|
+
* `free` otherwise. Pure information — no behavior depends on this.
|
|
5046
|
+
*
|
|
5047
|
+
* @returns {Promise<{tier: string, features: object, hasKey: boolean}>}
|
|
5584
5048
|
*/
|
|
5585
5049
|
async getLicenseTierInfo() {
|
|
5586
5050
|
try {
|
|
5587
5051
|
const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
|
|
5588
5052
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
5589
|
-
|
|
5590
|
-
return {
|
|
5591
|
-
tier: "free",
|
|
5592
|
-
features: {
|
|
5593
|
-
premium: false,
|
|
5594
|
-
advanced: false,
|
|
5595
|
-
enterprise: false
|
|
5596
|
-
}
|
|
5597
|
-
};
|
|
5598
|
-
}
|
|
5599
|
-
const licenseData = await this.getLicenseByKey(licenseKey);
|
|
5053
|
+
const hasKey = !!licenseKey;
|
|
5600
5054
|
return {
|
|
5601
|
-
tier:
|
|
5055
|
+
tier: hasKey ? "pro" : "free",
|
|
5056
|
+
hasKey,
|
|
5602
5057
|
features: {
|
|
5603
|
-
premium:
|
|
5604
|
-
advanced:
|
|
5605
|
-
enterprise:
|
|
5058
|
+
premium: hasKey,
|
|
5059
|
+
advanced: hasKey,
|
|
5060
|
+
enterprise: hasKey
|
|
5606
5061
|
}
|
|
5607
5062
|
};
|
|
5608
5063
|
} catch (error) {
|
|
5609
|
-
strapi2.log.error("Error getting license tier info:", error);
|
|
5610
5064
|
return {
|
|
5611
5065
|
tier: "free",
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
advanced: false,
|
|
5615
|
-
enterprise: false
|
|
5616
|
-
}
|
|
5066
|
+
hasKey: false,
|
|
5067
|
+
features: { premium: false, advanced: false, enterprise: false }
|
|
5617
5068
|
};
|
|
5618
5069
|
}
|
|
5619
5070
|
}
|