strapi-plugin-magic-mark 3.1.0 → 3.2.1

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.
@@ -2,6 +2,65 @@ import crypto from "crypto";
2
2
  import os from "os";
3
3
  import { readFileSync } from "fs";
4
4
  import { join } from "path";
5
+ const PLUGIN_NAME = "magic-mark";
6
+ const PREFIX = "[Magic-Mark]";
7
+ function formatMessage(prefix, args) {
8
+ if (args.length === 0) return prefix;
9
+ const parts = args.map(
10
+ (arg) => typeof arg === "string" ? arg : JSON.stringify(arg)
11
+ );
12
+ return `${prefix} ${parts.join(" ")}`;
13
+ }
14
+ function createLogger(strapi) {
15
+ const getDebugMode = () => {
16
+ try {
17
+ const config2 = strapi.config.get(`plugin::${PLUGIN_NAME}`) || {};
18
+ return config2.debug === true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ };
23
+ return {
24
+ /**
25
+ * Log info - only when debug: true
26
+ */
27
+ info: (...args) => {
28
+ if (getDebugMode()) {
29
+ strapi.log.info(formatMessage(PREFIX, args));
30
+ }
31
+ },
32
+ /**
33
+ * Log debug - only when debug: true
34
+ */
35
+ debug: (...args) => {
36
+ if (getDebugMode()) {
37
+ strapi.log.debug(formatMessage(PREFIX, args));
38
+ }
39
+ },
40
+ /**
41
+ * Log warning - only when debug: true
42
+ */
43
+ warn: (...args) => {
44
+ if (getDebugMode()) {
45
+ strapi.log.warn(formatMessage(PREFIX, args));
46
+ }
47
+ },
48
+ /**
49
+ * Log error - only when debug: true
50
+ */
51
+ error: (...args) => {
52
+ if (getDebugMode()) {
53
+ strapi.log.error(formatMessage(PREFIX, args));
54
+ }
55
+ },
56
+ /**
57
+ * Force log - always logged (for critical errors only)
58
+ */
59
+ forceError: (...args) => {
60
+ strapi.log.error(formatMessage(PREFIX, args));
61
+ }
62
+ };
63
+ }
5
64
  const magicMarkActions = {
6
65
  actions: [
7
66
  {
@@ -21,68 +80,74 @@ const magicMarkActions = {
21
80
  ]
22
81
  };
23
82
  const register = async ({ strapi }) => {
83
+ const log = createLogger(strapi);
24
84
  await strapi.admin.services.permission.actionProvider.registerMany(
25
85
  magicMarkActions.actions
26
86
  );
27
- strapi.log.info("[Magic-Mark] Plugin registered successfully");
87
+ log.info("Plugin registered successfully");
28
88
  };
29
89
  const bootstrap = ({ strapi }) => {
30
- strapi.log.info("[Magic-Mark] Plugin bootstrapping...");
90
+ const log = createLogger(strapi);
91
+ log.info("Plugin bootstrapping...");
31
92
  try {
32
93
  const licenseGuardService2 = strapi.plugin("magic-mark").service("license-guard");
33
94
  setTimeout(async () => {
34
95
  const licenseStatus = await licenseGuardService2.initialize();
35
96
  if (!licenseStatus.valid) {
36
- strapi.log.error("╔════════════════════════════════════════════════════════════════╗");
37
- strapi.log.error("║ [ERROR] MAGICMARK PLUGIN - NO VALID LICENSE ║");
38
- strapi.log.error("║ ║");
39
- strapi.log.error("║ This plugin requires a valid license to operate. ║");
40
- strapi.log.error("║ Please activate your license via Admin UI: ║");
41
- strapi.log.error("║ Go to Settings → MagicMark → License ║");
42
- strapi.log.error("║ ║");
43
- strapi.log.error("║ The plugin will run with limited functionality until ║");
44
- strapi.log.error("║ a valid license is activated. ║");
45
- strapi.log.error("╚════════════════════════════════════════════════════════════════╝");
97
+ log.error("╔════════════════════════════════════════════════════════════════╗");
98
+ log.error("║ [ERROR] MAGICMARK PLUGIN - NO VALID LICENSE ║");
99
+ log.error("║ ║");
100
+ log.error("║ This plugin requires a valid license to operate. ║");
101
+ log.error("║ Please activate your license via Admin UI: ║");
102
+ log.error("║ Go to Settings → MagicMark → License ║");
103
+ log.error("║ ║");
104
+ log.error("║ The plugin will run with limited functionality until ║");
105
+ log.error("║ a valid license is activated. ║");
106
+ log.error("╚════════════════════════════════════════════════════════════════╝");
46
107
  } else if (licenseStatus.valid) {
47
108
  const pluginStore = strapi.store({
48
109
  type: "plugin",
49
110
  name: "magic-mark"
50
111
  });
51
112
  const storedKey = await pluginStore.get({ key: "licenseKey" });
52
- strapi.log.info("╔════════════════════════════════════════════════════════════════╗");
53
- strapi.log.info("║ [SUCCESS] MAGICMARK PLUGIN LICENSE ACTIVE ║");
54
- strapi.log.info("║ ║");
113
+ log.info("╔════════════════════════════════════════════════════════════════╗");
114
+ log.info("║ [SUCCESS] MAGICMARK PLUGIN LICENSE ACTIVE ║");
115
+ log.info("║ ║");
55
116
  if (licenseStatus.data) {
56
- strapi.log.info(`║ License: ${licenseStatus.data.licenseKey} ║`);
57
- strapi.log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + "║");
58
- strapi.log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + "║");
117
+ log.info(`║ License: ${licenseStatus.data.licenseKey} ║`);
118
+ log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + "║");
119
+ log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + "║");
59
120
  } else if (storedKey) {
60
- strapi.log.info(`║ License: ${storedKey} (Offline Mode) ║`);
61
- strapi.log.info(`║ Status: Grace Period Active ║`);
121
+ log.info(`║ License: ${storedKey} (Offline Mode) ║`);
122
+ log.info(`║ Status: Grace Period Active ║`);
62
123
  }
63
- strapi.log.info("║ ║");
64
- strapi.log.info("║ [PING] Auto-pinging every 15 minutes ║");
65
- strapi.log.info("╚════════════════════════════════════════════════════════════════╝");
124
+ log.info("║ ║");
125
+ log.info("║ [PING] Auto-pinging every 15 minutes ║");
126
+ log.info("╚════════════════════════════════════════════════════════════════╝");
66
127
  }
67
128
  }, 3e3);
68
129
  } catch (error) {
69
- strapi.log.error("[ERROR] Error initializing License Guard:", error);
130
+ log.error("[ERROR] Error initializing License Guard:", error);
70
131
  }
