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

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