strapi-plugin-magic-sessionmanager 3.7.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/admin/src/components/LicenseGuard.jsx +6 -6
- package/admin/src/components/SessionDetailModal.jsx +12 -12
- package/admin/src/components/SessionInfoCard.jsx +3 -3
- package/admin/src/components/SessionInfoPanel.jsx +3 -2
- package/admin/src/hooks/useLicense.js +1 -1
- package/admin/src/index.js +2 -2
- package/admin/src/pages/Analytics.jsx +2 -2
- package/admin/src/pages/HomePage.jsx +11 -14
- package/admin/src/pages/License.jsx +2 -2
- package/admin/src/pages/Settings.jsx +24 -25
- package/admin/src/pages/SettingsNew.jsx +21 -21
- package/admin/src/utils/parseUserAgent.js +7 -7
- package/dist/_chunks/{Analytics-Bi-vcT63.js → Analytics-BBdv1I5y.js} +4 -4
- package/dist/_chunks/{Analytics-BM9i88xu.mjs → Analytics-Dv9f_0eZ.mjs} +4 -4
- package/dist/_chunks/{App-DcnJOCL9.mjs → App-CIQ-7sa7.mjs} +26 -31
- package/dist/_chunks/{App-BbiNy_cT.js → App-CJaZPNjt.js} +26 -31
- package/dist/_chunks/{License-kYo8j2yl.js → License-D24rgaZQ.js} +3 -3
- package/dist/_chunks/{License-DsxP-MAL.mjs → License-nrmFxoBm.mjs} +3 -3
- package/dist/_chunks/{Settings-jW0TOE_d.js → Settings-CqxgjU0y.js} +26 -26
- package/dist/_chunks/{Settings-C3sW9eBD.mjs → Settings-D5dLEGc_.mjs} +26 -26
- package/dist/_chunks/{index-DG9XeVSg.mjs → index-Duk1_Wrz.mjs} +15 -15
- package/dist/_chunks/{index-Dr2HT-Dd.js → index-WH04CS1c.js} +15 -15
- package/dist/_chunks/{useLicense-BL_3bX9O.js → useLicense-BwOlCyhc.js} +2 -2
- package/dist/_chunks/{useLicense-DOkJX-tk.mjs → useLicense-Ce8GaxB0.mjs} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +250 -119
- package/dist/server/index.mjs +250 -119
- package/package.json +1 -1
- package/server/src/bootstrap.js +106 -28
- package/server/src/controllers/license.js +4 -4
- package/server/src/controllers/session.js +67 -13
- package/server/src/destroy.js +1 -1
- package/server/src/middlewares/last-seen.js +13 -7
- package/server/src/register.js +4 -4
- package/server/src/routes/content-api.js +11 -2
- package/server/src/services/geolocation.js +4 -2
- package/server/src/services/license-guard.js +13 -10
- package/server/src/services/notifications.js +20 -20
- package/server/src/services/service.js +1 -1
- package/server/src/services/session.js +63 -33
- package/server/src/utils/encryption.js +1 -1
package/dist/server/index.js
CHANGED
|
@@ -8,7 +8,7 @@ function getDefaultExportFromCjs(x) {
|
|
|
8
8
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
9
9
|
}
|
|
10
10
|
var register$1 = async ({ strapi: strapi2 }) => {
|
|
11
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
11
|
+
strapi2.log.info("[magic-sessionmanager] [START] Plugin registration starting...");
|
|
12
12
|
try {
|
|
13
13
|
const userCT = strapi2.contentType("plugin::users-permissions.user");
|
|
14
14
|
if (!userCT) {
|
|
@@ -17,11 +17,11 @@ var register$1 = async ({ strapi: strapi2 }) => {
|
|
|
17
17
|
}
|
|
18
18
|
if (userCT.attributes && userCT.attributes.sessions) {
|
|
19
19
|
delete userCT.attributes.sessions;
|
|
20
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
20
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Removed sessions field from User content type");
|
|
21
21
|
}
|
|
22
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
22
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Plugin registered successfully");
|
|
23
23
|
} catch (err) {
|
|
24
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
24
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Registration error:", err);
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
const getClientIp$1 = (ctx) => {
|
|
@@ -98,7 +98,7 @@ function getEncryptionKey() {
|
|
|
98
98
|
}
|
|
99
99
|
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT || "default-insecure-key";
|
|
100
100
|
const key = crypto$1.createHash("sha256").update(strapiKeys).digest();
|
|
101
|
-
console.warn("[magic-sessionmanager/encryption]
|
|
101
|
+
console.warn("[magic-sessionmanager/encryption] [WARNING] No SESSION_ENCRYPTION_KEY found. Using fallback (not recommended for production).");
|
|
102
102
|
console.warn("[magic-sessionmanager/encryption] Set SESSION_ENCRYPTION_KEY in .env for better security.");
|
|
103
103
|
return key;
|
|
104
104
|
}
|
|
@@ -149,20 +149,21 @@ var encryption = {
|
|
|
149
149
|
decryptToken: decryptToken$3,
|
|
150
150
|
generateSessionId: generateSessionId$1
|
|
151
151
|
};
|
|
152
|
+
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
152
153
|
var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
153
154
|
return async (ctx, next) => {
|
|
154
|
-
if (ctx.state.user && ctx.state.user.
|
|
155
|
+
if (ctx.state.user && ctx.state.user.documentId) {
|
|
155
156
|
try {
|
|
156
|
-
const userId = ctx.state.user.
|
|
157
|
-
const activeSessions = await strapi2.
|
|
157
|
+
const userId = ctx.state.user.documentId;
|
|
158
|
+
const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
|
|
158
159
|
filters: {
|
|
159
|
-
user: {
|
|
160
|
+
user: { documentId: userId },
|
|
160
161
|
isActive: true
|
|
161
162
|
},
|
|
162
163
|
limit: 1
|
|
163
164
|
});
|
|
164
165
|
if (!activeSessions || activeSessions.length === 0) {
|
|
165
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
166
|
+
strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Blocked request - User ${userId} has no active sessions`);
|
|
166
167
|
return ctx.unauthorized("All sessions have been terminated. Please login again.");
|
|
167
168
|
}
|
|
168
169
|
} catch (err) {
|
|
@@ -170,9 +171,9 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
await next();
|
|
173
|
-
if (ctx.state.user && ctx.state.user.
|
|
174
|
+
if (ctx.state.user && ctx.state.user.documentId) {
|
|
174
175
|
try {
|
|
175
|
-
const userId = ctx.state.user.
|
|
176
|
+
const userId = ctx.state.user.documentId;
|
|
176
177
|
const sessionId = ctx.state.sessionId;
|
|
177
178
|
await sessionService.touch({
|
|
178
179
|
userId,
|
|
@@ -186,15 +187,17 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
|
186
187
|
};
|
|
187
188
|
const getClientIp = getClientIp_1;
|
|
188
189
|
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2 } = encryption;
|
|
190
|
+
const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
|
|
191
|
+
const USER_UID$2 = "plugin::users-permissions.user";
|
|
189
192
|
var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
190
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
193
|
+
strapi2.log.info("[magic-sessionmanager] [START] Bootstrap starting...");
|
|
191
194
|
try {
|
|
192
195
|
const licenseGuardService = strapi2.plugin("magic-sessionmanager").service("license-guard");
|
|
193
196
|
setTimeout(async () => {
|
|
194
197
|
const licenseStatus = await licenseGuardService.initialize();
|
|
195
198
|
if (!licenseStatus.valid) {
|
|
196
199
|
strapi2.log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
197
|
-
strapi2.log.error("║
|
|
200
|
+
strapi2.log.error("║ [ERROR] SESSION MANAGER - NO VALID LICENSE ║");
|
|
198
201
|
strapi2.log.error("║ ║");
|
|
199
202
|
strapi2.log.error("║ This plugin requires a valid license to operate. ║");
|
|
200
203
|
strapi2.log.error("║ Please activate your license via Admin UI: ║");
|
|
@@ -210,7 +213,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
210
213
|
});
|
|
211
214
|
const storedKey = await pluginStore.get({ key: "licenseKey" });
|
|
212
215
|
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
213
|
-
strapi2.log.info("║
|
|
216
|
+
strapi2.log.info("║ [SUCCESS] SESSION MANAGER LICENSE ACTIVE ║");
|
|
214
217
|
strapi2.log.info("║ ║");
|
|
215
218
|
if (licenseStatus.data) {
|
|
216
219
|
strapi2.log.info(`║ License: ${licenseStatus.data.licenseKey}`.padEnd(66) + "║");
|
|
@@ -221,7 +224,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
221
224
|
strapi2.log.info(`║ Status: Grace Period Active`.padEnd(66) + "║");
|
|
222
225
|
}
|
|
223
226
|
strapi2.log.info("║ ║");
|
|
224
|
-
strapi2.log.info("║
|
|
227
|
+
strapi2.log.info("║ [RELOAD] Auto-pinging every 15 minutes ║");
|
|
225
228
|
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
226
229
|
}
|
|
227
230
|
}, 3e3);
|
|
@@ -237,7 +240,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
237
240
|
strapi2.log.error("[magic-sessionmanager] Periodic cleanup error:", err);
|
|
238
241
|
}
|
|
239
242
|
}, cleanupInterval);
|
|
240
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
243
|
+
strapi2.log.info("[magic-sessionmanager] [TIME] Periodic cleanup scheduled (every 30 minutes)");
|
|
241
244
|
if (!strapi2.sessionManagerIntervals) {
|
|
242
245
|
strapi2.sessionManagerIntervals = {};
|
|
243
246
|
}
|
|
@@ -253,7 +256,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
253
256
|
ctx.body = { message: "Logged out successfully" };
|
|
254
257
|
return;
|
|
255
258
|
}
|
|
256
|
-
const allSessions = await strapi2.
|
|
259
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
257
260
|
filters: {
|
|
258
261
|
isActive: true
|
|
259
262
|
}
|
|
@@ -268,8 +271,8 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
268
271
|
}
|
|
269
272
|
});
|
|
270
273
|
if (matchingSession) {
|
|
271
|
-
await sessionService.terminateSession({ sessionId: matchingSession.
|
|
272
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
274
|
+
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
275
|
+
strapi2.log.info(`[magic-sessionmanager] [LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
|
|
273
276
|
}
|
|
274
277
|
ctx.status = 200;
|
|
275
278
|
ctx.body = { message: "Logged out successfully" };
|
|
@@ -283,7 +286,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
283
286
|
auth: false
|
|
284
287
|
}
|
|
285
288
|
}]);
|
|
286
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
289
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] /api/auth/logout route registered");
|
|
287
290
|
strapi2.server.use(async (ctx, next) => {
|
|
288
291
|
await next();
|
|
289
292
|
const isAuthLocal = ctx.path === "/api/auth/local" && ctx.method === "POST";
|
|
@@ -293,7 +296,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
293
296
|
const user = ctx.body.user;
|
|
294
297
|
const ip = getClientIp(ctx);
|
|
295
298
|
const userAgent = ctx.request.headers?.["user-agent"] || ctx.request.header?.["user-agent"] || "unknown";
|
|
296
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
299
|
+
strapi2.log.info(`[magic-sessionmanager] [CHECK] Login detected! User: ${user.documentId || user.id} (${user.email || user.username}) from IP: ${ip}`);
|
|
297
300
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
298
301
|
let shouldBlock = false;
|
|
299
302
|
let blockReason = "";
|
|
@@ -335,7 +338,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
335
338
|
}
|
|
336
339
|
}
|
|
337
340
|
if (shouldBlock) {
|
|
338
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
341
|
+
strapi2.log.warn(`[magic-sessionmanager] [BLOCKED] Blocking login: ${blockReason}`);
|
|
339
342
|
ctx.status = 403;
|
|
340
343
|
ctx.body = {
|
|
341
344
|
error: {
|
|
@@ -346,8 +349,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
346
349
|
};
|
|
347
350
|
return;
|
|
348
351
|
}
|
|
352
|
+
let userDocId = user.documentId;
|
|
353
|
+
if (!userDocId && user.id) {
|
|
354
|
+
const fullUser = await strapi2.entityService.findOne(USER_UID$2, user.id);
|
|
355
|
+
userDocId = fullUser?.documentId || user.id;
|
|
356
|
+
}
|
|
349
357
|
const newSession = await sessionService.createSession({
|
|
350
|
-
userId:
|
|
358
|
+
userId: userDocId,
|
|
351
359
|
ip,
|
|
352
360
|
userAgent,
|
|
353
361
|
token: ctx.body.jwt,
|
|
@@ -355,7 +363,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
355
363
|
refreshToken: ctx.body.refreshToken
|
|
356
364
|
// Store Refresh Token (encrypted) if exists
|
|
357
365
|
});
|
|
358
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
366
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
359
367
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
360
368
|
try {
|
|
361
369
|
const notificationService = strapi2.plugin("magic-sessionmanager").service("notifications");
|
|
@@ -393,18 +401,18 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
393
401
|
}
|
|
394
402
|
}
|
|
395
403
|
} catch (err) {
|
|
396
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
404
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error creating session:", err);
|
|
397
405
|
}
|
|
398
406
|
}
|
|
399
407
|
});
|
|
400
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
408
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Login/Logout interceptor middleware mounted");
|
|
401
409
|
strapi2.server.use(async (ctx, next) => {
|
|
402
410
|
const isRefreshToken = ctx.path === "/api/auth/refresh" && ctx.method === "POST";
|
|
403
411
|
if (isRefreshToken) {
|
|
404
412
|
try {
|
|
405
413
|
const refreshToken = ctx.request.body?.refreshToken;
|
|
406
414
|
if (refreshToken) {
|
|
407
|
-
const allSessions = await strapi2.
|
|
415
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
408
416
|
filters: {
|
|
409
417
|
isActive: true
|
|
410
418
|
}
|
|
@@ -419,7 +427,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
419
427
|
}
|
|
420
428
|
});
|
|
421
429
|
if (!matchingSession) {
|
|
422
|
-
strapi2.log.warn("[magic-sessionmanager]
|
|
430
|
+
strapi2.log.warn("[magic-sessionmanager] [BLOCKED] Blocked refresh token request - no active session");
|
|
423
431
|
ctx.status = 401;
|
|
424
432
|
ctx.body = {
|
|
425
433
|
error: {
|
|
@@ -430,7 +438,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
430
438
|
};
|
|
431
439
|
return;
|
|
432
440
|
}
|
|
433
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
441
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Refresh token allowed for session ${matchingSession.documentId}`);
|
|
434
442
|
}
|
|
435
443
|
} catch (err) {
|
|
436
444
|
strapi2.log.error("[magic-sessionmanager] Error checking refresh token:", err);
|
|
@@ -443,7 +451,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
443
451
|
const newAccessToken = ctx.body.jwt;
|
|
444
452
|
const newRefreshToken = ctx.body.refreshToken;
|
|
445
453
|
if (oldRefreshToken) {
|
|
446
|
-
const allSessions = await strapi2.
|
|
454
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
447
455
|
filters: {
|
|
448
456
|
isActive: true
|
|
449
457
|
}
|
|
@@ -460,14 +468,15 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
460
468
|
if (matchingSession) {
|
|
461
469
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
462
470
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
463
|
-
await strapi2.
|
|
471
|
+
await strapi2.documents(SESSION_UID$2).update({
|
|
472
|
+
documentId: matchingSession.documentId,
|
|
464
473
|
data: {
|
|
465
474
|
token: encryptedToken,
|
|
466
475
|
refreshToken: encryptedRefreshToken,
|
|
467
476
|
lastActive: /* @__PURE__ */ new Date()
|
|
468
477
|
}
|
|
469
478
|
});
|
|
470
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
479
|
+
strapi2.log.info(`[magic-sessionmanager] [REFRESH] Tokens refreshed for session ${matchingSession.documentId}`);
|
|
471
480
|
}
|
|
472
481
|
}
|
|
473
482
|
} catch (err) {
|
|
@@ -475,17 +484,60 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
475
484
|
}
|
|
476
485
|
}
|
|
477
486
|
});
|
|
478
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
487
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Refresh Token interceptor middleware mounted");
|
|
479
488
|
strapi2.server.use(
|
|
480
489
|
lastSeen({ strapi: strapi2, sessionService })
|
|
481
490
|
);
|
|
482
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
483
|
-
strapi2
|
|
484
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
491
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] LastSeen middleware mounted");
|
|
492
|
+
await ensureContentApiPermissions(strapi2);
|
|
493
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Bootstrap complete");
|
|
494
|
+
strapi2.log.info("[magic-sessionmanager] [READY] Session Manager ready! Sessions stored in plugin::magic-sessionmanager.session");
|
|
485
495
|
} catch (err) {
|
|
486
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
496
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Bootstrap error:", err);
|
|
487
497
|
}
|
|
488
498
|
};
|
|
499
|
+
async function ensureContentApiPermissions(strapi2) {
|
|
500
|
+
try {
|
|
501
|
+
const authenticatedRole = await strapi2.query("plugin::users-permissions.role").findOne({
|
|
502
|
+
where: { type: "authenticated" }
|
|
503
|
+
});
|
|
504
|
+
if (!authenticatedRole) {
|
|
505
|
+
strapi2.log.warn("[magic-sessionmanager] Authenticated role not found - skipping permission setup");
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const requiredActions = [
|
|
509
|
+
"plugin::magic-sessionmanager.session.logout",
|
|
510
|
+
"plugin::magic-sessionmanager.session.logoutAll",
|
|
511
|
+
"plugin::magic-sessionmanager.session.getOwnSessions",
|
|
512
|
+
"plugin::magic-sessionmanager.session.getUserSessions"
|
|
513
|
+
];
|
|
514
|
+
const existingPermissions = await strapi2.query("plugin::users-permissions.permission").findMany({
|
|
515
|
+
where: {
|
|
516
|
+
role: authenticatedRole.id,
|
|
517
|
+
action: { $in: requiredActions }
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
const existingActions = existingPermissions.map((p) => p.action);
|
|
521
|
+
const missingActions = requiredActions.filter((action) => !existingActions.includes(action));
|
|
522
|
+
if (missingActions.length === 0) {
|
|
523
|
+
strapi2.log.debug("[magic-sessionmanager] Content-API permissions already configured");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
for (const action of missingActions) {
|
|
527
|
+
await strapi2.query("plugin::users-permissions.permission").create({
|
|
528
|
+
data: {
|
|
529
|
+
action,
|
|
530
|
+
role: authenticatedRole.id
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
strapi2.log.info(`[magic-sessionmanager] [PERMISSION] Enabled ${action} for authenticated users`);
|
|
534
|
+
}
|
|
535
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Content-API permissions configured for authenticated users");
|
|
536
|
+
} catch (err) {
|
|
537
|
+
strapi2.log.warn("[magic-sessionmanager] Could not auto-configure permissions:", err.message);
|
|
538
|
+
strapi2.log.warn("[magic-sessionmanager] Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
489
541
|
var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
490
542
|
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
491
543
|
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
@@ -495,7 +547,7 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
495
547
|
clearInterval(strapi2.sessionManagerIntervals.cleanup);
|
|
496
548
|
strapi2.log.info("[magic-sessionmanager] 🛑 Session cleanup interval stopped");
|
|
497
549
|
}
|
|
498
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
550
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Plugin cleanup completed");
|
|
499
551
|
};
|
|
500
552
|
var config$1 = {
|
|
501
553
|
default: {
|
|
@@ -638,13 +690,22 @@ var contentApi$1 = {
|
|
|
638
690
|
// ============================================================
|
|
639
691
|
// SESSION QUERIES
|
|
640
692
|
// ============================================================
|
|
693
|
+
{
|
|
694
|
+
method: "GET",
|
|
695
|
+
path: "/my-sessions",
|
|
696
|
+
handler: "session.getOwnSessions",
|
|
697
|
+
config: {
|
|
698
|
+
auth: { strategies: ["users-permissions"] },
|
|
699
|
+
description: "Get own sessions (automatically uses authenticated user)"
|
|
700
|
+
}
|
|
701
|
+
},
|
|
641
702
|
{
|
|
642
703
|
method: "GET",
|
|
643
704
|
path: "/user/:userId/sessions",
|
|
644
705
|
handler: "session.getUserSessions",
|
|
645
706
|
config: {
|
|
646
707
|
auth: { strategies: ["users-permissions"] },
|
|
647
|
-
description: "Get
|
|
708
|
+
description: "Get sessions by userId (validates user can only see own sessions)"
|
|
648
709
|
}
|
|
649
710
|
}
|
|
650
711
|
]
|
|
@@ -803,6 +864,8 @@ var routes$1 = {
|
|
|
803
864
|
"content-api": contentApi
|
|
804
865
|
};
|
|
805
866
|
const { decryptToken: decryptToken$1 } = encryption;
|
|
867
|
+
const SESSION_UID$1 = "plugin::magic-sessionmanager.session";
|
|
868
|
+
const USER_UID$1 = "plugin::users-permissions.user";
|
|
806
869
|
var session$3 = {
|
|
807
870
|
/**
|
|
808
871
|
* Get ALL sessions (active + inactive) - Admin only
|
|
@@ -842,6 +905,30 @@ var session$3 = {
|
|
|
842
905
|
ctx.throw(500, "Error fetching active sessions");
|
|
843
906
|
}
|
|
844
907
|
},
|
|
908
|
+
/**
|
|
909
|
+
* Get own sessions (authenticated user)
|
|
910
|
+
* GET /api/magic-sessionmanager/my-sessions
|
|
911
|
+
* Automatically uses the authenticated user's documentId
|
|
912
|
+
*/
|
|
913
|
+
async getOwnSessions(ctx) {
|
|
914
|
+
try {
|
|
915
|
+
const userId = ctx.state.user?.documentId;
|
|
916
|
+
if (!userId) {
|
|
917
|
+
return ctx.throw(401, "Unauthorized");
|
|
918
|
+
}
|
|
919
|
+
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
920
|
+
const sessions = await sessionService.getUserSessions(userId);
|
|
921
|
+
ctx.body = {
|
|
922
|
+
data: sessions,
|
|
923
|
+
meta: {
|
|
924
|
+
count: sessions.length
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
} catch (err) {
|
|
928
|
+
strapi.log.error("[magic-sessionmanager] Error fetching own sessions:", err);
|
|
929
|
+
ctx.throw(500, "Error fetching sessions");
|
|
930
|
+
}
|
|
931
|
+
},
|
|
845
932
|
/**
|
|
846
933
|
* Get user's sessions
|
|
847
934
|
* GET /magic-sessionmanager/user/:userId/sessions (Admin API)
|
|
@@ -852,9 +939,9 @@ var session$3 = {
|
|
|
852
939
|
try {
|
|
853
940
|
const { userId } = ctx.params;
|
|
854
941
|
const isAdminRequest = ctx.state.userAbility || ctx.state.admin;
|
|
855
|
-
const
|
|
856
|
-
if (!isAdminRequest &&
|
|
857
|
-
strapi.log.warn(`[magic-sessionmanager] Security: User ${
|
|
942
|
+
const requestingUserDocId = ctx.state.user?.documentId;
|
|
943
|
+
if (!isAdminRequest && requestingUserDocId && String(requestingUserDocId) !== String(userId)) {
|
|
944
|
+
strapi.log.warn(`[magic-sessionmanager] Security: User ${requestingUserDocId} tried to access sessions of user ${userId}`);
|
|
858
945
|
return ctx.forbidden("You can only access your own sessions");
|
|
859
946
|
}
|
|
860
947
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
@@ -875,15 +962,15 @@ var session$3 = {
|
|
|
875
962
|
*/
|
|
876
963
|
async logout(ctx) {
|
|
877
964
|
try {
|
|
878
|
-
const userId = ctx.state.user?.
|
|
965
|
+
const userId = ctx.state.user?.documentId;
|
|
879
966
|
const token = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
880
967
|
if (!userId) {
|
|
881
968
|
return ctx.throw(401, "Unauthorized");
|
|
882
969
|
}
|
|
883
970
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
884
|
-
const sessions = await strapi.
|
|
971
|
+
const sessions = await strapi.documents(SESSION_UID$1).findMany({
|
|
885
972
|
filters: {
|
|
886
|
-
user: {
|
|
973
|
+
user: { documentId: userId },
|
|
887
974
|
isActive: true
|
|
888
975
|
}
|
|
889
976
|
});
|
|
@@ -897,8 +984,8 @@ var session$3 = {
|
|
|
897
984
|
}
|
|
898
985
|
});
|
|
899
986
|
if (matchingSession) {
|
|
900
|
-
await sessionService.terminateSession({ sessionId: matchingSession.
|
|
901
|
-
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.
|
|
987
|
+
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
988
|
+
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.documentId})`);
|
|
902
989
|
}
|
|
903
990
|
ctx.body = {
|
|
904
991
|
message: "Logged out successfully"
|
|
@@ -914,7 +1001,7 @@ var session$3 = {
|
|
|
914
1001
|
*/
|
|
915
1002
|
async logoutAll(ctx) {
|
|
916
1003
|
try {
|
|
917
|
-
const userId = ctx.state.user?.
|
|
1004
|
+
const userId = ctx.state.user?.documentId;
|
|
918
1005
|
if (!userId) {
|
|
919
1006
|
return ctx.throw(401, "Unauthorized");
|
|
920
1007
|
}
|
|
@@ -1054,23 +1141,34 @@ var session$3 = {
|
|
|
1054
1141
|
/**
|
|
1055
1142
|
* Toggle user blocked status
|
|
1056
1143
|
* POST /magic-sessionmanager/user/:userId/toggle-block
|
|
1144
|
+
* Supports both numeric id (from Content Manager) and documentId
|
|
1057
1145
|
*/
|
|
1058
1146
|
async toggleUserBlock(ctx) {
|
|
1059
1147
|
try {
|
|
1060
1148
|
const { userId } = ctx.params;
|
|
1061
|
-
|
|
1149
|
+
let userDocumentId = userId;
|
|
1150
|
+
let user = null;
|
|
1151
|
+
user = await strapi.documents(USER_UID$1).findOne({ documentId: userId });
|
|
1152
|
+
if (!user && !isNaN(userId)) {
|
|
1153
|
+
const numericUser = await strapi.entityService.findOne(USER_UID$1, parseInt(userId, 10));
|
|
1154
|
+
if (numericUser) {
|
|
1155
|
+
userDocumentId = numericUser.documentId;
|
|
1156
|
+
user = numericUser;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1062
1159
|
if (!user) {
|
|
1063
1160
|
return ctx.throw(404, "User not found");
|
|
1064
1161
|
}
|
|
1065
1162
|
const newBlockedStatus = !user.blocked;
|
|
1066
|
-
await strapi.
|
|
1163
|
+
await strapi.documents(USER_UID$1).update({
|
|
1164
|
+
documentId: userDocumentId,
|
|
1067
1165
|
data: {
|
|
1068
1166
|
blocked: newBlockedStatus
|
|
1069
1167
|
}
|
|
1070
1168
|
});
|
|
1071
1169
|
if (newBlockedStatus) {
|
|
1072
1170
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1073
|
-
await sessionService.terminateSession({ userId });
|
|
1171
|
+
await sessionService.terminateSession({ userId: userDocumentId });
|
|
1074
1172
|
}
|
|
1075
1173
|
ctx.body = {
|
|
1076
1174
|
message: `User ${newBlockedStatus ? "blocked" : "unblocked"} successfully`,
|
|
@@ -1257,16 +1355,16 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
1257
1355
|
const licenseGuard2 = strapi2.plugin("magic-sessionmanager").service("license-guard");
|
|
1258
1356
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
1259
1357
|
if (!verification.valid) {
|
|
1260
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1358
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
|
|
1261
1359
|
return ctx.badRequest("Invalid or expired license key");
|
|
1262
1360
|
}
|
|
1263
1361
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
1264
1362
|
if (!license2) {
|
|
1265
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1363
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] License not found in database: ${trimmedKey.substring(0, 8)}...`);
|
|
1266
1364
|
return ctx.badRequest("License not found");
|
|
1267
1365
|
}
|
|
1268
1366
|
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
1269
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1367
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (Attempted: ${trimmedEmail})`);
|
|
1270
1368
|
return ctx.badRequest("Email address does not match this license key");
|
|
1271
1369
|
}
|
|
1272
1370
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
@@ -1276,7 +1374,7 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
1276
1374
|
pingInterval,
|
|
1277
1375
|
data: verification.data
|
|
1278
1376
|
};
|
|
1279
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1377
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
|
|
1280
1378
|
return ctx.send({
|
|
1281
1379
|
success: true,
|
|
1282
1380
|
message: "License key validated and activated successfully",
|
|
@@ -1398,6 +1496,8 @@ var controllers$1 = {
|
|
|
1398
1496
|
settings
|
|
1399
1497
|
};
|
|
1400
1498
|
const { encryptToken, decryptToken, generateSessionId } = encryption;
|
|
1499
|
+
const SESSION_UID = "plugin::magic-sessionmanager.session";
|
|
1500
|
+
const USER_UID = "plugin::users-permissions.user";
|
|
1401
1501
|
var session$1 = ({ strapi: strapi2 }) => ({
|
|
1402
1502
|
/**
|
|
1403
1503
|
* Create a new session record
|
|
@@ -1410,23 +1510,24 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1410
1510
|
const sessionId = generateSessionId(userId);
|
|
1411
1511
|
const encryptedToken = token ? encryptToken(token) : null;
|
|
1412
1512
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1413
|
-
const session2 = await strapi2.
|
|
1513
|
+
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1414
1514
|
data: {
|
|
1415
1515
|
user: userId,
|
|
1516
|
+
// userId should be documentId (string)
|
|
1416
1517
|
ipAddress: ip.substring(0, 45),
|
|
1417
1518
|
userAgent: userAgent.substring(0, 500),
|
|
1418
1519
|
loginTime: now,
|
|
1419
1520
|
lastActive: now,
|
|
1420
1521
|
isActive: true,
|
|
1421
1522
|
token: encryptedToken,
|
|
1422
|
-
//
|
|
1523
|
+
// [SUCCESS] Encrypted Access Token
|
|
1423
1524
|
refreshToken: encryptedRefreshToken,
|
|
1424
|
-
//
|
|
1525
|
+
// [SUCCESS] Encrypted Refresh Token
|
|
1425
1526
|
sessionId
|
|
1426
|
-
//
|
|
1527
|
+
// [SUCCESS] Unique identifier
|
|
1427
1528
|
}
|
|
1428
1529
|
});
|
|
1429
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1530
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
1430
1531
|
return session2;
|
|
1431
1532
|
} catch (err) {
|
|
1432
1533
|
strapi2.log.error("[magic-sessionmanager] Error creating session:", err);
|
|
@@ -1435,6 +1536,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1435
1536
|
},
|
|
1436
1537
|
/**
|
|
1437
1538
|
* Terminate a session or all sessions for a user
|
|
1539
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1438
1540
|
* @param {Object} params - { sessionId | userId }
|
|
1439
1541
|
* @returns {Promise<void>}
|
|
1440
1542
|
*/
|
|
@@ -1442,7 +1544,8 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1442
1544
|
try {
|
|
1443
1545
|
const now = /* @__PURE__ */ new Date();
|
|
1444
1546
|
if (sessionId) {
|
|
1445
|
-
await strapi2.
|
|
1547
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1548
|
+
documentId: sessionId,
|
|
1446
1549
|
data: {
|
|
1447
1550
|
isActive: false,
|
|
1448
1551
|
logoutTime: now
|
|
@@ -1450,21 +1553,30 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1450
1553
|
});
|
|
1451
1554
|
strapi2.log.info(`[magic-sessionmanager] Session ${sessionId} terminated`);
|
|
1452
1555
|
} else if (userId) {
|
|
1453
|
-
|
|
1556
|
+
let userDocumentId = userId;
|
|
1557
|
+
if (!isNaN(userId)) {
|
|
1558
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1559
|
+
if (user) {
|
|
1560
|
+
userDocumentId = user.documentId;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1454
1564
|
filters: {
|
|
1455
|
-
user: {
|
|
1565
|
+
user: { documentId: userDocumentId },
|
|
1566
|
+
// Deep filtering syntax
|
|
1456
1567
|
isActive: true
|
|
1457
1568
|
}
|
|
1458
1569
|
});
|
|
1459
1570
|
for (const session2 of activeSessions) {
|
|
1460
|
-
await strapi2.
|
|
1571
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1572
|
+
documentId: session2.documentId,
|
|
1461
1573
|
data: {
|
|
1462
1574
|
isActive: false,
|
|
1463
1575
|
logoutTime: now
|
|
1464
1576
|
}
|
|
1465
1577
|
});
|
|
1466
1578
|
}
|
|
1467
|
-
strapi2.log.info(`[magic-sessionmanager] All sessions terminated for user ${
|
|
1579
|
+
strapi2.log.info(`[magic-sessionmanager] All sessions terminated for user ${userDocumentId}`);
|
|
1468
1580
|
}
|
|
1469
1581
|
} catch (err) {
|
|
1470
1582
|
strapi2.log.error("[magic-sessionmanager] Error terminating session:", err);
|
|
@@ -1477,7 +1589,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1477
1589
|
*/
|
|
1478
1590
|
async getAllSessions() {
|
|
1479
1591
|
try {
|
|
1480
|
-
const sessions = await strapi2.
|
|
1592
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1481
1593
|
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1482
1594
|
sort: { loginTime: "desc" },
|
|
1483
1595
|
limit: 1e3
|
|
@@ -1509,7 +1621,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1509
1621
|
*/
|
|
1510
1622
|
async getActiveSessions() {
|
|
1511
1623
|
try {
|
|
1512
|
-
const sessions = await strapi2.
|
|
1624
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1513
1625
|
filters: { isActive: true },
|
|
1514
1626
|
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1515
1627
|
sort: { loginTime: "desc" }
|
|
@@ -1536,13 +1648,21 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1536
1648
|
},
|
|
1537
1649
|
/**
|
|
1538
1650
|
* Get all sessions for a specific user
|
|
1539
|
-
*
|
|
1651
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1652
|
+
* @param {string|number} userId - User documentId or numeric id
|
|
1540
1653
|
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
1541
1654
|
*/
|
|
1542
1655
|
async getUserSessions(userId) {
|
|
1543
1656
|
try {
|
|
1544
|
-
|
|
1545
|
-
|
|
1657
|
+
let userDocumentId = userId;
|
|
1658
|
+
if (!isNaN(userId)) {
|
|
1659
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1660
|
+
if (user) {
|
|
1661
|
+
userDocumentId = user.documentId;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1665
|
+
filters: { user: { documentId: userDocumentId } },
|
|
1546
1666
|
sort: { loginTime: "desc" }
|
|
1547
1667
|
});
|
|
1548
1668
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
@@ -1576,17 +1696,19 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1576
1696
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1577
1697
|
const rateLimit = config2.lastSeenRateLimit || 3e4;
|
|
1578
1698
|
if (sessionId) {
|
|
1579
|
-
const session2 = await strapi2.
|
|
1699
|
+
const session2 = await strapi2.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
1580
1700
|
if (session2 && session2.lastActive) {
|
|
1581
1701
|
const lastActiveTime = new Date(session2.lastActive).getTime();
|
|
1582
1702
|
const currentTime = now.getTime();
|
|
1583
1703
|
if (currentTime - lastActiveTime > rateLimit) {
|
|
1584
|
-
await strapi2.
|
|
1704
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1705
|
+
documentId: sessionId,
|
|
1585
1706
|
data: { lastActive: now }
|
|
1586
1707
|
});
|
|
1587
1708
|
}
|
|
1588
1709
|
} else if (session2) {
|
|
1589
|
-
await strapi2.
|
|
1710
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1711
|
+
documentId: sessionId,
|
|
1590
1712
|
data: { lastActive: now }
|
|
1591
1713
|
});
|
|
1592
1714
|
}
|
|
@@ -1605,22 +1727,23 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1605
1727
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1606
1728
|
const now = /* @__PURE__ */ new Date();
|
|
1607
1729
|
const cutoffTime = new Date(now.getTime() - inactivityTimeout);
|
|
1608
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1609
|
-
const activeSessions = await strapi2.
|
|
1730
|
+
strapi2.log.info(`[magic-sessionmanager] [CLEANUP] Cleaning up sessions inactive since before ${cutoffTime.toISOString()}`);
|
|
1731
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1610
1732
|
filters: { isActive: true },
|
|
1611
|
-
fields: ["
|
|
1733
|
+
fields: ["lastActive", "loginTime"]
|
|
1612
1734
|
});
|
|
1613
1735
|
let deactivatedCount = 0;
|
|
1614
1736
|
for (const session2 of activeSessions) {
|
|
1615
1737
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1616
1738
|
if (lastActiveTime < cutoffTime) {
|
|
1617
|
-
await strapi2.
|
|
1739
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1740
|
+
documentId: session2.documentId,
|
|
1618
1741
|
data: { isActive: false }
|
|
1619
1742
|
});
|
|
1620
1743
|
deactivatedCount++;
|
|
1621
1744
|
}
|
|
1622
1745
|
}
|
|
1623
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1746
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Cleanup complete: ${deactivatedCount} sessions deactivated`);
|
|
1624
1747
|
return deactivatedCount;
|
|
1625
1748
|
} catch (err) {
|
|
1626
1749
|
strapi2.log.error("[magic-sessionmanager] Error cleaning up inactive sessions:", err);
|
|
@@ -1635,8 +1758,8 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1635
1758
|
*/
|
|
1636
1759
|
async deleteSession(sessionId) {
|
|
1637
1760
|
try {
|
|
1638
|
-
await strapi2.
|
|
1639
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1761
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: sessionId });
|
|
1762
|
+
strapi2.log.info(`[magic-sessionmanager] [DELETE] Session ${sessionId} permanently deleted`);
|
|
1640
1763
|
return true;
|
|
1641
1764
|
} catch (err) {
|
|
1642
1765
|
strapi2.log.error("[magic-sessionmanager] Error deleting session:", err);
|
|
@@ -1650,17 +1773,16 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1650
1773
|
*/
|
|
1651
1774
|
async deleteInactiveSessions() {
|
|
1652
1775
|
try {
|
|
1653
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1654
|
-
const inactiveSessions = await strapi2.
|
|
1655
|
-
filters: { isActive: false }
|
|
1656
|
-
fields: ["id"]
|
|
1776
|
+
strapi2.log.info("[magic-sessionmanager] [DELETE] Deleting all inactive sessions...");
|
|
1777
|
+
const inactiveSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1778
|
+
filters: { isActive: false }
|
|
1657
1779
|
});
|
|
1658
1780
|
let deletedCount = 0;
|
|
1659
1781
|
for (const session2 of inactiveSessions) {
|
|
1660
|
-
await strapi2.
|
|
1782
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: session2.documentId });
|
|
1661
1783
|
deletedCount++;
|
|
1662
1784
|
}
|
|
1663
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1785
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
1664
1786
|
return deletedCount;
|
|
1665
1787
|
} catch (err) {
|
|
1666
1788
|
strapi2.log.error("[magic-sessionmanager] Error deleting inactive sessions:", err);
|
|
@@ -1668,8 +1790,13 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1668
1790
|
}
|
|
1669
1791
|
}
|
|
1670
1792
|
});
|
|
1793
|
+
const version = "4.0.0";
|
|
1794
|
+
const require$$2 = {
|
|
1795
|
+
version
|
|
1796
|
+
};
|
|
1671
1797
|
const crypto = require$$0__default.default;
|
|
1672
1798
|
const os = require$$1__default.default;
|
|
1799
|
+
const pluginPkg = require$$2;
|
|
1673
1800
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
1674
1801
|
var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
1675
1802
|
/**
|
|
@@ -1724,7 +1851,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1724
1851
|
}
|
|
1725
1852
|
},
|
|
1726
1853
|
getUserAgent() {
|
|
1727
|
-
|
|
1854
|
+
const pluginVersion = pluginPkg.version;
|
|
1855
|
+
const strapiVersion = strapi2.config.get("info.strapi") || "5.0.0";
|
|
1856
|
+
return `MagicSessionManager/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
1728
1857
|
},
|
|
1729
1858
|
async createLicense({ email, firstName, lastName }) {
|
|
1730
1859
|
try {
|
|
@@ -1750,14 +1879,14 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1750
1879
|
});
|
|
1751
1880
|
const data = await response.json();
|
|
1752
1881
|
if (data.success) {
|
|
1753
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1882
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] License created:", data.data.licenseKey);
|
|
1754
1883
|
return data.data;
|
|
1755
1884
|
} else {
|
|
1756
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
1885
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] License creation failed:", data);
|
|
1757
1886
|
return null;
|
|
1758
1887
|
}
|
|
1759
1888
|
} catch (error) {
|
|
1760
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
1889
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error creating license:", error);
|
|
1761
1890
|
return null;
|
|
1762
1891
|
}
|
|
1763
1892
|
},
|
|
@@ -1842,10 +1971,10 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1842
1971
|
name: "magic-sessionmanager"
|
|
1843
1972
|
});
|
|
1844
1973
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
1845
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1974
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
1846
1975
|
},
|
|
1847
1976
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
1848
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1977
|
+
strapi2.log.info(`[magic-sessionmanager] [TIME] Starting license pings every ${intervalMinutes} minutes`);
|
|
1849
1978
|
this.pingLicense(licenseKey);
|
|
1850
1979
|
const interval = setInterval(async () => {
|
|
1851
1980
|
try {
|
|
@@ -1862,14 +1991,14 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1862
1991
|
*/
|
|
1863
1992
|
async initialize() {
|
|
1864
1993
|
try {
|
|
1865
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1994
|
+
strapi2.log.info("[magic-sessionmanager] [SECURE] Initializing License Guard...");
|
|
1866
1995
|
const pluginStore = strapi2.store({
|
|
1867
1996
|
type: "plugin",
|
|
1868
1997
|
name: "magic-sessionmanager"
|
|
1869
1998
|
});
|
|
1870
1999
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
1871
2000
|
if (!licenseKey) {
|
|
1872
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
2001
|
+
strapi2.log.info("[magic-sessionmanager] [INFO] No license found - Running in demo mode");
|
|
1873
2002
|
return {
|
|
1874
2003
|
valid: false,
|
|
1875
2004
|
demo: true,
|
|
@@ -1904,7 +2033,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1904
2033
|
gracePeriod: verification.gracePeriod || false
|
|
1905
2034
|
};
|
|
1906
2035
|
} else {
|
|
1907
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
2036
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] License validation failed");
|
|
1908
2037
|
return {
|
|
1909
2038
|
valid: false,
|
|
1910
2039
|
demo: true,
|
|
@@ -1913,7 +2042,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1913
2042
|
};
|
|
1914
2043
|
}
|
|
1915
2044
|
} catch (error) {
|
|
1916
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
2045
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error initializing License Guard:", error);
|
|
1917
2046
|
return {
|
|
1918
2047
|
valid: false,
|
|
1919
2048
|
demo: true,
|
|
@@ -2026,9 +2155,11 @@ var geolocation$1 = ({ strapi: strapi2 }) => ({
|
|
|
2026
2155
|
},
|
|
2027
2156
|
/**
|
|
2028
2157
|
* Get country flag emoji
|
|
2158
|
+
* @param {string} countryCode - ISO 2-letter country code
|
|
2159
|
+
* @returns {string} Flag emoji or empty string
|
|
2029
2160
|
*/
|
|
2030
2161
|
getCountryFlag(countryCode) {
|
|
2031
|
-
if (!countryCode) return "
|
|
2162
|
+
if (!countryCode) return "";
|
|
2032
2163
|
const codePoints = countryCode.toUpperCase().split("").map((char) => 127397 + char.charCodeAt());
|
|
2033
2164
|
return String.fromCodePoint(...codePoints);
|
|
2034
2165
|
},
|
|
@@ -2040,7 +2171,7 @@ var geolocation$1 = ({ strapi: strapi2 }) => ({
|
|
|
2040
2171
|
ip: ipAddress,
|
|
2041
2172
|
country: "Unknown",
|
|
2042
2173
|
country_code: "XX",
|
|
2043
|
-
country_flag: "
|
|
2174
|
+
country_flag: "[GEO]",
|
|
2044
2175
|
city: "Unknown",
|
|
2045
2176
|
region: "Unknown",
|
|
2046
2177
|
timezone: "Unknown",
|
|
@@ -2090,12 +2221,12 @@ var notifications$1 = ({ strapi: strapi2 }) => ({
|
|
|
2090
2221
|
strapi2.log.debug("[magic-sessionmanager/notifications] Using default fallback templates");
|
|
2091
2222
|
return {
|
|
2092
2223
|
suspiciousLogin: {
|
|
2093
|
-
subject: "
|
|
2224
|
+
subject: "[ALERT] Suspicious Login Alert - Session Manager",
|
|
2094
2225
|
html: `
|
|
2095
2226
|
<html>
|
|
2096
2227
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
2097
2228
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
|
|
2098
|
-
<h2 style="color: #dc2626;"
|
|
2229
|
+
<h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
|
|
2099
2230
|
<p>A potentially suspicious login was detected for your account.</p>
|
|
2100
2231
|
|
|
2101
2232
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -2130,7 +2261,7 @@ var notifications$1 = ({ strapi: strapi2 }) => ({
|
|
|
2130
2261
|
</div>
|
|
2131
2262
|
</body>
|
|
2132
2263
|
</html>`,
|
|
2133
|
-
text:
|
|
2264
|
+
text: `[ALERT] Suspicious Login Detected
|
|
2134
2265
|
|
|
2135
2266
|
A potentially suspicious login was detected for your account.
|
|
2136
2267
|
|
|
@@ -2145,9 +2276,9 @@ Login Details:
|
|
|
2145
2276
|
Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`
|
|
2146
2277
|
},
|
|
2147
2278
|
newLocation: {
|
|
2148
|
-
subject: "
|
|
2149
|
-
html: `<h2
|
|
2150
|
-
text:
|
|
2279
|
+
subject: "[LOCATION] New Location Login Detected",
|
|
2280
|
+
html: `<h2>[LOCATION] New Location Login</h2><p>Account: {{user.email}}</p><p>Time: {{session.loginTime}}</p><p>Location: {{geo.city}}, {{geo.country}}</p><p>IP: {{session.ipAddress}}</p>`,
|
|
2281
|
+
text: `[LOCATION] New Location Login
|
|
2151
2282
|
|
|
2152
2283
|
Account: {{user.email}}
|
|
2153
2284
|
Time: {{session.loginTime}}
|
|
@@ -2155,9 +2286,9 @@ Location: {{geo.city}}, {{geo.country}}
|
|
|
2155
2286
|
IP: {{session.ipAddress}}`
|
|
2156
2287
|
},
|
|
2157
2288
|
vpnProxy: {
|
|
2158
|
-
subject: "
|
|
2159
|
-
html: `<h2
|
|
2160
|
-
text:
|
|
2289
|
+
subject: "[WARNING] VPN/Proxy Login Detected",
|
|
2290
|
+
html: `<h2>[WARNING] VPN/Proxy Detected</h2><p>Account: {{user.email}}</p><p>Time: {{session.loginTime}}</p><p>IP: {{session.ipAddress}}</p><p>VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}</p>`,
|
|
2291
|
+
text: `[WARNING] VPN/Proxy Detected
|
|
2161
2292
|
|
|
2162
2293
|
Account: {{user.email}}
|
|
2163
2294
|
Time: {{session.loginTime}}
|
|
@@ -2301,17 +2432,17 @@ VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`
|
|
|
2301
2432
|
title: this.getEventTitle(event),
|
|
2302
2433
|
color: this.getEventColor(event),
|
|
2303
2434
|
fields: [
|
|
2304
|
-
{ name: "
|
|
2435
|
+
{ name: "User", value: `${user.email}
|
|
2305
2436
|
${user.username || "N/A"}`, inline: true },
|
|
2306
|
-
{ name: "
|
|
2307
|
-
{ name: "
|
|
2437
|
+
{ name: "IP", value: session2.ipAddress, inline: true },
|
|
2438
|
+
{ name: "Time", value: new Date(session2.loginTime).toLocaleString(), inline: false }
|
|
2308
2439
|
],
|
|
2309
2440
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2310
2441
|
footer: { text: "Magic Session Manager" }
|
|
2311
2442
|
};
|
|
2312
2443
|
if (geoData) {
|
|
2313
2444
|
embed.fields.push({
|
|
2314
|
-
name: "
|
|
2445
|
+
name: "[LOCATION] Location",
|
|
2315
2446
|
value: `${geoData.country_flag} ${geoData.city}, ${geoData.country}`,
|
|
2316
2447
|
inline: true
|
|
2317
2448
|
});
|
|
@@ -2321,7 +2452,7 @@ ${user.username || "N/A"}`, inline: true },
|
|
|
2321
2452
|
if (geoData.isProxy) warnings.push("Proxy");
|
|
2322
2453
|
if (geoData.isThreat) warnings.push("Threat");
|
|
2323
2454
|
embed.fields.push({
|
|
2324
|
-
name: "
|
|
2455
|
+
name: "[WARNING] Security",
|
|
2325
2456
|
value: `${warnings.join(", ")} detected
|
|
2326
2457
|
Score: ${geoData.securityScore}/100`,
|
|
2327
2458
|
inline: true
|
|
@@ -2332,13 +2463,13 @@ Score: ${geoData.securityScore}/100`,
|
|
|
2332
2463
|
},
|
|
2333
2464
|
getEventTitle(event) {
|
|
2334
2465
|
const titles = {
|
|
2335
|
-
"login.suspicious": "
|
|
2336
|
-
"login.new_location": "
|
|
2337
|
-
"login.vpn": "
|
|
2338
|
-
"login.threat": "
|
|
2339
|
-
"session.terminated": "
|
|
2466
|
+
"login.suspicious": "[ALERT] Suspicious Login",
|
|
2467
|
+
"login.new_location": "[LOCATION] New Location Login",
|
|
2468
|
+
"login.vpn": "[WARNING] VPN Login Detected",
|
|
2469
|
+
"login.threat": "[THREAT] Threat IP Login",
|
|
2470
|
+
"session.terminated": "[INFO] Session Terminated"
|
|
2340
2471
|
};
|
|
2341
|
-
return titles[event] || "
|
|
2472
|
+
return titles[event] || "[STATS] Session Event";
|
|
2342
2473
|
},
|
|
2343
2474
|
getEventColor(event) {
|
|
2344
2475
|
const colors = {
|