strapi-plugin-magic-sessionmanager 4.2.11 → 4.2.13
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/dist/server/index.js +272 -50
- package/dist/server/index.mjs +272 -50
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -219,6 +219,18 @@ var encryption = {
|
|
|
219
219
|
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
220
220
|
const { hashToken: hashToken$3 } = encryption;
|
|
221
221
|
const lastTouchCache = /* @__PURE__ */ new Map();
|
|
222
|
+
const CACHE_MAX_SIZE = 1e4;
|
|
223
|
+
const CACHE_CLEANUP_AGE = 60 * 60 * 1e3;
|
|
224
|
+
function cleanupOldCacheEntries() {
|
|
225
|
+
if (lastTouchCache.size < CACHE_MAX_SIZE) return;
|
|
226
|
+
const now = Date.now();
|
|
227
|
+
const cutoff = now - CACHE_CLEANUP_AGE;
|
|
228
|
+
for (const [key, timestamp] of lastTouchCache.entries()) {
|
|
229
|
+
if (timestamp < cutoff) {
|
|
230
|
+
lastTouchCache.delete(key);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
222
234
|
var lastSeen = ({ strapi: strapi2 }) => {
|
|
223
235
|
return async (ctx, next) => {
|
|
224
236
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
@@ -226,7 +238,30 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
226
238
|
await next();
|
|
227
239
|
return;
|
|
228
240
|
}
|
|
229
|
-
const skipPaths = [
|
|
241
|
+
const skipPaths = [
|
|
242
|
+
"/admin",
|
|
243
|
+
// Admin panel routes (have their own auth)
|
|
244
|
+
"/_health",
|
|
245
|
+
// Health check
|
|
246
|
+
"/favicon.ico",
|
|
247
|
+
// Static assets
|
|
248
|
+
"/api/auth/local",
|
|
249
|
+
// Login endpoint
|
|
250
|
+
"/api/auth/register",
|
|
251
|
+
// Registration endpoint
|
|
252
|
+
"/api/auth/forgot-password",
|
|
253
|
+
// Password reset
|
|
254
|
+
"/api/auth/reset-password",
|
|
255
|
+
// Password reset
|
|
256
|
+
"/api/auth/logout",
|
|
257
|
+
// Logout endpoint (handled separately)
|
|
258
|
+
"/api/auth/refresh",
|
|
259
|
+
// Refresh token (has own validation in bootstrap.js)
|
|
260
|
+
"/api/connect",
|
|
261
|
+
// OAuth providers
|
|
262
|
+
"/api/magic-link"
|
|
263
|
+
// Magic link auth (if using magic-link plugin)
|
|
264
|
+
];
|
|
230
265
|
if (skipPaths.some((p) => ctx.path.startsWith(p))) {
|
|
231
266
|
await next();
|
|
232
267
|
return;
|
|
@@ -248,10 +283,8 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
248
283
|
ctx.state.sessionUserId = matchingSession.user.documentId;
|
|
249
284
|
}
|
|
250
285
|
} else {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return ctx.unauthorized("This session has been terminated. Please login again.");
|
|
254
|
-
}
|
|
286
|
+
strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (token hash: ${currentTokenHash.substring(0, 8)}...)`);
|
|
287
|
+
return ctx.unauthorized("This session has been terminated. Please login again.");
|
|
255
288
|
}
|
|
256
289
|
} catch (err) {
|
|
257
290
|
strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
|
|
@@ -265,6 +298,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
265
298
|
const lastTouch = lastTouchCache.get(matchingSession.documentId) || 0;
|
|
266
299
|
if (now - lastTouch > rateLimit) {
|
|
267
300
|
lastTouchCache.set(matchingSession.documentId, now);
|
|
301
|
+
cleanupOldCacheEntries();
|
|
268
302
|
await strapi2.documents(SESSION_UID$3).update({
|
|
269
303
|
documentId: matchingSession.documentId,
|
|
270
304
|
data: { lastActive: /* @__PURE__ */ new Date() }
|
|
@@ -351,20 +385,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
351
385
|
ctx.body = { message: "Logged out successfully" };
|
|
352
386
|
return;
|
|
353
387
|
}
|
|
354
|
-
const
|
|
388
|
+
const tokenHashValue = hashToken$2(token);
|
|
389
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
355
390
|
filters: {
|
|
391
|
+
tokenHash: tokenHashValue,
|
|
356
392
|
isActive: true
|
|
357
393
|
}
|
|
358
394
|
});
|
|
359
|
-
const matchingSession = allSessions.find((session2) => {
|
|
360
|
-
if (!session2.token) return false;
|
|
361
|
-
try {
|
|
362
|
-
const decrypted = decryptToken$2(session2.token);
|
|
363
|
-
return decrypted === token;
|
|
364
|
-
} catch (err) {
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
395
|
if (matchingSession) {
|
|
369
396
|
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
370
397
|
log.info(`[LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
|
|
@@ -455,8 +482,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
455
482
|
userAgent,
|
|
456
483
|
token: ctx.body.jwt,
|
|
457
484
|
// Store Access Token (encrypted)
|
|
458
|
-
refreshToken: ctx.body.refreshToken
|
|
485
|
+
refreshToken: ctx.body.refreshToken,
|
|
459
486
|
// Store Refresh Token (encrypted) if exists
|
|
487
|
+
geoData
|
|
488
|
+
// Store geolocation data if available
|
|
460
489
|
});
|
|
461
490
|
log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
462
491
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
@@ -507,20 +536,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
507
536
|
try {
|
|
508
537
|
const refreshToken = ctx.request.body?.refreshToken;
|
|
509
538
|
if (refreshToken) {
|
|
510
|
-
const
|
|
539
|
+
const refreshTokenHashValue = hashToken$2(refreshToken);
|
|
540
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
511
541
|
filters: {
|
|
542
|
+
refreshTokenHash: refreshTokenHashValue,
|
|
512
543
|
isActive: true
|
|
513
544
|
}
|
|
514
545
|
});
|
|
515
|
-
const matchingSession = allSessions.find((session2) => {
|
|
516
|
-
if (!session2.refreshToken) return false;
|
|
517
|
-
try {
|
|
518
|
-
const decrypted = decryptToken$2(session2.refreshToken);
|
|
519
|
-
return decrypted === refreshToken;
|
|
520
|
-
} catch (err) {
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
546
|
if (!matchingSession) {
|
|
525
547
|
log.warn("[BLOCKED] Blocked refresh token request - no active session");
|
|
526
548
|
ctx.status = 401;
|
|
@@ -546,20 +568,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
546
568
|
const newAccessToken = ctx.body.jwt;
|
|
547
569
|
const newRefreshToken = ctx.body.refreshToken;
|
|
548
570
|
if (oldRefreshToken) {
|
|
549
|
-
const
|
|
571
|
+
const oldRefreshTokenHash = hashToken$2(oldRefreshToken);
|
|
572
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
550
573
|
filters: {
|
|
574
|
+
refreshTokenHash: oldRefreshTokenHash,
|
|
551
575
|
isActive: true
|
|
552
576
|
}
|
|
553
577
|
});
|
|
554
|
-
const matchingSession = allSessions.find((session2) => {
|
|
555
|
-
if (!session2.refreshToken) return false;
|
|
556
|
-
try {
|
|
557
|
-
const decrypted = decryptToken$2(session2.refreshToken);
|
|
558
|
-
return decrypted === oldRefreshToken;
|
|
559
|
-
} catch (err) {
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
578
|
if (matchingSession) {
|
|
564
579
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
565
580
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
@@ -688,11 +703,11 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
688
703
|
const log = createLogger$2(strapi2);
|
|
689
704
|
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
690
705
|
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
691
|
-
log.info("
|
|
706
|
+
log.info("[STOP] License pinging stopped");
|
|
692
707
|
}
|
|
693
708
|
if (strapi2.sessionManagerIntervals && strapi2.sessionManagerIntervals.cleanup) {
|
|
694
709
|
clearInterval(strapi2.sessionManagerIntervals.cleanup);
|
|
695
|
-
log.info("
|
|
710
|
+
log.info("[STOP] Session cleanup interval stopped");
|
|
696
711
|
}
|
|
697
712
|
log.info("[SUCCESS] Plugin cleanup completed");
|
|
698
713
|
};
|
|
@@ -1173,6 +1188,7 @@ var session$3 = {
|
|
|
1173
1188
|
* GET /api/magic-sessionmanager/my-sessions
|
|
1174
1189
|
* Automatically uses the authenticated user's documentId
|
|
1175
1190
|
* Marks which session is the current one (based on JWT token hash)
|
|
1191
|
+
* Fetches geolocation data on-demand if not already stored
|
|
1176
1192
|
*/
|
|
1177
1193
|
async getOwnSessions(ctx) {
|
|
1178
1194
|
try {
|
|
@@ -1189,7 +1205,8 @@ var session$3 = {
|
|
|
1189
1205
|
const config2 = strapi.config.get("plugin::magic-sessionmanager") || {};
|
|
1190
1206
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1191
1207
|
const now = /* @__PURE__ */ new Date();
|
|
1192
|
-
const
|
|
1208
|
+
const geolocationService = strapi.plugin("magic-sessionmanager").service("geolocation");
|
|
1209
|
+
const sessionsWithCurrent = await Promise.all(allSessions.map(async (session2) => {
|
|
1193
1210
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1194
1211
|
const timeSinceActive = now - lastActiveTime;
|
|
1195
1212
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -1198,6 +1215,39 @@ var session$3 = {
|
|
|
1198
1215
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
1199
1216
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1200
1217
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1218
|
+
let geoLocation = session2.geoLocation;
|
|
1219
|
+
if (typeof geoLocation === "string") {
|
|
1220
|
+
try {
|
|
1221
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1222
|
+
} catch (e) {
|
|
1223
|
+
geoLocation = null;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (!geoLocation && session2.ipAddress) {
|
|
1227
|
+
try {
|
|
1228
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
1229
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
1230
|
+
geoLocation = {
|
|
1231
|
+
country: geoData.country,
|
|
1232
|
+
country_code: geoData.country_code,
|
|
1233
|
+
country_flag: geoData.country_flag,
|
|
1234
|
+
city: geoData.city,
|
|
1235
|
+
region: geoData.region,
|
|
1236
|
+
timezone: geoData.timezone
|
|
1237
|
+
};
|
|
1238
|
+
strapi.documents(SESSION_UID$1).update({
|
|
1239
|
+
documentId: session2.documentId,
|
|
1240
|
+
data: {
|
|
1241
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
1242
|
+
securityScore: geoData.securityScore || null
|
|
1243
|
+
}
|
|
1244
|
+
}).catch(() => {
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
} catch (geoErr) {
|
|
1248
|
+
strapi.log.debug("[magic-sessionmanager] Geolocation lookup failed:", geoErr.message);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1201
1251
|
const {
|
|
1202
1252
|
token,
|
|
1203
1253
|
tokenHash,
|
|
@@ -1206,6 +1256,8 @@ var session$3 = {
|
|
|
1206
1256
|
locale,
|
|
1207
1257
|
publishedAt,
|
|
1208
1258
|
// Remove Strapi internal fields
|
|
1259
|
+
geoLocation: _geo,
|
|
1260
|
+
// Remove raw geoLocation
|
|
1209
1261
|
...sessionWithoutTokens
|
|
1210
1262
|
} = session2;
|
|
1211
1263
|
return {
|
|
@@ -1213,11 +1265,13 @@ var session$3 = {
|
|
|
1213
1265
|
deviceType,
|
|
1214
1266
|
browserName,
|
|
1215
1267
|
osName,
|
|
1268
|
+
geoLocation,
|
|
1269
|
+
// Parsed object or null
|
|
1216
1270
|
isCurrentSession,
|
|
1217
1271
|
isTrulyActive,
|
|
1218
1272
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1219
1273
|
};
|
|
1220
|
-
});
|
|
1274
|
+
}));
|
|
1221
1275
|
sessionsWithCurrent.sort((a, b) => {
|
|
1222
1276
|
if (a.isCurrentSession) return -1;
|
|
1223
1277
|
if (b.isCurrentSession) return 1;
|
|
@@ -1319,6 +1373,7 @@ var session$3 = {
|
|
|
1319
1373
|
* Get current session info based on JWT token hash
|
|
1320
1374
|
* GET /api/magic-sessionmanager/current-session
|
|
1321
1375
|
* Returns the session associated with the current JWT token
|
|
1376
|
+
* Fetches geolocation on-demand if not already stored
|
|
1322
1377
|
*/
|
|
1323
1378
|
async getCurrentSession(ctx) {
|
|
1324
1379
|
try {
|
|
@@ -1347,6 +1402,40 @@ var session$3 = {
|
|
|
1347
1402
|
const deviceType = currentSession.deviceType || parsedUA.deviceType;
|
|
1348
1403
|
const browserName = currentSession.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1349
1404
|
const osName = currentSession.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1405
|
+
let geoLocation = currentSession.geoLocation;
|
|
1406
|
+
if (typeof geoLocation === "string") {
|
|
1407
|
+
try {
|
|
1408
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1409
|
+
} catch (e) {
|
|
1410
|
+
geoLocation = null;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
if (!geoLocation && currentSession.ipAddress) {
|
|
1414
|
+
try {
|
|
1415
|
+
const geolocationService = strapi.plugin("magic-sessionmanager").service("geolocation");
|
|
1416
|
+
const geoData = await geolocationService.getIpInfo(currentSession.ipAddress);
|
|
1417
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
1418
|
+
geoLocation = {
|
|
1419
|
+
country: geoData.country,
|
|
1420
|
+
country_code: geoData.country_code,
|
|
1421
|
+
country_flag: geoData.country_flag,
|
|
1422
|
+
city: geoData.city,
|
|
1423
|
+
region: geoData.region,
|
|
1424
|
+
timezone: geoData.timezone
|
|
1425
|
+
};
|
|
1426
|
+
strapi.documents(SESSION_UID$1).update({
|
|
1427
|
+
documentId: currentSession.documentId,
|
|
1428
|
+
data: {
|
|
1429
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
1430
|
+
securityScore: geoData.securityScore || null
|
|
1431
|
+
}
|
|
1432
|
+
}).catch(() => {
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
} catch (geoErr) {
|
|
1436
|
+
strapi.log.debug("[magic-sessionmanager] Geolocation lookup failed:", geoErr.message);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1350
1439
|
const {
|
|
1351
1440
|
token: _,
|
|
1352
1441
|
tokenHash: _th,
|
|
@@ -1354,6 +1443,7 @@ var session$3 = {
|
|
|
1354
1443
|
refreshTokenHash: _rth,
|
|
1355
1444
|
locale: _l,
|
|
1356
1445
|
publishedAt: _p,
|
|
1446
|
+
geoLocation: _geo,
|
|
1357
1447
|
...sessionWithoutTokens
|
|
1358
1448
|
} = currentSession;
|
|
1359
1449
|
ctx.body = {
|
|
@@ -1362,6 +1452,8 @@ var session$3 = {
|
|
|
1362
1452
|
deviceType,
|
|
1363
1453
|
browserName,
|
|
1364
1454
|
osName,
|
|
1455
|
+
geoLocation,
|
|
1456
|
+
// Parsed object or null
|
|
1365
1457
|
isCurrentSession: true,
|
|
1366
1458
|
isTrulyActive: timeSinceActive < inactivityTimeout,
|
|
1367
1459
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1905,10 +1997,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1905
1997
|
return {
|
|
1906
1998
|
/**
|
|
1907
1999
|
* Create a new session record
|
|
1908
|
-
* @param {Object} params - { userId, ip, userAgent, token, refreshToken }
|
|
2000
|
+
* @param {Object} params - { userId, ip, userAgent, token, refreshToken, geoData }
|
|
1909
2001
|
* @returns {Promise<Object>} Created session
|
|
1910
2002
|
*/
|
|
1911
|
-
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
|
|
2003
|
+
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken, geoData }) {
|
|
1912
2004
|
try {
|
|
1913
2005
|
const now = /* @__PURE__ */ new Date();
|
|
1914
2006
|
const sessionId = generateSessionId(userId);
|
|
@@ -1916,6 +2008,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1916
2008
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1917
2009
|
const tokenHashValue = token ? hashToken(token) : null;
|
|
1918
2010
|
const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
|
|
2011
|
+
const parsedUA = parseUserAgent(userAgent);
|
|
1919
2012
|
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1920
2013
|
data: {
|
|
1921
2014
|
user: userId,
|
|
@@ -1933,8 +2026,22 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1933
2026
|
// Encrypted Refresh Token
|
|
1934
2027
|
refreshTokenHash: refreshTokenHashValue,
|
|
1935
2028
|
// SHA-256 hash for fast lookup
|
|
1936
|
-
sessionId
|
|
2029
|
+
sessionId,
|
|
1937
2030
|
// Unique identifier
|
|
2031
|
+
// Device info from User-Agent
|
|
2032
|
+
deviceType: parsedUA.deviceType,
|
|
2033
|
+
browserName: parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName,
|
|
2034
|
+
osName: parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName,
|
|
2035
|
+
// Geolocation data (if available from Premium features)
|
|
2036
|
+
geoLocation: geoData ? JSON.stringify({
|
|
2037
|
+
country: geoData.country,
|
|
2038
|
+
country_code: geoData.country_code,
|
|
2039
|
+
country_flag: geoData.country_flag,
|
|
2040
|
+
city: geoData.city,
|
|
2041
|
+
region: geoData.region,
|
|
2042
|
+
timezone: geoData.timezone
|
|
2043
|
+
}) : null,
|
|
2044
|
+
securityScore: geoData?.securityScore || null
|
|
1938
2045
|
}
|
|
1939
2046
|
});
|
|
1940
2047
|
log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
@@ -1995,6 +2102,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1995
2102
|
},
|
|
1996
2103
|
/**
|
|
1997
2104
|
* Get ALL sessions (active + inactive) with accurate online status
|
|
2105
|
+
* Fetches geolocation on-demand for sessions without it (limited to prevent API abuse)
|
|
1998
2106
|
* @returns {Promise<Array>} All sessions with enhanced data
|
|
1999
2107
|
*/
|
|
2000
2108
|
async getAllSessions() {
|
|
@@ -2007,8 +2115,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2007
2115
|
});
|
|
2008
2116
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2009
2117
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2118
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2119
|
+
let geoLookupsRemaining = 20;
|
|
2010
2120
|
const now = /* @__PURE__ */ new Date();
|
|
2011
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2121
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2012
2122
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2013
2123
|
const timeSinceActive = now - lastActiveTime;
|
|
2014
2124
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -2016,6 +2126,40 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2016
2126
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2017
2127
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2018
2128
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2129
|
+
let geoLocation = session2.geoLocation;
|
|
2130
|
+
if (typeof geoLocation === "string") {
|
|
2131
|
+
try {
|
|
2132
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2133
|
+
} catch (e) {
|
|
2134
|
+
geoLocation = null;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
if (!geoLocation && session2.ipAddress && geoLookupsRemaining > 0) {
|
|
2138
|
+
geoLookupsRemaining--;
|
|
2139
|
+
try {
|
|
2140
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2141
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2142
|
+
geoLocation = {
|
|
2143
|
+
country: geoData.country,
|
|
2144
|
+
country_code: geoData.country_code,
|
|
2145
|
+
country_flag: geoData.country_flag,
|
|
2146
|
+
city: geoData.city,
|
|
2147
|
+
region: geoData.region,
|
|
2148
|
+
timezone: geoData.timezone
|
|
2149
|
+
};
|
|
2150
|
+
strapi2.documents(SESSION_UID).update({
|
|
2151
|
+
documentId: session2.documentId,
|
|
2152
|
+
data: {
|
|
2153
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2154
|
+
securityScore: geoData.securityScore || null
|
|
2155
|
+
}
|
|
2156
|
+
}).catch(() => {
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
} catch (geoErr) {
|
|
2160
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2019
2163
|
const {
|
|
2020
2164
|
token,
|
|
2021
2165
|
tokenHash,
|
|
@@ -2023,6 +2167,8 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2023
2167
|
refreshTokenHash,
|
|
2024
2168
|
locale,
|
|
2025
2169
|
publishedAt,
|
|
2170
|
+
geoLocation: _geo,
|
|
2171
|
+
// Remove raw geoLocation, we use parsed version
|
|
2026
2172
|
...safeSession
|
|
2027
2173
|
} = session2;
|
|
2028
2174
|
return {
|
|
@@ -2030,10 +2176,12 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2030
2176
|
deviceType,
|
|
2031
2177
|
browserName,
|
|
2032
2178
|
osName,
|
|
2179
|
+
geoLocation,
|
|
2180
|
+
// Parsed object or null
|
|
2033
2181
|
isTrulyActive,
|
|
2034
2182
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2035
2183
|
};
|
|
2036
|
-
});
|
|
2184
|
+
}));
|
|
2037
2185
|
return enhancedSessions;
|
|
2038
2186
|
} catch (err) {
|
|
2039
2187
|
log.error("Error getting all sessions:", err);
|
|
@@ -2042,6 +2190,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2042
2190
|
},
|
|
2043
2191
|
/**
|
|
2044
2192
|
* Get all active sessions with accurate online status
|
|
2193
|
+
* Fetches geolocation on-demand for sessions without it
|
|
2045
2194
|
* @returns {Promise<Array>} Active sessions with user data and online status
|
|
2046
2195
|
*/
|
|
2047
2196
|
async getActiveSessions() {
|
|
@@ -2053,8 +2202,9 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2053
2202
|
});
|
|
2054
2203
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2055
2204
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2205
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2056
2206
|
const now = /* @__PURE__ */ new Date();
|
|
2057
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2207
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2058
2208
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2059
2209
|
const timeSinceActive = now - lastActiveTime;
|
|
2060
2210
|
const isTrulyActive = timeSinceActive < inactivityTimeout;
|
|
@@ -2062,6 +2212,39 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2062
2212
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2063
2213
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2064
2214
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2215
|
+
let geoLocation = session2.geoLocation;
|
|
2216
|
+
if (typeof geoLocation === "string") {
|
|
2217
|
+
try {
|
|
2218
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2219
|
+
} catch (e) {
|
|
2220
|
+
geoLocation = null;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
if (!geoLocation && session2.ipAddress) {
|
|
2224
|
+
try {
|
|
2225
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2226
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2227
|
+
geoLocation = {
|
|
2228
|
+
country: geoData.country,
|
|
2229
|
+
country_code: geoData.country_code,
|
|
2230
|
+
country_flag: geoData.country_flag,
|
|
2231
|
+
city: geoData.city,
|
|
2232
|
+
region: geoData.region,
|
|
2233
|
+
timezone: geoData.timezone
|
|
2234
|
+
};
|
|
2235
|
+
strapi2.documents(SESSION_UID).update({
|
|
2236
|
+
documentId: session2.documentId,
|
|
2237
|
+
data: {
|
|
2238
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2239
|
+
securityScore: geoData.securityScore || null
|
|
2240
|
+
}
|
|
2241
|
+
}).catch(() => {
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
} catch (geoErr) {
|
|
2245
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2065
2248
|
const {
|
|
2066
2249
|
token,
|
|
2067
2250
|
tokenHash,
|
|
@@ -2069,6 +2252,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2069
2252
|
refreshTokenHash,
|
|
2070
2253
|
locale,
|
|
2071
2254
|
publishedAt,
|
|
2255
|
+
geoLocation: _geo,
|
|
2072
2256
|
...safeSession
|
|
2073
2257
|
} = session2;
|
|
2074
2258
|
return {
|
|
@@ -2076,10 +2260,11 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2076
2260
|
deviceType,
|
|
2077
2261
|
browserName,
|
|
2078
2262
|
osName,
|
|
2263
|
+
geoLocation,
|
|
2079
2264
|
isTrulyActive,
|
|
2080
2265
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2081
2266
|
};
|
|
2082
|
-
});
|
|
2267
|
+
}));
|
|
2083
2268
|
return enhancedSessions.filter((s) => s.isTrulyActive);
|
|
2084
2269
|
} catch (err) {
|
|
2085
2270
|
log.error("Error getting active sessions:", err);
|
|
@@ -2089,6 +2274,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2089
2274
|
/**
|
|
2090
2275
|
* Get all sessions for a specific user
|
|
2091
2276
|
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
2277
|
+
* Fetches geolocation on-demand for sessions without it
|
|
2092
2278
|
* @param {string|number} userId - User documentId or numeric id
|
|
2093
2279
|
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
2094
2280
|
*/
|
|
@@ -2107,8 +2293,9 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2107
2293
|
});
|
|
2108
2294
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2109
2295
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2296
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2110
2297
|
const now = /* @__PURE__ */ new Date();
|
|
2111
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2298
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2112
2299
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2113
2300
|
const timeSinceActive = now - lastActiveTime;
|
|
2114
2301
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -2116,6 +2303,39 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2116
2303
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2117
2304
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2118
2305
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2306
|
+
let geoLocation = session2.geoLocation;
|
|
2307
|
+
if (typeof geoLocation === "string") {
|
|
2308
|
+
try {
|
|
2309
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2310
|
+
} catch (e) {
|
|
2311
|
+
geoLocation = null;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
if (!geoLocation && session2.ipAddress) {
|
|
2315
|
+
try {
|
|
2316
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2317
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2318
|
+
geoLocation = {
|
|
2319
|
+
country: geoData.country,
|
|
2320
|
+
country_code: geoData.country_code,
|
|
2321
|
+
country_flag: geoData.country_flag,
|
|
2322
|
+
city: geoData.city,
|
|
2323
|
+
region: geoData.region,
|
|
2324
|
+
timezone: geoData.timezone
|
|
2325
|
+
};
|
|
2326
|
+
strapi2.documents(SESSION_UID).update({
|
|
2327
|
+
documentId: session2.documentId,
|
|
2328
|
+
data: {
|
|
2329
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2330
|
+
securityScore: geoData.securityScore || null
|
|
2331
|
+
}
|
|
2332
|
+
}).catch(() => {
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
} catch (geoErr) {
|
|
2336
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2119
2339
|
const {
|
|
2120
2340
|
token,
|
|
2121
2341
|
tokenHash,
|
|
@@ -2123,6 +2343,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2123
2343
|
refreshTokenHash,
|
|
2124
2344
|
locale,
|
|
2125
2345
|
publishedAt,
|
|
2346
|
+
geoLocation: _geo,
|
|
2126
2347
|
...safeSession
|
|
2127
2348
|
} = session2;
|
|
2128
2349
|
return {
|
|
@@ -2130,10 +2351,11 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2130
2351
|
deviceType,
|
|
2131
2352
|
browserName,
|
|
2132
2353
|
osName,
|
|
2354
|
+
geoLocation,
|
|
2133
2355
|
isTrulyActive,
|
|
2134
2356
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2135
2357
|
};
|
|
2136
|
-
});
|
|
2358
|
+
}));
|
|
2137
2359
|
return enhancedSessions;
|
|
2138
2360
|
} catch (err) {
|
|
2139
2361
|
log.error("Error getting user sessions:", err);
|
|
@@ -2246,7 +2468,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2246
2468
|
}
|
|
2247
2469
|
};
|
|
2248
2470
|
};
|
|
2249
|
-
const version = "4.2.
|
|
2471
|
+
const version = "4.2.13";
|
|
2250
2472
|
const require$$2 = {
|
|
2251
2473
|
version
|
|
2252
2474
|
};
|
package/dist/server/index.mjs
CHANGED
|
@@ -215,6 +215,18 @@ var encryption = {
|
|
|
215
215
|
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
216
216
|
const { hashToken: hashToken$3 } = encryption;
|
|
217
217
|
const lastTouchCache = /* @__PURE__ */ new Map();
|
|
218
|
+
const CACHE_MAX_SIZE = 1e4;
|
|
219
|
+
const CACHE_CLEANUP_AGE = 60 * 60 * 1e3;
|
|
220
|
+
function cleanupOldCacheEntries() {
|
|
221
|
+
if (lastTouchCache.size < CACHE_MAX_SIZE) return;
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
const cutoff = now - CACHE_CLEANUP_AGE;
|
|
224
|
+
for (const [key, timestamp] of lastTouchCache.entries()) {
|
|
225
|
+
if (timestamp < cutoff) {
|
|
226
|
+
lastTouchCache.delete(key);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
218
230
|
var lastSeen = ({ strapi: strapi2 }) => {
|
|
219
231
|
return async (ctx, next) => {
|
|
220
232
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
@@ -222,7 +234,30 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
222
234
|
await next();
|
|
223
235
|
return;
|
|
224
236
|
}
|
|
225
|
-
const skipPaths = [
|
|
237
|
+
const skipPaths = [
|
|
238
|
+
"/admin",
|
|
239
|
+
// Admin panel routes (have their own auth)
|
|
240
|
+
"/_health",
|
|
241
|
+
// Health check
|
|
242
|
+
"/favicon.ico",
|
|
243
|
+
// Static assets
|
|
244
|
+
"/api/auth/local",
|
|
245
|
+
// Login endpoint
|
|
246
|
+
"/api/auth/register",
|
|
247
|
+
// Registration endpoint
|
|
248
|
+
"/api/auth/forgot-password",
|
|
249
|
+
// Password reset
|
|
250
|
+
"/api/auth/reset-password",
|
|
251
|
+
// Password reset
|
|
252
|
+
"/api/auth/logout",
|
|
253
|
+
// Logout endpoint (handled separately)
|
|
254
|
+
"/api/auth/refresh",
|
|
255
|
+
// Refresh token (has own validation in bootstrap.js)
|
|
256
|
+
"/api/connect",
|
|
257
|
+
// OAuth providers
|
|
258
|
+
"/api/magic-link"
|
|
259
|
+
// Magic link auth (if using magic-link plugin)
|
|
260
|
+
];
|
|
226
261
|
if (skipPaths.some((p) => ctx.path.startsWith(p))) {
|
|
227
262
|
await next();
|
|
228
263
|
return;
|
|
@@ -244,10 +279,8 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
244
279
|
ctx.state.sessionUserId = matchingSession.user.documentId;
|
|
245
280
|
}
|
|
246
281
|
} else {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return ctx.unauthorized("This session has been terminated. Please login again.");
|
|
250
|
-
}
|
|
282
|
+
strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (token hash: ${currentTokenHash.substring(0, 8)}...)`);
|
|
283
|
+
return ctx.unauthorized("This session has been terminated. Please login again.");
|
|
251
284
|
}
|
|
252
285
|
} catch (err) {
|
|
253
286
|
strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
|
|
@@ -261,6 +294,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
261
294
|
const lastTouch = lastTouchCache.get(matchingSession.documentId) || 0;
|
|
262
295
|
if (now - lastTouch > rateLimit) {
|
|
263
296
|
lastTouchCache.set(matchingSession.documentId, now);
|
|
297
|
+
cleanupOldCacheEntries();
|
|
264
298
|
await strapi2.documents(SESSION_UID$3).update({
|
|
265
299
|
documentId: matchingSession.documentId,
|
|
266
300
|
data: { lastActive: /* @__PURE__ */ new Date() }
|
|
@@ -347,20 +381,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
347
381
|
ctx.body = { message: "Logged out successfully" };
|
|
348
382
|
return;
|
|
349
383
|
}
|
|
350
|
-
const
|
|
384
|
+
const tokenHashValue = hashToken$2(token);
|
|
385
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
351
386
|
filters: {
|
|
387
|
+
tokenHash: tokenHashValue,
|
|
352
388
|
isActive: true
|
|
353
389
|
}
|
|
354
390
|
});
|
|
355
|
-
const matchingSession = allSessions.find((session2) => {
|
|
356
|
-
if (!session2.token) return false;
|
|
357
|
-
try {
|
|
358
|
-
const decrypted = decryptToken$2(session2.token);
|
|
359
|
-
return decrypted === token;
|
|
360
|
-
} catch (err) {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
391
|
if (matchingSession) {
|
|
365
392
|
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
366
393
|
log.info(`[LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
|
|
@@ -451,8 +478,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
451
478
|
userAgent,
|
|
452
479
|
token: ctx.body.jwt,
|
|
453
480
|
// Store Access Token (encrypted)
|
|
454
|
-
refreshToken: ctx.body.refreshToken
|
|
481
|
+
refreshToken: ctx.body.refreshToken,
|
|
455
482
|
// Store Refresh Token (encrypted) if exists
|
|
483
|
+
geoData
|
|
484
|
+
// Store geolocation data if available
|
|
456
485
|
});
|
|
457
486
|
log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
458
487
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
@@ -503,20 +532,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
503
532
|
try {
|
|
504
533
|
const refreshToken = ctx.request.body?.refreshToken;
|
|
505
534
|
if (refreshToken) {
|
|
506
|
-
const
|
|
535
|
+
const refreshTokenHashValue = hashToken$2(refreshToken);
|
|
536
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
507
537
|
filters: {
|
|
538
|
+
refreshTokenHash: refreshTokenHashValue,
|
|
508
539
|
isActive: true
|
|
509
540
|
}
|
|
510
541
|
});
|
|
511
|
-
const matchingSession = allSessions.find((session2) => {
|
|
512
|
-
if (!session2.refreshToken) return false;
|
|
513
|
-
try {
|
|
514
|
-
const decrypted = decryptToken$2(session2.refreshToken);
|
|
515
|
-
return decrypted === refreshToken;
|
|
516
|
-
} catch (err) {
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
542
|
if (!matchingSession) {
|
|
521
543
|
log.warn("[BLOCKED] Blocked refresh token request - no active session");
|
|
522
544
|
ctx.status = 401;
|
|
@@ -542,20 +564,13 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
542
564
|
const newAccessToken = ctx.body.jwt;
|
|
543
565
|
const newRefreshToken = ctx.body.refreshToken;
|
|
544
566
|
if (oldRefreshToken) {
|
|
545
|
-
const
|
|
567
|
+
const oldRefreshTokenHash = hashToken$2(oldRefreshToken);
|
|
568
|
+
const matchingSession = await strapi2.documents(SESSION_UID$2).findFirst({
|
|
546
569
|
filters: {
|
|
570
|
+
refreshTokenHash: oldRefreshTokenHash,
|
|
547
571
|
isActive: true
|
|
548
572
|
}
|
|
549
573
|
});
|
|
550
|
-
const matchingSession = allSessions.find((session2) => {
|
|
551
|
-
if (!session2.refreshToken) return false;
|
|
552
|
-
try {
|
|
553
|
-
const decrypted = decryptToken$2(session2.refreshToken);
|
|
554
|
-
return decrypted === oldRefreshToken;
|
|
555
|
-
} catch (err) {
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
574
|
if (matchingSession) {
|
|
560
575
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
561
576
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
@@ -684,11 +699,11 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
684
699
|
const log = createLogger$2(strapi2);
|
|
685
700
|
if (strapi2.licenseGuard && strapi2.licenseGuard.pingInterval) {
|
|
686
701
|
clearInterval(strapi2.licenseGuard.pingInterval);
|
|
687
|
-
log.info("
|
|
702
|
+
log.info("[STOP] License pinging stopped");
|
|
688
703
|
}
|
|
689
704
|
if (strapi2.sessionManagerIntervals && strapi2.sessionManagerIntervals.cleanup) {
|
|
690
705
|
clearInterval(strapi2.sessionManagerIntervals.cleanup);
|
|
691
|
-
log.info("
|
|
706
|
+
log.info("[STOP] Session cleanup interval stopped");
|
|
692
707
|
}
|
|
693
708
|
log.info("[SUCCESS] Plugin cleanup completed");
|
|
694
709
|
};
|
|
@@ -1169,6 +1184,7 @@ var session$3 = {
|
|
|
1169
1184
|
* GET /api/magic-sessionmanager/my-sessions
|
|
1170
1185
|
* Automatically uses the authenticated user's documentId
|
|
1171
1186
|
* Marks which session is the current one (based on JWT token hash)
|
|
1187
|
+
* Fetches geolocation data on-demand if not already stored
|
|
1172
1188
|
*/
|
|
1173
1189
|
async getOwnSessions(ctx) {
|
|
1174
1190
|
try {
|
|
@@ -1185,7 +1201,8 @@ var session$3 = {
|
|
|
1185
1201
|
const config2 = strapi.config.get("plugin::magic-sessionmanager") || {};
|
|
1186
1202
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
1187
1203
|
const now = /* @__PURE__ */ new Date();
|
|
1188
|
-
const
|
|
1204
|
+
const geolocationService = strapi.plugin("magic-sessionmanager").service("geolocation");
|
|
1205
|
+
const sessionsWithCurrent = await Promise.all(allSessions.map(async (session2) => {
|
|
1189
1206
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1190
1207
|
const timeSinceActive = now - lastActiveTime;
|
|
1191
1208
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -1194,6 +1211,39 @@ var session$3 = {
|
|
|
1194
1211
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
1195
1212
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1196
1213
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1214
|
+
let geoLocation = session2.geoLocation;
|
|
1215
|
+
if (typeof geoLocation === "string") {
|
|
1216
|
+
try {
|
|
1217
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1218
|
+
} catch (e) {
|
|
1219
|
+
geoLocation = null;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (!geoLocation && session2.ipAddress) {
|
|
1223
|
+
try {
|
|
1224
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
1225
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
1226
|
+
geoLocation = {
|
|
1227
|
+
country: geoData.country,
|
|
1228
|
+
country_code: geoData.country_code,
|
|
1229
|
+
country_flag: geoData.country_flag,
|
|
1230
|
+
city: geoData.city,
|
|
1231
|
+
region: geoData.region,
|
|
1232
|
+
timezone: geoData.timezone
|
|
1233
|
+
};
|
|
1234
|
+
strapi.documents(SESSION_UID$1).update({
|
|
1235
|
+
documentId: session2.documentId,
|
|
1236
|
+
data: {
|
|
1237
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
1238
|
+
securityScore: geoData.securityScore || null
|
|
1239
|
+
}
|
|
1240
|
+
}).catch(() => {
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
} catch (geoErr) {
|
|
1244
|
+
strapi.log.debug("[magic-sessionmanager] Geolocation lookup failed:", geoErr.message);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1197
1247
|
const {
|
|
1198
1248
|
token,
|
|
1199
1249
|
tokenHash,
|
|
@@ -1202,6 +1252,8 @@ var session$3 = {
|
|
|
1202
1252
|
locale,
|
|
1203
1253
|
publishedAt,
|
|
1204
1254
|
// Remove Strapi internal fields
|
|
1255
|
+
geoLocation: _geo,
|
|
1256
|
+
// Remove raw geoLocation
|
|
1205
1257
|
...sessionWithoutTokens
|
|
1206
1258
|
} = session2;
|
|
1207
1259
|
return {
|
|
@@ -1209,11 +1261,13 @@ var session$3 = {
|
|
|
1209
1261
|
deviceType,
|
|
1210
1262
|
browserName,
|
|
1211
1263
|
osName,
|
|
1264
|
+
geoLocation,
|
|
1265
|
+
// Parsed object or null
|
|
1212
1266
|
isCurrentSession,
|
|
1213
1267
|
isTrulyActive,
|
|
1214
1268
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1215
1269
|
};
|
|
1216
|
-
});
|
|
1270
|
+
}));
|
|
1217
1271
|
sessionsWithCurrent.sort((a, b) => {
|
|
1218
1272
|
if (a.isCurrentSession) return -1;
|
|
1219
1273
|
if (b.isCurrentSession) return 1;
|
|
@@ -1315,6 +1369,7 @@ var session$3 = {
|
|
|
1315
1369
|
* Get current session info based on JWT token hash
|
|
1316
1370
|
* GET /api/magic-sessionmanager/current-session
|
|
1317
1371
|
* Returns the session associated with the current JWT token
|
|
1372
|
+
* Fetches geolocation on-demand if not already stored
|
|
1318
1373
|
*/
|
|
1319
1374
|
async getCurrentSession(ctx) {
|
|
1320
1375
|
try {
|
|
@@ -1343,6 +1398,40 @@ var session$3 = {
|
|
|
1343
1398
|
const deviceType = currentSession.deviceType || parsedUA.deviceType;
|
|
1344
1399
|
const browserName = currentSession.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1345
1400
|
const osName = currentSession.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1401
|
+
let geoLocation = currentSession.geoLocation;
|
|
1402
|
+
if (typeof geoLocation === "string") {
|
|
1403
|
+
try {
|
|
1404
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1405
|
+
} catch (e) {
|
|
1406
|
+
geoLocation = null;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (!geoLocation && currentSession.ipAddress) {
|
|
1410
|
+
try {
|
|
1411
|
+
const geolocationService = strapi.plugin("magic-sessionmanager").service("geolocation");
|
|
1412
|
+
const geoData = await geolocationService.getIpInfo(currentSession.ipAddress);
|
|
1413
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
1414
|
+
geoLocation = {
|
|
1415
|
+
country: geoData.country,
|
|
1416
|
+
country_code: geoData.country_code,
|
|
1417
|
+
country_flag: geoData.country_flag,
|
|
1418
|
+
city: geoData.city,
|
|
1419
|
+
region: geoData.region,
|
|
1420
|
+
timezone: geoData.timezone
|
|
1421
|
+
};
|
|
1422
|
+
strapi.documents(SESSION_UID$1).update({
|
|
1423
|
+
documentId: currentSession.documentId,
|
|
1424
|
+
data: {
|
|
1425
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
1426
|
+
securityScore: geoData.securityScore || null
|
|
1427
|
+
}
|
|
1428
|
+
}).catch(() => {
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
} catch (geoErr) {
|
|
1432
|
+
strapi.log.debug("[magic-sessionmanager] Geolocation lookup failed:", geoErr.message);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1346
1435
|
const {
|
|
1347
1436
|
token: _,
|
|
1348
1437
|
tokenHash: _th,
|
|
@@ -1350,6 +1439,7 @@ var session$3 = {
|
|
|
1350
1439
|
refreshTokenHash: _rth,
|
|
1351
1440
|
locale: _l,
|
|
1352
1441
|
publishedAt: _p,
|
|
1442
|
+
geoLocation: _geo,
|
|
1353
1443
|
...sessionWithoutTokens
|
|
1354
1444
|
} = currentSession;
|
|
1355
1445
|
ctx.body = {
|
|
@@ -1358,6 +1448,8 @@ var session$3 = {
|
|
|
1358
1448
|
deviceType,
|
|
1359
1449
|
browserName,
|
|
1360
1450
|
osName,
|
|
1451
|
+
geoLocation,
|
|
1452
|
+
// Parsed object or null
|
|
1361
1453
|
isCurrentSession: true,
|
|
1362
1454
|
isTrulyActive: timeSinceActive < inactivityTimeout,
|
|
1363
1455
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1901,10 +1993,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1901
1993
|
return {
|
|
1902
1994
|
/**
|
|
1903
1995
|
* Create a new session record
|
|
1904
|
-
* @param {Object} params - { userId, ip, userAgent, token, refreshToken }
|
|
1996
|
+
* @param {Object} params - { userId, ip, userAgent, token, refreshToken, geoData }
|
|
1905
1997
|
* @returns {Promise<Object>} Created session
|
|
1906
1998
|
*/
|
|
1907
|
-
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
|
|
1999
|
+
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken, geoData }) {
|
|
1908
2000
|
try {
|
|
1909
2001
|
const now = /* @__PURE__ */ new Date();
|
|
1910
2002
|
const sessionId = generateSessionId(userId);
|
|
@@ -1912,6 +2004,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1912
2004
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1913
2005
|
const tokenHashValue = token ? hashToken(token) : null;
|
|
1914
2006
|
const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
|
|
2007
|
+
const parsedUA = parseUserAgent(userAgent);
|
|
1915
2008
|
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1916
2009
|
data: {
|
|
1917
2010
|
user: userId,
|
|
@@ -1929,8 +2022,22 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1929
2022
|
// Encrypted Refresh Token
|
|
1930
2023
|
refreshTokenHash: refreshTokenHashValue,
|
|
1931
2024
|
// SHA-256 hash for fast lookup
|
|
1932
|
-
sessionId
|
|
2025
|
+
sessionId,
|
|
1933
2026
|
// Unique identifier
|
|
2027
|
+
// Device info from User-Agent
|
|
2028
|
+
deviceType: parsedUA.deviceType,
|
|
2029
|
+
browserName: parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName,
|
|
2030
|
+
osName: parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName,
|
|
2031
|
+
// Geolocation data (if available from Premium features)
|
|
2032
|
+
geoLocation: geoData ? JSON.stringify({
|
|
2033
|
+
country: geoData.country,
|
|
2034
|
+
country_code: geoData.country_code,
|
|
2035
|
+
country_flag: geoData.country_flag,
|
|
2036
|
+
city: geoData.city,
|
|
2037
|
+
region: geoData.region,
|
|
2038
|
+
timezone: geoData.timezone
|
|
2039
|
+
}) : null,
|
|
2040
|
+
securityScore: geoData?.securityScore || null
|
|
1934
2041
|
}
|
|
1935
2042
|
});
|
|
1936
2043
|
log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
@@ -1991,6 +2098,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1991
2098
|
},
|
|
1992
2099
|
/**
|
|
1993
2100
|
* Get ALL sessions (active + inactive) with accurate online status
|
|
2101
|
+
* Fetches geolocation on-demand for sessions without it (limited to prevent API abuse)
|
|
1994
2102
|
* @returns {Promise<Array>} All sessions with enhanced data
|
|
1995
2103
|
*/
|
|
1996
2104
|
async getAllSessions() {
|
|
@@ -2003,8 +2111,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2003
2111
|
});
|
|
2004
2112
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2005
2113
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2114
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2115
|
+
let geoLookupsRemaining = 20;
|
|
2006
2116
|
const now = /* @__PURE__ */ new Date();
|
|
2007
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2117
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2008
2118
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2009
2119
|
const timeSinceActive = now - lastActiveTime;
|
|
2010
2120
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -2012,6 +2122,40 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2012
2122
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2013
2123
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2014
2124
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2125
|
+
let geoLocation = session2.geoLocation;
|
|
2126
|
+
if (typeof geoLocation === "string") {
|
|
2127
|
+
try {
|
|
2128
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2129
|
+
} catch (e) {
|
|
2130
|
+
geoLocation = null;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
if (!geoLocation && session2.ipAddress && geoLookupsRemaining > 0) {
|
|
2134
|
+
geoLookupsRemaining--;
|
|
2135
|
+
try {
|
|
2136
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2137
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2138
|
+
geoLocation = {
|
|
2139
|
+
country: geoData.country,
|
|
2140
|
+
country_code: geoData.country_code,
|
|
2141
|
+
country_flag: geoData.country_flag,
|
|
2142
|
+
city: geoData.city,
|
|
2143
|
+
region: geoData.region,
|
|
2144
|
+
timezone: geoData.timezone
|
|
2145
|
+
};
|
|
2146
|
+
strapi2.documents(SESSION_UID).update({
|
|
2147
|
+
documentId: session2.documentId,
|
|
2148
|
+
data: {
|
|
2149
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2150
|
+
securityScore: geoData.securityScore || null
|
|
2151
|
+
}
|
|
2152
|
+
}).catch(() => {
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
} catch (geoErr) {
|
|
2156
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2015
2159
|
const {
|
|
2016
2160
|
token,
|
|
2017
2161
|
tokenHash,
|
|
@@ -2019,6 +2163,8 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2019
2163
|
refreshTokenHash,
|
|
2020
2164
|
locale,
|
|
2021
2165
|
publishedAt,
|
|
2166
|
+
geoLocation: _geo,
|
|
2167
|
+
// Remove raw geoLocation, we use parsed version
|
|
2022
2168
|
...safeSession
|
|
2023
2169
|
} = session2;
|
|
2024
2170
|
return {
|
|
@@ -2026,10 +2172,12 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2026
2172
|
deviceType,
|
|
2027
2173
|
browserName,
|
|
2028
2174
|
osName,
|
|
2175
|
+
geoLocation,
|
|
2176
|
+
// Parsed object or null
|
|
2029
2177
|
isTrulyActive,
|
|
2030
2178
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2031
2179
|
};
|
|
2032
|
-
});
|
|
2180
|
+
}));
|
|
2033
2181
|
return enhancedSessions;
|
|
2034
2182
|
} catch (err) {
|
|
2035
2183
|
log.error("Error getting all sessions:", err);
|
|
@@ -2038,6 +2186,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2038
2186
|
},
|
|
2039
2187
|
/**
|
|
2040
2188
|
* Get all active sessions with accurate online status
|
|
2189
|
+
* Fetches geolocation on-demand for sessions without it
|
|
2041
2190
|
* @returns {Promise<Array>} Active sessions with user data and online status
|
|
2042
2191
|
*/
|
|
2043
2192
|
async getActiveSessions() {
|
|
@@ -2049,8 +2198,9 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2049
2198
|
});
|
|
2050
2199
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2051
2200
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2201
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2052
2202
|
const now = /* @__PURE__ */ new Date();
|
|
2053
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2203
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2054
2204
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2055
2205
|
const timeSinceActive = now - lastActiveTime;
|
|
2056
2206
|
const isTrulyActive = timeSinceActive < inactivityTimeout;
|
|
@@ -2058,6 +2208,39 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2058
2208
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2059
2209
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2060
2210
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2211
|
+
let geoLocation = session2.geoLocation;
|
|
2212
|
+
if (typeof geoLocation === "string") {
|
|
2213
|
+
try {
|
|
2214
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2215
|
+
} catch (e) {
|
|
2216
|
+
geoLocation = null;
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
if (!geoLocation && session2.ipAddress) {
|
|
2220
|
+
try {
|
|
2221
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2222
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2223
|
+
geoLocation = {
|
|
2224
|
+
country: geoData.country,
|
|
2225
|
+
country_code: geoData.country_code,
|
|
2226
|
+
country_flag: geoData.country_flag,
|
|
2227
|
+
city: geoData.city,
|
|
2228
|
+
region: geoData.region,
|
|
2229
|
+
timezone: geoData.timezone
|
|
2230
|
+
};
|
|
2231
|
+
strapi2.documents(SESSION_UID).update({
|
|
2232
|
+
documentId: session2.documentId,
|
|
2233
|
+
data: {
|
|
2234
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2235
|
+
securityScore: geoData.securityScore || null
|
|
2236
|
+
}
|
|
2237
|
+
}).catch(() => {
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
} catch (geoErr) {
|
|
2241
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2061
2244
|
const {
|
|
2062
2245
|
token,
|
|
2063
2246
|
tokenHash,
|
|
@@ -2065,6 +2248,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2065
2248
|
refreshTokenHash,
|
|
2066
2249
|
locale,
|
|
2067
2250
|
publishedAt,
|
|
2251
|
+
geoLocation: _geo,
|
|
2068
2252
|
...safeSession
|
|
2069
2253
|
} = session2;
|
|
2070
2254
|
return {
|
|
@@ -2072,10 +2256,11 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2072
2256
|
deviceType,
|
|
2073
2257
|
browserName,
|
|
2074
2258
|
osName,
|
|
2259
|
+
geoLocation,
|
|
2075
2260
|
isTrulyActive,
|
|
2076
2261
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2077
2262
|
};
|
|
2078
|
-
});
|
|
2263
|
+
}));
|
|
2079
2264
|
return enhancedSessions.filter((s) => s.isTrulyActive);
|
|
2080
2265
|
} catch (err) {
|
|
2081
2266
|
log.error("Error getting active sessions:", err);
|
|
@@ -2085,6 +2270,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2085
2270
|
/**
|
|
2086
2271
|
* Get all sessions for a specific user
|
|
2087
2272
|
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
2273
|
+
* Fetches geolocation on-demand for sessions without it
|
|
2088
2274
|
* @param {string|number} userId - User documentId or numeric id
|
|
2089
2275
|
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
2090
2276
|
*/
|
|
@@ -2103,8 +2289,9 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2103
2289
|
});
|
|
2104
2290
|
const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
|
|
2105
2291
|
const inactivityTimeout = config2.inactivityTimeout || 15 * 60 * 1e3;
|
|
2292
|
+
const geolocationService = strapi2.plugin("magic-sessionmanager").service("geolocation");
|
|
2106
2293
|
const now = /* @__PURE__ */ new Date();
|
|
2107
|
-
const enhancedSessions = sessions.map((session2) => {
|
|
2294
|
+
const enhancedSessions = await Promise.all(sessions.map(async (session2) => {
|
|
2108
2295
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
2109
2296
|
const timeSinceActive = now - lastActiveTime;
|
|
2110
2297
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
@@ -2112,6 +2299,39 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2112
2299
|
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2113
2300
|
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2114
2301
|
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2302
|
+
let geoLocation = session2.geoLocation;
|
|
2303
|
+
if (typeof geoLocation === "string") {
|
|
2304
|
+
try {
|
|
2305
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2306
|
+
} catch (e) {
|
|
2307
|
+
geoLocation = null;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
if (!geoLocation && session2.ipAddress) {
|
|
2311
|
+
try {
|
|
2312
|
+
const geoData = await geolocationService.getIpInfo(session2.ipAddress);
|
|
2313
|
+
if (geoData && geoData.country !== "Unknown") {
|
|
2314
|
+
geoLocation = {
|
|
2315
|
+
country: geoData.country,
|
|
2316
|
+
country_code: geoData.country_code,
|
|
2317
|
+
country_flag: geoData.country_flag,
|
|
2318
|
+
city: geoData.city,
|
|
2319
|
+
region: geoData.region,
|
|
2320
|
+
timezone: geoData.timezone
|
|
2321
|
+
};
|
|
2322
|
+
strapi2.documents(SESSION_UID).update({
|
|
2323
|
+
documentId: session2.documentId,
|
|
2324
|
+
data: {
|
|
2325
|
+
geoLocation: JSON.stringify(geoLocation),
|
|
2326
|
+
securityScore: geoData.securityScore || null
|
|
2327
|
+
}
|
|
2328
|
+
}).catch(() => {
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
} catch (geoErr) {
|
|
2332
|
+
log.debug("Geolocation lookup failed:", geoErr.message);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2115
2335
|
const {
|
|
2116
2336
|
token,
|
|
2117
2337
|
tokenHash,
|
|
@@ -2119,6 +2339,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2119
2339
|
refreshTokenHash,
|
|
2120
2340
|
locale,
|
|
2121
2341
|
publishedAt,
|
|
2342
|
+
geoLocation: _geo,
|
|
2122
2343
|
...safeSession
|
|
2123
2344
|
} = session2;
|
|
2124
2345
|
return {
|
|
@@ -2126,10 +2347,11 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2126
2347
|
deviceType,
|
|
2127
2348
|
browserName,
|
|
2128
2349
|
osName,
|
|
2350
|
+
geoLocation,
|
|
2129
2351
|
isTrulyActive,
|
|
2130
2352
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
2131
2353
|
};
|
|
2132
|
-
});
|
|
2354
|
+
}));
|
|
2133
2355
|
return enhancedSessions;
|
|
2134
2356
|
} catch (err) {
|
|
2135
2357
|
log.error("Error getting user sessions:", err);
|
|
@@ -2242,7 +2464,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2242
2464
|
}
|
|
2243
2465
|
};
|
|
2244
2466
|
};
|
|
2245
|
-
const version = "4.2.
|
|
2467
|
+
const version = "4.2.13";
|
|
2246
2468
|
const require$$2 = {
|
|
2247
2469
|
version
|
|
2248
2470
|
};
|
package/package.json
CHANGED