71
- strapi.log.info("[Magic-Mark] Plugin bootstrapped successfully");
132
+ log.info("Plugin bootstrapped successfully");
72
133
  };
73
134
  const destroy = ({ strapi }) => {
135
+ const log = createLogger(strapi);
74
136
  try {
75
137
  const licenseGuardService2 = strapi.plugin("magic-mark")?.service("license-guard");
76
138
  if (licenseGuardService2) {
77
139
  licenseGuardService2.cleanup();
78
- strapi.log.info("[SUCCESS] License Guard cleanup completed");
140
+ log.info("[SUCCESS] License Guard cleanup completed");
79
141
  }
80
142
  } catch (error) {
81
- strapi.log.error("[ERROR] Error during License Guard cleanup:", error);
143
+ log.error("[ERROR] Error during License Guard cleanup:", error);
82
144
  }
83
145
  };
84
146
  const config = {
85
- default: {},
147
+ default: {
148
+ // Enable debug logging (set to true to see all plugin logs)
149
+ debug: false
150
+ },
86
151
  validator: () => {
87
152
  }
88
153
  };
@@ -1069,507 +1134,510 @@ try {
1069
1134
  } catch (e) {
1070
1135
  }
1071
1136
  const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
1072
- const licenseGuardService = ({ strapi }) => ({
1073
- /**
1074
- * Get license server URL (hardcoded and immutable for security)
1075
- * @returns The fixed license server URL - cannot be overridden
1076
- */
1077
- getLicenseServerUrl() {
1078
- return LICENSE_SERVER_URL;
1079
- },
1080
- /**
1081
- * Generate a unique device ID based on machine identifiers
1082
- */
1083
- generateDeviceId() {
1084
- try {
1085
- const networkInterfaces = os.networkInterfaces();
1086
- const macAddresses = [];
1087
- Object.values(networkInterfaces).forEach((interfaces) => {
1088
- interfaces?.forEach((iface) => {
1089
- if (iface.mac && iface.mac !== "00:00:00:00:00:00") {
1090
- macAddresses.push(iface.mac);
1091
- }
1137
+ const licenseGuardService = ({ strapi }) => {
1138
+ const log = createLogger(strapi);
1139
+ return {
1140
+ /**
1141
+ * Get license server URL (hardcoded and immutable for security)
1142
+ * @returns The fixed license server URL - cannot be overridden
1143
+ */
1144
+ getLicenseServerUrl() {
1145
+ return LICENSE_SERVER_URL;
1146
+ },
1147
+ /**
1148
+ * Generate a unique device ID based on machine identifiers
1149
+ */
1150
+ generateDeviceId() {
1151
+ try {
1152
+ const networkInterfaces = os.networkInterfaces();
1153
+ const macAddresses = [];
1154
+ Object.values(networkInterfaces).forEach((interfaces) => {
1155
+ interfaces?.forEach((iface) => {
1156
+ if (iface.mac && iface.mac !== "00:00:00:00:00:00") {
1157
+ macAddresses.push(iface.mac);
1158
+ }
1159
+ });
1092
1160
  });
1093
- });
1094
- const identifier = `${macAddresses.join("-")}-${os.hostname()}`;
1095
- return crypto.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
1096
- } catch (error) {
1097
- strapi.log.error("Error generating device ID:", error);
1098
- return crypto.randomBytes(16).toString("hex");
1099
- }
1100
- },
1101
- /**
1102
- * Get device name
1103
- */
1104
- getDeviceName() {
1105
- try {
1106
- return os.hostname() || "Unknown Device";
1107
- } catch (error) {
1108
- return "Unknown Device";
1109
- }
1110
- },
1111
- /**
1112
- * Get server IP address
1113
- */
1114
- getIpAddress() {
1115
- try {
1116
- const networkInterfaces = os.networkInterfaces();
1117
- for (const name of Object.keys(networkInterfaces)) {
1118
- const interfaces = networkInterfaces[name];
1119
- if (interfaces) {
1120
- for (const iface of interfaces) {
1121
- if (iface.family === "IPv4" && !iface.internal) {
1122
- return iface.address;
1161
+ const identifier = `${macAddresses.join("-")}-${os.hostname()}`;
1162
+ return crypto.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
1163
+ } catch (error) {
1164
+ log.error("Error generating device ID:", error);
1165
+ return crypto.randomBytes(16).toString("hex");
1166
+ }
1167
+ },
1168
+ /**
1169
+ * Get device name
1170
+ */
1171
+ getDeviceName() {
1172
+ try {
1173
+ return os.hostname() || "Unknown Device";
1174
+ } catch (error) {
1175
+ return "Unknown Device";
1176
+ }
1177
+ },
1178
+ /**
1179
+ * Get server IP address
1180
+ */
1181
+ getIpAddress() {
1182
+ try {
1183
+ const networkInterfaces = os.networkInterfaces();
1184
+ for (const name of Object.keys(networkInterfaces)) {
1185
+ const interfaces = networkInterfaces[name];
1186
+ if (interfaces) {
1187
+ for (const iface of interfaces) {
1188
+ if (iface.family === "IPv4" && !iface.internal) {
1189
+ return iface.address;
1190
+ }
1123
1191
  }
1124
1192
  }
1125
1193
  }
1194
+ return "127.0.0.1";
1195
+ } catch (error) {
1196
+ return "127.0.0.1";
1126
1197
  }
1127
- return "127.0.0.1";
1128
- } catch (error) {
1129
- return "127.0.0.1";
1130
- }
1131
- },
1132
- /**
1133
- * Get user agent (server context)
1134
- */
1135
- getUserAgent() {
1136
- const strapiVersion = strapi.config.get("info.strapi") || "5.0.0";
1137
- return `MagicMark/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
1138
- },
1139
- /**
1140
- * Create a license
1141
- */
1142
- async createLicense({ email, firstName, lastName }) {
1143
- try {
1144
- const deviceId = this.generateDeviceId();
1145
- const deviceName = this.getDeviceName();
1146
- const ipAddress = this.getIpAddress();
1147
- const userAgent = this.getUserAgent();
1148
- const licenseServerUrl = this.getLicenseServerUrl();
1149
- const response = await fetch(`${licenseServerUrl}/api/licenses/create`, {
1150
- method: "POST",
1151
- headers: {
1152
- "Content-Type": "application/json"
1153
- },
1154
- body: JSON.stringify({
1155
- email,
1156
- firstName,
1157
- lastName,
1158
- deviceName,
1159
- deviceId,
1160
- ipAddress,
1161
- userAgent,
1162
- pluginName: "magic-mark",
1163
- productName: "MagicMark - Advanced Query Builder"
1164
- })
1165
- });
1166
- const data = await response.json();
1167
- if (data.success) {
1168
- strapi.log.info("[SUCCESS] License created successfully:", data.data.licenseKey);
1169
- return data.data;
1170
- } else {
1171
- strapi.log.error("[ERROR] License creation failed:", data);
1198
+ },
1199
+ /**
1200
+ * Get user agent (server context)
1201
+ */
1202
+ getUserAgent() {
1203
+ const strapiVersion = strapi.config.get("info.strapi") || "5.0.0";
1204
+ return `MagicMark/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
1205
+ },
1206
+ /**
1207
+ * Create a license
1208
+ */
1209
+ async createLicense({ email, firstName, lastName }) {
1210
+ try {
1211
+ const deviceId = this.generateDeviceId();
1212
+ const deviceName = this.getDeviceName();
1213
+ const ipAddress = this.getIpAddress();
1214
+ const userAgent = this.getUserAgent();
1215
+ const licenseServerUrl = this.getLicenseServerUrl();
1216
+ const response = await fetch(`${licenseServerUrl}/api/licenses/create`, {
1217
+ method: "POST",
1218
+ headers: {
1219
+ "Content-Type": "application/json"
1220
+ },
1221
+ body: JSON.stringify({
1222
+ email,
1223
+ firstName,
1224
+ lastName,
1225
+ deviceName,
1226
+ deviceId,
1227
+ ipAddress,
1228
+ userAgent,
1229
+ pluginName: "magic-mark",
1230
+ productName: "MagicMark - Advanced Query Builder"
1231
+ })
1232
+ });
1233
+ const data = await response.json();
1234
+ if (data.success) {
1235
+ log.info("[SUCCESS] License created successfully:", data.data.licenseKey);
1236
+ return data.data;
1237
+ } else {
1238
+ log.error("[ERROR] License creation failed:", data);
1239
+ return null;
1240
+ }
1241
+ } catch (error) {
1242
+ log.error("[ERROR] Error creating license:", error);
1172
1243
  return null;
1173
1244
  }
1174
- } catch (error) {
1175
- strapi.log.error("[ERROR] Error creating license:", error);
1176
- return null;
1177
- }
1178
- },
1179
- /**
1180
- * Verify a license (with grace period support)
1181
- */
1182
- async verifyLicense(licenseKey, allowGracePeriod = false) {
1183
- try {
1184
- const controller = new AbortController();
1185
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
1186
- const licenseServerUrl = this.getLicenseServerUrl();
1187
- const response = await fetch(`${licenseServerUrl}/api/licenses/verify`, {
1188
- method: "POST",
1189
- headers: {
1190
- "Content-Type": "application/json"
1191
- },
1192
- body: JSON.stringify({
1193
- licenseKey,
1194
- pluginName: "magic-mark",
1195
- productName: "MagicMark - Advanced Query Builder"
1196
- }),
1197
- signal: controller.signal
1198
- });
1199
- clearTimeout(timeoutId);
1200
- const data = await response.json();
1201
- if (data.success) {
1202
- const isValid = data.data.isActive && !data.data.isExpired;
1203
- const statusInfo = data.data.isExpired ? "EXPIRED" : data.data.isActive ? "ACTIVE" : "INACTIVE";
1204
- strapi.log.info(`[SUCCESS] License verified online: ${statusInfo} (Key: ${licenseKey?.substring(0, 8)}...)`);
1205
- if (isValid) {
1206
- const pluginStore = strapi.store({
1207
- type: "plugin",
1208
- name: "magic-mark"
1209
- });
1210
- await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
1245
+ },
1246
+ /**
1247
+ * Verify a license (with grace period support)
1248
+ */
1249
+ async verifyLicense(licenseKey, allowGracePeriod = false) {
1250
+ try {
1251
+ const controller = new AbortController();
1252
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
1253
+ const licenseServerUrl = this.getLicenseServerUrl();
1254
+ const response = await fetch(`${licenseServerUrl}/api/licenses/verify`, {
1255
+ method: "POST",
1256
+ headers: {
1257
+ "Content-Type": "application/json"
1258
+ },
1259
+ body: JSON.stringify({
1260
+ licenseKey,
1261
+ pluginName: "magic-mark",
1262
+ productName: "MagicMark - Advanced Query Builder"
1263
+ }),
1264
+ signal: controller.signal
1265
+ });
1266
+ clearTimeout(timeoutId);
1267
+ const data = await response.json();
1268
+ if (data.success) {
1269
+ const isValid = data.data.isActive && !data.data.isExpired;
1270
+ const statusInfo = data.data.isExpired ? "EXPIRED" : data.data.isActive ? "ACTIVE" : "INACTIVE";
1271
+ log.info(`[SUCCESS] License verified online: ${statusInfo} (Key: ${licenseKey?.substring(0, 8)}...)`);
1272
+ if (isValid) {
1273
+ const pluginStore = strapi.store({
1274
+ type: "plugin",
1275
+ name: "magic-mark"
1276
+ });
1277
+ await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
1278
+ }
1279
+ return {
1280
+ valid: isValid,
1281
+ data: data.data
1282
+ };
1283
+ } else {
1284
+ log.warn(`[WARN] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1285
+ return { valid: false, data: null };
1211
1286
  }
1212
- return {
1213
- valid: isValid,
1214
- data: data.data
1215
- };
1216
- } else {
1217
- strapi.log.warn(`[WARN] License verification failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1287
+ } catch (error) {
1288
+ if (allowGracePeriod) {
1289
+ log.warn(`[WARN] Cannot verify license online: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1290
+ log.info(`[GRACE] Grace period active - accepting stored license key`);
1291
+ return { valid: true, data: null, gracePeriod: true };
1292
+ }
1293
+ log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1218
1294
  return { valid: false, data: null };
1219
1295
  }
1220
- } catch (error) {
1221
- if (allowGracePeriod) {
1222
- strapi.log.warn(`[WARN] Cannot verify license online: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1223
- strapi.log.info(`[GRACE] Grace period active - accepting stored license key`);
1224
- return { valid: true, data: null, gracePeriod: true };
1225
- }
1226
- strapi.log.error(`[ERROR] Error verifying license: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1227
- return { valid: false, data: null };
1228
- }
1229
- },
1230
- /**
1231
- * Ping a license (lightweight check)
1232
- */
1233
- async pingLicense(licenseKey) {
1234
- try {
1235
- const licenseServerUrl = this.getLicenseServerUrl();
1236
- const response = await fetch(`${licenseServerUrl}/api/licenses/ping`, {
1237
- method: "POST",
1238
- headers: {
1239
- "Content-Type": "application/json"
1240
- },
1241
- body: JSON.stringify({
1242
- licenseKey,
1243
- pluginName: "magic-mark",
1244
- productName: "MagicMark - Advanced Query Builder"
1245
- })
1246
- });
1247
- const data = await response.json();
1248
- if (data.success) {
1249
- strapi.log.debug(`[PING] License ping successful: ${data.data?.isActive ? "ACTIVE" : "INACTIVE"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1250
- return data.data;
1251
- } else {
1252
- strapi.log.debug(`[WARN] License ping failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1296
+ },
1297
+ /**
1298
+ * Ping a license (lightweight check)
1299
+ */
1300
+ async pingLicense(licenseKey) {
1301
+ try {
1302
+ const licenseServerUrl = this.getLicenseServerUrl();
1303
+ const response = await fetch(`${licenseServerUrl}/api/licenses/ping`, {
1304
+ method: "POST",
1305
+ headers: {
1306
+ "Content-Type": "application/json"
1307
+ },
1308
+ body: JSON.stringify({
1309
+ licenseKey,
1310
+ pluginName: "magic-mark",
1311
+ productName: "MagicMark - Advanced Query Builder"
1312
+ })
1313
+ });
1314
+ const data = await response.json();
1315
+ if (data.success) {
1316
+ log.debug(`[PING] License ping successful: ${data.data?.isActive ? "ACTIVE" : "INACTIVE"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1317
+ return data.data;
1318
+ } else {
1319
+ log.debug(`[WARN] License ping failed: ${data.message || "Unknown error"} (Key: ${licenseKey?.substring(0, 8)}...)`);
1320
+ return null;
1321
+ }
1322
+ } catch (error) {
1323
+ log.debug(`License ping error: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1253
1324
  return null;
1254
1325
  }
1255
- } catch (error) {
1256
- strapi.log.debug(`License ping error: ${error.message} (Key: ${licenseKey?.substring(0, 8)}...)`);
1257
- return null;
1258
- }
1259
- },
1260
- /**
1261
- * Get license by key
1262
- */
1263
- async getLicenseByKey(licenseKey) {
1264
- try {
1265
- const licenseServerUrl = this.getLicenseServerUrl();
1266
- const response = await fetch(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
1267
- const data = await response.json();
1268
- if (data.success) {
1269
- return data.data;
1326
+ },
1327
+ /**
1328
+ * Get license by key
1329
+ */
1330
+ async getLicenseByKey(licenseKey) {
1331
+ try {
1332
+ const licenseServerUrl = this.getLicenseServerUrl();
1333
+ const response = await fetch(`${licenseServerUrl}/api/licenses/key/${licenseKey}`);
1334
+ const data = await response.json();
1335
+ if (data.success) {
1336
+ return data.data;
1337
+ }
1338
+ return null;
1339
+ } catch (error) {
1340
+ log.error("[ERROR] Error fetching license by key:", error);
1341
+ return null;
1270
1342
  }
1271
- return null;
1272
- } catch (error) {
1273
- strapi.log.error("[ERROR] Error fetching license by key:", error);
1274
- return null;
1275
- }
1276
- },
1277
- /**
1278
- * Get current license data from store (fetches fresh from server)
1279
- * This is the correct way to get license data with all feature flags
1280
- */
1281
- async getCurrentLicense() {
1282
- try {
1283
- const pluginStore = strapi.store({
1284
- type: "plugin",
1285
- name: "magic-mark"
1286
- });
1287
- const licenseKey = await pluginStore.get({ key: "licenseKey" });
1288
- if (!licenseKey) {
1343
+ },
1344
+ /**
1345
+ * Get current license data from store (fetches fresh from server)
1346
+ * This is the correct way to get license data with all feature flags
1347
+ */
1348
+ async getCurrentLicense() {
1349
+ try {
1350
+ const pluginStore = strapi.store({
1351
+ type: "plugin",
1352
+ name: "magic-mark"
1353
+ });
1354
+ const licenseKey = await pluginStore.get({ key: "licenseKey" });
1355
+ if (!licenseKey) {
1356
+ return null;
1357
+ }
1358
+ const license2 = await this.getLicenseByKey(licenseKey);
1359
+ return license2;
1360
+ } catch (error) {
1361
+ log.error("[LICENSE] Error loading current license:", error);
1289
1362
  return null;
1290
1363
  }
1291
- const license2 = await this.getLicenseByKey(licenseKey);
1292
- return license2;
1293
- } catch (error) {
1294
- strapi.log.error("[LICENSE] Error loading current license:", error);
1295
- return null;
1296
- }
1297
- },
1298
- /**
1299
- * Start periodic pinging for a license
1300
- */
1301
- startPinging(licenseKey, intervalMinutes = 15) {
1302
- const intervalMs = intervalMinutes * 60 * 1e3;
1303
- this.pingLicense(licenseKey);
1304
- const pingInterval = setInterval(async () => {
1305
- await this.pingLicense(licenseKey);
1306
- }, intervalMs);
1307
- strapi.log.info(`[PING] Started pinging license every ${intervalMinutes} minutes`);
1308
- return pingInterval;
1309
- },
1310
- /**
1311
- * Initialize license guard
1312
- * Checks for existing license or prompts for creation
1313
- */
1314
- async initialize() {
1315
- try {
1316
- strapi.log.info("[LICENSE] Initializing License Guard...");
1317
- const pluginStore = strapi.store({
1318
- type: "plugin",
1319
- name: "magic-mark"
1320
- });
1321
- const licenseKey = await pluginStore.get({ key: "licenseKey" });
1322
- const lastValidated = await pluginStore.get({ key: "lastValidated" });
1323
- const now = /* @__PURE__ */ new Date();
1324
- const gracePeriodHours = 24;
1325
- let withinGracePeriod = false;
1326
- if (lastValidated) {
1327
- const lastValidatedDate = new Date(lastValidated);
1328
- const hoursSinceValidation = (now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60);
1329
- withinGracePeriod = hoursSinceValidation < gracePeriodHours;
1330
- }
1331
- strapi.log.info("──────────────────────────────────────────────────────────");
1332
- strapi.log.info(`[STORE] Plugin Store Check:`);
1333
- if (licenseKey) {
1334
- strapi.log.info(` [OK] License Key found: ${licenseKey}`);
1335
- strapi.log.info(` [KEY] Key (short): ${licenseKey.substring(0, 8)}...`);
1364
+ },
1365
+ /**
1366
+ * Start periodic pinging for a license
1367
+ */
1368
+ startPinging(licenseKey, intervalMinutes = 15) {
1369
+ const intervalMs = intervalMinutes * 60 * 1e3;
1370
+ this.pingLicense(licenseKey);
1371
+ const pingInterval = setInterval(async () => {
1372
+ await this.pingLicense(licenseKey);
1373
+ }, intervalMs);
1374
+ log.info(`[PING] Started pinging license every ${intervalMinutes} minutes`);
1375
+ return pingInterval;
1376
+ },
1377
+ /**
1378
+ * Initialize license guard
1379
+ * Checks for existing license or prompts for creation
1380
+ */
1381
+ async initialize() {
1382
+ try {
1383
+ log.info("[LICENSE] Initializing License Guard...");
1384
+ const pluginStore = strapi.store({
1385
+ type: "plugin",
1386
+ name: "magic-mark"
1387
+ });
1388
+ const licenseKey = await pluginStore.get({ key: "licenseKey" });
1389
+ const lastValidated = await pluginStore.get({ key: "lastValidated" });
1390
+ const now = /* @__PURE__ */ new Date();
1391
+ const gracePeriodHours = 24;
1392
+ let withinGracePeriod = false;
1336
1393
  if (lastValidated) {
1337
1394
  const lastValidatedDate = new Date(lastValidated);
1338
- const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
1339
- strapi.log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? "ACTIVE" : "EXPIRED"})`);
1340
- } else {
1341
- strapi.log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
1395
+ const hoursSinceValidation = (now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60);
1396
+ withinGracePeriod = hoursSinceValidation < gracePeriodHours;
1342
1397
  }
1343
- } else {
1344
- strapi.log.info(` [NONE] No license key stored`);
1345
- }
1346
- strapi.log.info("──────────────────────────────────────────────────────────");
1347
- if (licenseKey) {
1348
- strapi.log.info("[VERIFY] Verifying stored license key...");
1349
- const verification = await this.verifyLicense(licenseKey, true);
1350
- if (verification.valid) {
1351
- if (verification.gracePeriod) {
1352
- strapi.log.info("[SUCCESS] License accepted (offline mode / grace period)");
1398
+ log.info("──────────────────────────────────────────────────────────");
1399
+ log.info(`[STORE] Plugin Store Check:`);
1400
+ if (licenseKey) {
1401
+ log.info(` [OK] License Key found: ${licenseKey}`);
1402
+ log.info(` [KEY] Key (short): ${licenseKey.substring(0, 8)}...`);
1403
+ if (lastValidated) {
1404
+ const lastValidatedDate = new Date(lastValidated);
1405
+ const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60));
1406
+ log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? "ACTIVE" : "EXPIRED"})`);
1353
1407
  } else {
1354
- strapi.log.info("[SUCCESS] License is valid and active");
1408
+ log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
1355
1409
  }
1356
- const pingInterval = this.startPinging(licenseKey, 15);
1357
- strapi.licenseGuard = {
1358
- licenseKey,
1359
- pingInterval,
1360
- data: verification.data
1361
- };
1362
- return { valid: true, data: verification.data };
1363
1410
  } else {
1364
- strapi.log.warn("[WARN] Stored license is invalid or expired");
1365
- if (!withinGracePeriod) {
1366
- await pluginStore.delete({ key: "licenseKey" });
1367
- await pluginStore.delete({ key: "lastValidated" });
1411
+ log.info(` [NONE] No license key stored`);
1412
+ }
1413
+ log.info("──────────────────────────────────────────────────────────");
1414
+ if (licenseKey) {
1415
+ log.info("[VERIFY] Verifying stored license key...");
1416
+ const verification = await this.verifyLicense(licenseKey, true);
1417
+ if (verification.valid) {
1418
+ if (verification.gracePeriod) {
1419
+ log.info("[SUCCESS] License accepted (offline mode / grace period)");
1420
+ } else {
1421
+ log.info("[SUCCESS] License is valid and active");
1422
+ }
1423
+ const pingInterval = this.startPinging(licenseKey, 15);
1424
+ strapi.licenseGuard = {
1425
+ licenseKey,
1426
+ pingInterval,
1427
+ data: verification.data
1428
+ };
1429
+ return { valid: true, data: verification.data };
1430
+ } else {
1431
+ log.warn("[WARN] Stored license is invalid or expired");
1432
+ if (!withinGracePeriod) {
1433
+ await pluginStore.delete({ key: "licenseKey" });
1434
+ await pluginStore.delete({ key: "lastValidated" });
1435
+ }
1368
1436
  }
1369
1437
  }
1438
+ log.warn("[WARN] No valid license found. Plugin will run with limited functionality.");
1439
+ return { valid: false, demo: true };
1440
+ } catch (error) {
1441
+ log.error("[ERROR] Error initializing license guard:", error);
1442
+ return { valid: false, error: error.message };
1370
1443
  }
1371
- strapi.log.warn("[WARN] No valid license found. Plugin will run with limited functionality.");
1372
- return { valid: false, demo: true };
1373
- } catch (error) {
1374
- strapi.log.error("[ERROR] Error initializing license guard:", error);
1375
- return { valid: false, error: error.message };
1376
- }
1377
- },
1378
- /**
1379
- * Store license key after creation
1380
- */
1381
- async storeLicenseKey(licenseKey) {
1382
- try {
1383
- strapi.log.info(`[STORE] Storing license key: ${licenseKey}`);
1384
- const pluginStore = strapi.store({
1385
- type: "plugin",
1386
- name: "magic-mark"
1387
- });
1388
- await pluginStore.set({ key: "licenseKey", value: licenseKey });
1389
- await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
1390
- const stored = await pluginStore.get({ key: "licenseKey" });
1391
- if (stored === licenseKey) {
1392
- strapi.log.info("[SUCCESS] License key stored and verified successfully");
1393
- return true;
1394
- } else {
1395
- strapi.log.error("[ERROR] License key storage verification failed");
1444
+ },
1445
+ /**
1446
+ * Store license key after creation
1447
+ */
1448
+ async storeLicenseKey(licenseKey) {
1449
+ try {
1450
+ log.info(`[STORE] Storing license key: ${licenseKey}`);
1451
+ const pluginStore = strapi.store({
1452
+ type: "plugin",
1453
+ name: "magic-mark"
1454
+ });
1455
+ await pluginStore.set({ key: "licenseKey", value: licenseKey });
1456
+ await pluginStore.set({ key: "lastValidated", value: (/* @__PURE__ */ new Date()).toISOString() });
1457
+ const stored = await pluginStore.get({ key: "licenseKey" });
1458
+ if (stored === licenseKey) {
1459
+ log.info("[SUCCESS] License key stored and verified successfully");
1460
+ return true;
1461
+ } else {
1462
+ log.error("[ERROR] License key storage verification failed");
1463
+ return false;
1464
+ }
1465
+ } catch (error) {
1466
+ log.error("[ERROR] Error storing license key:", error);
1396
1467
  return false;
1397
1468
  }
1398
- } catch (error) {
1399
- strapi.log.error("[ERROR] Error storing license key:", error);
1400
- return false;
1401
- }
1402
- },
1403
- /**
1404
- * Get bookmark limit based on license tier
1405
- * @returns Bookmark limit: 10 (free), 50 (premium), -1 (advanced/unlimited)
1406
- */
1407
- async getBookmarkLimit() {
1408
- try {
1409
- const licenseGuard = strapi.licenseGuard;
1410
- const licenseData = licenseGuard?.data;
1411
- if (licenseData?.featureAdvanced) {
1412
- return -1;
1413
- }
1414
- if (licenseData?.featurePremium) {
1415
- return 50;
1469
+ },
1470
+ /**
1471
+ * Get bookmark limit based on license tier
1472
+ * @returns Bookmark limit: 10 (free), 50 (premium), -1 (advanced/unlimited)
1473
+ */
1474
+ async getBookmarkLimit() {
1475
+ try {
1476
+ const licenseGuard = strapi.licenseGuard;
1477
+ const licenseData = licenseGuard?.data;
1478
+ if (licenseData?.featureAdvanced) {
1479
+ return -1;
1480
+ }
1481
+ if (licenseData?.featurePremium) {
1482
+ return 50;
1483
+ }
1484
+ return 10;
1485
+ } catch (error) {
1486
+ log.debug("[LICENSE] Error getting bookmark limit, using free tier");
1487
+ return 10;
1416
1488
  }
1417
- return 10;
1418
- } catch (error) {
1419
- strapi.log.debug("[LICENSE] Error getting bookmark limit, using free tier");
1420
- return 10;
1421
- }
1422
- },
1423
- /**
1424
- * Check if user can create more bookmarks
1425
- * @param userId - User's documentId
1426
- * @returns Object with canCreate boolean and current/max counts
1427
- */
1428
- async canCreateBookmark(userId) {
1429
- try {
1430
- const limit = await this.getBookmarkLimit();
1431
- if (limit === -1) {
1432
- return { canCreate: true, current: 0, max: -1 };
1489
+ },
1490
+ /**
1491
+ * Check if user can create more bookmarks
1492
+ * @param userId - User's documentId
1493
+ * @returns Object with canCreate boolean and current/max counts
1494
+ */
1495
+ async canCreateBookmark(userId) {
1496
+ try {
1497
+ const limit = await this.getBookmarkLimit();
1498
+ if (limit === -1) {
1499
+ return { canCreate: true, current: 0, max: -1 };
1500
+ }
1501
+ const BOOKMARK_UID2 = "plugin::magic-mark.bookmark";
1502
+ const bookmarks = await strapi.documents(BOOKMARK_UID2).findMany({
1503
+ filters: { creatorId: userId }
1504
+ });
1505
+ const currentCount = bookmarks?.length || 0;
1506
+ if (currentCount >= limit) {
1507
+ return {
1508
+ canCreate: false,
1509
+ current: currentCount,
1510
+ max: limit,
1511
+ message: `Bookmark limit reached (${currentCount}/${limit}). Upgrade to create more bookmarks.`
1512
+ };
1513
+ }
1514
+ return { canCreate: true, current: currentCount, max: limit };
1515
+ } catch (error) {
1516
+ log.error("[LICENSE] Error checking bookmark limit:", error);
1517
+ return { canCreate: true, current: 0, max: 10 };
1433
1518
  }
1434
- const BOOKMARK_UID2 = "plugin::magic-mark.bookmark";
1435
- const bookmarks = await strapi.documents(BOOKMARK_UID2).findMany({
1436
- filters: { creatorId: userId }
1437
- });
1438
- const currentCount = bookmarks?.length || 0;
1439
- if (currentCount >= limit) {
1519
+ },
1520
+ /**
1521
+ * Get current license limits and feature flags
1522
+ * @param userId - User's documentId for bookmark count
1523
+ * @returns License limits object
1524
+ */
1525
+ async getLicenseLimits(userId) {
1526
+ try {
1527
+ const license2 = await this.getCurrentLicense();
1528
+ log.info("[LICENSE] getLicenseLimits - license exists:", !!license2);
1529
+ if (license2) {
1530
+ log.info("[LICENSE] getLicenseLimits - featurePremium:", license2.featurePremium);
1531
+ log.info("[LICENSE] getLicenseLimits - featureAdvanced:", license2.featureAdvanced);
1532
+ }
1533
+ let tier = "free";
1534
+ if (license2?.featureEnterprise === true) tier = "enterprise";
1535
+ else if (license2?.featureAdvanced === true) tier = "advanced";
1536
+ else if (license2?.featurePremium === true) tier = "premium";
1537
+ const isPremium = tier === "premium" || tier === "advanced" || tier === "enterprise";
1538
+ const isAdvanced = tier === "advanced" || tier === "enterprise";
1539
+ log.info("[LICENSE] getLicenseLimits - detected tier:", tier);
1540
+ const bookmarkCheck = await this.canCreateBookmark(userId);
1440
1541
  return {
1441
- canCreate: false,
1442
- current: currentCount,
1443
- max: limit,
1444
- message: `Bookmark limit reached (${currentCount}/${limit}). Upgrade to create more bookmarks.`
1542
+ maxBookmarks: bookmarkCheck.max,
1543
+ currentBookmarks: bookmarkCheck.current,
1544
+ canCreate: bookmarkCheck.canCreate,
1545
+ tier,
1546
+ features: {
1547
+ queryHistory: isPremium || isAdvanced,
1548
+ export: isPremium || isAdvanced,
1549
+ analytics: isAdvanced,
1550
+ bulkOperations: isAdvanced,
1551
+ customIntegrations: isAdvanced
1552
+ }
1553
+ };
1554
+ } catch (error) {
1555
+ log.error("[LICENSE] Error getting license limits:", error);
1556
+ return {
1557
+ maxBookmarks: 10,
1558
+ currentBookmarks: 0,
1559
+ canCreate: true,
1560
+ tier: "free",
1561
+ features: {
1562
+ queryHistory: false,
1563
+ export: false,
1564
+ analytics: false,
1565
+ bulkOperations: false,
1566
+ customIntegrations: false
1567
+ }
1445
1568
  };
1446
1569
  }
1447
- return { canCreate: true, current: currentCount, max: limit };
1448
- } catch (error) {
1449
- strapi.log.error("[LICENSE] Error checking bookmark limit:", error);
1450
- return { canCreate: true, current: 0, max: 10 };
1451
- }
1452
- },
1453
- /**
1454
- * Get current license limits and feature flags
1455
- * @param userId - User's documentId for bookmark count
1456
- * @returns License limits object
1457
- */
1458
- async getLicenseLimits(userId) {
1459
- try {
1460
- const license2 = await this.getCurrentLicense();
1461
- strapi.log.info("[LICENSE] getLicenseLimits - license exists:", !!license2);
1462
- if (license2) {
1463
- strapi.log.info("[LICENSE] getLicenseLimits - featurePremium:", license2.featurePremium);
1464
- strapi.log.info("[LICENSE] getLicenseLimits - featureAdvanced:", license2.featureAdvanced);
1465
- }
1466
- let tier = "free";
1467
- if (license2?.featureEnterprise === true) tier = "enterprise";
1468
- else if (license2?.featureAdvanced === true) tier = "advanced";
1469
- else if (license2?.featurePremium === true) tier = "premium";
1470
- const isPremium = tier === "premium" || tier === "advanced" || tier === "enterprise";
1471
- const isAdvanced = tier === "advanced" || tier === "enterprise";
1472
- strapi.log.info("[LICENSE] getLicenseLimits - detected tier:", tier);
1473
- const bookmarkCheck = await this.canCreateBookmark(userId);
1474
- return {
1475
- maxBookmarks: bookmarkCheck.max,
1476
- currentBookmarks: bookmarkCheck.current,
1477
- canCreate: bookmarkCheck.canCreate,
1478
- tier,
1479
- features: {
1480
- queryHistory: isPremium || isAdvanced,
1481
- export: isPremium || isAdvanced,
1482
- analytics: isAdvanced,
1483
- bulkOperations: isAdvanced,
1484
- customIntegrations: isAdvanced
1485
- }
1486
- };
1487
- } catch (error) {
1488
- strapi.log.error("[LICENSE] Error getting license limits:", error);
1489
- return {
1490
- maxBookmarks: 10,
1491
- currentBookmarks: 0,
1492
- canCreate: true,
1493
- tier: "free",
1494
- features: {
1495
- queryHistory: false,
1496
- export: false,
1497
- analytics: false,
1498
- bulkOperations: false,
1499
- customIntegrations: false
1500
- }
1501
- };
1502
- }
1503
- },
1504
- /**
1505
- * Check if a specific feature is available based on license
1506
- * @param feature - Feature key to check
1507
- * @returns Boolean indicating if feature is available
1508
- */
1509
- hasFeature(feature) {
1510
- try {
1511
- const licenseGuard = strapi.licenseGuard;
1512
- const licenseData = licenseGuard?.data;
1513
- const isPremium = licenseData?.featurePremium || false;
1514
- const isAdvanced = licenseData?.featureAdvanced || false;
1515
- const featureRequirements = {
1516
- // Free tier
1517
- basicBookmarks: "free",
1518
- basicFilters: "free",
1519
- // Premium tier
1520
- extendedBookmarks: "premium",
1521
- queryHistory: "premium",
1522
- exportBookmarks: "premium",
1523
- sharedBookmarks: "premium",
1524
- // Advanced tier
1525
- unlimitedBookmarks: "advanced",
1526
- advancedFilters: "advanced",
1527
- subGroups: "advanced",
1528
- bulkOperations: "advanced",
1529
- analytics: "advanced",
1530
- customIntegrations: "advanced"
1531
- };
1532
- const requiredTier = featureRequirements[feature] || "free";
1533
- let currentTierLevel = 0;
1534
- if (isAdvanced) currentTierLevel = 2;
1535
- else if (isPremium) currentTierLevel = 1;
1536
- const tierLevels = {
1537
- free: 0,
1538
- premium: 1,
1539
- advanced: 2
1540
- };
1541
- return currentTierLevel >= tierLevels[requiredTier];
1542
- } catch (error) {
1543
- strapi.log.debug("[LICENSE] Error checking feature:", feature);
1544
- return false;
1545
- }
1546
- },
1547
- /**
1548
- * Get current tier name
1549
- * @returns 'free' | 'premium' | 'advanced'
1550
- */
1551
- getCurrentTier() {
1552
- try {
1570
+ },
1571
+ /**
1572
+ * Check if a specific feature is available based on license
1573
+ * @param feature - Feature key to check
1574
+ * @returns Boolean indicating if feature is available
1575
+ */
1576
+ hasFeature(feature) {
1577
+ try {
1578
+ const licenseGuard = strapi.licenseGuard;
1579
+ const licenseData = licenseGuard?.data;
1580
+ const isPremium = licenseData?.featurePremium || false;
1581
+ const isAdvanced = licenseData?.featureAdvanced || false;
1582
+ const featureRequirements = {
1583
+ // Free tier
1584
+ basicBookmarks: "free",
1585
+ basicFilters: "free",
1586
+ // Premium tier
1587
+ extendedBookmarks: "premium",
1588
+ queryHistory: "premium",
1589
+ exportBookmarks: "premium",
1590
+ sharedBookmarks: "premium",
1591
+ // Advanced tier
1592
+ unlimitedBookmarks: "advanced",
1593
+ advancedFilters: "advanced",
1594
+ subGroups: "advanced",
1595
+ bulkOperations: "advanced",
1596
+ analytics: "advanced",
1597
+ customIntegrations: "advanced"
1598
+ };
1599
+ const requiredTier = featureRequirements[feature] || "free";
1600
+ let currentTierLevel = 0;
1601
+ if (isAdvanced) currentTierLevel = 2;
1602
+ else if (isPremium) currentTierLevel = 1;
1603
+ const tierLevels = {
1604
+ free: 0,
1605
+ premium: 1,
1606
+ advanced: 2
1607
+ };
1608
+ return currentTierLevel >= tierLevels[requiredTier];
1609
+ } catch (error) {
1610
+ log.debug("[LICENSE] Error checking feature:", feature);
1611
+ return false;
1612
+ }
1613
+ },
1614
+ /**
1615
+ * Get current tier name
1616
+ * @returns 'free' | 'premium' | 'advanced'
1617
+ */
1618
+ getCurrentTier() {
1619
+ try {
1620
+ const licenseGuard = strapi.licenseGuard;
1621
+ const licenseData = licenseGuard?.data;
1622
+ if (licenseData?.featureAdvanced) return "advanced";
1623
+ if (licenseData?.featurePremium) return "premium";
1624
+ return "free";
1625
+ } catch (error) {
1626
+ return "free";
1627
+ }
1628
+ },
1629
+ /**
1630
+ * Cleanup on plugin destroy
1631
+ */
1632
+ cleanup() {
1553
1633
  const licenseGuard = strapi.licenseGuard;
1554
- const licenseData = licenseGuard?.data;
1555
- if (licenseData?.featureAdvanced) return "advanced";
1556
- if (licenseData?.featurePremium) return "premium";
1557
- return "free";
1558
- } catch (error) {
1559
- return "free";
1560
- }
1561
- },
1562
- /**
1563
- * Cleanup on plugin destroy
1564
- */
1565
- cleanup() {
1566
- const licenseGuard = strapi.licenseGuard;
1567
- if (licenseGuard && licenseGuard.pingInterval) {
1568
- clearInterval(licenseGuard.pingInterval);
1569
- strapi.log.info("[STOP] License pinging stopped");
1634
+ if (licenseGuard && licenseGuard.pingInterval) {
1635
+ clearInterval(licenseGuard.pingInterval);
1636
+ log.info("[STOP] License pinging stopped");
1637
+ }
1570
1638
  }
1571
- }
1572
- });
1639
+ };
1640
+ };
1573
1641
  const services = {
1574
1642
  bookmarks: bookmarkService,
1575
1643
  "license-guard": licenseGuardService