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.mjs
CHANGED
|
@@ -4,7 +4,7 @@ function getDefaultExportFromCjs(x) {
|
|
|
4
4
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
5
5
|
}
|
|
6
6
|
var register$1 = async ({ strapi: strapi2 }) => {
|
|
7
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
7
|
+
strapi2.log.info("[magic-sessionmanager] [START] Plugin registration starting...");
|
|
8
8
|
try {
|
|
9
9
|
const userCT = strapi2.contentType("plugin::users-permissions.user");
|
|
10
10
|
if (!userCT) {
|
|
@@ -13,11 +13,11 @@ var register$1 = async ({ strapi: strapi2 }) => {
|
|
|
13
13
|
}
|
|
14
14
|
if (userCT.attributes && userCT.attributes.sessions) {
|
|
15
15
|
delete userCT.attributes.sessions;
|
|
16
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
16
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Removed sessions field from User content type");
|
|
17
17
|
}
|
|
18
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
18
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Plugin registered successfully");
|
|
19
19
|
} catch (err) {
|
|
20
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
20
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Registration error:", err);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
const getClientIp$1 = (ctx) => {
|
|
@@ -94,7 +94,7 @@ function getEncryptionKey() {
|
|
|
94
94
|
}
|
|
95
95
|
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT || "default-insecure-key";
|
|
96
96
|
const key = crypto$1.createHash("sha256").update(strapiKeys).digest();
|
|
97
|
-
console.warn("[magic-sessionmanager/encryption]
|
|
97
|
+
console.warn("[magic-sessionmanager/encryption] [WARNING] No SESSION_ENCRYPTION_KEY found. Using fallback (not recommended for production).");
|
|
98
98
|
console.warn("[magic-sessionmanager/encryption] Set SESSION_ENCRYPTION_KEY in .env for better security.");
|
|
99
99
|
return key;
|
|
100
100
|
}
|
|
@@ -145,20 +145,21 @@ var encryption = {
|
|
|
145
145
|
decryptToken: decryptToken$3,
|
|
146
146
|
generateSessionId: generateSessionId$1
|
|
147
147
|
};
|
|
148
|
+
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
148
149
|
var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
149
150
|
return async (ctx, next) => {
|
|
150
|
-
if (ctx.state.user && ctx.state.user.
|
|
151
|
+
if (ctx.state.user && ctx.state.user.documentId) {
|
|
151
152
|
try {
|
|
152
|
-
const userId = ctx.state.user.
|
|
153
|
-
const activeSessions = await strapi2.
|
|
153
|
+
const userId = ctx.state.user.documentId;
|
|
154
|
+
const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
|
|
154
155
|
filters: {
|
|
155
|
-
user: {
|
|
156
|
+
user: { documentId: userId },
|
|
156
157
|
isActive: true
|
|
157
158
|
},
|
|
158
159
|
limit: 1
|
|
159
160
|
});
|
|
160
161
|
if (!activeSessions || activeSessions.length === 0) {
|
|
161
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
162
|
+
strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Blocked request - User ${userId} has no active sessions`);
|
|
162
163
|
return ctx.unauthorized("All sessions have been terminated. Please login again.");
|
|
163
164
|
}
|
|
164
165
|
} catch (err) {
|
|
@@ -166,9 +167,9 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
await next();
|
|
169
|
-
if (ctx.state.user && ctx.state.user.
|
|
170
|
+
if (ctx.state.user && ctx.state.user.documentId) {
|
|
170
171
|
try {
|
|
171
|
-
const userId = ctx.state.user.
|
|
172
|
+
const userId = ctx.state.user.documentId;
|
|
172
173
|
const sessionId = ctx.state.sessionId;
|
|
173
174
|
await sessionService.touch({
|
|
174
175
|
userId,
|
|
@@ -182,15 +183,17 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
|
|
|
182
183
|
};
|
|
183
184
|
const getClientIp = getClientIp_1;
|
|
184
185
|
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2 } = encryption;
|
|
186
|
+
const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
|
|
187
|
+
const USER_UID$2 = "plugin::users-permissions.user";
|
|
185
188
|
var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
186
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
189
|
+
strapi2.log.info("[magic-sessionmanager] [START] Bootstrap starting...");
|
|
187
190
|
try {
|
|
188
191
|
const licenseGuardService = strapi2.plugin("magic-sessionmanager").service("license-guard");
|
|
189
192
|
setTimeout(async () => {
|
|
190
193
|
const licenseStatus = await licenseGuardService.initialize();
|
|
191
194
|
if (!licenseStatus.valid) {
|
|
192
195
|
strapi2.log.error("╔════════════════════════════════════════════════════════════════╗");
|
|
193
|
-
strapi2.log.error("║
|
|
196
|
+
strapi2.log.error("║ [ERROR] SESSION MANAGER - NO VALID LICENSE ║");
|
|
194
197
|
strapi2.log.error("║ ║");
|
|
195
198
|
strapi2.log.error("║ This plugin requires a valid license to operate. ║");
|
|
196
199
|
strapi2.log.error("║ Please activate your license via Admin UI: ║");
|
|
@@ -206,7 +209,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
206
209
|
});
|
|
207
210
|
const storedKey = await pluginStore.get({ key: "licenseKey" });
|
|
208
211
|
strapi2.log.info("╔════════════════════════════════════════════════════════════════╗");
|
|
209
|
-
strapi2.log.info("║
|
|
212
|
+
strapi2.log.info("║ [SUCCESS] SESSION MANAGER LICENSE ACTIVE ║");
|
|
210
213
|
strapi2.log.info("║ ║");
|
|
211
214
|
if (licenseStatus.data) {
|
|
212
215
|
strapi2.log.info(`║ License: ${licenseStatus.data.licenseKey}`.padEnd(66) + "║");
|
|
@@ -217,7 +220,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
217
220
|
strapi2.log.info(`║ Status: Grace Period Active`.padEnd(66) + "║");
|
|
218
221
|
}
|
|
219
222
|
strapi2.log.info("║ ║");
|
|
220
|
-
strapi2.log.info("║
|
|
223
|
+
strapi2.log.info("║ [RELOAD] Auto-pinging every 15 minutes ║");
|
|
221
224
|
strapi2.log.info("╚════════════════════════════════════════════════════════════════╝");
|
|
222
225
|
}
|
|
223
226
|
}, 3e3);
|
|
@@ -233,7 +236,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
233
236
|
strapi2.log.error("[magic-sessionmanager] Periodic cleanup error:", err);
|
|
234
237
|
}
|
|
235
238
|
}, cleanupInterval);
|
|
236
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
239
|
+
strapi2.log.info("[magic-sessionmanager] [TIME] Periodic cleanup scheduled (every 30 minutes)");
|
|
237
240
|
if (!strapi2.sessionManagerIntervals) {
|
|
238
241
|
strapi2.sessionManagerIntervals = {};
|
|
239
242
|
}
|
|
@@ -249,7 +252,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
249
252
|
ctx.body = { message: "Logged out successfully" };
|
|
250
253
|
return;
|
|
251
254
|
}
|
|
252
|
-
const allSessions = await strapi2.
|
|
255
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
253
256
|
filters: {
|
|
254
257
|
isActive: true
|
|
255
258
|
}
|
|
@@ -264,8 +267,8 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
264
267
|
}
|
|
265
268
|
});
|
|
266
269
|
if (matchingSession) {
|
|
267
|
-
await sessionService.terminateSession({ sessionId: matchingSession.
|
|
268
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
270
|
+
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
271
|
+
strapi2.log.info(`[magic-sessionmanager] [LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
|
|
269
272
|
}
|
|
270
273
|
ctx.status = 200;
|
|
271
274
|
ctx.body = { message: "Logged out successfully" };
|
|
@@ -279,7 +282,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
279
282
|
auth: false
|
|
280
283
|
}
|
|
281
284
|
}]);
|
|
282
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
285
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] /api/auth/logout route registered");
|
|
283
286
|
strapi2.server.use(async (ctx, next) => {
|
|
284
287
|
await next();
|
|
285
288
|
const isAuthLocal = ctx.path === "/api/auth/local" && ctx.method === "POST";
|
|
@@ -289,7 +292,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
289
292
|
const user = ctx.body.user;
|
|
290
293
|
const ip = getClientIp(ctx);
|
|
291
294
|
const userAgent = ctx.request.headers?.["user-agent"] || ctx.request.header?.["user-agent"] || "unknown";
|
|
292
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
295
|
+
strapi2.log.info(`[magic-sessionmanager] [CHECK] Login detected! User: ${user.documentId || user.id} (${user.email || user.username}) from IP: ${ip}`);
|
|
293
296
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
294
297
|
let shouldBlock = false;
|
|
295
298
|
let blockReason = "";
|
|
@@ -331,7 +334,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
331
334
|
}
|
|
332
335
|
}
|
|
333
336
|
if (shouldBlock) {
|
|
334
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
337
|
+
strapi2.log.warn(`[magic-sessionmanager] [BLOCKED] Blocking login: ${blockReason}`);
|
|
335
338
|
ctx.status = 403;
|
|
336
339
|
ctx.body = {
|
|
337
340
|
error: {
|
|
@@ -342,8 +345,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
342
345
|
};
|
|
343
346
|
return;
|
|
344
347
|
}
|
|
348
|
+
let userDocId = user.documentId;
|
|
349
|
+
if (!userDocId && user.id) {
|
|
350
|
+
const fullUser = await strapi2.entityService.findOne(USER_UID$2, user.id);
|
|
351
|
+
userDocId = fullUser?.documentId || user.id;
|
|
352
|
+
}
|
|
345
353
|
const newSession = await sessionService.createSession({
|
|
346
|
-
userId:
|
|
354
|
+
userId: userDocId,
|
|
347
355
|
ip,
|
|
348
356
|
userAgent,
|
|
349
357
|
token: ctx.body.jwt,
|
|
@@ -351,7 +359,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
351
359
|
refreshToken: ctx.body.refreshToken
|
|
352
360
|
// Store Refresh Token (encrypted) if exists
|
|
353
361
|
});
|
|
354
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
362
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
355
363
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
356
364
|
try {
|
|
357
365
|
const notificationService = strapi2.plugin("magic-sessionmanager").service("notifications");
|
|
@@ -389,18 +397,18 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
389
397
|
}
|
|
390
398
|
}
|
|
391
399
|
} catch (err) {
|
|
392
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
400
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error creating session:", err);
|
|
393
401
|
}
|
|
394
402
|
}
|
|
395
403
|
});
|
|
396
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
404
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Login/Logout interceptor middleware mounted");
|
|
397
405
|
strapi2.server.use(async (ctx, next) => {
|
|
398
406
|
const isRefreshToken = ctx.path === "/api/auth/refresh" && ctx.method === "POST";
|
|
399
407
|
if (isRefreshToken) {
|
|
400
408
|
try {
|
|
401
409
|
const refreshToken = ctx.request.body?.refreshToken;
|
|
402
410
|
if (refreshToken) {
|
|
403
|
-
const allSessions = await strapi2.
|
|
411
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
404
412
|
filters: {
|
|
405
413
|
isActive: true
|
|
406
414
|
}
|
|
@@ -415,7 +423,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
415
423
|
}
|
|
416
424
|
});
|
|
417
425
|
if (!matchingSession) {
|
|
418
|
-
strapi2.log.warn("[magic-sessionmanager]
|
|
426
|
+
strapi2.log.warn("[magic-sessionmanager] [BLOCKED] Blocked refresh token request - no active session");
|
|
419
427
|
ctx.status = 401;
|
|
420
428
|
ctx.body = {
|
|
421
429
|
error: {
|
|
@@ -426,7 +434,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
426
434
|
};
|
|
427
435
|
return;
|
|
428
436
|
}
|
|
429
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
437
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Refresh token allowed for session ${matchingSession.documentId}`);
|
|
430
438
|
}
|
|
431
439
|
} catch (err) {
|
|
432
440
|
strapi2.log.error("[magic-sessionmanager] Error checking refresh token:", err);
|
|
@@ -439,7 +447,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
439
447
|
const newAccessToken = ctx.body.jwt;
|
|
440
448
|
const newRefreshToken = ctx.body.refreshToken;
|
|
441
449
|
if (oldRefreshToken) {
|
|
442
|
-
const allSessions = await strapi2.
|
|
450
|
+
const allSessions = await strapi2.documents(SESSION_UID$2).findMany({
|
|
443
451
|
filters: {
|
|
444
452
|
isActive: true
|
|
445
453
|
}
|
|
@@ -456,14 +464,15 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
456
464
|
if (matchingSession) {
|
|
457
465
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
458
466
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
459
|
-
await strapi2.
|
|
467
|
+
await strapi2.documents(SESSION_UID$2).update({
|
|
468
|
+
documentId: matchingSession.documentId,
|
|
460
469
|
data: {
|
|
461
470
|
token: encryptedToken,
|
|
462
471
|
refreshToken: encryptedRefreshToken,
|
|
463
472
|
lastActive: /* @__PURE__ */ new Date()
|
|
464
473
|
}
|
|
465
474
|
});
|
|
466
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
475
|
+
strapi2.log.info(`[magic-sessionmanager] [REFRESH] Tokens refreshed for session ${matchingSession.documentId}`);
|
|
467
476
|
}
|
|
468
477
|
}
|
|
469
478
|
} catch (err) {
|
|
@@ -471,17 +480,60 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
471
480
|
}
|
|
472
481
|
}
|
|
473
482
|
});
|
|
474
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
483
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Refresh Token interceptor middleware mounted");
|
|
475
484
|
strapi2.server.use(
|
|
476
485
|
lastSeen({ strapi: strapi2, sessionService })
|
|
477
486
|
);
|
|
478
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
479
|
-
strapi2
|
|
480
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
487
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] LastSeen middleware mounted");
|
|
488
|
+
await ensureContentApiPermissions(strapi2);
|
|
489
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Bootstrap complete");
|
|
490
|
+
strapi2.log.info("[magic-sessionmanager] [READY] Session Manager ready! Sessions stored in plugin::magic-sessionmanager.session");
|
|
481
491
|
} catch (err) {
|
|
482
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
492
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Bootstrap error:", err);
|
|
483
493
|
}
|
|
484
494
|
};
|
|
495
|
+
async function ensureContentApiPermissions(strapi2) {
|
|
496
|
+
try {
|
|
497
|
+
const authenticatedRole = await strapi2.query("plugin::users-permissions.role").findOne({
|
|
498
|
+
where: { type: "authenticated" }
|
|
499
|
+
});
|
|
500
|
+
if (!authenticatedRole) {
|
|
501
|
+
strapi2.log.warn("[magic-sessionmanager] Authenticated role not found - skipping permission setup");
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const requiredActions = [
|
|
505
|
+
"plugin::magic-sessionmanager.session.logout",
|
|
506
|
+
"plugin::magic-sessionmanager.session.logoutAll",
|
|
507
|
+
"plugin::magic-sessionmanager.session.getOwnSessions",
|
|
508
|
+
"plugin::magic-sessionmanager.session.getUserSessions"
|
|
509
|
+
];
|
|
510
|
+
const existingPermissions = await strapi2.query("plugin::users-permissions.permission").findMany({
|
|
511
|
+
where: {
|
|
512
|
+
role: authenticatedRole.id,
|
|
513
|
+
action: { $in: requiredActions }
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
const existingActions = existingPermissions.map((p) => p.action);
|
|
517
|
+
const missingActions = requiredActions.filter((action) => !existingActions.includes(action));
|
|
518
|
+
if (missingActions.length === 0) {
|
|
519
|
+
strapi2.log.debug("[magic-sessionmanager] Content-API permissions already configured");
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
for (const action of missingActions) {
|
|
523
|
+
await strapi2.query("plugin::users-permissions.permission").create({
|
|
524
|
+
data: {
|
|
525
|
+
action,
|
|
526
|
+
role: authenticatedRole.id
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
strapi2.log.info(`[magic-sessionmanager] [PERMISSION] Enabled ${action} for authenticated users`);
|
|
530
|
+
}
|
|
531
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Content-API permissions configured for authenticated users");
|
|
532
|
+
} catch (err) {
|
|
533
|
+
strapi2.log.warn("[magic-sessionmanager] Could not auto-configure permissions:", err.message);
|
|
534
|
+
strapi2.log.warn("[magic-sessionmanager] Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
485
537
|
var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
486
538
|
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
487
539
|
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
@@ -491,7 +543,7 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
491
543
|
clearInterval(strapi2.sessionManagerIntervals.cleanup);
|
|
492
544
|
strapi2.log.info("[magic-sessionmanager] 🛑 Session cleanup interval stopped");
|
|
493
545
|
}
|
|
494
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
546
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] Plugin cleanup completed");
|
|
495
547
|
};
|
|
496
548
|
var config$1 = {
|
|
497
549
|
default: {
|
|
@@ -634,13 +686,22 @@ var contentApi$1 = {
|
|
|
634
686
|
// ============================================================
|
|
635
687
|
// SESSION QUERIES
|
|
636
688
|
// ============================================================
|
|
689
|
+
{
|
|
690
|
+
method: "GET",
|
|
691
|
+
path: "/my-sessions",
|
|
692
|
+
handler: "session.getOwnSessions",
|
|
693
|
+
config: {
|
|
694
|
+
auth: { strategies: ["users-permissions"] },
|
|
695
|
+
description: "Get own sessions (automatically uses authenticated user)"
|
|
696
|
+
}
|
|
697
|
+
},
|
|
637
698
|
{
|
|
638
699
|
method: "GET",
|
|
639
700
|
path: "/user/:userId/sessions",
|
|
640
701
|
handler: "session.getUserSessions",
|
|
641
702
|
config: {
|
|
642
703
|
auth: { strategies: ["users-permissions"] },
|
|
643
|
-
description: "Get
|
|
704
|
+
description: "Get sessions by userId (validates user can only see own sessions)"
|
|
644
705
|
}
|
|
645
706
|
}
|
|
646
707
|
]
|
|
@@ -799,6 +860,8 @@ var routes$1 = {
|
|
|
799
860
|
"content-api": contentApi
|
|
800
861
|
};
|
|
801
862
|
const { decryptToken: decryptToken$1 } = encryption;
|
|
863
|
+
const SESSION_UID$1 = "plugin::magic-sessionmanager.session";
|
|
864
|
+
const USER_UID$1 = "plugin::users-permissions.user";
|
|
802
865
|
var session$3 = {
|
|
803
866
|
/**
|
|
804
867
|
* Get ALL sessions (active + inactive) - Admin only
|
|
@@ -838,6 +901,30 @@ var session$3 = {
|
|
|
838
901
|
ctx.throw(500, "Error fetching active sessions");
|
|
839
902
|
}
|
|
840
903
|
},
|
|
904
|
+
/**
|
|
905
|
+
* Get own sessions (authenticated user)
|
|
906
|
+
* GET /api/magic-sessionmanager/my-sessions
|
|
907
|
+
* Automatically uses the authenticated user's documentId
|
|
908
|
+
*/
|
|
909
|
+
async getOwnSessions(ctx) {
|
|
910
|
+
try {
|
|
911
|
+
const userId = ctx.state.user?.documentId;
|
|
912
|
+
if (!userId) {
|
|
913
|
+
return ctx.throw(401, "Unauthorized");
|
|
914
|
+
}
|
|
915
|
+
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
916
|
+
const sessions = await sessionService.getUserSessions(userId);
|
|
917
|
+
ctx.body = {
|
|
918
|
+
data: sessions,
|
|
919
|
+
meta: {
|
|
920
|
+
count: sessions.length
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
} catch (err) {
|
|
924
|
+
strapi.log.error("[magic-sessionmanager] Error fetching own sessions:", err);
|
|
925
|
+
ctx.throw(500, "Error fetching sessions");
|
|
926
|
+
}
|
|
927
|
+
},
|
|
841
928
|
/**
|
|
842
929
|
* Get user's sessions
|
|
843
930
|
* GET /magic-sessionmanager/user/:userId/sessions (Admin API)
|
|
@@ -848,9 +935,9 @@ var session$3 = {
|
|
|
848
935
|
try {
|
|
849
936
|
const { userId } = ctx.params;
|
|
850
937
|
const isAdminRequest = ctx.state.userAbility || ctx.state.admin;
|
|
851
|
-
const
|
|
852
|
-
if (!isAdminRequest &&
|
|
853
|
-
strapi.log.warn(`[magic-sessionmanager] Security: User ${
|
|
938
|
+
const requestingUserDocId = ctx.state.user?.documentId;
|
|
939
|
+
if (!isAdminRequest && requestingUserDocId && String(requestingUserDocId) !== String(userId)) {
|
|
940
|
+
strapi.log.warn(`[magic-sessionmanager] Security: User ${requestingUserDocId} tried to access sessions of user ${userId}`);
|
|
854
941
|
return ctx.forbidden("You can only access your own sessions");
|
|
855
942
|
}
|
|
856
943
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
@@ -871,15 +958,15 @@ var session$3 = {
|
|
|
871
958
|
*/
|
|
872
959
|
async logout(ctx) {
|
|
873
960
|
try {
|
|
874
|
-
const userId = ctx.state.user?.
|
|
961
|
+
const userId = ctx.state.user?.documentId;
|
|
875
962
|
const token = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
876
963
|
if (!userId) {
|
|
877
964
|
return ctx.throw(401, "Unauthorized");
|
|
878
965
|
}
|
|
879
966
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
880
|
-
const sessions = await strapi.
|
|
967
|
+
const sessions = await strapi.documents(SESSION_UID$1).findMany({
|
|
881
968
|
filters: {
|
|
882
|
-
user: {
|
|
969
|
+
user: { documentId: userId },
|
|
883
970
|
isActive: true
|
|
884
971
|
}
|
|
885
972
|
});
|
|
@@ -893,8 +980,8 @@ var session$3 = {
|
|
|
893
980
|
}
|
|
894
981
|
});
|
|
895
982
|
if (matchingSession) {
|
|
896
|
-
await sessionService.terminateSession({ sessionId: matchingSession.
|
|
897
|
-
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.
|
|
983
|
+
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
984
|
+
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.documentId})`);
|
|
898
985
|
}
|
|
899
986
|
ctx.body = {
|
|
900
987
|
message: "Logged out successfully"
|
|
@@ -910,7 +997,7 @@ var session$3 = {
|
|
|
910
997
|
*/
|
|
911
998
|
async logoutAll(ctx) {
|
|
912
999
|
try {
|
|
913
|
-
const userId = ctx.state.user?.
|
|
1000
|
+
const userId = ctx.state.user?.documentId;
|
|
914
1001
|
if (!userId) {
|
|
915
1002
|
return ctx.throw(401, "Unauthorized");
|
|
916
1003
|
}
|
|
@@ -1050,23 +1137,34 @@ var session$3 = {
|
|
|
1050
1137
|
/**
|
|
1051
1138
|
* Toggle user blocked status
|
|
1052
1139
|
* POST /magic-sessionmanager/user/:userId/toggle-block
|
|
1140
|
+
* Supports both numeric id (from Content Manager) and documentId
|
|
1053
1141
|
*/
|
|
1054
1142
|
async toggleUserBlock(ctx) {
|
|
1055
1143
|
try {
|
|
1056
1144
|
const { userId } = ctx.params;
|
|
1057
|
-
|
|
1145
|
+
let userDocumentId = userId;
|
|
1146
|
+
let user = null;
|
|
1147
|
+
user = await strapi.documents(USER_UID$1).findOne({ documentId: userId });
|
|
1148
|
+
if (!user && !isNaN(userId)) {
|
|
1149
|
+
const numericUser = await strapi.entityService.findOne(USER_UID$1, parseInt(userId, 10));
|
|
1150
|
+
if (numericUser) {
|
|
1151
|
+
userDocumentId = numericUser.documentId;
|
|
1152
|
+
user = numericUser;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1058
1155
|
if (!user) {
|
|
1059
1156
|
return ctx.throw(404, "User not found");
|
|
1060
1157
|
}
|
|
1061
1158
|
const newBlockedStatus = !user.blocked;
|
|
1062
|
-
await strapi.
|
|
1159
|
+
await strapi.documents(USER_UID$1).update({
|
|
1160
|
+
documentId: userDocumentId,
|
|
1063
1161
|
data: {
|
|
1064
1162
|
blocked: newBlockedStatus
|
|
1065
1163
|
}
|
|
1066
1164
|
});
|
|
1067
1165
|
if (newBlockedStatus) {
|
|
1068
1166
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1069
|
-
await sessionService.terminateSession({ userId });
|
|
1167
|
+
await sessionService.terminateSession({ userId: userDocumentId });
|
|
1070
1168
|
}
|
|
1071
1169
|
ctx.body = {
|
|
1072
1170
|
message: `User ${newBlockedStatus ? "blocked" : "unblocked"} successfully`,
|
|
@@ -1253,16 +1351,16 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
1253
1351
|
const licenseGuard2 = strapi2.plugin("magic-sessionmanager").service("license-guard");
|
|
1254
1352
|
const verification = await licenseGuard2.verifyLicense(trimmedKey);
|
|
1255
1353
|
if (!verification.valid) {
|
|
1256
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1354
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] Invalid license key attempted: ${trimmedKey.substring(0, 8)}...`);
|
|
1257
1355
|
return ctx.badRequest("Invalid or expired license key");
|
|
1258
1356
|
}
|
|
1259
1357
|
const license2 = await licenseGuard2.getLicenseByKey(trimmedKey);
|
|
1260
1358
|
if (!license2) {
|
|
1261
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1359
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] License not found in database: ${trimmedKey.substring(0, 8)}...`);
|
|
1262
1360
|
return ctx.badRequest("License not found");
|
|
1263
1361
|
}
|
|
1264
1362
|
if (license2.email.toLowerCase() !== trimmedEmail) {
|
|
1265
|
-
strapi2.log.warn(`[magic-sessionmanager]
|
|
1363
|
+
strapi2.log.warn(`[magic-sessionmanager] [WARNING] Email mismatch for license key: ${trimmedKey.substring(0, 8)}... (Attempted: ${trimmedEmail})`);
|
|
1266
1364
|
return ctx.badRequest("Email address does not match this license key");
|
|
1267
1365
|
}
|
|
1268
1366
|
await licenseGuard2.storeLicenseKey(trimmedKey);
|
|
@@ -1272,7 +1370,7 @@ var license$1 = ({ strapi: strapi2 }) => ({
|
|
|
1272
1370
|
pingInterval,
|
|
1273
1371
|
data: verification.data
|
|
1274
1372
|
};
|
|
1275
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1373
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Existing license key validated and stored: ${trimmedKey.substring(0, 8)}... (Email: ${trimmedEmail})`);
|
|
1276
1374
|
return ctx.send({
|
|
1277
1375
|
success: true,
|
|
1278
1376
|
message: "License key validated and activated successfully",
|
|
@@ -1394,6 +1492,8 @@ var controllers$1 = {
|
|
|
1394
1492
|
settings
|
|
1395
1493
|
};
|
|
1396
1494
|
const { encryptToken, decryptToken, generateSessionId } = encryption;
|
|
1495
|
+
const SESSION_UID = "plugin::magic-sessionmanager.session";
|
|
1496
|
+
const USER_UID = "plugin::users-permissions.user";
|
|
1397
1497
|
var session$1 = ({ strapi: strapi2 }) => ({
|
|
1398
1498
|
/**
|
|
1399
1499
|
* Create a new session record
|
|
@@ -1406,23 +1506,24 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1406
1506
|
const sessionId = generateSessionId(userId);
|
|
1407
1507
|
const encryptedToken = token ? encryptToken(token) : null;
|
|
1408
1508
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1409
|
-
const session2 = await strapi2.
|
|
1509
|
+
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1410
1510
|
data: {
|
|
1411
1511
|
user: userId,
|
|
1512
|
+
// userId should be documentId (string)
|
|
1412
1513
|
ipAddress: ip.substring(0, 45),
|
|
1413
1514
|
userAgent: userAgent.substring(0, 500),
|
|
1414
1515
|
loginTime: now,
|
|
1415
1516
|
lastActive: now,
|
|
1416
1517
|
isActive: true,
|
|
1417
1518
|
token: encryptedToken,
|
|
1418
|
-
//
|
|
1519
|
+
// [SUCCESS] Encrypted Access Token
|
|
1419
1520
|
refreshToken: encryptedRefreshToken,
|
|
1420
|
-
//
|
|
1521
|
+
// [SUCCESS] Encrypted Refresh Token
|
|
1421
1522
|
sessionId
|
|
1422
|
-
//
|
|
1523
|
+
// [SUCCESS] Unique identifier
|
|
1423
1524
|
}
|
|
1424
1525
|
});
|
|
1425
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1526
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
1426
1527
|
return session2;
|
|
1427
1528
|
} catch (err) {
|
|
1428
1529
|
strapi2.log.error("[magic-sessionmanager] Error creating session:", err);
|
|
@@ -1431,6 +1532,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1431
1532
|
},
|
|
1432
1533
|
/**
|
|
1433
1534
|
* Terminate a session or all sessions for a user
|
|
1535
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1434
1536
|
* @param {Object} params - { sessionId | userId }
|
|
1435
1537
|
* @returns {Promise<void>}
|
|
1436
1538
|
*/
|
|
@@ -1438,7 +1540,8 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1438
1540
|
try {
|
|
1439
1541
|
const now = /* @__PURE__ */ new Date();
|
|
1440
1542
|
if (sessionId) {
|
|
1441
|
-
await strapi2.
|
|
1543
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1544
|
+
documentId: sessionId,
|
|
1442
1545
|
data: {
|
|
1443
1546
|
isActive: false,
|
|
1444
1547
|
logoutTime: now
|
|
@@ -1446,21 +1549,30 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1446
1549
|
});
|
|
1447
1550
|
strapi2.log.info(`[magic-sessionmanager] Session ${sessionId} terminated`);
|
|
1448
1551
|
} else if (userId) {
|
|
1449
|
-
|
|
1552
|
+
let userDocumentId = userId;
|
|
1553
|
+
if (!isNaN(userId)) {
|
|
1554
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1555
|
+
if (user) {
|
|
1556
|
+
userDocumentId = user.documentId;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1450
1560
|
filters: {
|
|
1451
|
-
user: {
|
|
1561
|
+
user: { documentId: userDocumentId },
|
|
1562
|
+
// Deep filtering syntax
|
|
1452
1563
|
isActive: true
|
|
1453
1564
|
}
|
|
1454
1565
|
});
|
|
1455
1566
|
for (const session2 of activeSessions) {
|
|
1456
|
-
await strapi2.
|
|
1567
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1568
|
+
documentId: session2.documentId,
|
|
1457
1569
|
data: {
|
|
1458
1570
|
isActive: false,
|
|
1459
1571
|
logoutTime: now
|
|
1460
1572
|
}
|
|
1461
1573
|
});
|
|
1462
1574
|
}
|
|
1463
|
-
strapi2.log.info(`[magic-sessionmanager] All sessions terminated for user ${
|
|
1575
|
+
strapi2.log.info(`[magic-sessionmanager] All sessions terminated for user ${userDocumentId}`);
|
|
1464
1576
|
}
|
|
1465
1577
|
} catch (err) {
|
|
1466
1578
|
strapi2.log.error("[magic-sessionmanager] Error terminating session:", err);
|
|
@@ -1473,7 +1585,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1473
1585
|
*/
|
|
1474
1586
|
async getAllSessions() {
|
|
1475
1587
|
try {
|
|
1476
|
-
const sessions = await strapi2.
|
|
1588
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1477
1589
|
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1478
1590
|
sort: { loginTime: "desc" },
|
|
1479
1591
|
limit: 1e3
|
|
@@ -1505,7 +1617,7 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1505
1617
|
*/
|
|
1506
1618
|
async getActiveSessions() {
|
|
1507
1619
|
try {
|
|
1508
|
-
const sessions = await strapi2.
|
|
1620
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1509
1621
|
filters: { isActive: true },
|
|
1510
1622
|
populate: { user: { fields: ["id", "email", "username"] } },
|
|
1511
1623
|
sort: { loginTime: "desc" }
|
|
@@ -1532,13 +1644,21 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1532
1644
|
},
|
|
1533
1645
|
/**
|
|
1534
1646
|
* Get all sessions for a specific user
|
|
1535
|
-
*
|
|
1647
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
1648
|
+
* @param {string|number} userId - User documentId or numeric id
|
|
1536
1649
|
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
1537
1650
|
*/
|
|
1538
1651
|
async getUserSessions(userId) {
|
|
1539
1652
|
try {
|
|
1540
|
-
|
|
1541
|
-
|
|
1653
|
+
let userDocumentId = userId;
|
|
1654
|
+
if (!isNaN(userId)) {
|
|
1655
|
+
const user = await strapi2.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
1656
|
+
if (user) {
|
|
1657
|
+
userDocumentId = user.documentId;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
const sessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1661
|
+
filters: { user: { documentId: userDocumentId } },
|
|
1542
1662
|
sort: { loginTime: "desc" }
|
|
1543
1663
|
});
|
|
1544
1664
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
@@ -1572,17 +1692,19 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1572
1692
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
1573
1693
|
const rateLimit = config2.lastSeenRateLimit || 3e4;
|
|
1574
1694
|
if (sessionId) {
|
|
1575
|
-
const session2 = await strapi2.
|
|
1695
|
+
const session2 = await strapi2.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
1576
1696
|
if (session2 && session2.lastActive) {
|
|
1577
1697
|
const lastActiveTime = new Date(session2.lastActive).getTime();
|
|
1578
1698
|
const currentTime = now.getTime();
|
|
1579
1699
|
if (currentTime - lastActiveTime > rateLimit) {
|
|
1580
|
-
await strapi2.
|
|
1700
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1701
|
+
documentId: sessionId,
|
|
1581
1702
|
data: { lastActive: now }
|
|
1582
1703
|
});
|
|
1583
1704
|
}
|
|
1584
1705
|
} else if (session2) {
|
|
1585
|
-
await strapi2.
|
|
1706
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1707
|
+
documentId: sessionId,
|
|
1586
1708
|
data: { lastActive: now }
|
|
1587
1709
|
});
|
|
1588
1710
|
}
|
|
@@ -1601,22 +1723,23 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1601
1723
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1602
1724
|
const now = /* @__PURE__ */ new Date();
|
|
1603
1725
|
const cutoffTime = new Date(now.getTime() - inactivityTimeout);
|
|
1604
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1605
|
-
const activeSessions = await strapi2.
|
|
1726
|
+
strapi2.log.info(`[magic-sessionmanager] [CLEANUP] Cleaning up sessions inactive since before ${cutoffTime.toISOString()}`);
|
|
1727
|
+
const activeSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1606
1728
|
filters: { isActive: true },
|
|
1607
|
-
fields: ["
|
|
1729
|
+
fields: ["lastActive", "loginTime"]
|
|
1608
1730
|
});
|
|
1609
1731
|
let deactivatedCount = 0;
|
|
1610
1732
|
for (const session2 of activeSessions) {
|
|
1611
1733
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1612
1734
|
if (lastActiveTime < cutoffTime) {
|
|
1613
|
-
await strapi2.
|
|
1735
|
+
await strapi2.documents(SESSION_UID).update({
|
|
1736
|
+
documentId: session2.documentId,
|
|
1614
1737
|
data: { isActive: false }
|
|
1615
1738
|
});
|
|
1616
1739
|
deactivatedCount++;
|
|
1617
1740
|
}
|
|
1618
1741
|
}
|
|
1619
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1742
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Cleanup complete: ${deactivatedCount} sessions deactivated`);
|
|
1620
1743
|
return deactivatedCount;
|
|
1621
1744
|
} catch (err) {
|
|
1622
1745
|
strapi2.log.error("[magic-sessionmanager] Error cleaning up inactive sessions:", err);
|
|
@@ -1631,8 +1754,8 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1631
1754
|
*/
|
|
1632
1755
|
async deleteSession(sessionId) {
|
|
1633
1756
|
try {
|
|
1634
|
-
await strapi2.
|
|
1635
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1757
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: sessionId });
|
|
1758
|
+
strapi2.log.info(`[magic-sessionmanager] [DELETE] Session ${sessionId} permanently deleted`);
|
|
1636
1759
|
return true;
|
|
1637
1760
|
} catch (err) {
|
|
1638
1761
|
strapi2.log.error("[magic-sessionmanager] Error deleting session:", err);
|
|
@@ -1646,17 +1769,16 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1646
1769
|
*/
|
|
1647
1770
|
async deleteInactiveSessions() {
|
|
1648
1771
|
try {
|
|
1649
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1650
|
-
const inactiveSessions = await strapi2.
|
|
1651
|
-
filters: { isActive: false }
|
|
1652
|
-
fields: ["id"]
|
|
1772
|
+
strapi2.log.info("[magic-sessionmanager] [DELETE] Deleting all inactive sessions...");
|
|
1773
|
+
const inactiveSessions = await strapi2.documents(SESSION_UID).findMany({
|
|
1774
|
+
filters: { isActive: false }
|
|
1653
1775
|
});
|
|
1654
1776
|
let deletedCount = 0;
|
|
1655
1777
|
for (const session2 of inactiveSessions) {
|
|
1656
|
-
await strapi2.
|
|
1778
|
+
await strapi2.documents(SESSION_UID).delete({ documentId: session2.documentId });
|
|
1657
1779
|
deletedCount++;
|
|
1658
1780
|
}
|
|
1659
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1781
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
1660
1782
|
return deletedCount;
|
|
1661
1783
|
} catch (err) {
|
|
1662
1784
|
strapi2.log.error("[magic-sessionmanager] Error deleting inactive sessions:", err);
|
|
@@ -1664,8 +1786,13 @@ var session$1 = ({ strapi: strapi2 }) => ({
|
|
|
1664
1786
|
}
|
|
1665
1787
|
}
|
|
1666
1788
|
});
|
|
1789
|
+
const version = "4.0.0";
|
|
1790
|
+
const require$$2 = {
|
|
1791
|
+
version
|
|
1792
|
+
};
|
|
1667
1793
|
const crypto = require$$0$1;
|
|
1668
1794
|
const os = require$$1;
|
|
1795
|
+
const pluginPkg = require$$2;
|
|
1669
1796
|
const LICENSE_SERVER_URL = "https://magicapi.fitlex.me";
|
|
1670
1797
|
var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
1671
1798
|
/**
|
|
@@ -1720,7 +1847,9 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1720
1847
|
}
|
|
1721
1848
|
},
|
|
1722
1849
|
getUserAgent() {
|
|
1723
|
-
|
|
1850
|
+
const pluginVersion = pluginPkg.version;
|
|
1851
|
+
const strapiVersion = strapi2.config.get("info.strapi") || "5.0.0";
|
|
1852
|
+
return `MagicSessionManager/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
1724
1853
|
},
|
|
1725
1854
|
async createLicense({ email, firstName, lastName }) {
|
|
1726
1855
|
try {
|
|
@@ -1746,14 +1875,14 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1746
1875
|
});
|
|
1747
1876
|
const data = await response.json();
|
|
1748
1877
|
if (data.success) {
|
|
1749
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1878
|
+
strapi2.log.info("[magic-sessionmanager] [SUCCESS] License created:", data.data.licenseKey);
|
|
1750
1879
|
return data.data;
|
|
1751
1880
|
} else {
|
|
1752
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
1881
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] License creation failed:", data);
|
|
1753
1882
|
return null;
|
|
1754
1883
|
}
|
|
1755
1884
|
} catch (error) {
|
|
1756
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
1885
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error creating license:", error);
|
|
1757
1886
|
return null;
|
|
1758
1887
|
}
|
|
1759
1888
|
},
|
|
@@ -1838,10 +1967,10 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1838
1967
|
name: "magic-sessionmanager"
|
|
1839
1968
|
});
|
|
1840
1969
|
await pluginStore.set({ key: "licenseKey", value: licenseKey });
|
|
1841
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1970
|
+
strapi2.log.info(`[magic-sessionmanager] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
1842
1971
|
},
|
|
1843
1972
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
1844
|
-
strapi2.log.info(`[magic-sessionmanager]
|
|
1973
|
+
strapi2.log.info(`[magic-sessionmanager] [TIME] Starting license pings every ${intervalMinutes} minutes`);
|
|
1845
1974
|
this.pingLicense(licenseKey);
|
|
1846
1975
|
const interval = setInterval(async () => {
|
|
1847
1976
|
try {
|
|
@@ -1858,14 +1987,14 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1858
1987
|
*/
|
|
1859
1988
|
async initialize() {
|
|
1860
1989
|
try {
|
|
1861
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1990
|
+
strapi2.log.info("[magic-sessionmanager] [SECURE] Initializing License Guard...");
|
|
1862
1991
|
const pluginStore = strapi2.store({
|
|
1863
1992
|
type: "plugin",
|
|
1864
1993
|
name: "magic-sessionmanager"
|
|
1865
1994
|
});
|
|
1866
1995
|
const licenseKey = await pluginStore.get({ key: "licenseKey" });
|
|
1867
1996
|
if (!licenseKey) {
|
|
1868
|
-
strapi2.log.info("[magic-sessionmanager]
|
|
1997
|
+
strapi2.log.info("[magic-sessionmanager] [INFO] No license found - Running in demo mode");
|
|
1869
1998
|
return {
|
|
1870
1999
|
valid: false,
|
|
1871
2000
|
demo: true,
|
|
@@ -1900,7 +2029,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1900
2029
|
gracePeriod: verification.gracePeriod || false
|
|
1901
2030
|
};
|
|
1902
2031
|
} else {
|
|
1903
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
2032
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] License validation failed");
|
|
1904
2033
|
return {
|
|
1905
2034
|
valid: false,
|
|
1906
2035
|
demo: true,
|
|
@@ -1909,7 +2038,7 @@ var licenseGuard$1 = ({ strapi: strapi2 }) => ({
|
|
|
1909
2038
|
};
|
|
1910
2039
|
}
|
|
1911
2040
|
} catch (error) {
|
|
1912
|
-
strapi2.log.error("[magic-sessionmanager]
|
|
2041
|
+
strapi2.log.error("[magic-sessionmanager] [ERROR] Error initializing License Guard:", error);
|
|
1913
2042
|
return {
|
|
1914
2043
|
valid: false,
|
|
1915
2044
|
demo: true,
|
|
@@ -2022,9 +2151,11 @@ var geolocation$1 = ({ strapi: strapi2 }) => ({
|
|
|
2022
2151
|
},
|
|
2023
2152
|
/**
|
|
2024
2153
|
* Get country flag emoji
|
|
2154
|
+
* @param {string} countryCode - ISO 2-letter country code
|
|
2155
|
+
* @returns {string} Flag emoji or empty string
|
|
2025
2156
|
*/
|
|
2026
2157
|
getCountryFlag(countryCode) {
|
|
2027
|
-
if (!countryCode) return "
|
|
2158
|
+
if (!countryCode) return "";
|
|
2028
2159
|
const codePoints = countryCode.toUpperCase().split("").map((char) => 127397 + char.charCodeAt());
|
|
2029
2160
|
return String.fromCodePoint(...codePoints);
|
|
2030
2161
|
},
|
|
@@ -2036,7 +2167,7 @@ var geolocation$1 = ({ strapi: strapi2 }) => ({
|
|
|
2036
2167
|
ip: ipAddress,
|
|
2037
2168
|
country: "Unknown",
|
|
2038
2169
|
country_code: "XX",
|
|
2039
|
-
country_flag: "
|
|
2170
|
+
country_flag: "[GEO]",
|
|
2040
2171
|
city: "Unknown",
|
|
2041
2172
|
region: "Unknown",
|
|
2042
2173
|
timezone: "Unknown",
|
|
@@ -2086,12 +2217,12 @@ var notifications$1 = ({ strapi: strapi2 }) => ({
|
|
|
2086
2217
|
strapi2.log.debug("[magic-sessionmanager/notifications] Using default fallback templates");
|
|
2087
2218
|
return {
|
|
2088
2219
|
suspiciousLogin: {
|
|
2089
|
-
subject: "
|
|
2220
|
+
subject: "[ALERT] Suspicious Login Alert - Session Manager",
|
|
2090
2221
|
html: `
|
|
2091
2222
|
<html>
|
|
2092
2223
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
2093
2224
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
|
|
2094
|
-
<h2 style="color: #dc2626;"
|
|
2225
|
+
<h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
|
|
2095
2226
|
<p>A potentially suspicious login was detected for your account.</p>
|
|
2096
2227
|
|
|
2097
2228
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -2126,7 +2257,7 @@ var notifications$1 = ({ strapi: strapi2 }) => ({
|
|
|
2126
2257
|
</div>
|
|
2127
2258
|
</body>
|
|
2128
2259
|
</html>`,
|
|
2129
|
-
text:
|
|
2260
|
+
text: `[ALERT] Suspicious Login Detected
|
|
2130
2261
|
|
|
2131
2262
|
A potentially suspicious login was detected for your account.
|
|
2132
2263
|
|
|
@@ -2141,9 +2272,9 @@ Login Details:
|
|
|
2141
2272
|
Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`
|
|
2142
2273
|
},
|
|
2143
2274
|
newLocation: {
|
|
2144
|
-
subject: "
|
|
2145
|
-
html: `<h2
|
|
2146
|
-
text:
|
|
2275
|
+
subject: "[LOCATION] New Location Login Detected",
|
|
2276
|
+
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>`,
|
|
2277
|
+
text: `[LOCATION] New Location Login
|
|
2147
2278
|
|
|
2148
2279
|
Account: {{user.email}}
|
|
2149
2280
|
Time: {{session.loginTime}}
|
|
@@ -2151,9 +2282,9 @@ Location: {{geo.city}}, {{geo.country}}
|
|
|
2151
2282
|
IP: {{session.ipAddress}}`
|
|
2152
2283
|
},
|
|
2153
2284
|
vpnProxy: {
|
|
2154
|
-
subject: "
|
|
2155
|
-
html: `<h2
|
|
2156
|
-
text:
|
|
2285
|
+
subject: "[WARNING] VPN/Proxy Login Detected",
|
|
2286
|
+
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>`,
|
|
2287
|
+
text: `[WARNING] VPN/Proxy Detected
|
|
2157
2288
|
|
|
2158
2289
|
Account: {{user.email}}
|
|
2159
2290
|
Time: {{session.loginTime}}
|
|
@@ -2297,17 +2428,17 @@ VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`
|
|
|
2297
2428
|
title: this.getEventTitle(event),
|
|
2298
2429
|
color: this.getEventColor(event),
|
|
2299
2430
|
fields: [
|
|
2300
|
-
{ name: "
|
|
2431
|
+
{ name: "User", value: `${user.email}
|
|
2301
2432
|
${user.username || "N/A"}`, inline: true },
|
|
2302
|
-
{ name: "
|
|
2303
|
-
{ name: "
|
|
2433
|
+
{ name: "IP", value: session2.ipAddress, inline: true },
|
|
2434
|
+
{ name: "Time", value: new Date(session2.loginTime).toLocaleString(), inline: false }
|
|
2304
2435
|
],
|
|
2305
2436
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2306
2437
|
footer: { text: "Magic Session Manager" }
|
|
2307
2438
|
};
|
|
2308
2439
|
if (geoData) {
|
|
2309
2440
|
embed.fields.push({
|
|
2310
|
-
name: "
|
|
2441
|
+
name: "[LOCATION] Location",
|
|
2311
2442
|
value: `${geoData.country_flag} ${geoData.city}, ${geoData.country}`,
|
|
2312
2443
|
inline: true
|
|
2313
2444
|
});
|
|
@@ -2317,7 +2448,7 @@ ${user.username || "N/A"}`, inline: true },
|
|
|
2317
2448
|
if (geoData.isProxy) warnings.push("Proxy");
|
|
2318
2449
|
if (geoData.isThreat) warnings.push("Threat");
|
|
2319
2450
|
embed.fields.push({
|
|
2320
|
-
name: "
|
|
2451
|
+
name: "[WARNING] Security",
|
|
2321
2452
|
value: `${warnings.join(", ")} detected
|
|
2322
2453
|
Score: ${geoData.securityScore}/100`,
|
|
2323
2454
|
inline: true
|
|
@@ -2328,13 +2459,13 @@ Score: ${geoData.securityScore}/100`,
|
|
|
2328
2459
|
},
|
|
2329
2460
|
getEventTitle(event) {
|
|
2330
2461
|
const titles = {
|
|
2331
|
-
"login.suspicious": "
|
|
2332
|
-
"login.new_location": "
|
|
2333
|
-
"login.vpn": "
|
|
2334
|
-
"login.threat": "
|
|
2335
|
-
"session.terminated": "
|
|
2462
|
+
"login.suspicious": "[ALERT] Suspicious Login",
|
|
2463
|
+
"login.new_location": "[LOCATION] New Location Login",
|
|
2464
|
+
"login.vpn": "[WARNING] VPN Login Detected",
|
|
2465
|
+
"login.threat": "[THREAT] Threat IP Login",
|
|
2466
|
+
"session.terminated": "[INFO] Session Terminated"
|
|
2336
2467
|
};
|
|
2337
|
-
return titles[event] || "
|
|
2468
|
+
return titles[event] || "[STATS] Session Event";
|
|
2338
2469
|
},
|
|
2339
2470
|
getEventColor(event) {
|
|
2340
2471
|
const colors = {
|