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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const os = require('os');
|
|
8
|
+
const pluginPkg = require('../../../package.json');
|
|
8
9
|
|
|
9
10
|
// FIXED LICENSE SERVER URL
|
|
10
11
|
const LICENSE_SERVER_URL = 'https://magicapi.fitlex.me';
|
|
@@ -68,7 +69,9 @@ module.exports = ({ strapi }) => ({
|
|
|
68
69
|
},
|
|
69
70
|
|
|
70
71
|
getUserAgent() {
|
|
71
|
-
|
|
72
|
+
const pluginVersion = pluginPkg.version || '1.0.0';
|
|
73
|
+
const strapiVersion = strapi.config.get('info.strapi') || '5.0.0';
|
|
74
|
+
return `MagicSessionManager/${pluginVersion} Strapi/${strapiVersion} Node/${process.version} ${os.platform()}/${os.release()}`;
|
|
72
75
|
},
|
|
73
76
|
|
|
74
77
|
async createLicense({ email, firstName, lastName }) {
|
|
@@ -98,14 +101,14 @@ module.exports = ({ strapi }) => ({
|
|
|
98
101
|
const data = await response.json();
|
|
99
102
|
|
|
100
103
|
if (data.success) {
|
|
101
|
-
strapi.log.info('[magic-sessionmanager]
|
|
104
|
+
strapi.log.info('[magic-sessionmanager] [SUCCESS] License created:', data.data.licenseKey);
|
|
102
105
|
return data.data;
|
|
103
106
|
} else {
|
|
104
|
-
strapi.log.error('[magic-sessionmanager]
|
|
107
|
+
strapi.log.error('[magic-sessionmanager] [ERROR] License creation failed:', data);
|
|
105
108
|
return null;
|
|
106
109
|
}
|
|
107
110
|
} catch (error) {
|
|
108
|
-
strapi.log.error('[magic-sessionmanager]
|
|
111
|
+
strapi.log.error('[magic-sessionmanager] [ERROR] Error creating license:', error);
|
|
109
112
|
return null;
|
|
110
113
|
}
|
|
111
114
|
},
|
|
@@ -204,11 +207,11 @@ module.exports = ({ strapi }) => ({
|
|
|
204
207
|
name: 'magic-sessionmanager'
|
|
205
208
|
});
|
|
206
209
|
await pluginStore.set({ key: 'licenseKey', value: licenseKey });
|
|
207
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
210
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
208
211
|
},
|
|
209
212
|
|
|
210
213
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
211
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
214
|
+
strapi.log.info(`[magic-sessionmanager] [TIME] Starting license pings every ${intervalMinutes} minutes`);
|
|
212
215
|
|
|
213
216
|
// Immediate ping
|
|
214
217
|
this.pingLicense(licenseKey);
|
|
@@ -230,7 +233,7 @@ module.exports = ({ strapi }) => ({
|
|
|
230
233
|
*/
|
|
231
234
|
async initialize() {
|
|
232
235
|
try {
|
|
233
|
-
strapi.log.info('[magic-sessionmanager]
|
|
236
|
+
strapi.log.info('[magic-sessionmanager] [SECURE] Initializing License Guard...');
|
|
234
237
|
|
|
235
238
|
// Check if license key exists in plugin store
|
|
236
239
|
const pluginStore = strapi.store({
|
|
@@ -240,7 +243,7 @@ module.exports = ({ strapi }) => ({
|
|
|
240
243
|
const licenseKey = await pluginStore.get({ key: 'licenseKey' });
|
|
241
244
|
|
|
242
245
|
if (!licenseKey) {
|
|
243
|
-
strapi.log.info('[magic-sessionmanager]
|
|
246
|
+
strapi.log.info('[magic-sessionmanager] [INFO] No license found - Running in demo mode');
|
|
244
247
|
return {
|
|
245
248
|
valid: false,
|
|
246
249
|
demo: true,
|
|
@@ -287,7 +290,7 @@ module.exports = ({ strapi }) => ({
|
|
|
287
290
|
gracePeriod: verification.gracePeriod || false,
|
|
288
291
|
};
|
|
289
292
|
} else {
|
|
290
|
-
strapi.log.error('[magic-sessionmanager]
|
|
293
|
+
strapi.log.error('[magic-sessionmanager] [ERROR] License validation failed');
|
|
291
294
|
return {
|
|
292
295
|
valid: false,
|
|
293
296
|
demo: true,
|
|
@@ -296,7 +299,7 @@ module.exports = ({ strapi }) => ({
|
|
|
296
299
|
};
|
|
297
300
|
}
|
|
298
301
|
} catch (error) {
|
|
299
|
-
strapi.log.error('[magic-sessionmanager]
|
|
302
|
+
strapi.log.error('[magic-sessionmanager] [ERROR] Error initializing License Guard:', error);
|
|
300
303
|
return {
|
|
301
304
|
valid: false,
|
|
302
305
|
demo: true,
|
|
@@ -37,12 +37,12 @@ module.exports = ({ strapi }) => ({
|
|
|
37
37
|
strapi.log.debug('[magic-sessionmanager/notifications] Using default fallback templates');
|
|
38
38
|
return {
|
|
39
39
|
suspiciousLogin: {
|
|
40
|
-
subject: '
|
|
40
|
+
subject: '[ALERT] Suspicious Login Alert - Session Manager',
|
|
41
41
|
html: `
|
|
42
42
|
<html>
|
|
43
43
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
44
44
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
|
|
45
|
-
<h2 style="color: #dc2626;"
|
|
45
|
+
<h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
|
|
46
46
|
<p>A potentially suspicious login was detected for your account.</p>
|
|
47
47
|
|
|
48
48
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -77,17 +77,17 @@ module.exports = ({ strapi }) => ({
|
|
|
77
77
|
</div>
|
|
78
78
|
</body>
|
|
79
79
|
</html>`,
|
|
80
|
-
text:
|
|
80
|
+
text: `[ALERT] Suspicious Login Detected\n\nA potentially suspicious login was detected for your account.\n\nAccount: {{user.email}}\nUsername: {{user.username}}\n\nLogin Details:\n- Time: {{session.loginTime}}\n- IP: {{session.ipAddress}}\n- Location: {{geo.city}}, {{geo.country}}\n\nSecurity: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`,
|
|
81
81
|
},
|
|
82
82
|
newLocation: {
|
|
83
|
-
subject: '
|
|
84
|
-
html: `<h2
|
|
85
|
-
text:
|
|
83
|
+
subject: '[LOCATION] New Location Login Detected',
|
|
84
|
+
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>`,
|
|
85
|
+
text: `[LOCATION] New Location Login\n\nAccount: {{user.email}}\nTime: {{session.loginTime}}\nLocation: {{geo.city}}, {{geo.country}}\nIP: {{session.ipAddress}}`,
|
|
86
86
|
},
|
|
87
87
|
vpnProxy: {
|
|
88
|
-
subject: '
|
|
89
|
-
html: `<h2
|
|
90
|
-
text:
|
|
88
|
+
subject: '[WARNING] VPN/Proxy Login Detected',
|
|
89
|
+
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>`,
|
|
90
|
+
text: `[WARNING] VPN/Proxy Detected\n\nAccount: {{user.email}}\nTime: {{session.loginTime}}\nIP: {{session.ipAddress}}\nVPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`,
|
|
91
91
|
},
|
|
92
92
|
};
|
|
93
93
|
},
|
|
@@ -262,9 +262,9 @@ module.exports = ({ strapi }) => ({
|
|
|
262
262
|
title: this.getEventTitle(event),
|
|
263
263
|
color: this.getEventColor(event),
|
|
264
264
|
fields: [
|
|
265
|
-
{ name: '
|
|
266
|
-
{ name: '
|
|
267
|
-
{ name: '
|
|
265
|
+
{ name: 'User', value: `${user.email}\n${user.username || 'N/A'}`, inline: true },
|
|
266
|
+
{ name: 'IP', value: session.ipAddress, inline: true },
|
|
267
|
+
{ name: 'Time', value: new Date(session.loginTime).toLocaleString(), inline: false },
|
|
268
268
|
],
|
|
269
269
|
timestamp: new Date().toISOString(),
|
|
270
270
|
footer: { text: 'Magic Session Manager' },
|
|
@@ -272,7 +272,7 @@ module.exports = ({ strapi }) => ({
|
|
|
272
272
|
|
|
273
273
|
if (geoData) {
|
|
274
274
|
embed.fields.push({
|
|
275
|
-
name: '
|
|
275
|
+
name: '[LOCATION] Location',
|
|
276
276
|
value: `${geoData.country_flag} ${geoData.city}, ${geoData.country}`,
|
|
277
277
|
inline: true,
|
|
278
278
|
});
|
|
@@ -284,7 +284,7 @@ module.exports = ({ strapi }) => ({
|
|
|
284
284
|
if (geoData.isThreat) warnings.push('Threat');
|
|
285
285
|
|
|
286
286
|
embed.fields.push({
|
|
287
|
-
name: '
|
|
287
|
+
name: '[WARNING] Security',
|
|
288
288
|
value: `${warnings.join(', ')} detected\nScore: ${geoData.securityScore}/100`,
|
|
289
289
|
inline: true,
|
|
290
290
|
});
|
|
@@ -296,13 +296,13 @@ module.exports = ({ strapi }) => ({
|
|
|
296
296
|
|
|
297
297
|
getEventTitle(event) {
|
|
298
298
|
const titles = {
|
|
299
|
-
'login.suspicious': '
|
|
300
|
-
'login.new_location': '
|
|
301
|
-
'login.vpn': '
|
|
302
|
-
'login.threat': '
|
|
303
|
-
'session.terminated': '
|
|
299
|
+
'login.suspicious': '[ALERT] Suspicious Login',
|
|
300
|
+
'login.new_location': '[LOCATION] New Location Login',
|
|
301
|
+
'login.vpn': '[WARNING] VPN Login Detected',
|
|
302
|
+
'login.threat': '[THREAT] Threat IP Login',
|
|
303
|
+
'session.terminated': '[INFO] Session Terminated',
|
|
304
304
|
};
|
|
305
|
-
return titles[event] || '
|
|
305
|
+
return titles[event] || '[STATS] Session Event';
|
|
306
306
|
},
|
|
307
307
|
|
|
308
308
|
getEventColor(event) {
|
|
@@ -9,11 +9,17 @@ const { encryptToken, decryptToken, generateSessionId } = require('../utils/encr
|
|
|
9
9
|
*
|
|
10
10
|
* SECURITY: JWT tokens are encrypted before storing in database using AES-256-GCM
|
|
11
11
|
*
|
|
12
|
+
* [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
|
|
13
|
+
*
|
|
12
14
|
* TODO: For production multi-instance deployments, use Redis for:
|
|
13
15
|
* - Session store instead of DB
|
|
14
16
|
* - Rate limiting locks
|
|
15
17
|
* - Distributed session state
|
|
16
18
|
*/
|
|
19
|
+
|
|
20
|
+
const SESSION_UID = 'plugin::magic-sessionmanager.session';
|
|
21
|
+
const USER_UID = 'plugin::users-permissions.user';
|
|
22
|
+
|
|
17
23
|
module.exports = ({ strapi }) => ({
|
|
18
24
|
/**
|
|
19
25
|
* Create a new session record
|
|
@@ -31,21 +37,22 @@ module.exports = ({ strapi }) => ({
|
|
|
31
37
|
const encryptedToken = token ? encryptToken(token) : null;
|
|
32
38
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
// Using Document Service API (Strapi v5)
|
|
41
|
+
const session = await strapi.documents(SESSION_UID).create({
|
|
35
42
|
data: {
|
|
36
|
-
user: userId,
|
|
43
|
+
user: userId, // userId should be documentId (string)
|
|
37
44
|
ipAddress: ip.substring(0, 45),
|
|
38
45
|
userAgent: userAgent.substring(0, 500),
|
|
39
46
|
loginTime: now,
|
|
40
47
|
lastActive: now,
|
|
41
48
|
isActive: true,
|
|
42
|
-
token: encryptedToken, //
|
|
43
|
-
refreshToken: encryptedRefreshToken, //
|
|
44
|
-
sessionId: sessionId, //
|
|
49
|
+
token: encryptedToken, // [SUCCESS] Encrypted Access Token
|
|
50
|
+
refreshToken: encryptedRefreshToken, // [SUCCESS] Encrypted Refresh Token
|
|
51
|
+
sessionId: sessionId, // [SUCCESS] Unique identifier
|
|
45
52
|
},
|
|
46
53
|
});
|
|
47
54
|
|
|
48
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
55
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Session ${session.documentId} (${sessionId}) created for user ${userId}`);
|
|
49
56
|
|
|
50
57
|
return session;
|
|
51
58
|
} catch (err) {
|
|
@@ -56,6 +63,7 @@ module.exports = ({ strapi }) => ({
|
|
|
56
63
|
|
|
57
64
|
/**
|
|
58
65
|
* Terminate a session or all sessions for a user
|
|
66
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
59
67
|
* @param {Object} params - { sessionId | userId }
|
|
60
68
|
* @returns {Promise<void>}
|
|
61
69
|
*/
|
|
@@ -64,7 +72,9 @@ module.exports = ({ strapi }) => ({
|
|
|
64
72
|
const now = new Date();
|
|
65
73
|
|
|
66
74
|
if (sessionId) {
|
|
67
|
-
|
|
75
|
+
// Using Document Service API (Strapi v5)
|
|
76
|
+
await strapi.documents(SESSION_UID).update({
|
|
77
|
+
documentId: sessionId,
|
|
68
78
|
data: {
|
|
69
79
|
isActive: false,
|
|
70
80
|
logoutTime: now,
|
|
@@ -73,17 +83,27 @@ module.exports = ({ strapi }) => ({
|
|
|
73
83
|
|
|
74
84
|
strapi.log.info(`[magic-sessionmanager] Session ${sessionId} terminated`);
|
|
75
85
|
} else if (userId) {
|
|
76
|
-
//
|
|
77
|
-
|
|
86
|
+
// Strapi v5: If numeric id provided, look up documentId first
|
|
87
|
+
let userDocumentId = userId;
|
|
88
|
+
if (!isNaN(userId)) {
|
|
89
|
+
const user = await strapi.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
90
|
+
if (user) {
|
|
91
|
+
userDocumentId = user.documentId;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Find all active sessions for user - use Deep Filtering (Strapi v5)
|
|
96
|
+
const activeSessions = await strapi.documents(SESSION_UID).findMany({
|
|
78
97
|
filters: {
|
|
79
|
-
user: {
|
|
98
|
+
user: { documentId: userDocumentId }, // Deep filtering syntax
|
|
80
99
|
isActive: true,
|
|
81
100
|
},
|
|
82
101
|
});
|
|
83
102
|
|
|
84
103
|
// Terminate all active sessions
|
|
85
104
|
for (const session of activeSessions) {
|
|
86
|
-
await strapi.
|
|
105
|
+
await strapi.documents(SESSION_UID).update({
|
|
106
|
+
documentId: session.documentId,
|
|
87
107
|
data: {
|
|
88
108
|
isActive: false,
|
|
89
109
|
logoutTime: now,
|
|
@@ -91,7 +111,7 @@ module.exports = ({ strapi }) => ({
|
|
|
91
111
|
});
|
|
92
112
|
}
|
|
93
113
|
|
|
94
|
-
strapi.log.info(`[magic-sessionmanager] All sessions terminated for user ${
|
|
114
|
+
strapi.log.info(`[magic-sessionmanager] All sessions terminated for user ${userDocumentId}`);
|
|
95
115
|
}
|
|
96
116
|
} catch (err) {
|
|
97
117
|
strapi.log.error('[magic-sessionmanager] Error terminating session:', err);
|
|
@@ -105,7 +125,7 @@ module.exports = ({ strapi }) => ({
|
|
|
105
125
|
*/
|
|
106
126
|
async getAllSessions() {
|
|
107
127
|
try {
|
|
108
|
-
const sessions = await strapi.
|
|
128
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
109
129
|
populate: { user: { fields: ['id', 'email', 'username'] } },
|
|
110
130
|
sort: { loginTime: 'desc' },
|
|
111
131
|
limit: 1000, // Reasonable limit
|
|
@@ -147,7 +167,7 @@ module.exports = ({ strapi }) => ({
|
|
|
147
167
|
*/
|
|
148
168
|
async getActiveSessions() {
|
|
149
169
|
try {
|
|
150
|
-
const sessions = await strapi.
|
|
170
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
151
171
|
filters: { isActive: true },
|
|
152
172
|
populate: { user: { fields: ['id', 'email', 'username'] } },
|
|
153
173
|
sort: { loginTime: 'desc' },
|
|
@@ -186,13 +206,23 @@ module.exports = ({ strapi }) => ({
|
|
|
186
206
|
|
|
187
207
|
/**
|
|
188
208
|
* Get all sessions for a specific user
|
|
189
|
-
*
|
|
209
|
+
* Supports both numeric id (legacy) and documentId (Strapi v5)
|
|
210
|
+
* @param {string|number} userId - User documentId or numeric id
|
|
190
211
|
* @returns {Promise<Array>} User's sessions with accurate online status
|
|
191
212
|
*/
|
|
192
213
|
async getUserSessions(userId) {
|
|
193
214
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
// Strapi v5: If numeric id provided, look up documentId first
|
|
216
|
+
let userDocumentId = userId;
|
|
217
|
+
if (!isNaN(userId)) {
|
|
218
|
+
const user = await strapi.entityService.findOne(USER_UID, parseInt(userId, 10));
|
|
219
|
+
if (user) {
|
|
220
|
+
userDocumentId = user.documentId;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
225
|
+
filters: { user: { documentId: userDocumentId } },
|
|
196
226
|
sort: { loginTime: 'desc' },
|
|
197
227
|
});
|
|
198
228
|
|
|
@@ -241,20 +271,20 @@ module.exports = ({ strapi }) => ({
|
|
|
241
271
|
|
|
242
272
|
// Update session lastActive only
|
|
243
273
|
if (sessionId) {
|
|
244
|
-
const session = await strapi.
|
|
274
|
+
const session = await strapi.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
245
275
|
|
|
246
276
|
if (session && session.lastActive) {
|
|
247
277
|
const lastActiveTime = new Date(session.lastActive).getTime();
|
|
248
278
|
const currentTime = now.getTime();
|
|
249
279
|
|
|
250
280
|
if (currentTime - lastActiveTime > rateLimit) {
|
|
251
|
-
await strapi.
|
|
281
|
+
await strapi.documents(SESSION_UID).update({ documentId: sessionId,
|
|
252
282
|
data: { lastActive: now },
|
|
253
283
|
});
|
|
254
284
|
}
|
|
255
285
|
} else if (session) {
|
|
256
286
|
// First time or null
|
|
257
|
-
await strapi.
|
|
287
|
+
await strapi.documents(SESSION_UID).update({ documentId: sessionId,
|
|
258
288
|
data: { lastActive: now },
|
|
259
289
|
});
|
|
260
290
|
}
|
|
@@ -279,12 +309,12 @@ module.exports = ({ strapi }) => ({
|
|
|
279
309
|
const now = new Date();
|
|
280
310
|
const cutoffTime = new Date(now.getTime() - inactivityTimeout);
|
|
281
311
|
|
|
282
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
312
|
+
strapi.log.info(`[magic-sessionmanager] [CLEANUP] Cleaning up sessions inactive since before ${cutoffTime.toISOString()}`);
|
|
283
313
|
|
|
284
314
|
// Find all active sessions
|
|
285
|
-
const activeSessions = await strapi.
|
|
315
|
+
const activeSessions = await strapi.documents(SESSION_UID).findMany({
|
|
286
316
|
filters: { isActive: true },
|
|
287
|
-
fields: ['
|
|
317
|
+
fields: ['lastActive', 'loginTime'],
|
|
288
318
|
});
|
|
289
319
|
|
|
290
320
|
// Deactivate old sessions
|
|
@@ -293,14 +323,15 @@ module.exports = ({ strapi }) => ({
|
|
|
293
323
|
const lastActiveTime = session.lastActive ? new Date(session.lastActive) : new Date(session.loginTime);
|
|
294
324
|
|
|
295
325
|
if (lastActiveTime < cutoffTime) {
|
|
296
|
-
await strapi.
|
|
326
|
+
await strapi.documents(SESSION_UID).update({
|
|
327
|
+
documentId: session.documentId,
|
|
297
328
|
data: { isActive: false },
|
|
298
329
|
});
|
|
299
330
|
deactivatedCount++;
|
|
300
331
|
}
|
|
301
332
|
}
|
|
302
333
|
|
|
303
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
334
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Cleanup complete: ${deactivatedCount} sessions deactivated`);
|
|
304
335
|
return deactivatedCount;
|
|
305
336
|
} catch (err) {
|
|
306
337
|
strapi.log.error('[magic-sessionmanager] Error cleaning up inactive sessions:', err);
|
|
@@ -316,8 +347,8 @@ module.exports = ({ strapi }) => ({
|
|
|
316
347
|
*/
|
|
317
348
|
async deleteSession(sessionId) {
|
|
318
349
|
try {
|
|
319
|
-
await strapi.
|
|
320
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
350
|
+
await strapi.documents(SESSION_UID).delete({ documentId: sessionId });
|
|
351
|
+
strapi.log.info(`[magic-sessionmanager] [DELETE] Session ${sessionId} permanently deleted`);
|
|
321
352
|
return true;
|
|
322
353
|
} catch (err) {
|
|
323
354
|
strapi.log.error('[magic-sessionmanager] Error deleting session:', err);
|
|
@@ -332,23 +363,22 @@ module.exports = ({ strapi }) => ({
|
|
|
332
363
|
*/
|
|
333
364
|
async deleteInactiveSessions() {
|
|
334
365
|
try {
|
|
335
|
-
strapi.log.info('[magic-sessionmanager]
|
|
366
|
+
strapi.log.info('[magic-sessionmanager] [DELETE] Deleting all inactive sessions...');
|
|
336
367
|
|
|
337
|
-
// Find all inactive sessions
|
|
338
|
-
const inactiveSessions = await strapi.
|
|
368
|
+
// Find all inactive sessions (documentId is always included automatically)
|
|
369
|
+
const inactiveSessions = await strapi.documents(SESSION_UID).findMany({
|
|
339
370
|
filters: { isActive: false },
|
|
340
|
-
fields: ['id'],
|
|
341
371
|
});
|
|
342
372
|
|
|
343
373
|
let deletedCount = 0;
|
|
344
374
|
|
|
345
375
|
// Delete each inactive session
|
|
346
376
|
for (const session of inactiveSessions) {
|
|
347
|
-
await strapi.
|
|
377
|
+
await strapi.documents(SESSION_UID).delete({ documentId: session.documentId });
|
|
348
378
|
deletedCount++;
|
|
349
379
|
}
|
|
350
380
|
|
|
351
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
381
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
352
382
|
return deletedCount;
|
|
353
383
|
} catch (err) {
|
|
354
384
|
strapi.log.error('[magic-sessionmanager] Error deleting inactive sessions:', err);
|
|
@@ -31,7 +31,7 @@ function getEncryptionKey() {
|
|
|
31
31
|
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT || 'default-insecure-key';
|
|
32
32
|
const key = crypto.createHash('sha256').update(strapiKeys).digest();
|
|
33
33
|
|
|
34
|
-
console.warn('[magic-sessionmanager/encryption]
|
|
34
|
+
console.warn('[magic-sessionmanager/encryption] [WARNING] No SESSION_ENCRYPTION_KEY found. Using fallback (not recommended for production).');
|
|
35
35
|
console.warn('[magic-sessionmanager/encryption] Set SESSION_ENCRYPTION_KEY in .env for better security.');
|
|
36
36
|
|
|
37
37
|
return key;
|