strapi-plugin-magic-link-v5 5.5.7 → 5.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- const licenseStatus = await licenseGuardService.initialize();
374
- if (!licenseStatus.valid && licenseStatus.demo) {
375
- strapi2.log.warn("╔════════════════════════════════════════════════════════════════╗");
376
- strapi2.log.warn("║ [WARNING] MAGIC LINK PLUGIN RUNNING IN DEMO MODE ║");
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.error("[ERROR] Error initializing License Guard:", error);
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.error("[ERROR] Error during License Guard cleanup:", error);
412
+ strapi2.log.debug("[magic-link] license-guard cleanup warning:", error.message);
438
413
  }
439
414
  };
440
415
  var passwordlessCompat$2 = (config2, { strapi: strapi2 }) => {
@@ -1219,9 +1194,7 @@ var auth$1 = {
1219
1194
  return i18n.sendError(ctx, "token.invalid", 400);
1220
1195
  }
1221
1196
  const settings = await magicLink2.settings();
1222
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
1223
- const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
1224
- if (settings.otp_enabled && hasOTPFeature) {
1197
+ if (settings.otp_enabled) {
1225
1198
  await strapi.documents("plugin::magic-link.token").update({
1226
1199
  documentId: token2.documentId,
1227
1200
  data: {
@@ -1688,7 +1661,6 @@ Thanks for joining. Your Magic Link is on the way.`,
1688
1661
  const { email: normalizedEmail, totpCode } = parseBody$1(loginWithTotpSchema, ctx.request.body);
1689
1662
  const magicLink2 = strapi.plugin("magic-link").service("magic-link");
1690
1663
  const otpService = strapi.plugin("magic-link").service("otp");
1691
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
1692
1664
  const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
1693
1665
  const userService = strapi.plugin("users-permissions").service("user");
1694
1666
  const jwtService = strapi.plugin("users-permissions").service("jwt");
@@ -1707,10 +1679,6 @@ Thanks for joining. Your Magic Link is on the way.`,
1707
1679
  if (!settings.totp_as_primary_auth) {
1708
1680
  return ctx.forbidden("TOTP login is not enabled");
1709
1681
  }
1710
- const hasFeature = await licenseGuard2.hasFeature("otp-totp");
1711
- if (!hasFeature) {
1712
- return ctx.forbidden("TOTP login requires Advanced license");
1713
- }
1714
1682
  const GENERIC_INVALID = "Invalid credentials";
1715
1683
  const users = await strapi.documents("plugin::users-permissions.user").findMany({
1716
1684
  filters: { email: normalizedEmail },
@@ -2883,49 +2851,43 @@ var jwt$1 = {
2883
2851
  };
2884
2852
  var license$1 = {
2885
2853
  /**
2886
- * Get current license status
2854
+ * Returns the current license status. Always reports the plugin as
2855
+ * usable; `hasKey` indicates whether the admin has activated a key.
2856
+ *
2857
+ * @route GET /magic-link/license/status
2858
+ * @returns {{ success: true, valid: true, hasKey: boolean, data: object|null }}
2887
2859
  */
2888
2860
  async getStatus(ctx) {
2889
2861
  try {
2890
2862
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
2891
- const pluginStore = strapi.store({
2892
- type: "plugin",
2893
- name: "magic-link"
2894
- });
2863
+ const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
2895
2864
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
2896
2865
  if (!licenseKey) {
2897
2866
  return ctx.send({
2898
- success: false,
2899
- demo: true,
2900
- valid: false,
2901
- message: "No license found. Running in demo mode."
2867
+ success: true,
2868
+ valid: true,
2869
+ // plugin is always usable in the marketplace build
2870
+ hasKey: false,
2871
+ data: null,
2872
+ message: "Plugin is active. License key activation is optional."
2902
2873
  });
2903
2874
  }
2904
- const verification = await licenseGuard2.verifyLicense(licenseKey);
2905
2875
  const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
2906
2876
  return ctx.send({
2907
2877
  success: true,
2908
- valid: verification.valid,
2909
- demo: false,
2878
+ valid: true,
2879
+ hasKey: true,
2910
2880
  data: {
2911
2881
  licenseKey,
2912
2882
  email: license2?.email || null,
2913
2883
  firstName: license2?.firstName || null,
2914
2884
  lastName: license2?.lastName || null,
2915
- isActive: license2?.isActive || false,
2916
- isExpired: license2?.isExpired || false,
2917
- isOnline: license2?.isOnline || false,
2918
- expiresAt: license2?.expiresAt,
2919
- lastPingAt: license2?.lastPingAt,
2920
- deviceName: license2?.deviceName,
2921
- deviceId: license2?.deviceId,
2922
- ipAddress: license2?.ipAddress,
2923
- features: {
2924
- premium: license2?.featurePremium || false,
2925
- advanced: license2?.featureAdvanced || false,
2926
- enterprise: license2?.featureEnterprise || false,
2927
- custom: license2?.featureCustom || false
2928
- },
2885
+ isActive: license2?.isActive ?? true,
2886
+ isExpired: license2?.isExpired ?? false,
2887
+ expiresAt: license2?.expiresAt || null,
2888
+ deviceName: license2?.deviceName || null,
2889
+ deviceId: license2?.deviceId || null,
2890
+ ipAddress: license2?.ipAddress || null,
2929
2891
  maxDevices: license2?.maxDevices || 1,
2930
2892
  currentDevices: license2?.currentDevices || 0
2931
2893
  }
@@ -2936,7 +2898,11 @@ var license$1 = {
2936
2898
  }
2937
2899
  },
2938
2900
  /**
2939
- * Auto-create license with logged-in admin user data
2901
+ * Auto-create a license using the currently logged-in admin user's
2902
+ * data. Best-effort: a remote failure simply leaves the install
2903
+ * un-keyed, the plugin keeps working.
2904
+ *
2905
+ * @route POST /magic-link/license/auto-create
2940
2906
  */
2941
2907
  async autoCreate(ctx) {
2942
2908
  try {
@@ -2951,15 +2917,9 @@ var license$1 = {
2951
2917
  lastName: adminUser.lastname || "User"
2952
2918
  });
2953
2919
  if (!license2) {
2954
- return ctx.badRequest("Failed to create license");
2920
+ return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
2955
2921
  }
2956
2922
  await licenseGuard2.storeLicenseKey(license2.licenseKey);
2957
- const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
2958
- strapi.licenseGuard = {
2959
- licenseKey: license2.licenseKey,
2960
- pingInterval,
2961
- data: license2
2962
- };
2963
2923
  return ctx.send({
2964
2924
  success: true,
2965
2925
  message: "License automatically created and activated",
@@ -2971,26 +2931,22 @@ var license$1 = {
2971
2931
  }
2972
2932
  },
2973
2933
  /**
2974
- * Create and activate a new license
2934
+ * Create and activate a new license with explicit user details.
2935
+ *
2936
+ * @route POST /magic-link/license/create
2975
2937
  */
2976
2938
  async createAndActivate(ctx) {
2977
2939
  try {
2978
- const { email: email2, firstName, lastName } = ctx.request.body;
2940
+ const { email: email2, firstName, lastName } = ctx.request.body || {};
2979
2941
  if (!email2 || !firstName || !lastName) {
2980
2942
  return ctx.badRequest("Email, firstName, and lastName are required");
2981
2943
  }
2982
2944
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
2983
2945
  const license2 = await licenseGuard2.createLicense({ email: email2, firstName, lastName });
2984
2946
  if (!license2) {
2985
- return ctx.badRequest("Failed to create license");
2947
+ return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
2986
2948
  }
2987
2949
  await licenseGuard2.storeLicenseKey(license2.licenseKey);
2988
- const pingInterval = licenseGuard2.startPinging(license2.licenseKey, 15);
2989
- strapi.licenseGuard = {
2990
- licenseKey: license2.licenseKey,
2991
- pingInterval,
2992
- data: license2
2993
- };
2994
2950
  return ctx.send({
2995
2951
  success: true,
2996
2952
  message: "License created and activated successfully",
@@ -3002,39 +2958,40 @@ var license$1 = {
3002
2958
  }
3003
2959
  },
3004
2960
  /**
3005
- * Manually ping the current license
2961
+ * Manual refresh: re-verifies the stored license once and returns the
2962
+ * latest details. Replaces the periodic-ping endpoint that used to run
2963
+ * every 15 minutes — admins can call this on demand from the UI to
2964
+ * pull updated info.
2965
+ *
2966
+ * @route POST /magic-link/license/ping
3006
2967
  */
3007
2968
  async ping(ctx) {
3008
2969
  try {
3009
- const pluginStore = strapi.store({
3010
- type: "plugin",
3011
- name: "magic-link"
3012
- });
2970
+ const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
3013
2971
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
3014
2972
  if (!licenseKey) {
3015
2973
  return ctx.badRequest("No license key found");
3016
2974
  }
3017
2975
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3018
- const pingResult = await licenseGuard2.pingLicense(licenseKey);
3019
- if (!pingResult) {
3020
- return ctx.badRequest("Ping failed");
3021
- }
2976
+ const verification = await licenseGuard2.verifyLicense(licenseKey);
3022
2977
  return ctx.send({
3023
2978
  success: true,
3024
- message: "License pinged successfully",
3025
- data: pingResult
2979
+ message: verification.valid ? "License refreshed successfully" : "License key could not be verified, but the plugin keeps working.",
2980
+ data: verification.data
3026
2981
  });
3027
2982
  } catch (error) {
3028
- strapi.log.error("Error pinging license:", error);
3029
- return ctx.badRequest("Error pinging license");
2983
+ strapi.log.error("Error refreshing license:", error);
2984
+ return ctx.badRequest("Error refreshing license");
3030
2985
  }
3031
2986
  },
3032
2987
  /**
3033
- * Store and validate an existing license key
3034
- */
2988
+ * Store and validate an existing license key.
2989
+ *
2990
+ * @route POST /magic-link/license/store-key
2991
+ */
3035
2992
  async storeKey(ctx) {
3036
2993
  try {
3037
- const { licenseKey, email: email2 } = ctx.request.body;
2994
+ const { licenseKey, email: email2 } = ctx.request.body || {};
3038
2995
  if (!licenseKey || !licenseKey.trim()) {
3039
2996
  return ctx.badRequest("License key is required");
3040
2997
  }
@@ -3046,26 +3003,35 @@ var license$1 = {
3046
3003
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3047
3004
  const verification = await licenseGuard2.verifyLicense(trimmedKey);
3048
3005
  if (!verification.valid) {
3049
- strapi.log.warn(`[WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
3006
+ if (verification.networkError) {
3007
+ strapi.log.warn(
3008
+ `[WARNING] License server unreachable during activation. Storing key anyway: ${trimmedKey.substring(0, 8)}...`
3009
+ );
3010
+ await licenseGuard2.storeLicenseKey(trimmedKey);
3011
+ return ctx.send({
3012
+ success: true,
3013
+ message: "License key stored (license server was unreachable; will retry on the next manual refresh).",
3014
+ data: { licenseKey: trimmedKey, email: trimmedEmail }
3015
+ });
3016
+ }
3017
+ strapi.log.warn(`[WARNING] Invalid license key: ${trimmedKey.substring(0, 8)}...`);
3050
3018
  return ctx.badRequest("Invalid or expired license key");
3051
3019
  }
3052
3020
  const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
3053
3021
  if (!license2) {
3054
- strapi.log.warn(`[WARNING] License not found in database: ${trimmedKey.substring(0, 8)}...`);
3022
+ strapi.log.warn(`[WARNING] License not found: ${trimmedKey.substring(0, 8)}...`);
3055
3023
  return ctx.badRequest("License not found");
3056
3024
  }
3057
- if (license2.email.toLowerCase() !== trimmedEmail) {
3058
- strapi.log.warn(`[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (Attempted: ${trimmedEmail})`);
3025
+ if (license2.email && license2.email.toLowerCase() !== trimmedEmail) {
3026
+ strapi.log.warn(
3027
+ `[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (attempted: ${trimmedEmail})`
3028
+ );
3059
3029
  return ctx.badRequest("Email address does not match this license key");
3060
3030
  }
3061
3031
  await licenseGuard2.storeLicenseKey(trimmedKey);
3062
- const pingInterval = licenseGuard2.startPinging(trimmedKey, 15);
3063
- strapi.licenseGuard = {
3064
- licenseKey: trimmedKey,
3065
- pingInterval,
3066
- data: verification.data
3067
- };
3068
- strapi.log.info(`[SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
3032
+ strapi.log.info(
3033
+ `[SUCCESS] License key validated and stored: ${trimmedKey.substring(0, 8)}... (email: ${trimmedEmail})`
3034
+ );
3069
3035
  return ctx.send({
3070
3036
  success: true,
3071
3037
  message: "License key validated and activated successfully",
@@ -3217,11 +3183,6 @@ var otp$3 = {
3217
3183
  if (!otpSettings.enabled) {
3218
3184
  return ctx.badRequest("OTP is not enabled");
3219
3185
  }
3220
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3221
- const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
3222
- if (!hasOTPFeature) {
3223
- return ctx.forbidden("OTP feature requires Premium license or higher");
3224
- }
3225
3186
  const rlFail = await enforceOtpRateLimits(ctx, normalizedEmail, "send");
3226
3187
  if (rlFail) return ctx.tooManyRequests(rlFail.message);
3227
3188
  const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
@@ -3513,11 +3474,6 @@ var otp$3 = {
3513
3474
  */
3514
3475
  async setupTOTP(ctx) {
3515
3476
  try {
3516
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3517
- const hasFeature = await licenseGuard2.hasFeature("otp-totp");
3518
- if (!hasFeature) {
3519
- return ctx.forbidden("TOTP feature requires Advanced or Enterprise license");
3520
- }
3521
3477
  const { email: email2, id: userId } = ctx.state.user;
3522
3478
  const otpService = strapi.plugin("magic-link").service("otp");
3523
3479
  const setupData = await otpService.setupTOTP(userId, email2);
@@ -3587,11 +3543,6 @@ var otp$3 = {
3587
3543
  */
3588
3544
  async generateBackupCodes(ctx) {
3589
3545
  try {
3590
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3591
- const hasFeature = await licenseGuard2.hasFeature("otp-backup-codes");
3592
- if (!hasFeature) {
3593
- return ctx.forbidden("Backup codes feature requires Enterprise license");
3594
- }
3595
3546
  const { id: userId } = ctx.state.user;
3596
3547
  const otpService = strapi.plugin("magic-link").service("otp");
3597
3548
  const backupCodes = await otpService.generateBackupCodes(userId);
@@ -3761,61 +3712,6 @@ const passwordlessCompat = passwordlessCompat$2;
3761
3712
  var middlewares$1 = {
3762
3713
  "passwordless-compat": passwordlessCompat
3763
3714
  };
3764
- const { PolicyError } = require$$1__default$1.default.errors;
3765
- var licenseCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
3766
- try {
3767
- const licenseGuard2 = strapi2.plugin("magic-link").service("license-guard");
3768
- const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
3769
- const licenseKey = await pluginStore.get({ key: "licenseKey" });
3770
- if (!licenseKey) {
3771
- strapi2.log.warn("[ACCESS-DENIED] No license key found");
3772
- throw new PolicyError("No license found. Please activate the plugin first.", {
3773
- policy: "license-check",
3774
- errCode: "LICENSE_MISSING"
3775
- });
3776
- }
3777
- const verification = await licenseGuard2.verifyLicense(licenseKey, true);
3778
- if (!verification.valid) {
3779
- strapi2.log.warn("[ACCESS-DENIED] Invalid license");
3780
- throw new PolicyError("Invalid or expired license.", {
3781
- policy: "license-check",
3782
- errCode: "LICENSE_INVALID"
3783
- });
3784
- }
3785
- const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
3786
- if (!license2) {
3787
- strapi2.log.warn("[ACCESS-DENIED] License not found in database");
3788
- throw new PolicyError("License not found. Please contact support.", {
3789
- policy: "license-check",
3790
- errCode: "LICENSE_NOT_FOUND"
3791
- });
3792
- }
3793
- if (!license2.isActive) {
3794
- strapi2.log.warn("[ACCESS-DENIED] License is inactive");
3795
- throw new PolicyError("License is inactive. Please activate your license.", {
3796
- policy: "license-check",
3797
- errCode: "LICENSE_INACTIVE"
3798
- });
3799
- }
3800
- if (license2.isExpired) {
3801
- strapi2.log.warn("[ACCESS-DENIED] License has expired");
3802
- throw new PolicyError("License has expired. Please renew your license.", {
3803
- policy: "license-check",
3804
- errCode: "LICENSE_EXPIRED"
3805
- });
3806
- }
3807
- return true;
3808
- } catch (error) {
3809
- if (error instanceof PolicyError) {
3810
- throw error;
3811
- }
3812
- strapi2.log.error("Error checking license:", error);
3813
- throw new PolicyError("Error verifying license. Please try again.", {
3814
- policy: "license-check",
3815
- errCode: "LICENSE_CHECK_ERROR"
3816
- });
3817
- }
3818
- };
3819
3715
  var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
3820
3716
  const pluginStore = strapi2.store({
3821
3717
  type: "plugin",
@@ -3837,10 +3733,8 @@ var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) =
3837
3733
  }
3838
3734
  return true;
3839
3735
  };
3840
- const licenseCheck = licenseCheck$1;
3841
3736
  const compatibilityCheck = compatibilityCheck$1;
3842
3737
  var policies$1 = {
3843
- "license-check": licenseCheck,
3844
3738
  "compatibility-check": compatibilityCheck
3845
3739
  };
3846
3740
  var contentApi$1 = {
@@ -3852,7 +3746,7 @@ var contentApi$1 = {
3852
3746
  handler: "auth.login",
3853
3747
  config: {
3854
3748
  auth: false,
3855
- policies: ["plugin::magic-link.license-check"]
3749
+ policies: []
3856
3750
  }
3857
3751
  },
3858
3752
  {
@@ -3861,7 +3755,7 @@ var contentApi$1 = {
3861
3755
  handler: "auth.sendLink",
3862
3756
  config: {
3863
3757
  auth: false,
3864
- policies: ["plugin::magic-link.license-check"]
3758
+ policies: []
3865
3759
  }
3866
3760
  },
3867
3761
  // OTP Routes
@@ -3871,7 +3765,7 @@ var contentApi$1 = {
3871
3765
  handler: "otp.send",
3872
3766
  config: {
3873
3767
  auth: false,
3874
- policies: ["plugin::magic-link.license-check"]
3768
+ policies: []
3875
3769
  }
3876
3770
  },
3877
3771
  {
@@ -3880,7 +3774,7 @@ var contentApi$1 = {
3880
3774
  handler: "otp.verify",
3881
3775
  config: {
3882
3776
  auth: false,
3883
- policies: ["plugin::magic-link.license-check"]
3777
+ policies: []
3884
3778
  }
3885
3779
  },
3886
3780
  {
@@ -3889,7 +3783,7 @@ var contentApi$1 = {
3889
3783
  handler: "otp.resend",
3890
3784
  config: {
3891
3785
  auth: false,
3892
- policies: ["plugin::magic-link.license-check"]
3786
+ policies: []
3893
3787
  }
3894
3788
  },
3895
3789
  // MFA Routes
@@ -3899,7 +3793,7 @@ var contentApi$1 = {
3899
3793
  handler: "auth.verifyMFATOTP",
3900
3794
  config: {
3901
3795
  auth: false,
3902
- policies: ["plugin::magic-link.license-check"]
3796
+ policies: []
3903
3797
  }
3904
3798
  },
3905
3799
  {
@@ -3908,7 +3802,7 @@ var contentApi$1 = {
3908
3802
  handler: "auth.loginWithTOTP",
3909
3803
  config: {
3910
3804
  auth: false,
3911
- policies: ["plugin::magic-link.license-check"]
3805
+ policies: []
3912
3806
  }
3913
3807
  }
3914
3808
  ]
@@ -4208,10 +4102,7 @@ var compatibility$1 = {
4208
4102
  handler: "auth.login",
4209
4103
  config: {
4210
4104
  auth: false,
4211
- policies: [
4212
- "plugin::magic-link.compatibility-check",
4213
- "plugin::magic-link.license-check"
4214
- ],
4105
+ policies: ["plugin::magic-link.compatibility-check"],
4215
4106
  description: "Compatibility route for strapi-plugin-passwordless"
4216
4107
  }
4217
4108
  },
@@ -4222,10 +4113,7 @@ var compatibility$1 = {
4222
4113
  handler: "auth.sendLink",
4223
4114
  config: {
4224
4115
  auth: false,
4225
- policies: [
4226
- "plugin::magic-link.compatibility-check",
4227
- "plugin::magic-link.license-check"
4228
- ],
4116
+ policies: ["plugin::magic-link.compatibility-check"],
4229
4117
  description: "Compatibility route for strapi-plugin-passwordless"
4230
4118
  }
4231
4119
  }
@@ -4867,265 +4755,8 @@ var store$1 = ({ strapi: strapi2 }) => ({
4867
4755
  return pluginStore.set({ key: "settings", value: settings });
4868
4756
  }
4869
4757
  });
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
4758
  const crypto$1 = require$$0__default.default;
5127
4759
  const os = require$$1__default$3.default;
5128
- const features = features$1;
5129
4760
  const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
5130
4761
  const envTimeout = Number(process.env.MAGIC_LICENSE_TIMEOUT_MS);
5131
4762
  const DEFAULT_FETCH_TIMEOUT_MS = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 12e3;
@@ -5133,7 +4764,7 @@ const FETCH_RETRIES = 1;
5133
4764
  const FETCH_RETRY_BACKOFF_MS = 750;
5134
4765
  async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
5135
4766
  let lastError;
5136
- for (let attempt = 0; attempt <= FETCH_RETRIES; attempt++) {
4767
+ for (let attempt = 0; attempt <= FETCH_RETRIES; attempt += 1) {
5137
4768
  const controller2 = new AbortController();
5138
4769
  const timer = setTimeout(() => controller2.abort(), timeoutMs);
5139
4770
  try {
@@ -5152,15 +4783,22 @@ async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TI
5152
4783
  throw lastError;
5153
4784
  }
5154
4785
  var licenseGuard$1 = ({ strapi: strapi2 }) => ({
4786
+ // ======================================================================
4787
+ // Network helpers (used only during admin-side key activation)
4788
+ // ======================================================================
5155
4789
  /**
5156
- * Get license server URL (hardcoded and immutable for security)
5157
- * @returns {string} The fixed license server URL
4790
+ * License server URL. Hard-coded — kept simple because the only call
4791
+ * site is the admin-side activation form. Not user-configurable.
4792
+ * @returns {string}
5158
4793
  */
5159
4794
  getLicenseServerUrl() {
5160
4795
  return LICENSE_SERVER_URL;
5161
4796
  },
5162
4797
  /**
5163
- * Generate a unique device ID based on machine identifiers
4798
+ * Hashed device identifier used by the upstream activation server to
4799
+ * de-duplicate seats. Hash of MAC addresses + hostname; the raw values
4800
+ * never leave the host.
4801
+ * @returns {string}
5164
4802
  */
5165
4803
  generateDeviceId() {
5166
4804
  try {
@@ -5177,11 +4815,12 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5177
4815
  return crypto$1.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
5178
4816
  } catch (error) {
5179
4817
  strapi2.log.error("Error generating device ID:", error);
5180
- return crypto$1.randomBytes(16).digest("hex");
4818
+ return crypto$1.randomBytes(16).toString("hex");
5181
4819
  }
5182
4820
  },
5183
4821
  /**
5184
- * Get device name
4822
+ * Friendly device name for the activation payload.
4823
+ * @returns {string}
5185
4824
  */
5186
4825
  getDeviceName() {
5187
4826
  try {
@@ -5191,7 +4830,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5191
4830
  }
5192
4831
  },
5193
4832
  /**
5194
- * Get server IP address
4833
+ * First non-internal IPv4 address. Best-effort, used in the activation
4834
+ * payload only.
4835
+ * @returns {string}
5195
4836
  */
5196
4837
  getIpAddress() {
5197
4838
  try {
@@ -5209,13 +4850,22 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5209
4850
  }
5210
4851
  },
5211
4852
  /**
5212
- * Get user agent (server context)
4853
+ * Server-side user-agent string for the activation payload.
4854
+ * @returns {string}
5213
4855
  */
5214
4856
  getUserAgent() {
5215
4857
  return `Strapi/${strapi2.config.info.strapi} Node/${process.version} ${os.platform()}/${os.release()}`;
5216
4858
  },
4859
+ // ======================================================================
4860
+ // License creation / verification (admin activation path only)
4861
+ // ======================================================================
5217
4862
  /**
5218
- * Create a license
4863
+ * Create a license on the upstream activation server. Used by the
4864
+ * admin "auto-create" / "create" flows. Failure to create is non-fatal
4865
+ * — the plugin keeps working, the user just doesn't get a key.
4866
+ *
4867
+ * @param {{ email: string, firstName?: string, lastName?: string }} args
4868
+ * @returns {Promise<object|null>} Upstream license payload, or null on failure
5219
4869
  */
5220
4870
  async createLicense({ email: email2, firstName, lastName }) {
5221
4871
  try {
@@ -5226,9 +4876,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5226
4876
  const licenseServerUrl = this.getLicenseServerUrl();
5227
4877
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/create`, {
5228
4878
  method: "POST",
5229
- headers: {
5230
- "Content-Type": "application/json"
5231
- },
4879
+ headers: { "Content-Type": "application/json" },
5232
4880
  body: JSON.stringify({
5233
4881
  email: email2,
5234
4882
  firstName,
@@ -5243,390 +4891,201 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5243
4891
  });
5244
4892
  const data = await response.json();
5245
4893
  if (data.success) {
5246
- strapi2.log.info("[SUCCESS] License created successfully:", data.data.licenseKey);
4894
+ strapi2.log.info("[SUCCESS] License created:", data.data.licenseKey);
5247
4895
  return data.data;
5248
- } else {
5249
- strapi2.log.error("[ERROR] License creation failed:", data);
5250
- return null;
5251
4896
  }
4897
+ strapi2.log.warn("[WARNING] License creation rejected by server:", data.message || "unknown");
4898
+ return null;
5252
4899
  } catch (error) {
5253
- strapi2.log.error("[ERROR] Error creating license:", error);
4900
+ strapi2.log.error("[ERROR] Error creating license:", error.message);
5254
4901
  return null;
5255
4902
  }
5256
4903
  },
5257
4904
  /**
5258
- * Verify a license (with grace period support)
4905
+ * Verify a license against the upstream activation server.
4906
+ * Called once when the admin saves a license key — never during
4907
+ * runtime feature checks, never on a schedule.
4908
+ *
4909
+ * @param {string} licenseKey
4910
+ * @returns {Promise<{valid: boolean, data: object|null}>}
5259
4911
  */
5260
- async verifyLicense(licenseKey, allowGracePeriod = false) {
4912
+ async verifyLicense(licenseKey) {
5261
4913
  try {
5262
- const controller2 = new AbortController();
5263
- const timeoutId = setTimeout(() => controller2.abort(), 5e3);
5264
4914
  const licenseServerUrl = this.getLicenseServerUrl();
5265
4915
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/verify`, {
5266
4916
  method: "POST",
5267
- headers: {
5268
- "Content-Type": "application/json"
5269
- },
4917
+ headers: { "Content-Type": "application/json" },
5270
4918
  body: JSON.stringify({
5271
4919
  licenseKey,
5272
4920
  pluginName: "magic-link",
5273
4921
  productName: "Magic Link - Passwordless Authentication"
5274
- }),
5275
- signal: controller2.signal
4922
+ })
5276
4923
  });
5277
- clearTimeout(timeoutId);
5278
4924
  const data = await response.json();
5279
4925
  if (data.success) {
5280
- const isValid = data.data.isActive && !data.data.isExpired;
4926
+ const isValid = !!(data.data && data.data.isActive && !data.data.isExpired);
5281
4927
  if (isValid) {
5282
- const pluginStore = strapi2.store({
5283
- type: "plugin",
5284
- name: "magic-link"
5285
- });
4928
+ const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5286
4929
  await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
5287
4930
  }
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 };
5295
- }
5296
- } catch (error) {
5297
- if (allowGracePeriod) {
5298
- strapi2.log.warn(`[WARNING] Cannot verify license online: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
5299
- strapi2.log.info(`[INFO] Grace period active - accepting stored license key`);
5300
- return { valid: true, data: null, gracePeriod: true };
4931
+ return { valid: isValid, data: data.data };
5301
4932
  }
5302
- strapi2.log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
4933
+ strapi2.log.warn(
4934
+ `[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`
4935
+ );
5303
4936
  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
4937
  } 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
- } catch (error) {
5358
- strapi2.log.error("[ERROR] Error upgrading license:", error);
5359
- return null;
4938
+ strapi2.log.warn(
4939
+ `[WARNING] License server unreachable during activation: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...). Activation will not block this install.`
4940
+ );
4941
+ return { valid: false, data: null, networkError: true };
5360
4942
  }
5361
4943
  },
5362
4944
  /**
5363
- * Get license by key
4945
+ * Look up a license by key on the upstream server. Used by the admin
4946
+ * License page to display details. Network failures yield null.
4947
+ *
4948
+ * @param {string} licenseKey
4949
+ * @returns {Promise<object|null>}
5364
4950
  */
5365
4951
  async getLicenseByKey(licenseKey) {
5366
4952
  try {
5367
4953
  const licenseServerUrl = this.getLicenseServerUrl();
5368
4954
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
5369
4955
  const data = await response.json();
5370
- if (data.success) {
5371
- return data.data;
5372
- }
4956
+ if (data.success) return data.data;
5373
4957
  return null;
5374
4958
  } catch (error) {
5375
- strapi2.log.error("[ERROR] Error fetching license by key:", error);
4959
+ strapi2.log.warn("[WARNING] Could not fetch license by key:", error.message);
5376
4960
  return null;
5377
4961
  }
5378
4962
  },
5379
4963
  /**
5380
- * Get licenses by email
4964
+ * Look up licenses by user email. Used by admin "auto-create" to
4965
+ * decide whether to reuse an existing key.
4966
+ *
4967
+ * @param {string} email
4968
+ * @returns {Promise<Array>}
5381
4969
  */
5382
4970
  async getLicensesByEmail(email2) {
5383
4971
  try {
5384
4972
  const licenseServerUrl = this.getLicenseServerUrl();
5385
- const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`);
4973
+ const response = await fetchWithTimeout(
4974
+ `${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`
4975
+ );
5386
4976
  const data = await response.json();
5387
- if (data.success) {
5388
- return data.data;
5389
- }
4977
+ if (data.success) return data.data;
5390
4978
  return [];
5391
4979
  } catch (error) {
5392
- strapi2.log.error("[ERROR] Error fetching licenses by email:", error);
4980
+ strapi2.log.warn("[WARNING] Could not fetch licenses by email:", error.message);
5393
4981
  return [];
5394
4982
  }
5395
4983
  },
4984
+ // ======================================================================
4985
+ // Storage (plugin store)
4986
+ // ======================================================================
5396
4987
  /**
5397
- * Get online statistics
5398
- */
5399
- async getOnlineStats() {
5400
- try {
5401
- const licenseServerUrl = this.getLicenseServerUrl();
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
4988
+ * Persist a license key to the plugin store. Used by the admin
4989
+ * activation form.
4990
+ *
4991
+ * @param {string} licenseKey
4992
+ * @returns {Promise<boolean>}
5471
4993
  */
5472
4994
  async storeLicenseKey(licenseKey) {
5473
4995
  try {
5474
- strapi2.log.info(`[LICENSE] Storing license key: ${licenseKey?.substring(0, 8)}...`);
5475
- const pluginStore = strapi2.store({
5476
- type: "plugin",
5477
- name: "magic-link"
5478
- });
4996
+ const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5479
4997
  await pluginStore.set({ key: "licenseKey", value: licenseKey });
5480
4998
  await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
5481
- const stored = await pluginStore.get({ key: "licenseKey" });
5482
- if (stored === licenseKey) {
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
- }
4999
+ strapi2.log.info("[SUCCESS] License key stored");
5000
+ return true;
5489
5001
  } catch (error) {
5490
- strapi2.log.error("[ERROR] Error storing license key:", error);
5002
+ strapi2.log.error("[ERROR] Error storing license key:", error.message);
5491
5003
  return false;
5492
5004
  }
5493
5005
  },
5006
+ // ======================================================================
5007
+ // Lifecycle (no-ops in the marketplace build)
5008
+ // ======================================================================
5009
+ /**
5010
+ * Boot-time initializer. In the marketplace build this is a no-op:
5011
+ * we do not auto-verify the stored key and we do not start a periodic
5012
+ * ping. Kept as a stable export so bootstrap.js continues to work.
5013
+ *
5014
+ * @returns {Promise<{valid: boolean}>}
5015
+ */
5016
+ async initialize() {
5017
+ return { valid: true };
5018
+ },
5494
5019
  /**
5495
- * Cleanup on plugin destroy
5020
+ * Plugin destroy hook. No-op (no interval to clear in the marketplace
5021
+ * build).
5496
5022
  */
5497
5023
  cleanup() {
5498
- if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
5499
- clearInterval(strapi2.licenseGuard.pingInterval);
5500
- strapi2.log.info("[INFO] License pinging stopped");
5501
- }
5502
5024
  },
5025
+ // ======================================================================
5026
+ // Permissive feature stubs (kept for backward compatibility with any
5027
+ // legacy caller — they always permit, never gate)
5028
+ // ======================================================================
5503
5029
  /**
5504
- * Check if a feature is available
5505
- * @param {string} featureName - Feature name to check
5506
- * @returns {boolean}
5030
+ * Always-permissive feature check. Kept so any legacy `if (await
5031
+ * licenseGuard.hasFeature(...))` site continues to compile and runs
5032
+ * the gated branch.
5033
+ *
5034
+ * @returns {Promise<boolean>} always true
5507
5035
  */
5508
- async hasFeature(featureName) {
5509
- try {
5510
- const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
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
- }
5036
+ // eslint-disable-next-line no-unused-vars
5037
+ async hasFeature(_featureName) {
5038
+ return true;
5521
5039
  },
5522
5040
  /**
5523
- * Get max allowed tokens
5524
- * @returns {number} Max tokens (-1 = unlimited)
5041
+ * @returns {Promise<number>} -1 (unlimited)
5525
5042
  */
5526
5043
  async getMaxTokens() {
5527
- try {
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
- }
5044
+ return -1;
5539
5045
  },
5540
5046
  /**
5541
- * Get max allowed sessions
5542
- * @returns {number} Max sessions (-1 = unlimited)
5047
+ * @returns {Promise<number>} -1 (unlimited)
5543
5048
  */
5544
5049
  async getMaxSessions() {
5545
- try {
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
- }
5050
+ return -1;
5557
5051
  },
5558
5052
  /**
5559
- * Get max allowed IP bans
5560
- * @returns {number} Max IP bans (-1 = unlimited)
5053
+ * @returns {Promise<number>} -1 (unlimited)
5561
5054
  */
5562
5055
  async getMaxIPBans() {
5563
- try {
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
- }
5056
+ return -1;
5575
5057
  },
5576
5058
  /**
5577
- * Get available OTP types
5578
- * @returns {Array} Available OTP types
5059
+ * @returns {Promise<string[]>} every supported OTP type
5579
5060
  */
5580
5061
  async getAvailableOTPTypes() {
5581
- try {
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
- }
5062
+ return ["email", "totp", "backup-codes"];
5593
5063
  },
5594
5064
  /**
5595
- * Get license tier info
5596
- * @returns {Object} License tier information
5065
+ * Tier info for UI display. Reports `pro` when a key is stored,
5066
+ * `free` otherwise. Pure information — no behavior depends on this.
5067
+ *
5068
+ * @returns {Promise<{tier: string, features: object, hasKey: boolean}>}
5597
5069
  */
5598
5070
  async getLicenseTierInfo() {
5599
5071
  try {
5600
5072
  const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5601
5073
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
5602
- if (!licenseKey) {
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);
5074
+ const hasKey = !!licenseKey;
5613
5075
  return {
5614
- tier: licenseData?.tier || "free",
5076
+ tier: hasKey ? "pro" : "free",
5077
+ hasKey,
5615
5078
  features: {
5616
- premium: licenseData?.featurePremium || licenseData?.features?.premium || false,
5617
- advanced: licenseData?.featureAdvanced || licenseData?.features?.advanced || false,
5618
- enterprise: licenseData?.featureEnterprise || licenseData?.features?.enterprise || false
5079
+ premium: hasKey,
5080
+ advanced: hasKey,
5081
+ enterprise: hasKey
5619
5082
  }
5620
5083
  };
5621
5084
  } catch (error) {
5622
- strapi2.log.error("Error getting license tier info:", error);
5623
5085
  return {
5624
5086
  tier: "free",
5625
- features: {
5626
- premium: false,
5627
- advanced: false,
5628
- enterprise: false
5629
- }
5087
+ hasKey: false,
5088
+ features: { premium: false, advanced: false, enterprise: false }
5630
5089
  };
5631
5090
  }
5632
5091
  }