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