strapi-plugin-magic-sessionmanager 4.1.0 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/server/index.js +635 -559
- package/dist/server/index.mjs +635 -559
- package/package.json +1 -1
- package/server/src/bootstrap.js +57 -53
- package/server/src/config/index.js +3 -0
- package/server/src/destroy.js +7 -3
- package/server/src/register.js +9 -5
- package/server/src/services/license-guard.js +21 -16
- package/server/src/services/session.js +24 -19
- package/server/src/utils/logger.js +84 -0
package/dist/server/index.js
CHANGED
|
@@ -7,21 +7,83 @@ const require$$1__default = /* @__PURE__ */ _interopDefault(require$$1);
|
|
|
7
7
|
function getDefaultExportFromCjs(x) {
|
|
8
8
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
9
9
|
}
|
|
10
|
+
const PLUGIN_NAME = "magic-sessionmanager";
|
|
11
|
+
const PREFIX = "[magic-sessionmanager]";
|
|
12
|
+
function formatMessage(prefix, args) {
|
|
13
|
+
if (args.length === 0) return prefix;
|
|
14
|
+
const parts = args.map(
|
|
15
|
+
(arg) => typeof arg === "string" ? arg : JSON.stringify(arg)
|
|
16
|
+
);
|
|
17
|
+
return `${prefix} ${parts.join(" ")}`;
|
|
18
|
+
}
|
|
19
|
+
function createLogger$5(strapi2) {
|
|
20
|
+
const getDebugMode = () => {
|
|
21
|
+
try {
|
|
22
|
+
const config2 = strapi2.config.get(`plugin::${PLUGIN_NAME}`) || {};
|
|
23
|
+
return config2.debug === true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
/**
|
|
30
|
+
* Log info - only when debug: true
|
|
31
|
+
*/
|
|
32
|
+
info: (...args) => {
|
|
33
|
+
if (getDebugMode()) {
|
|
34
|
+
strapi2.log.info(formatMessage(PREFIX, args));
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Log debug - only when debug: true
|
|
39
|
+
*/
|
|
40
|
+
debug: (...args) => {
|
|
41
|
+
if (getDebugMode()) {
|
|
42
|
+
strapi2.log.debug(formatMessage(PREFIX, args));
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Log warning - only when debug: true
|
|
47
|
+
*/
|
|
48
|
+
warn: (...args) => {
|
|
49
|
+
if (getDebugMode()) {
|
|
50
|
+
strapi2.log.warn(formatMessage(PREFIX, args));
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Log error - only when debug: true
|
|
55
|
+
*/
|
|
56
|
+
error: (...args) => {
|
|
57
|
+
if (getDebugMode()) {
|
|
58
|
+
strapi2.log.error(formatMessage(PREFIX, args));
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Force log - always logged (for critical errors only)
|
|
63
|
+
*/
|
|
64
|
+
forceError: (...args) => {
|
|
65
|
+
strapi2.log.error(formatMessage(PREFIX, args));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
var logger = { createLogger: createLogger$5 };
|
|
70
|
+
const { createLogger: createLogger$4 } = logger;
|
|
10
71
|
var register$1 = async ({ strapi: strapi2 }) => {
|
|
11
|
-
|
|
72
|
+
const log = createLogger$4(strapi2);
|
|
73
|
+
log.info("[START] Plugin registration starting...");
|
|
12
74
|
try {
|
|
13
75
|
const userCT = strapi2.contentType("plugin::users-permissions.user");
|
|
14
76
|
if (!userCT) {
|
|
15
|
-
|
|
77
|
+
log.error("User content type not found");
|
|
16
78
|
return;
|
|
17
79
|
}
|
|
18
80
|
if (userCT.attributes && userCT.attributes.sessions) {
|
|
19
81
|
delete userCT.attributes.sessions;
|
|
20
|
-
|
|
82
|
+
log.info("[SUCCESS] Removed sessions field from User content type");
|
|
21
83
|
}
|
|
22
|
-
|
|
84
|
+
log.info("[SUCCESS] Plugin registered successfully");
|
|
23
85
|
} catch (err) {
|
|
24
|
-
|
|
86
|
+
log.error("[ERROR] Registration error:", err);
|
|
25
87
|
}
|
|
26
88
|
};
|
|
27
89
|
const getClientIp$1 = (ctx) => {
|
|
@@ -187,49 +249,51 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
|
187
249
|
};
|
|
188
250
|
const getClientIp = getClientIp_1;
|
|
189
251
|
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2 } = encryption;
|
|
252
|
+
const { createLogger: createLogger$3 } = logger;
|
|
190
253
|
const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
|
|
191
254
|
const USER_UID$2 = "plugin::users-permissions.user";
|
|
192
255
|
var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
193
|
-
|
|
256
|
+
const log = createLogger$3(strapi2);
|
|
257
|
+
log.info("[START] Bootstrap starting...");
|
|
194
258
|
try {
|
|
195
259
|
const licenseGuardService = strapi2.plugin("magic-sessionmanager").service("license-guard");
|
|
196
260
|
setTimeout(async () => {
|
|
197
261
|
const licenseStatus = await licenseGuardService.initialize();
|
|
198
262
|
if (!licenseStatus.valid) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
263
|
+
log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
264
|
+
log.error("║ [ERROR] SESSION MANAGER - NO VALID LICENSE ║");
|
|
265
|
+
log.error("║ ║");
|
|
266
|
+
log.error("║ This plugin requires a valid license to operate. ║");
|
|
267
|
+
log.error("║ Please activate your license via Admin UI: ║");
|
|
268
|
+
log.error("║ Go to Settings → Sessions → License ║");
|
|
269
|
+
log.error("║ ║");
|
|
270
|
+
log.error("║ The plugin will run with limited functionality until ║");
|
|
271
|
+
log.error("║ a valid license is activated. ║");
|
|
272
|
+
log.error("╚════════════════════════════════════════════════════════════════╝");
|
|
209
273
|
} else if (licenseStatus.valid) {
|
|
210
274
|
const pluginStore = strapi2.store({
|
|
211
275
|
type: "plugin",
|
|
212
276
|
name: "magic-sessionmanager"
|
|
213
277
|
});
|
|
214
278
|
const storedKey = await pluginStore.get({ key: "licenseKey" });
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
279
|
+
log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
280
|
+
log.info("║ [SUCCESS] SESSION MANAGER LICENSE ACTIVE ║");
|
|
281
|
+
log.info("║ ║");
|
|
218
282
|
if (licenseStatus.data) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
283
|
+
log.info(`║ License: ${licenseStatus.data.licenseKey}`.padEnd(66) + "║");
|
|
284
|
+
log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + "║");
|
|
285
|
+
log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + "║");
|
|
222
286
|
} else if (storedKey) {
|
|
223
|
-
|
|
224
|
-
|
|
287
|
+
log.info(`║ License: ${storedKey} (Offline Mode)`.padEnd(66) + "║");
|
|
288
|
+
log.info(`║ Status: Grace Period Active`.padEnd(66) + "║");
|
|
225
289
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
290
|
+
log.info("║ ║");
|
|
291
|
+
log.info("║ [RELOAD] Auto-pinging every 15 minutes ║");
|
|
292
|
+
log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
229
293
|
}
|
|
230
294
|
}, 3e3);
|
|
231
295
|
const sessionService = strapi2.plugin("magic-sessionmanager").service("session");
|
|
232
|
-
|
|
296
|
+
log.info("Running initial session cleanup...");
|
|
233
297
|
await sessionService.cleanupInactiveSessions();
|
|
234
298
|
const cleanupInterval = 30 * 60 * 1e3;
|
|
235
299
|
const cleanupIntervalHandle = setInterval(async () => {
|
|
@@ -237,10 +301,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
237
301
|
const service = strapi2.plugin("magic-sessionmanager").service("session");
|
|
238
302
|
await service.cleanupInactiveSessions();
|
|
239
303
|
} catch (err) {
|
|
240
|
-
|
|
304
|
+
log.error("Periodic cleanup error:", err);
|
|
241
305
|
}
|
|
242
306
|
}, cleanupInterval);
|
|
243
|
-
|
|
307
|
+
log.info("[TIME] Periodic cleanup scheduled (every 30 minutes)");
|
|
244
308
|
if (!strapi2.sessionManagerIntervals) {
|
|
245
309
|
strapi2.sessionManagerIntervals = {};
|
|
246
310
|
}
|
|
@@ -272,12 +336,12 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
272
336
|
});
|
|
273
337
|
if (matchingSession) {
|
|
274
338
|
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
275
|
-
|
|
339
|
+
log.info(`[LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
|
|
276
340
|
}
|
|
277
341
|
ctx.status = 200;
|
|
278
342
|
ctx.body = { message: "Logged out successfully" };
|
|
279
343
|
} catch (err) {
|
|
280
|
-
|
|
344
|
+
log.error("Logout error:", err);
|
|
281
345
|
ctx.status = 200;
|
|
282
346
|
ctx.body = { message: "Logged out successfully" };
|
|
283
347
|
}
|
|
@@ -286,7 +350,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
286
350
|
auth: false
|
|
287
351
|
}
|
|
288
352
|
}]);
|
|
289
|
-
|
|
353
|
+
log.info("[SUCCESS] /api/auth/logout route registered");
|
|
290
354
|
strapi2.server.use(async (ctx, next) => {
|
|
291
355
|
await next();
|
|
292
356
|
const isAuthLocal = ctx.path === "/api/auth/local" && ctx.method === "POST";
|
|
@@ -296,7 +360,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
296
360
|
const user = ctx.body.user;
|
|
297
361
|
const ip = getClientIp(ctx);
|
|
298
362
|
const userAgent = ctx.request.headers?.["user-agent"] || ctx.request.header?.["user-agent"] || "unknown";
|
|
299
|
-
|
|
363
|
+
log.info(`[CHECK] Login detected! User: ${user.documentId || user.id} (${user.email || user.username}) from IP: ${ip}`);
|
|
300
364
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
301
365
|
let shouldBlock = false;
|
|
302
366
|
let blockReason = "";
|
|
@@ -334,11 +398,11 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
334
398
|
}
|
|
335
399
|
}
|
|
336
400
|
} catch (geoErr) {
|
|
337
|
-
|
|
401
|
+
log.warn("Geolocation check failed:", geoErr.message);
|
|
338
402
|
}
|
|
339
403
|
}
|
|
340
404
|
if (shouldBlock) {
|
|
341
|
-
|
|
405
|
+
log.warn(`[BLOCKED] Blocking login: ${blockReason}`);
|
|
342
406
|
ctx.status = 403;
|
|
343
407
|
ctx.body = {
|
|
344
408
|
error: {
|
|
@@ -363,7 +427,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
363
427
|
refreshToken: ctx.body.refreshToken
|
|
364
428
|
// Store Refresh Token (encrypted) if exists
|
|
365
429
|
});
|
|
366
|
-
|
|
430
|
+
log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
367
431
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
368
432
|
try {
|
|
369
433
|
const notificationService = strapi2.plugin("magic-sessionmanager").service("notifications");
|
|
@@ -397,15 +461,15 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
397
461
|
}
|
|
398
462
|
}
|
|
399
463
|
} catch (notifErr) {
|
|
400
|
-
|
|
464
|
+
log.warn("Notification failed:", notifErr.message);
|
|
401
465
|
}
|
|
402
466
|
}
|
|
403
467
|
} catch (err) {
|
|
404
|
-
|
|
468
|
+
log.error("[ERROR] Error creating session:", err);
|
|
405
469
|
}
|
|
406
470
|
}
|
|
407
471
|
});
|
|
408
|
-
|
|
472
|
+
log.info("[SUCCESS] Login/Logout interceptor middleware mounted");
|
|
409
473
|
strapi2.server.use(async (ctx, next) => {
|
|
410
474
|
const isRefreshToken = ctx.path === "/api/auth/refresh" && ctx.method === "POST";
|
|
411
475
|
if (isRefreshToken) {
|
|
@@ -427,7 +491,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
427
491
|
}
|
|
428
492
|
});
|
|
429
493
|
if (!matchingSession) {
|
|
430
|
-
|
|
494
|
+
log.warn("[BLOCKED] Blocked refresh token request - no active session");
|
|
431
495
|
ctx.status = 401;
|
|
432
496
|
ctx.body = {
|
|
433
497
|
error: {
|
|
@@ -438,10 +502,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
438
502
|
};
|
|
439
503
|
return;
|
|
440
504
|
}
|
|
441
|
-
|
|
505
|
+
log.info(`[SUCCESS] Refresh token allowed for session ${matchingSession.documentId}`);
|
|
442
506
|
}
|
|
443
507
|
} catch (err) {
|
|
444
|
-
|
|
508
|
+
log.error("Error checking refresh token:", err);
|
|
445
509
|
}
|
|
446
510
|
}
|
|
447
511
|
await next();
|
|
@@ -476,33 +540,33 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
476
540
|
lastActive: /* @__PURE__ */ new Date()
|
|
477
541
|
}
|
|
478
542
|
});
|
|
479
|
-
|
|
543
|
+
log.info(`[REFRESH] Tokens refreshed for session ${matchingSession.documentId}`);
|
|
480
544
|
}
|
|
481
545
|
}
|
|
482
546
|
} catch (err) {
|
|
483
|
-
|
|
547
|
+
log.error("Error updating refreshed tokens:", err);
|
|
484
548
|
}
|
|
485
549
|
}
|
|
486
550
|
});
|
|
487
|
-
|
|
551
|
+
log.info("[SUCCESS] Refresh Token interceptor middleware mounted");
|
|
488
552
|
strapi2.server.use(
|
|
489
553
|
lastSeen({ strapi: strapi2, sessionService })
|
|
490
554
|
);
|
|
491
|
-
|
|
492
|
-
await ensureContentApiPermissions(strapi2);
|
|
493
|
-
|
|
494
|
-
|
|
555
|
+
log.info("[SUCCESS] LastSeen middleware mounted");
|
|
556
|
+
await ensureContentApiPermissions(strapi2, log);
|
|
557
|
+
log.info("[SUCCESS] Bootstrap complete");
|
|
558
|
+
log.info("[READY] Session Manager ready! Sessions stored in plugin::magic-sessionmanager.session");
|
|
495
559
|
} catch (err) {
|
|
496
|
-
|
|
560
|
+
log.error("[ERROR] Bootstrap error:", err);
|
|
497
561
|
}
|
|
498
562
|
};
|
|
499
|
-
async function ensureContentApiPermissions(strapi2) {
|
|
563
|
+
async function ensureContentApiPermissions(strapi2, log) {
|
|
500
564
|
try {
|
|
501
565
|
const authenticatedRole = await strapi2.query("plugin::users-permissions.role").findOne({
|
|
502
566
|
where: { type: "authenticated" }
|
|
503
567
|
});
|
|
504
568
|
if (!authenticatedRole) {
|
|
505
|
-
|
|
569
|
+
log.warn("Authenticated role not found - skipping permission setup");
|
|
506
570
|
return;
|
|
507
571
|
}
|
|
508
572
|
const requiredActions = [
|
|
@@ -520,7 +584,7 @@ async function ensureContentApiPermissions(strapi2) {
|
|
|
520
584
|
const existingActions = existingPermissions.map((p) => p.action);
|
|
521
585
|
const missingActions = requiredActions.filter((action) => !existingActions.includes(action));
|
|
522
586
|
if (missingActions.length === 0) {
|
|
523
|
-
|
|
587
|
+
log.debug("Content-API permissions already configured");
|
|
524
588
|
return;
|
|
525
589
|
}
|
|
526
590
|
for (const action of missingActions) {
|
|
@@ -530,27 +594,31 @@ async function ensureContentApiPermissions(strapi2) {
|
|
|
530
594
|
role: authenticatedRole.id
|
|
531
595
|
}
|
|
532
596
|
});
|
|
533
|
-
|
|
597
|
+
log.info(`[PERMISSION] Enabled ${action} for authenticated users`);
|
|
534
598
|
}
|
|
535
|
-
|
|
599
|
+
log.info("[SUCCESS] Content-API permissions configured for authenticated users");
|
|
536
600
|
} catch (err) {
|
|
537
|
-
|
|
538
|
-
|
|
601
|
+
log.warn("Could not auto-configure permissions:", err.message);
|
|
602
|
+
log.warn("Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated");
|
|
539
603
|
}
|
|
540
604
|
}
|
|
605
|
+
const { createLogger: createLogger$2 } = logger;
|
|
541
606
|
var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
607
|
+
const log = createLogger$2(strapi2);
|
|
542
608
|
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
543
609
|
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
544
|
-
|
|
610
|
+
log.info("🛑 License pinging stopped");
|
|
545
611
|
}
|
|
546
612
|
if (strapi2.sessionManagerIntervals && strapi2.sessionManagerIntervals.cleanup) {
|
|
547
613
|
clearInterval(strapi2.sessionManagerIntervals.cleanup);
|
|
548
|
-
|
|
614
|
+
log.info("🛑 Session cleanup interval stopped");
|
|
549
615
|
}
|
|
550
|
-
|
|
616
|
+
log.info("[SUCCESS] Plugin cleanup completed");
|
|
551
617
|
};
|
|
552
618
|
var config$1 = {
|
|
553
619
|
default: {
|
|
620
|
+
// Enable debug logging (set to true to see all plugin logs)
|
|
621
|
+
debug: false,
|
|
554
622
|
// Rate limit for lastSeen updates (in milliseconds)
|
|
555
623
|
lastSeenRateLimit: 3e4,
|
|
556
624
|
// 30 seconds
|
|
@@ -1496,562 +1564,570 @@ var controllers$1 = {
|
|
|
1496
1564
|
settings
|
|
1497
1565
|
};
|
|
1498
1566
|
const { encryptToken, decryptToken, generateSessionId } = encryption;
|
|
1567
|
+
const { createLogger: createLogger$1 } = logger;
|
|
1499
1568
|
const SESSION_UID = "plugin::magic-sessionmanager.session";
|
|
1500
1569
|
const USER_UID = "plugin::users-permissions.user";
|
|
1501
|
-
var session$1 = ({ strapi: strapi2 }) =>
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
// userId should be documentId (string)
|
|
1517
|
-
ipAddress: ip.substring(0, 45),
|
|
1518
|
-
userAgent: userAgent.substring(0, 500),
|
|
1519
|
-
loginTime: now,
|
|
1520
|
-
lastActive: now,
|
|
1521
|
-
isActive: true,
|
|
1522
|
-
token: encryptedToken,
|
|
1523
|
-
// [SUCCESS] Encrypted Access Token
|
|
1524
|
-
refreshToken: encryptedRefreshToken,
|
|
1525
|
-
// [SUCCESS] Encrypted Refresh Token
|
|
1526
|
-
sessionId
|
|
1527
|
-
// [SUCCESS] Unique identifier
|
|
1528
|
-
}
|
|
1529
|
-
});
|
|
1530
|
-
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
1531
|
-
return session2;
|
|
1532
|
-
} catch (err) {
|
|
1533
|
-
strapi2.log.error("[magic-sessionmanager] Error creating session:", err);
|
|
1534
|
-
throw err;
|
|
1535
|
-
}
|
|
1536
|
-
},
|
|
1537
|
-
/**
|
|
1538
|
-
* Terminate a session or all sessions for a user
|
|
1539
|
-
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1540
|
-
* @param {Object} params - { sessionId | userId }
|
|
1541
|
-
* @returns {Promise<void>}
|
|
1542
|
-
*/
|
|
1543
|
-
async terminateSession({ sessionId, userId }) {
|
|
1544
|
-
try {
|
|
1545
|
-
const now = /* @__PURE__ */ new Date();
|
|
1546
|
-
if (sessionId) {
|
|
1547
|
-
await strapi2.documents(SESSION_UID).update({
|
|
1548
|
-
documentId: sessionId,
|
|
1570
|
+
var session$1 = ({ strapi: strapi2 }) => {
|
|
1571
|
+
const log = createLogger$1(strapi2);
|
|
1572
|
+
return {
|
|
1573
|
+
/**
|
|
1574
|
+
* Create a new session record
|
|
1575
|
+
* @param {Object} params - { userId, ip, userAgent, token, refreshToken }
|
|
1576
|
+
* @returns {Promise<Object>} Created session
|
|
1577
|
+
*/
|
|
1578
|
+
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
|
|
1579
|
+
try {
|
|
1580
|
+
const now = /* @__PURE__ */ new Date();
|
|
1581
|
+
const sessionId = generateSessionId(userId);
|
|
1582
|
+
const encryptedToken = token ? encryptToken(token) : null;
|
|
1583
|
+
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1584
|
+
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1549
1585
|
data: {
|
|
1550
|
-
|
|
1551
|
-
|
|
1586
|
+
user: userId,
|
|
1587
|
+
// userId should be documentId (string)
|
|
1588
|
+
ipAddress: ip.substring(0, 45),
|
|
1589
|
+
userAgent: userAgent.substring(0, 500),
|
|
1590
|
+
loginTime: now,
|
|
1591
|
+
lastActive: now,
|
|
1592
|
+
isActive: true,
|
|
1593
|
+
token: encryptedToken,
|
|
1594
|
+
// [SUCCESS] Encrypted Access Token
|
|
1595
|
+
refreshToken: encryptedRefreshToken,
|
|
1596
|
+
// [SUCCESS] Encrypted Refresh Token
|
|
1597
|
+
sessionId
|
|
1598
|
+
// [SUCCESS] Unique identifier
|
|
1552
1599
|
}
|
|
1553
1600
|
});
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1601
|
+
log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
1602
|
+
return session2;
|
|
1603
|
+
} catch (err) {
|
|
1604
|
+
log.error("Error creating session:", err);
|
|
1605
|
+
throw err;
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1608
|
+
/**
|
|
1609
|
+
* Terminate a session or all sessions for a user
|
|
1610
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1611
|
+
* @param {Object} params - { sessionId | userId }
|
|
1612
|
+
* @returns {Promise<void>}
|
|
1613
|
+
*/
|
|
1614
|
+
async terminateSession({ sessionId, userId }) {
|
|
1615
|
+
try {
|
|
1616
|
+
const now = /* @__PURE__ */ new Date();
|
|
1617
|
+
if (sessionId) {
|
|
1571
1618
|
await strapi2.documents(SESSION_UID).update({
|
|
1572
|
-
documentId:
|
|
1619
|
+
documentId: sessionId,
|
|
1573
1620
|
data: {
|
|
1574
1621
|
isActive: false,
|
|
1575
1622
|
logoutTime: now
|
|
1576
1623
|
}
|
|
1577
1624
|
});
|
|
1625
|
+
log.info(`Session ${sessionId} terminated`);
|
|
1626
|
+
} else if (userId) {
|
|
1627
|
+
let userDocumentId = userId;
|
|
1628
|
+
if (!isNaN(userId)) {
|
|
1629
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1630
|
+
if (user) {
|
|
1631
|
+
userDocumentId = user.documentId;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1635
|
+
filters: {
|
|
1636
|
+
user: { documentId: userDocumentId },
|
|
1637
|
+
// Deep filtering syntax
|
|
1638
|
+
isActive: true
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
for (const session2 of activeSessions) {
|
|
1642
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1643
|
+
documentId: session2.documentId,
|
|
1644
|
+
data: {
|
|
1645
|
+
isActive: false,
|
|
1646
|
+
logoutTime: now
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
log.info(`All sessions terminated for user ${userDocumentId}`);
|
|
1578
1651
|
}
|
|
1579
|
-
|
|
1652
|
+
} catch (err) {
|
|
1653
|
+
log.error("Error terminating session:", err);
|
|
1654
|
+
throw err;
|
|
1580
1655
|
}
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1660
|
-
if (user) {
|
|
1661
|
-
userDocumentId = user.documentId;
|
|
1656
|
+
},
|
|
1657
|
+
/**
|
|
1658
|
+
* Get ALL sessions (active + inactive) with accurate online status
|
|
1659
|
+
* @returns {Promise<Array>} All sessions with enhanced data
|
|
1660
|
+
*/
|
|
1661
|
+
async getAllSessions() {
|
|
1662
|
+
try {
|
|
1663
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1664
|
+
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1665
|
+
sort: { loginTime: "desc" },
|
|
1666
|
+
limit: 1e3
|
|
1667
|
+
// Reasonable limit
|
|
1668
|
+
});
|
|
1669
|
+
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1670
|
+
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1671
|
+
const now = /* @__PURE__ */ new Date();
|
|
1672
|
+
const enhancedSessions = sessions.map((session2) => {
|
|
1673
|
+
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1674
|
+
const timeSinceActive = now - lastActiveTime;
|
|
1675
|
+
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1676
|
+
const { token, ...sessionWithoutToken } = session2;
|
|
1677
|
+
return {
|
|
1678
|
+
...sessionWithoutToken,
|
|
1679
|
+
isTrulyActive,
|
|
1680
|
+
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1681
|
+
};
|
|
1682
|
+
});
|
|
1683
|
+
return enhancedSessions;
|
|
1684
|
+
} catch (err) {
|
|
1685
|
+
log.error("Error getting all sessions:", err);
|
|
1686
|
+
throw err;
|
|
1687
|
+
}
|
|
1688
|
+
},
|
|
1689
|
+
/**
|
|
1690
|
+
* Get all active sessions with accurate online status
|
|
1691
|
+
* @returns {Promise<Array>} Active sessions with user data and online status
|
|
1692
|
+
*/
|
|
1693
|
+
async getActiveSessions() {
|
|
1694
|
+
try {
|
|
1695
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1696
|
+
filters: { isActive: true },
|
|
1697
|
+
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1698
|
+
sort: { loginTime: "desc" }
|
|
1699
|
+
});
|
|
1700
|
+
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1701
|
+
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1702
|
+
const now = /* @__PURE__ */ new Date();
|
|
1703
|
+
const enhancedSessions = sessions.map((session2) => {
|
|
1704
|
+
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1705
|
+
const timeSinceActive = now - lastActiveTime;
|
|
1706
|
+
const isTrulyActive = timeSinceActive < inactivityTimeout;
|
|
1707
|
+
const { token, ...sessionWithoutToken } = session2;
|
|
1708
|
+
return {
|
|
1709
|
+
...sessionWithoutToken,
|
|
1710
|
+
isTrulyActive,
|
|
1711
|
+
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1712
|
+
};
|
|
1713
|
+
});
|
|
1714
|
+
return enhancedSessions.filter((s) => s.isTrulyActive);
|
|
1715
|
+
} catch (err) {
|
|
1716
|
+
log.error("Error getting active sessions:", err);
|
|
1717
|
+
throw err;
|
|
1718
|
+
}
|
|
1719
|
+
},
|
|
1720
|
+
/**
|
|
1721
|
+
* Get all sessions for a specific user
|
|
1722
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1723
|
+
* @param {string|number} userId - User documentId or numeric id
|
|
1724
|
+
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
1725
|
+
*/
|
|
1726
|
+
async getUserSessions(userId) {
|
|
1727
|
+
try {
|
|
1728
|
+
let userDocumentId = userId;
|
|
1729
|
+
if (!isNaN(userId)) {
|
|
1730
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1731
|
+
if (user) {
|
|
1732
|
+
userDocumentId = user.documentId;
|
|
1733
|
+
}
|
|
1662
1734
|
}
|
|
1735
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1736
|
+
filters: { user: { documentId: userDocumentId } },
|
|
1737
|
+
sort: { loginTime: "desc" }
|
|
1738
|
+
});
|
|
1739
|
+
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1740
|
+
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1741
|
+
const now = /* @__PURE__ */ new Date();
|
|
1742
|
+
const enhancedSessions = sessions.map((session2) => {
|
|
1743
|
+
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1744
|
+
const timeSinceActive = now - lastActiveTime;
|
|
1745
|
+
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1746
|
+
const { token, ...sessionWithoutToken } = session2;
|
|
1747
|
+
return {
|
|
1748
|
+
...sessionWithoutToken,
|
|
1749
|
+
isTrulyActive,
|
|
1750
|
+
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1751
|
+
};
|
|
1752
|
+
});
|
|
1753
|
+
return enhancedSessions;
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
log.error("Error getting user sessions:", err);
|
|
1756
|
+
throw err;
|
|
1663
1757
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
const
|
|
1673
|
-
const
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
},
|
|
1688
|
-
/**
|
|
1689
|
-
* Update lastActive timestamp on session (rate-limited to avoid DB noise)
|
|
1690
|
-
* @param {Object} params - { userId, sessionId }
|
|
1691
|
-
* @returns {Promise<void>}
|
|
1692
|
-
*/
|
|
1693
|
-
async touch({ userId, sessionId }) {
|
|
1694
|
-
try {
|
|
1695
|
-
const now = /* @__PURE__ */ new Date();
|
|
1696
|
-
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1697
|
-
const rateLimit = config2.lastSeenRateLimit || 3e4;
|
|
1698
|
-
if (sessionId) {
|
|
1699
|
-
const session2 = await strapi2.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
1700
|
-
if (session2 && session2.lastActive) {
|
|
1701
|
-
const lastActiveTime = new Date(session2.lastActive).getTime();
|
|
1702
|
-
const currentTime = now.getTime();
|
|
1703
|
-
if (currentTime - lastActiveTime > rateLimit) {
|
|
1758
|
+
},
|
|
1759
|
+
/**
|
|
1760
|
+
* Update lastActive timestamp on session (rate-limited to avoid DB noise)
|
|
1761
|
+
* @param {Object} params - { userId, sessionId }
|
|
1762
|
+
* @returns {Promise<void>}
|
|
1763
|
+
*/
|
|
1764
|
+
async touch({ userId, sessionId }) {
|
|
1765
|
+
try {
|
|
1766
|
+
const now = /* @__PURE__ */ new Date();
|
|
1767
|
+
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1768
|
+
const rateLimit = config2.lastSeenRateLimit || 3e4;
|
|
1769
|
+
if (sessionId) {
|
|
1770
|
+
const session2 = await strapi2.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
1771
|
+
if (session2 && session2.lastActive) {
|
|
1772
|
+
const lastActiveTime = new Date(session2.lastActive).getTime();
|
|
1773
|
+
const currentTime = now.getTime();
|
|
1774
|
+
if (currentTime - lastActiveTime > rateLimit) {
|
|
1775
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1776
|
+
documentId: sessionId,
|
|
1777
|
+
data: { lastActive: now }
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
} else if (session2) {
|
|
1704
1781
|
await strapi2.documents(SESSION_UID).update({
|
|
1705
1782
|
documentId: sessionId,
|
|
1706
1783
|
data: { lastActive: now }
|
|
1707
1784
|
});
|
|
1708
1785
|
}
|
|
1709
|
-
} else if (session2) {
|
|
1710
|
-
await strapi2.documents(SESSION_UID).update({
|
|
1711
|
-
documentId: sessionId,
|
|
1712
|
-
data: { lastActive: now }
|
|
1713
|
-
});
|
|
1714
1786
|
}
|
|
1787
|
+
} catch (err) {
|
|
1788
|
+
log.debug("Error touching session:", err.message);
|
|
1715
1789
|
}
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
});
|
|
1743
|
-
deactivatedCount++;
|
|
1790
|
+
},
|
|
1791
|
+
/**
|
|
1792
|
+
* Cleanup inactive sessions - set isActive to false for sessions older than inactivityTimeout
|
|
1793
|
+
* Should be called on bootstrap to clean up stale sessions
|
|
1794
|
+
*/
|
|
1795
|
+
async cleanupInactiveSessions() {
|
|
1796
|
+
try {
|
|
1797
|
+
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1798
|
+
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1799
|
+
const now = /* @__PURE__ */ new Date();
|
|
1800
|
+
const cutoffTime = new Date(now.getTime() - inactivityTimeout);
|
|
1801
|
+
log.info(`[CLEANUP] Cleaning up sessions inactive since before ${cutoffTime.toISOString()}`);
|
|
1802
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1803
|
+
filters: { isActive: true },
|
|
1804
|
+
fields: ["lastActive", "loginTime"]
|
|
1805
|
+
});
|
|
1806
|
+
let deactivatedCount = 0;
|
|
1807
|
+
for (const session2 of activeSessions) {
|
|
1808
|
+
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1809
|
+
if (lastActiveTime < cutoffTime) {
|
|
1810
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1811
|
+
documentId: session2.documentId,
|
|
1812
|
+
data: { isActive: false }
|
|
1813
|
+
});
|
|
1814
|
+
deactivatedCount++;
|
|
1815
|
+
}
|
|
1744
1816
|
}
|
|
1817
|
+
log.info(`[SUCCESS] Cleanup complete: ${deactivatedCount} sessions deactivated`);
|
|
1818
|
+
return deactivatedCount;
|
|
1819
|
+
} catch (err) {
|
|
1820
|
+
log.error("Error cleaning up inactive sessions:", err);
|
|
1821
|
+
throw err;
|
|
1745
1822
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1823
|
+
},
|
|
1824
|
+
/**
|
|
1825
|
+
* Delete a single session from database
|
|
1826
|
+
* WARNING: This permanently deletes the record!
|
|
1827
|
+
* @param {number} sessionId - Session ID to delete
|
|
1828
|
+
* @returns {Promise<boolean>} Success status
|
|
1829
|
+
*/
|
|
1830
|
+
async deleteSession(sessionId) {
|
|
1831
|
+
try {
|
|
1832
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: sessionId });
|
|
1833
|
+
log.info(`[DELETE] Session ${sessionId} permanently deleted`);
|
|
1834
|
+
return true;
|
|
1835
|
+
} catch (err) {
|
|
1836
|
+
log.error("Error deleting session:", err);
|
|
1837
|
+
throw err;
|
|
1838
|
+
}
|
|
1839
|
+
},
|
|
1840
|
+
/**
|
|
1841
|
+
* Delete all inactive sessions from database
|
|
1842
|
+
* WARNING: This permanently deletes records!
|
|
1843
|
+
* @returns {Promise<number>} Number of deleted sessions
|
|
1844
|
+
*/
|
|
1845
|
+
async deleteInactiveSessions() {
|
|
1846
|
+
try {
|
|
1847
|
+
log.info("[DELETE] Deleting all inactive sessions...");
|
|
1848
|
+
const inactiveSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1849
|
+
filters: { isActive: false }
|
|
1850
|
+
});
|
|
1851
|
+
let deletedCount = 0;
|
|
1852
|
+
for (const session2 of inactiveSessions) {
|
|
1853
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: session2.documentId });
|
|
1854
|
+
deletedCount++;
|
|
1855
|
+
}
|
|
1856
|
+
log.info(`[SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
1857
|
+
return deletedCount;
|
|
1858
|
+
} catch (err) {
|
|
1859
|
+
log.error("Error deleting inactive sessions:", err);
|
|
1860
|
+
throw err;
|
|
1784
1861
|
}
|
|
1785
|
-
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
1786
|
-
return deletedCount;
|
|
1787
|
-
} catch (err) {
|
|
1788
|
-
strapi2.log.error("[magic-sessionmanager] Error deleting inactive sessions:", err);
|
|
1789
|
-
throw err;
|
|
1790
1862
|
}
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
const version = "4.
|
|
1863
|
+
};
|
|
1864
|
+
};
|
|
1865
|
+
const version = "4.2.1";
|
|
1794
1866
|
const require$$2 = {
|
|
1795
1867
|
version
|
|
1796
1868
|
};
|
|
1797
1869
|
const crypto = require$$0__default.default;
|
|
1798
1870
|
const os = require$$1__default.default;
|
|
1799
1871
|
const pluginPkg = require$$2;
|
|
1872
|
+
const { createLogger } = logger;
|
|
1800
1873
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
1801
|
-
var licenseGuard$1 = ({ strapi: strapi2 }) =>
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1874
|
+
var licenseGuard$1 = ({ strapi: strapi2 }) => {
|
|
1875
|
+
const log = createLogger(strapi2);
|
|
1876
|
+
return {
|
|
1877
|
+
/**
|
|
1878
|
+
* Get license server URL
|
|
1879
|
+
*/
|
|
1880
|
+
getLicenseServerUrl() {
|
|
1881
|
+
return LICENSE_SERVER_URL;
|
|
1882
|
+
},
|
|
1883
|
+
/**
|
|
1884
|
+
* Generate device ID
|
|
1885
|
+
*/
|
|
1886
|
+
generateDeviceId() {
|
|
1887
|
+
try {
|
|
1888
|
+
const networkInterfaces = os.networkInterfaces();
|
|
1889
|
+
const macAddresses = [];
|
|
1890
|
+
Object.values(networkInterfaces).forEach((interfaces) => {
|
|
1891
|
+
interfaces?.forEach((iface) => {
|
|
1892
|
+
if (iface.mac && iface.mac !== "00:00:00:00:00:00") {
|
|
1893
|
+
macAddresses.push(iface.mac);
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1820
1896
|
});
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1897
|
+
const identifier = `${macAddresses.join("-")}-${os.hostname()}`;
|
|
1898
|
+
return crypto.createHash("sha256").update(identifier).digest("hex").substring(0, 32);
|
|
1899
|
+
} catch (error) {
|
|
1900
|
+
return crypto.randomBytes(16).toString("hex");
|
|
1901
|
+
}
|
|
1902
|
+
},
|
|
1903
|
+
getDeviceName() {
|
|
1904
|
+
try {
|
|
1905
|
+
return os.hostname() || "Unknown Device";
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
return "Unknown Device";
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
getIpAddress() {
|
|
1911
|
+
try {
|
|
1912
|
+
const networkInterfaces = os.networkInterfaces();
|
|
1913
|
+
for (const name of Object.keys(networkInterfaces)) {
|
|
1914
|
+
const interfaces = networkInterfaces[name];
|
|
1915
|
+
if (interfaces) {
|
|
1916
|
+
for (const iface of interfaces) {
|
|
1917
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
1918
|
+
return iface.address;
|
|
1919
|
+
}
|
|
1844
1920
|
}
|
|
1845
1921
|
}
|
|
1846
1922
|
}
|
|
1923
|
+
return "127.0.0.1";
|
|
1924
|
+
} catch (error) {
|
|
1925
|
+
return "127.0.0.1";
|
|
1847
1926
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1927
|
+
},
|
|
1928
|
+
getUserAgent() {
|
|
1929
|
+
const pluginVersion = pluginPkg.version;
|
|
1930
|
+
const strapiVersion = strapi2.config.get("info.strapi") || "5.0.0";
|
|
1931
|
+
return `MagicSessionManager/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
1932
|
+
},
|
|
1933
|
+
async createLicense({ email, firstName, lastName }) {
|
|
1934
|
+
try {
|
|
1935
|
+
const deviceId = this.generateDeviceId();
|
|
1936
|
+
const deviceName = this.getDeviceName();
|
|
1937
|
+
const ipAddress = this.getIpAddress();
|
|
1938
|
+
const userAgent = this.getUserAgent();
|
|
1939
|
+
const licenseServerUrl = this.getLicenseServerUrl();
|
|
1940
|
+
const response = await fetch(`${licenseServerUrl}/api/licenses/create`, {
|
|
1941
|
+
method: "POST",
|
|
1942
|
+
headers: { "Content-Type": "application/json" },
|
|
1943
|
+
body: JSON.stringify({
|
|
1944
|
+
email,
|
|
1945
|
+
firstName,
|
|
1946
|
+
lastName,
|
|
1947
|
+
deviceName,
|
|
1948
|
+
deviceId,
|
|
1949
|
+
ipAddress,
|
|
1950
|
+
userAgent,
|
|
1951
|
+
pluginName: "magic-sessionmanager",
|
|
1952
|
+
productName: "Magic Session Manager - Premium Session Tracking"
|
|
1953
|
+
})
|
|
1954
|
+
});
|
|
1955
|
+
const data = await response.json();
|
|
1956
|
+
if (data.success) {
|
|
1957
|
+
log.info("[SUCCESS] License created:", data.data.licenseKey);
|
|
1958
|
+
return data.data;
|
|
1959
|
+
} else {
|
|
1960
|
+
log.error("[ERROR] License creation failed:", data);
|
|
1961
|
+
return null;
|
|
1962
|
+
}
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
log.error("[ERROR] Error creating license:", error);
|
|
1886
1965
|
return null;
|
|
1887
1966
|
}
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1967
|
+
},
|
|
1968
|
+
async verifyLicense(licenseKey, allowGracePeriod = false) {
|
|
1969
|
+
try {
|
|
1970
|
+
const controller = new AbortController();
|
|
1971
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1972
|
+
const licenseServerUrl = this.getLicenseServerUrl();
|
|
1973
|
+
const response = await fetch(`${licenseServerUrl}/api/licenses/verify`, {
|
|
1974
|
+
method: "POST",
|
|
1975
|
+
headers: { "Content-Type": "application/json" },
|
|
1976
|
+
body: JSON.stringify({
|
|
1977
|
+
licenseKey,
|
|
1978
|
+
pluginName: "magic-sessionmanager",
|
|
1979
|
+
productName: "Magic Session Manager - Premium Session Tracking"
|
|
1980
|
+
}),
|
|
1981
|
+
signal: controller.signal
|
|
1982
|
+
});
|
|
1983
|
+
clearTimeout(timeoutId);
|
|
1984
|
+
const data = await response.json();
|
|
1985
|
+
if (data.success && data.data) {
|
|
1986
|
+
return { valid: true, data: data.data, gracePeriod: false };
|
|
1987
|
+
} else {
|
|
1988
|
+
return { valid: false, data: null };
|
|
1989
|
+
}
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
if (allowGracePeriod) {
|
|
1992
|
+
return { valid: true, data: null, gracePeriod: true };
|
|
1993
|
+
}
|
|
1913
1994
|
return { valid: false, data: null };
|
|
1914
1995
|
}
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
return
|
|
1996
|
+
},
|
|
1997
|
+
async getLicenseByKey(licenseKey) {
|
|
1998
|
+
try {
|
|
1999
|
+
const licenseServerUrl = this.getLicenseServerUrl();
|
|
2000
|
+
const url = `${licenseServerUrl}/api/licenses/key/${licenseKey}`;
|
|
2001
|
+
log.debug(`[magic-sessionmanager/license-guard] Fetching license from: ${url}`);
|
|
2002
|
+
const response = await fetch(url, {
|
|
2003
|
+
method: "GET",
|
|
2004
|
+
headers: { "Content-Type": "application/json" }
|
|
2005
|
+
});
|
|
2006
|
+
const data = await response.json();
|
|
2007
|
+
if (data.success && data.data) {
|
|
2008
|
+
log.debug(`[magic-sessionmanager/license-guard] License fetched: ${data.data.email}, featurePremium: ${data.data.featurePremium}`);
|
|
2009
|
+
return data.data;
|
|
2010
|
+
}
|
|
2011
|
+
log.warn(`[magic-sessionmanager/license-guard] License API returned no data`);
|
|
2012
|
+
return null;
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
log.error("[magic-sessionmanager/license-guard] Error fetching license by key:", error);
|
|
2015
|
+
return null;
|
|
1935
2016
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
} catch (error) {
|
|
1939
|
-
strapi2.log.error("[magic-sessionmanager/license-guard] Error fetching license by key:", error);
|
|
1940
|
-
return null;
|
|
1941
|
-
}
|
|
1942
|
-
},
|
|
1943
|
-
async pingLicense(licenseKey) {
|
|
1944
|
-
try {
|
|
1945
|
-
const deviceId = this.generateDeviceId();
|
|
1946
|
-
const deviceName = this.getDeviceName();
|
|
1947
|
-
const ipAddress = this.getIpAddress();
|
|
1948
|
-
const userAgent = this.getUserAgent();
|
|
1949
|
-
const licenseServerUrl = this.getLicenseServerUrl();
|
|
1950
|
-
const response = await fetch(`${licenseServerUrl}/api/licenses/ping`, {
|
|
1951
|
-
method: "POST",
|
|
1952
|
-
headers: { "Content-Type": "application/json" },
|
|
1953
|
-
body: JSON.stringify({
|
|
1954
|
-
licenseKey,
|
|
1955
|
-
deviceId,
|
|
1956
|
-
deviceName,
|
|
1957
|
-
ipAddress,
|
|
1958
|
-
userAgent,
|
|
1959
|
-
pluginName: "magic-sessionmanager"
|
|
1960
|
-
})
|
|
1961
|
-
});
|
|
1962
|
-
const data = await response.json();
|
|
1963
|
-
return data.success ? data.data : null;
|
|
1964
|
-
} catch (error) {
|
|
1965
|
-
return null;
|
|
1966
|
-
}
|
|
1967
|
-
},
|
|
1968
|
-
async storeLicenseKey(licenseKey) {
|
|
1969
|
-
const pluginStore = strapi2.store({
|
|
1970
|
-
type: "plugin",
|
|
1971
|
-
name: "magic-sessionmanager"
|
|
1972
|
-
});
|
|
1973
|
-
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
1974
|
-
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
1975
|
-
},
|
|
1976
|
-
startPinging(licenseKey, intervalMinutes = 15) {
|
|
1977
|
-
strapi2.log.info(`[magic-sessionmanager] [TIME] Starting license pings every ${intervalMinutes} minutes`);
|
|
1978
|
-
this.pingLicense(licenseKey);
|
|
1979
|
-
const interval = setInterval(async () => {
|
|
2017
|
+
},
|
|
2018
|
+
async pingLicense(licenseKey) {
|
|
1980
2019
|
try {
|
|
1981
|
-
|
|
2020
|
+
const deviceId = this.generateDeviceId();
|
|
2021
|
+
const deviceName = this.getDeviceName();
|
|
2022
|
+
const ipAddress = this.getIpAddress();
|
|
2023
|
+
const userAgent = this.getUserAgent();
|
|
2024
|
+
const licenseServerUrl = this.getLicenseServerUrl();
|
|
2025
|
+
const response = await fetch(`${licenseServerUrl}/api/licenses/ping`, {
|
|
2026
|
+
method: "POST",
|
|
2027
|
+
headers: { "Content-Type": "application/json" },
|
|
2028
|
+
body: JSON.stringify({
|
|
2029
|
+
licenseKey,
|
|
2030
|
+
deviceId,
|
|
2031
|
+
deviceName,
|
|
2032
|
+
ipAddress,
|
|
2033
|
+
userAgent,
|
|
2034
|
+
pluginName: "magic-sessionmanager"
|
|
2035
|
+
})
|
|
2036
|
+
});
|
|
2037
|
+
const data = await response.json();
|
|
2038
|
+
return data.success ? data.data : null;
|
|
1982
2039
|
} catch (error) {
|
|
1983
|
-
|
|
2040
|
+
return null;
|
|
1984
2041
|
}
|
|
1985
|
-
},
|
|
1986
|
-
|
|
1987
|
-
},
|
|
1988
|
-
/**
|
|
1989
|
-
* Initialize license guard
|
|
1990
|
-
* Checks for existing license and starts pinging
|
|
1991
|
-
*/
|
|
1992
|
-
async initialize() {
|
|
1993
|
-
try {
|
|
1994
|
-
strapi2.log.info("[magic-sessionmanager] [SECURE] Initializing License Guard...");
|
|
2042
|
+
},
|
|
2043
|
+
async storeLicenseKey(licenseKey) {
|
|
1995
2044
|
const pluginStore = strapi2.store({
|
|
1996
2045
|
type: "plugin",
|
|
1997
2046
|
name: "magic-sessionmanager"
|
|
1998
2047
|
});
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2048
|
+
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
2049
|
+
log.info(`[SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
2050
|
+
},
|
|
2051
|
+
startPinging(licenseKey, intervalMinutes = 15) {
|
|
2052
|
+
log.info(`[TIME] Starting license pings every ${intervalMinutes} minutes`);
|
|
2053
|
+
this.pingLicense(licenseKey);
|
|
2054
|
+
const interval = setInterval(async () => {
|
|
2055
|
+
try {
|
|
2056
|
+
await this.pingLicense(licenseKey);
|
|
2057
|
+
} catch (error) {
|
|
2058
|
+
log.error("Ping error:", error);
|
|
2059
|
+
}
|
|
2060
|
+
}, intervalMinutes * 60 * 1e3);
|
|
2061
|
+
return interval;
|
|
2062
|
+
},
|
|
2063
|
+
/**
|
|
2064
|
+
* Initialize license guard
|
|
2065
|
+
* Checks for existing license and starts pinging
|
|
2066
|
+
*/
|
|
2067
|
+
async initialize() {
|
|
2068
|
+
try {
|
|
2069
|
+
log.info("[SECURE] Initializing License Guard...");
|
|
2070
|
+
const pluginStore = strapi2.store({
|
|
2071
|
+
type: "plugin",
|
|
2072
|
+
name: "magic-sessionmanager"
|
|
2022
2073
|
});
|
|
2023
|
-
const
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2074
|
+
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
2075
|
+
if (!licenseKey) {
|
|
2076
|
+
log.info("[INFO] No license found - Running in demo mode");
|
|
2077
|
+
return {
|
|
2078
|
+
valid: false,
|
|
2079
|
+
demo: true,
|
|
2080
|
+
data: null
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
const lastValidated = await pluginStore.get({ key: "lastValidated" });
|
|
2084
|
+
const now = /* @__PURE__ */ new Date();
|
|
2085
|
+
const gracePeriodHours = 24;
|
|
2086
|
+
let withinGracePeriod = false;
|
|
2087
|
+
if (lastValidated) {
|
|
2088
|
+
const lastValidatedDate = new Date(lastValidated);
|
|
2089
|
+
const hoursSinceValidation = (now.getTime() - lastValidatedDate.getTime()) / (1e3 * 60 * 60);
|
|
2090
|
+
withinGracePeriod = hoursSinceValidation < gracePeriodHours;
|
|
2091
|
+
}
|
|
2092
|
+
const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
|
|
2093
|
+
if (verification.valid) {
|
|
2094
|
+
await pluginStore.set({
|
|
2095
|
+
key: "lastValidated",
|
|
2096
|
+
value: now.toISOString()
|
|
2097
|
+
});
|
|
2098
|
+
const pingInterval = this.startPinging(licenseKey, 15);
|
|
2099
|
+
strapi2.licenseGuard = {
|
|
2100
|
+
licenseKey,
|
|
2101
|
+
pingInterval,
|
|
2102
|
+
data: verification.data
|
|
2103
|
+
};
|
|
2104
|
+
return {
|
|
2105
|
+
valid: true,
|
|
2106
|
+
demo: false,
|
|
2107
|
+
data: verification.data,
|
|
2108
|
+
gracePeriod: verification.gracePeriod || false
|
|
2109
|
+
};
|
|
2110
|
+
} else {
|
|
2111
|
+
log.error("[ERROR] License validation failed");
|
|
2112
|
+
return {
|
|
2113
|
+
valid: false,
|
|
2114
|
+
demo: true,
|
|
2115
|
+
error: "Invalid or expired license",
|
|
2116
|
+
data: null
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
log.error("[ERROR] Error initializing License Guard:", error);
|
|
2037
2121
|
return {
|
|
2038
2122
|
valid: false,
|
|
2039
2123
|
demo: true,
|
|
2040
|
-
error:
|
|
2124
|
+
error: error.message,
|
|
2041
2125
|
data: null
|
|
2042
2126
|
};
|
|
2043
2127
|
}
|
|
2044
|
-
} catch (error) {
|
|
2045
|
-
strapi2.log.error("[magic-sessionmanager] [ERROR] Error initializing License Guard:", error);
|
|
2046
|
-
return {
|
|
2047
|
-
valid: false,
|
|
2048
|
-
demo: true,
|
|
2049
|
-
error: error.message,
|
|
2050
|
-
data: null
|
|
2051
|
-
};
|
|
2052
2128
|
}
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2129
|
+
};
|
|
2130
|
+
};
|
|
2055
2131
|
var geolocation$1 = ({ strapi: strapi2 }) => ({
|
|
2056
2132
|
/**
|
|
2057
2133
|
* Get IP information from ipapi.co
|