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.
@@ -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
- const licenseStatus = await licenseGuardService.initialize();
361
- if (!licenseStatus.valid && licenseStatus.demo) {
362
- strapi2.log.warn("╔════════════════════════════════════════════════════════════════╗");
363
- strapi2.log.warn("║ [WARNING] MAGIC LINK PLUGIN RUNNING IN DEMO MODE ║");
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.error("[ERROR] Error initializing License Guard:", error);
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.error("[ERROR] Error during License Guard cleanup:", error);
399
+ strapi2.log.debug("[magic-link] license-guard cleanup warning:", error.message);
425
400
  }
426
401
  };
427
402
  var passwordlessCompat$2 = (config2, { strapi: strapi2 }) => {
@@ -1206,9 +1181,7 @@ var auth$1 = {
1206
1181
  return i18n.sendError(ctx, "token.invalid", 400);
1207
1182
  }
1208
1183
  const settings = await magicLink2.settings();
1209
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
1210
- const hasOTPFeature = await licenseGuard2.hasFeature("otp-email");
1211
- if (settings.otp_enabled && hasOTPFeature) {
1184
+ if (settings.otp_enabled) {
1212
1185
  await strapi.documents("plugin::magic-link.token").update({
1213
1186
  documentId: token2.documentId,
1214
1187
  data: {
@@ -1675,7 +1648,6 @@ Thanks for joining. Your Magic Link is on the way.`,
1675
1648
  const { email: normalizedEmail, totpCode } = parseBody$1(loginWithTotpSchema, ctx.request.body);
1676
1649
  const magicLink2 = strapi.plugin("magic-link").service("magic-link");
1677
1650
  const otpService = strapi.plugin("magic-link").service("otp");
1678
- const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
1679
1651
  const rateLimiter2 = strapi.plugin("magic-link").service("rate-limiter");
1680
1652
  const userService = strapi.plugin("users-permissions").service("user");
1681
1653
  const jwtService = strapi.plugin("users-permissions").service("jwt");
@@ -1694,10 +1666,6 @@ Thanks for joining. Your Magic Link is on the way.`,
1694
1666
  if (!settings.totp_as_primary_auth) {
1695
1667
  return ctx.forbidden("TOTP login is not enabled");
1696
1668
  }
1697
- const hasFeature = await licenseGuard2.hasFeature("otp-totp");
1698
- if (!hasFeature) {
1699
- return ctx.forbidden("TOTP login requires Advanced license");
1700
- }
1701
1669
  const GENERIC_INVALID = "Invalid credentials";
1702
1670
  const users = await strapi.documents("plugin::users-permissions.user").findMany({
1703
1671
  filters: { email: normalizedEmail },
@@ -2870,49 +2838,43 @@ var jwt$1 = {
2870
2838
  };
2871
2839
  var license$1 = {
2872
2840
  /**
2873
- * Get current license status
2841
+ * Returns the current license status. Always reports the plugin as
2842
+ * usable; `hasKey` indicates whether the admin has activated a key.
2843
+ *
2844
+ * @route GET /magic-link/license/status
2845
+ * @returns {{ success: true, valid: true, hasKey: boolean, data: object|null }}
2874
2846
  */
2875
2847
  async getStatus(ctx) {
2876
2848
  try {
2877
2849
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
2878
- const pluginStore = strapi.store({
2879
- type: "plugin",
2880
- name: "magic-link"
2881
- });
2850
+ const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
2882
2851
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
2883
2852
  if (!licenseKey) {
2884
2853
  return ctx.send({
2885
- success: false,
2886
- demo: true,
2887
- valid: false,
2888
- message: "No license found. Running in demo mode."
2854
+ success: true,
2855
+ valid: true,
2856
+ // plugin is always usable in the marketplace build
2857
+ hasKey: false,
2858
+ data: null,
2859
+ message: "Plugin is active. License key activation is optional."
2889
2860
  });
2890
2861
  }
2891
- const verification = await licenseGuard2.verifyLicense(licenseKey);
2892
2862
  const license2 = await licenseGuard2.getLicenseByKey(licenseKey);
2893
2863
  return ctx.send({
2894
2864
  success: true,
2895
- valid: verification.valid,
2896
- demo: false,
2865
+ valid: true,
2866
+ hasKey: true,
2897
2867
  data: {
2898
2868
  licenseKey,
2899
2869
  email: license2?.email || null,
2900
2870
  firstName: license2?.firstName || null,
2901
2871
  lastName: license2?.lastName || null,
2902
- isActive: license2?.isActive || false,
2903
- isExpired: license2?.isExpired || false,
2904
- isOnline: license2?.isOnline || false,
2905
- expiresAt: license2?.expiresAt,
2906
- lastPingAt: license2?.lastPingAt,
2907
- deviceName: license2?.deviceName,
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
- },
2872
+ isActive: license2?.isActive ?? true,
2873
+ isExpired: license2?.isExpired ?? false,
2874
+ expiresAt: license2?.expiresAt || null,
2875
+ deviceName: license2?.deviceName || null,
2876
+ deviceId: license2?.deviceId || null,
2877
+ ipAddress: license2?.ipAddress || null,
2916
2878
  maxDevices: license2?.maxDevices || 1,
2917
2879
  currentDevices: license2?.currentDevices || 0
2918
2880
  }
@@ -2923,7 +2885,11 @@ var license$1 = {
2923
2885
  }
2924
2886
  },
2925
2887
  /**
2926
- * Auto-create license with logged-in admin user data
2888
+ * Auto-create a license using the currently logged-in admin user's
2889
+ * data. Best-effort: a remote failure simply leaves the install
2890
+ * un-keyed, the plugin keeps working.
2891
+ *
2892
+ * @route POST /magic-link/license/auto-create
2927
2893
  */
2928
2894
  async autoCreate(ctx) {
2929
2895
  try {
@@ -2938,15 +2904,9 @@ var license$1 = {
2938
2904
  lastName: adminUser.lastname || "User"
2939
2905
  });
2940
2906
  if (!license2) {
2941
- return ctx.badRequest("Failed to create license");
2907
+ return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
2942
2908
  }
2943
2909
  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
2910
  return ctx.send({
2951
2911
  success: true,
2952
2912
  message: "License automatically created and activated",
@@ -2958,26 +2918,22 @@ var license$1 = {
2958
2918
  }
2959
2919
  },
2960
2920
  /**
2961
- * Create and activate a new license
2921
+ * Create and activate a new license with explicit user details.
2922
+ *
2923
+ * @route POST /magic-link/license/create
2962
2924
  */
2963
2925
  async createAndActivate(ctx) {
2964
2926
  try {
2965
- const { email: email2, firstName, lastName } = ctx.request.body;
2927
+ const { email: email2, firstName, lastName } = ctx.request.body || {};
2966
2928
  if (!email2 || !firstName || !lastName) {
2967
2929
  return ctx.badRequest("Email, firstName, and lastName are required");
2968
2930
  }
2969
2931
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
2970
2932
  const license2 = await licenseGuard2.createLicense({ email: email2, firstName, lastName });
2971
2933
  if (!license2) {
2972
- return ctx.badRequest("Failed to create license");
2934
+ return ctx.badRequest("License server unreachable. The plugin keeps working without a key.");
2973
2935
  }
2974
2936
  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
2937
  return ctx.send({
2982
2938
  success: true,
2983
2939
  message: "License created and activated successfully",
@@ -2989,39 +2945,40 @@ var license$1 = {
2989
2945
  }
2990
2946
  },
2991
2947
  /**
2992
- * Manually ping the current license
2948
+ * Manual refresh: re-verifies the stored license once and returns the
2949
+ * latest details. Replaces the periodic-ping endpoint that used to run
2950
+ * every 15 minutes — admins can call this on demand from the UI to
2951
+ * pull updated info.
2952
+ *
2953
+ * @route POST /magic-link/license/ping
2993
2954
  */
2994
2955
  async ping(ctx) {
2995
2956
  try {
2996
- const pluginStore = strapi.store({
2997
- type: "plugin",
2998
- name: "magic-link"
2999
- });
2957
+ const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
3000
2958
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
3001
2959
  if (!licenseKey) {
3002
2960
  return ctx.badRequest("No license key found");
3003
2961
  }
3004
2962
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3005
- const pingResult = await licenseGuard2.pingLicense(licenseKey);
3006
- if (!pingResult) {
3007
- return ctx.badRequest("Ping failed");
3008
- }
2963
+ const verification = await licenseGuard2.verifyLicense(licenseKey);
3009
2964
  return ctx.send({
3010
2965
  success: true,
3011
- message: "License pinged successfully",
3012
- data: pingResult
2966
+ message: verification.valid ? "License refreshed successfully" : "License key could not be verified, but the plugin keeps working.",
2967
+ data: verification.data
3013
2968
  });
3014
2969
  } catch (error) {
3015
- strapi.log.error("Error pinging license:", error);
3016
- return ctx.badRequest("Error pinging license");
2970
+ strapi.log.error("Error refreshing license:", error);
2971
+ return ctx.badRequest("Error refreshing license");
3017
2972
  }
3018
2973
  },
3019
2974
  /**
3020
- * Store and validate an existing license key
3021
- */
2975
+ * Store and validate an existing license key.
2976
+ *
2977
+ * @route POST /magic-link/license/store-key
2978
+ */
3022
2979
  async storeKey(ctx) {
3023
2980
  try {
3024
- const { licenseKey, email: email2 } = ctx.request.body;
2981
+ const { licenseKey, email: email2 } = ctx.request.body || {};
3025
2982
  if (!licenseKey || !licenseKey.trim()) {
3026
2983
  return ctx.badRequest("License key is required");
3027
2984
  }
@@ -3033,26 +2990,35 @@ var license$1 = {
3033
2990
  const licenseGuard2 = strapi.plugin("magic-link").service("license-guard");
3034
2991
  const verification = await licenseGuard2.verifyLicense(trimmedKey);
3035
2992
  if (!verification.valid) {
3036
- strapi.log.warn(`[WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
2993
+ if (verification.networkError) {
2994
+ strapi.log.warn(
2995
+ `[WARNING] License server unreachable during activation. Storing key anyway: ${trimmedKey.substring(0, 8)}...`
2996
+ );
2997
+ await licenseGuard2.storeLicenseKey(trimmedKey);
2998
+ return ctx.send({
2999
+ success: true,
3000
+ message: "License key stored (license server was unreachable; will retry on the next manual refresh).",
3001
+ data: { licenseKey: trimmedKey, email: trimmedEmail }
3002
+ });
3003
+ }
3004
+ strapi.log.warn(`[WARNING] Invalid license key: ${trimmedKey.substring(0, 8)}...`);
3037
3005
  return ctx.badRequest("Invalid or expired license key");
3038
3006
  }
3039
3007
  const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
3040
3008
  if (!license2) {
3041
- strapi.log.warn(`[WARNING] License not found in database: ${trimmedKey.substring(0, 8)}...`);
3009
+ strapi.log.warn(`[WARNING] License not found: ${trimmedKey.substring(0, 8)}...`);
3042
3010
  return ctx.badRequest("License not found");
3043
3011
  }
3044
- if (license2.email.toLowerCase() !== trimmedEmail) {
3045
- strapi.log.warn(`[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (Attempted: ${trimmedEmail})`);
3012
+ if (license2.email && license2.email.toLowerCase() !== trimmedEmail) {
3013
+ strapi.log.warn(
3014
+ `[WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (attempted: ${trimmedEmail})`
3015
+ );
3046
3016
  return ctx.badRequest("Email address does not match this license key");
3047
3017
  }
3048
3018
  await licenseGuard2.storeLicenseKey(trimmedKey);
3049
- const pingInterval = licenseGuard2.startPinging(trimmedKey, 15);
3050
- strapi.licenseGuard = {
3051
- licenseKey: trimmedKey,
3052
- pingInterval,
3053
- data: verification.data
3054
- };
3055
- strapi.log.info(`[SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
3019
+ strapi.log.info(
3020
+ `[SUCCESS] License key validated and stored: ${trimmedKey.substring(0, 8)}... (email: ${trimmedEmail})`
3021
+ );
3056
3022
  return ctx.send({
3057
3023
  success: true,
3058
3024
  message: "License key validated and activated successfully",
@@ -3204,11 +3170,6 @@ var otp$3 = {
3204
3170
  if (!otpSettings.enabled) {
3205
3171
  return ctx.badRequest("OTP is not enabled");
3206
3172
  }
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
3173
  const rlFail = await enforceOtpRateLimits(ctx, normalizedEmail, "send");
3213
3174
  if (rlFail) return ctx.tooManyRequests(rlFail.message);
3214
3175
  const pluginStore = strapi.store({ type: "plugin", name: "magic-link" });
@@ -3500,11 +3461,6 @@ var otp$3 = {
3500
3461
  */
3501
3462
  async setupTOTP(ctx) {
3502
3463
  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
3464
  const { email: email2, id: userId } = ctx.state.user;
3509
3465
  const otpService = strapi.plugin("magic-link").service("otp");
3510
3466
  const setupData = await otpService.setupTOTP(userId, email2);
@@ -3574,11 +3530,6 @@ var otp$3 = {
3574
3530
  */
3575
3531
  async generateBackupCodes(ctx) {
3576
3532
  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
3533
  const { id: userId } = ctx.state.user;
3583
3534
  const otpService = strapi.plugin("magic-link").service("otp");
3584
3535
  const backupCodes = await otpService.generateBackupCodes(userId);
@@ -3748,61 +3699,6 @@ const passwordlessCompat = passwordlessCompat$2;
3748
3699
  var middlewares$1 = {
3749
3700
  "passwordless-compat": passwordlessCompat
3750
3701
  };
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
3702
  var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) => {
3807
3703
  const pluginStore = strapi2.store({
3808
3704
  type: "plugin",
@@ -3824,10 +3720,8 @@ var compatibilityCheck$1 = async (policyContext, config2, { strapi: strapi2 }) =
3824
3720
  }
3825
3721
  return true;
3826
3722
  };
3827
- const licenseCheck = licenseCheck$1;
3828
3723
  const compatibilityCheck = compatibilityCheck$1;
3829
3724
  var policies$1 = {
3830
- "license-check": licenseCheck,
3831
3725
  "compatibility-check": compatibilityCheck
3832
3726
  };
3833
3727
  var contentApi$1 = {
@@ -3839,7 +3733,7 @@ var contentApi$1 = {
3839
3733
  handler: "auth.login",
3840
3734
  config: {
3841
3735
  auth: false,
3842
- policies: ["plugin::magic-link.license-check"]
3736
+ policies: []
3843
3737
  }
3844
3738
  },
3845
3739
  {
@@ -3848,7 +3742,7 @@ var contentApi$1 = {
3848
3742
  handler: "auth.sendLink",
3849
3743
  config: {
3850
3744
  auth: false,
3851
- policies: ["plugin::magic-link.license-check"]
3745
+ policies: []
3852
3746
  }
3853
3747
  },
3854
3748
  // OTP Routes
@@ -3858,7 +3752,7 @@ var contentApi$1 = {
3858
3752
  handler: "otp.send",
3859
3753
  config: {
3860
3754
  auth: false,
3861
- policies: ["plugin::magic-link.license-check"]
3755
+ policies: []
3862
3756
  }
3863
3757
  },
3864
3758
  {
@@ -3867,7 +3761,7 @@ var contentApi$1 = {
3867
3761
  handler: "otp.verify",
3868
3762
  config: {
3869
3763
  auth: false,
3870
- policies: ["plugin::magic-link.license-check"]
3764
+ policies: []
3871
3765
  }
3872
3766
  },
3873
3767
  {
@@ -3876,7 +3770,7 @@ var contentApi$1 = {
3876
3770
  handler: "otp.resend",
3877
3771
  config: {
3878
3772
  auth: false,
3879
- policies: ["plugin::magic-link.license-check"]
3773
+ policies: []
3880
3774
  }
3881
3775
  },
3882
3776
  // MFA Routes
@@ -3886,7 +3780,7 @@ var contentApi$1 = {
3886
3780
  handler: "auth.verifyMFATOTP",
3887
3781
  config: {
3888
3782
  auth: false,
3889
- policies: ["plugin::magic-link.license-check"]
3783
+ policies: []
3890
3784
  }
3891
3785
  },
3892
3786
  {
@@ -3895,7 +3789,7 @@ var contentApi$1 = {
3895
3789
  handler: "auth.loginWithTOTP",
3896
3790
  config: {
3897
3791
  auth: false,
3898
- policies: ["plugin::magic-link.license-check"]
3792
+ policies: []
3899
3793
  }
3900
3794
  }
3901
3795
  ]
@@ -4195,10 +4089,7 @@ var compatibility$1 = {
4195
4089
  handler: "auth.login",
4196
4090
  config: {
4197
4091
  auth: false,
4198
- policies: [
4199
- "plugin::magic-link.compatibility-check",
4200
- "plugin::magic-link.license-check"
4201
- ],
4092
+ policies: ["plugin::magic-link.compatibility-check"],
4202
4093
  description: "Compatibility route for strapi-plugin-passwordless"
4203
4094
  }
4204
4095
  },
@@ -4209,10 +4100,7 @@ var compatibility$1 = {
4209
4100
  handler: "auth.sendLink",
4210
4101
  config: {
4211
4102
  auth: false,
4212
- policies: [
4213
- "plugin::magic-link.compatibility-check",
4214
- "plugin::magic-link.license-check"
4215
- ],
4103
+ policies: ["plugin::magic-link.compatibility-check"],
4216
4104
  description: "Compatibility route for strapi-plugin-passwordless"
4217
4105
  }
4218
4106
  }
@@ -4854,265 +4742,8 @@ var store$1 = ({ strapi: strapi2 }) => ({
4854
4742
  return pluginStore.set({ key: "settings", value: settings });
4855
4743
  }
4856
4744
  });
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
4745
  const crypto$1 = require$$0$3;
5114
4746
  const os = require$$1$3;
5115
- const features = features$1;
5116
4747
  const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
5117
4748
  const envTimeout = Number(process.env.MAGIC_LICENSE_TIMEOUT_MS);
5118
4749
  const DEFAULT_FETCH_TIMEOUT_MS = Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 12e3;
@@ -5120,7 +4751,7 @@ const FETCH_RETRIES = 1;
5120
4751
  const FETCH_RETRY_BACKOFF_MS = 750;
5121
4752
  async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
5122
4753
  let lastError;
5123
- for (let attempt = 0; attempt <= FETCH_RETRIES; attempt++) {
4754
+ for (let attempt = 0; attempt <= FETCH_RETRIES; attempt += 1) {
5124
4755
  const controller2 = new AbortController();
5125
4756
  const timer = setTimeout(() => controller2.abort(), timeoutMs);
5126
4757
  try {
@@ -5139,15 +4770,22 @@ async function fetchWithTimeout(url, options2 = {}, timeoutMs = DEFAULT_FETCH_TI
5139
4770
  throw lastError;
5140
4771
  }
5141
4772
  var licenseGuard$1 = ({ strapi: strapi2 }) => ({
4773
+ // ======================================================================
4774
+ // Network helpers (used only during admin-side key activation)
4775
+ // ======================================================================
5142
4776
  /**
5143
- * Get license server URL (hardcoded and immutable for security)
5144
- * @returns {string} The fixed license server URL
4777
+ * License server URL. Hard-coded — kept simple because the only call
4778
+ * site is the admin-side activation form. Not user-configurable.
4779
+ * @returns {string}
5145
4780
  */
5146
4781
  getLicenseServerUrl() {
5147
4782
  return LICENSE_SERVER_URL;
5148
4783
  },
5149
4784
  /**
5150
- * Generate a unique device ID based on machine identifiers
4785
+ * Hashed device identifier used by the upstream activation server to
4786
+ * de-duplicate seats. Hash of MAC addresses + hostname; the raw values
4787
+ * never leave the host.
4788
+ * @returns {string}
5151
4789
  */
5152
4790
  generateDeviceId() {
5153
4791
  try {
@@ -5164,11 +4802,12 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5164
4802
  return crypto$1.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
5165
4803
  } catch (error) {
5166
4804
  strapi2.log.error("Error generating device ID:", error);
5167
- return crypto$1.randomBytes(16).digest("hex");
4805
+ return crypto$1.randomBytes(16).toString("hex");
5168
4806
  }
5169
4807
  },
5170
4808
  /**
5171
- * Get device name
4809
+ * Friendly device name for the activation payload.
4810
+ * @returns {string}
5172
4811
  */
5173
4812
  getDeviceName() {
5174
4813
  try {
@@ -5178,7 +4817,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5178
4817
  }
5179
4818
  },
5180
4819
  /**
5181
- * Get server IP address
4820
+ * First non-internal IPv4 address. Best-effort, used in the activation
4821
+ * payload only.
4822
+ * @returns {string}
5182
4823
  */
5183
4824
  getIpAddress() {
5184
4825
  try {
@@ -5196,13 +4837,22 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5196
4837
  }
5197
4838
  },
5198
4839
  /**
5199
- * Get user agent (server context)
4840
+ * Server-side user-agent string for the activation payload.
4841
+ * @returns {string}
5200
4842
  */
5201
4843
  getUserAgent() {
5202
4844
  return `Strapi/${strapi2.config.info.strapi} Node/${process.version} ${os.platform()}/${os.release()}`;
5203
4845
  },
4846
+ // ======================================================================
4847
+ // License creation / verification (admin activation path only)
4848
+ // ======================================================================
5204
4849
  /**
5205
- * Create a license
4850
+ * Create a license on the upstream activation server. Used by the
4851
+ * admin "auto-create" / "create" flows. Failure to create is non-fatal
4852
+ * — the plugin keeps working, the user just doesn't get a key.
4853
+ *
4854
+ * @param {{ email: string, firstName?: string, lastName?: string }} args
4855
+ * @returns {Promise<object|null>} Upstream license payload, or null on failure
5206
4856
  */
5207
4857
  async createLicense({ email: email2, firstName, lastName }) {
5208
4858
  try {
@@ -5213,9 +4863,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5213
4863
  const licenseServerUrl = this.getLicenseServerUrl();
5214
4864
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/create`, {
5215
4865
  method: "POST",
5216
- headers: {
5217
- "Content-Type": "application/json"
5218
- },
4866
+ headers: { "Content-Type": "application/json" },
5219
4867
  body: JSON.stringify({
5220
4868
  email: email2,
5221
4869
  firstName,
@@ -5230,390 +4878,201 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
5230
4878
  });
5231
4879
  const data = await response.json();
5232
4880
  if (data.success) {
5233
- strapi2.log.info("[SUCCESS] License created successfully:", data.data.licenseKey);
4881
+ strapi2.log.info("[SUCCESS] License created:", data.data.licenseKey);
5234
4882
  return data.data;
5235
- } else {
5236
- strapi2.log.error("[ERROR] License creation failed:", data);
5237
- return null;
5238
4883
  }
4884
+ strapi2.log.warn("[WARNING] License creation rejected by server:", data.message || "unknown");
4885
+ return null;
5239
4886
  } catch (error) {
5240
- strapi2.log.error("[ERROR] Error creating license:", error);
4887
+ strapi2.log.error("[ERROR] Error creating license:", error.message);
5241
4888
  return null;
5242
4889
  }
5243
4890
  },
5244
4891
  /**
5245
- * Verify a license (with grace period support)
4892
+ * Verify a license against the upstream activation server.
4893
+ * Called once when the admin saves a license key — never during
4894
+ * runtime feature checks, never on a schedule.
4895
+ *
4896
+ * @param {string} licenseKey
4897
+ * @returns {Promise<{valid: boolean, data: object|null}>}
5246
4898
  */
5247
- async verifyLicense(licenseKey, allowGracePeriod = false) {
4899
+ async verifyLicense(licenseKey) {
5248
4900
  try {
5249
- const controller2 = new AbortController();
5250
- const timeoutId = setTimeout(() => controller2.abort(), 5e3);
5251
4901
  const licenseServerUrl = this.getLicenseServerUrl();
5252
4902
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/verify`, {
5253
4903
  method: "POST",
5254
- headers: {
5255
- "Content-Type": "application/json"
5256
- },
4904
+ headers: { "Content-Type": "application/json" },
5257
4905
  body: JSON.stringify({
5258
4906
  licenseKey,
5259
4907
  pluginName: "magic-link",
5260
4908
  productName: "Magic Link - Passwordless Authentication"
5261
- }),
5262
- signal: controller2.signal
4909
+ })
5263
4910
  });
5264
- clearTimeout(timeoutId);
5265
4911
  const data = await response.json();
5266
4912
  if (data.success) {
5267
- const isValid = data.data.isActive && !data.data.isExpired;
4913
+ const isValid = !!(data.data && data.data.isActive && !data.data.isExpired);
5268
4914
  if (isValid) {
5269
- const pluginStore = strapi2.store({
5270
- type: "plugin",
5271
- name: "magic-link"
5272
- });
4915
+ const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5273
4916
  await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
5274
4917
  }
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 };
5282
- }
5283
- } catch (error) {
5284
- if (allowGracePeriod) {
5285
- strapi2.log.warn(`[WARNING] Cannot verify license online: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
5286
- strapi2.log.info(`[INFO] Grace period active - accepting stored license key`);
5287
- return { valid: true, data: null, gracePeriod: true };
4918
+ return { valid: isValid, data: data.data };
5288
4919
  }
5289
- strapi2.log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
4920
+ strapi2.log.warn(
4921
+ `[WARNING] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`
4922
+ );
5290
4923
  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
4924
  } 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
- } catch (error) {
5345
- strapi2.log.error("[ERROR] Error upgrading license:", error);
5346
- return null;
4925
+ strapi2.log.warn(
4926
+ `[WARNING] License server unreachable during activation: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...). Activation will not block this install.`
4927
+ );
4928
+ return { valid: false, data: null, networkError: true };
5347
4929
  }
5348
4930
  },
5349
4931
  /**
5350
- * Get license by key
4932
+ * Look up a license by key on the upstream server. Used by the admin
4933
+ * License page to display details. Network failures yield null.
4934
+ *
4935
+ * @param {string} licenseKey
4936
+ * @returns {Promise<object|null>}
5351
4937
  */
5352
4938
  async getLicenseByKey(licenseKey) {
5353
4939
  try {
5354
4940
  const licenseServerUrl = this.getLicenseServerUrl();
5355
4941
  const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
5356
4942
  const data = await response.json();
5357
- if (data.success) {
5358
- return data.data;
5359
- }
4943
+ if (data.success) return data.data;
5360
4944
  return null;
5361
4945
  } catch (error) {
5362
- strapi2.log.error("[ERROR] Error fetching license by key:", error);
4946
+ strapi2.log.warn("[WARNING] Could not fetch license by key:", error.message);
5363
4947
  return null;
5364
4948
  }
5365
4949
  },
5366
4950
  /**
5367
- * Get licenses by email
4951
+ * Look up licenses by user email. Used by admin "auto-create" to
4952
+ * decide whether to reuse an existing key.
4953
+ *
4954
+ * @param {string} email
4955
+ * @returns {Promise<Array>}
5368
4956
  */
5369
4957
  async getLicensesByEmail(email2) {
5370
4958
  try {
5371
4959
  const licenseServerUrl = this.getLicenseServerUrl();
5372
- const response = await fetchWithTimeout(`${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`);
4960
+ const response = await fetchWithTimeout(
4961
+ `${licenseServerUrl}/api/licenses/email/${encodeURIComponent(email2)}`
4962
+ );
5373
4963
  const data = await response.json();
5374
- if (data.success) {
5375
- return data.data;
5376
- }
4964
+ if (data.success) return data.data;
5377
4965
  return [];
5378
4966
  } catch (error) {
5379
- strapi2.log.error("[ERROR] Error fetching licenses by email:", error);
4967
+ strapi2.log.warn("[WARNING] Could not fetch licenses by email:", error.message);
5380
4968
  return [];
5381
4969
  }
5382
4970
  },
4971
+ // ======================================================================
4972
+ // Storage (plugin store)
4973
+ // ======================================================================
5383
4974
  /**
5384
- * Get online statistics
5385
- */
5386
- async getOnlineStats() {
5387
- try {
5388
- const licenseServerUrl = this.getLicenseServerUrl();
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
4975
+ * Persist a license key to the plugin store. Used by the admin
4976
+ * activation form.
4977
+ *
4978
+ * @param {string} licenseKey
4979
+ * @returns {Promise<boolean>}
5458
4980
  */
5459
4981
  async storeLicenseKey(licenseKey) {
5460
4982
  try {
5461
- strapi2.log.info(`[LICENSE] Storing license key: ${licenseKey?.substring(0, 8)}...`);
5462
- const pluginStore = strapi2.store({
5463
- type: "plugin",
5464
- name: "magic-link"
5465
- });
4983
+ const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5466
4984
  await pluginStore.set({ key: "licenseKey", value: licenseKey });
5467
4985
  await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
5468
- const stored = await pluginStore.get({ key: "licenseKey" });
5469
- if (stored === licenseKey) {
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
- }
4986
+ strapi2.log.info("[SUCCESS] License key stored");
4987
+ return true;
5476
4988
  } catch (error) {
5477
- strapi2.log.error("[ERROR] Error storing license key:", error);
4989
+ strapi2.log.error("[ERROR] Error storing license key:", error.message);
5478
4990
  return false;
5479
4991
  }
5480
4992
  },
4993
+ // ======================================================================
4994
+ // Lifecycle (no-ops in the marketplace build)
4995
+ // ======================================================================
4996
+ /**
4997
+ * Boot-time initializer. In the marketplace build this is a no-op:
4998
+ * we do not auto-verify the stored key and we do not start a periodic
4999
+ * ping. Kept as a stable export so bootstrap.js continues to work.
5000
+ *
5001
+ * @returns {Promise<{valid: boolean}>}
5002
+ */
5003
+ async initialize() {
5004
+ return { valid: true };
5005
+ },
5481
5006
  /**
5482
- * Cleanup on plugin destroy
5007
+ * Plugin destroy hook. No-op (no interval to clear in the marketplace
5008
+ * build).
5483
5009
  */
5484
5010
  cleanup() {
5485
- if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
5486
- clearInterval(strapi2.licenseGuard.pingInterval);
5487
- strapi2.log.info("[INFO] License pinging stopped");
5488
- }
5489
5011
  },
5012
+ // ======================================================================
5013
+ // Permissive feature stubs (kept for backward compatibility with any
5014
+ // legacy caller — they always permit, never gate)
5015
+ // ======================================================================
5490
5016
  /**
5491
- * Check if a feature is available
5492
- * @param {string} featureName - Feature name to check
5493
- * @returns {boolean}
5017
+ * Always-permissive feature check. Kept so any legacy `if (await
5018
+ * licenseGuard.hasFeature(...))` site continues to compile and runs
5019
+ * the gated branch.
5020
+ *
5021
+ * @returns {Promise<boolean>} always true
5494
5022
  */
5495
- async hasFeature(featureName) {
5496
- try {
5497
- const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
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
- }
5023
+ // eslint-disable-next-line no-unused-vars
5024
+ async hasFeature(_featureName) {
5025
+ return true;
5508
5026
  },
5509
5027
  /**
5510
- * Get max allowed tokens
5511
- * @returns {number} Max tokens (-1 = unlimited)
5028
+ * @returns {Promise<number>} -1 (unlimited)
5512
5029
  */
5513
5030
  async getMaxTokens() {
5514
- try {
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
- }
5031
+ return -1;
5526
5032
  },
5527
5033
  /**
5528
- * Get max allowed sessions
5529
- * @returns {number} Max sessions (-1 = unlimited)
5034
+ * @returns {Promise<number>} -1 (unlimited)
5530
5035
  */
5531
5036
  async getMaxSessions() {
5532
- try {
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
- }
5037
+ return -1;
5544
5038
  },
5545
5039
  /**
5546
- * Get max allowed IP bans
5547
- * @returns {number} Max IP bans (-1 = unlimited)
5040
+ * @returns {Promise<number>} -1 (unlimited)
5548
5041
  */
5549
5042
  async getMaxIPBans() {
5550
- try {
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
- }
5043
+ return -1;
5562
5044
  },
5563
5045
  /**
5564
- * Get available OTP types
5565
- * @returns {Array} Available OTP types
5046
+ * @returns {Promise<string[]>} every supported OTP type
5566
5047
  */
5567
5048
  async getAvailableOTPTypes() {
5568
- try {
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
- }
5049
+ return ["email", "totp", "backup-codes"];
5580
5050
  },
5581
5051
  /**
5582
- * Get license tier info
5583
- * @returns {Object} License tier information
5052
+ * Tier info for UI display. Reports `pro` when a key is stored,
5053
+ * `free` otherwise. Pure information — no behavior depends on this.
5054
+ *
5055
+ * @returns {Promise<{tier: string, features: object, hasKey: boolean}>}
5584
5056
  */
5585
5057
  async getLicenseTierInfo() {
5586
5058
  try {
5587
5059
  const pluginStore = strapi2.store({ type: "plugin", name: "magic-link" });
5588
5060
  const licenseKey = await pluginStore.get({ key: "licenseKey" });
5589
- if (!licenseKey) {
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);
5061
+ const hasKey = !!licenseKey;
5600
5062
  return {
5601
- tier: licenseData?.tier || "free",
5063
+ tier: hasKey ? "pro" : "free",
5064
+ hasKey,
5602
5065
  features: {
5603
- premium: licenseData?.featurePremium || licenseData?.features?.premium || false,
5604
- advanced: licenseData?.featureAdvanced || licenseData?.features?.advanced || false,
5605
- enterprise: licenseData?.featureEnterprise || licenseData?.features?.enterprise || false
5066
+ premium: hasKey,
5067
+ advanced: hasKey,
5068
+ enterprise: hasKey
5606
5069
  }
5607
5070
  };
5608
5071
  } catch (error) {
5609
- strapi2.log.error("Error getting license tier info:", error);
5610
5072
  return {
5611
5073
  tier: "free",
5612
- features: {
5613
- premium: false,
5614
- advanced: false,
5615
- enterprise: false
5616
- }
5074
+ hasKey: false,
5075
+ features: { premium: false, advanced: false, enterprise: false }
5617
5076
  };
5618
5077
  }
5619
5078
  }