strapi-plugin-magic-sessionmanager 4.2.4 → 4.2.5

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.
Files changed (61) hide show
  1. package/dist/server/index.js +1 -1
  2. package/dist/server/index.mjs +1 -1
  3. package/package.json +1 -3
  4. package/admin/jsconfig.json +0 -10
  5. package/admin/src/components/Initializer.jsx +0 -11
  6. package/admin/src/components/LicenseGuard.jsx +0 -591
  7. package/admin/src/components/OnlineUsersWidget.jsx +0 -212
  8. package/admin/src/components/PluginIcon.jsx +0 -8
  9. package/admin/src/components/SessionDetailModal.jsx +0 -449
  10. package/admin/src/components/SessionInfoCard.jsx +0 -151
  11. package/admin/src/components/SessionInfoPanel.jsx +0 -385
  12. package/admin/src/components/index.jsx +0 -5
  13. package/admin/src/hooks/useLicense.js +0 -103
  14. package/admin/src/index.js +0 -149
  15. package/admin/src/pages/ActiveSessions.jsx +0 -12
  16. package/admin/src/pages/Analytics.jsx +0 -735
  17. package/admin/src/pages/App.jsx +0 -12
  18. package/admin/src/pages/HomePage.jsx +0 -1212
  19. package/admin/src/pages/License.jsx +0 -603
  20. package/admin/src/pages/Settings.jsx +0 -1646
  21. package/admin/src/pages/SettingsNew.jsx +0 -1204
  22. package/admin/src/pages/UpgradePage.jsx +0 -448
  23. package/admin/src/pages/index.jsx +0 -3
  24. package/admin/src/pluginId.js +0 -4
  25. package/admin/src/translations/de.json +0 -299
  26. package/admin/src/translations/en.json +0 -299
  27. package/admin/src/translations/es.json +0 -287
  28. package/admin/src/translations/fr.json +0 -287
  29. package/admin/src/translations/pt.json +0 -287
  30. package/admin/src/utils/getTranslation.js +0 -5
  31. package/admin/src/utils/index.js +0 -2
  32. package/admin/src/utils/parseUserAgent.js +0 -79
  33. package/admin/src/utils/theme.js +0 -85
  34. package/server/jsconfig.json +0 -10
  35. package/server/src/bootstrap.js +0 -492
  36. package/server/src/config/index.js +0 -23
  37. package/server/src/content-types/index.js +0 -9
  38. package/server/src/content-types/session/schema.json +0 -84
  39. package/server/src/controllers/controller.js +0 -11
  40. package/server/src/controllers/index.js +0 -11
  41. package/server/src/controllers/license.js +0 -266
  42. package/server/src/controllers/session.js +0 -433
  43. package/server/src/controllers/settings.js +0 -122
  44. package/server/src/destroy.js +0 -22
  45. package/server/src/index.js +0 -23
  46. package/server/src/middlewares/index.js +0 -5
  47. package/server/src/middlewares/last-seen.js +0 -62
  48. package/server/src/policies/index.js +0 -3
  49. package/server/src/register.js +0 -36
  50. package/server/src/routes/admin.js +0 -149
  51. package/server/src/routes/content-api.js +0 -60
  52. package/server/src/routes/index.js +0 -9
  53. package/server/src/services/geolocation.js +0 -182
  54. package/server/src/services/index.js +0 -13
  55. package/server/src/services/license-guard.js +0 -316
  56. package/server/src/services/notifications.js +0 -319
  57. package/server/src/services/service.js +0 -7
  58. package/server/src/services/session.js +0 -393
  59. package/server/src/utils/encryption.js +0 -121
  60. package/server/src/utils/getClientIp.js +0 -118
  61. package/server/src/utils/logger.js +0 -84
@@ -1,492 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Bootstrap: Mount middleware for session tracking
5
- * Sessions are managed via plugin::magic-sessionmanager.session content type
6
- *
7
- * [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
8
- *
9
- * NOTE: For multi-instance deployments, consider Redis locks or session store
10
- */
11
-
12
- const getClientIp = require('./utils/getClientIp');
13
- const { encryptToken, decryptToken } = require('./utils/encryption');
14
- const { createLogger } = require('./utils/logger');
15
-
16
- const SESSION_UID = 'plugin::magic-sessionmanager.session';
17
- const USER_UID = 'plugin::users-permissions.user';
18
-
19
- module.exports = async ({ strapi }) => {
20
- const log = createLogger(strapi);
21
-
22
- log.info('[START] Bootstrap starting...');
23
-
24
- try {
25
- // Initialize License Guard
26
- const licenseGuardService = strapi.plugin('magic-sessionmanager').service('license-guard');
27
-
28
- // Wait a bit for all services to be ready
29
- setTimeout(async () => {
30
- const licenseStatus = await licenseGuardService.initialize();
31
-
32
- if (!licenseStatus.valid) {
33
- log.error('╔════════════════════════════════════════════════════════════════╗');
34
- log.error('║ [ERROR] SESSION MANAGER - NO VALID LICENSE ║');
35
- log.error('║ ║');
36
- log.error('║ This plugin requires a valid license to operate. ║');
37
- log.error('║ Please activate your license via Admin UI: ║');
38
- log.error('║ Go to Settings → Sessions → License ║');
39
- log.error('║ ║');
40
- log.error('║ The plugin will run with limited functionality until ║');
41
- log.error('║ a valid license is activated. ║');
42
- log.error('╚════════════════════════════════════════════════════════════════╝');
43
- } else if (licenseStatus.valid) {
44
- const pluginStore = strapi.store({
45
- type: 'plugin',
46
- name: 'magic-sessionmanager',
47
- });
48
- const storedKey = await pluginStore.get({ key: 'licenseKey' });
49
-
50
- log.info('╔════════════════════════════════════════════════════════════════╗');
51
- log.info('║ [SUCCESS] SESSION MANAGER LICENSE ACTIVE ║');
52
- log.info('║ ║');
53
-
54
- if (licenseStatus.data) {
55
- log.info(`║ License: ${licenseStatus.data.licenseKey}`.padEnd(66) + '║');
56
- log.info(`║ User: ${licenseStatus.data.firstName} ${licenseStatus.data.lastName}`.padEnd(66) + '║');
57
- log.info(`║ Email: ${licenseStatus.data.email}`.padEnd(66) + '║');
58
- } else if (storedKey) {
59
- log.info(`║ License: ${storedKey} (Offline Mode)`.padEnd(66) + '║');
60
- log.info(`║ Status: Grace Period Active`.padEnd(66) + '║');
61
- }
62
-
63
- log.info('║ ║');
64
- log.info('║ [RELOAD] Auto-pinging every 15 minutes ║');
65
- log.info('╚════════════════════════════════════════════════════════════════╝');
66
- }
67
- }, 3000); // Wait 3 seconds for API to be ready
68
-
69
- // Get session service
70
- const sessionService = strapi
71
- .plugin('magic-sessionmanager')
72
- .service('session');
73
-
74
- // Cleanup inactive sessions on startup
75
- log.info('Running initial session cleanup...');
76
- await sessionService.cleanupInactiveSessions();
77
-
78
- // Schedule periodic cleanup every 30 minutes
79
- const cleanupInterval = 30 * 60 * 1000; // 30 minutes
80
-
81
- const cleanupIntervalHandle = setInterval(async () => {
82
- try {
83
- // Get fresh reference to service to avoid scope issues
84
- const service = strapi.plugin('magic-sessionmanager').service('session');
85
- await service.cleanupInactiveSessions();
86
- } catch (err) {
87
- log.error('Periodic cleanup error:', err);
88
- }
89
- }, cleanupInterval);
90
-
91
- log.info('[TIME] Periodic cleanup scheduled (every 30 minutes)');
92
-
93
- // Store interval handle for cleanup on shutdown
94
- if (!strapi.sessionManagerIntervals) {
95
- strapi.sessionManagerIntervals = {};
96
- }
97
- strapi.sessionManagerIntervals.cleanup = cleanupIntervalHandle;
98
-
99
- // HIGH PRIORITY: Register /api/auth/logout route BEFORE other plugins
100
- strapi.server.routes([{
101
- method: 'POST',
102
- path: '/api/auth/logout',
103
- handler: async (ctx) => {
104
- try {
105
- const token = ctx.request.headers?.authorization?.replace('Bearer ', '');
106
-
107
- if (!token) {
108
- ctx.status = 200;
109
- ctx.body = { message: 'Logged out successfully' };
110
- return;
111
- }
112
-
113
- // Find session by decrypting tokens and matching
114
- // Since tokens are encrypted, we need to get all active sessions and check each one
115
- const allSessions = await strapi.documents(SESSION_UID).findMany( {
116
- filters: {
117
- isActive: true,
118
- },
119
- });
120
-
121
- // Find matching session by decrypting and comparing tokens
122
- const matchingSession = allSessions.find(session => {
123
- if (!session.token) return false;
124
- try {
125
- const decrypted = decryptToken(session.token);
126
- return decrypted === token;
127
- } catch (err) {
128
- return false;
129
- }
130
- });
131
-
132
- if (matchingSession) {
133
- await sessionService.terminateSession({ sessionId: matchingSession.documentId });
134
- log.info(`[LOGOUT] Logout via /api/auth/logout - Session ${matchingSession.documentId} terminated`);
135
- }
136
-
137
- ctx.status = 200;
138
- ctx.body = { message: 'Logged out successfully' };
139
- } catch (err) {
140
- log.error('Logout error:', err);
141
- ctx.status = 200;
142
- ctx.body = { message: 'Logged out successfully' };
143
- }
144
- },
145
- config: {
146
- auth: false,
147
- },
148
- }]);
149
-
150
- log.info('[SUCCESS] /api/auth/logout route registered');
151
-
152
- // Middleware to intercept logins
153
- strapi.server.use(async (ctx, next) => {
154
- // Execute the actual request
155
- await next();
156
-
157
- // Check if this was a successful login request
158
- const isAuthLocal = ctx.path === '/api/auth/local' && ctx.method === 'POST';
159
- const isMagicLink = ctx.path.includes('/magic-link/login') && ctx.method === 'POST';
160
-
161
- if ((isAuthLocal || isMagicLink) && ctx.status === 200 && ctx.body && ctx.body.user) {
162
- try {
163
- const user = ctx.body.user;
164
-
165
- // Extract REAL client IP (handles proxies, load balancers, Cloudflare, etc.)
166
- const ip = getClientIp(ctx);
167
- const userAgent = ctx.request.headers?.['user-agent'] || ctx.request.header?.['user-agent'] || 'unknown';
168
-
169
- // Strapi v5: Use documentId for session creation
170
- log.info(`[CHECK] Login detected! User: ${user.documentId || user.id} (${user.email || user.username}) from IP: ${ip}`);
171
-
172
- // Get config
173
- const config = strapi.config.get('plugin::magic-sessionmanager') || {};
174
-
175
- // Check if we should analyze this session (Premium/Advanced features)
176
- let shouldBlock = false;
177
- let blockReason = '';
178
- let geoData = null;
179
-
180
- // Premium: Get geolocation data
181
- if (config.enableGeolocation || config.enableSecurityScoring) {
182
- try {
183
- const geolocationService = strapi.plugin('magic-sessionmanager').service('geolocation');
184
- geoData = await geolocationService.getIpInfo(ip);
185
-
186
- // Advanced: Auto-blocking
187
- if (config.blockSuspiciousSessions && geoData) {
188
- if (geoData.isThreat) {
189
- shouldBlock = true;
190
- blockReason = 'Known threat IP detected';
191
- } else if (geoData.isVpn && config.alertOnVpnProxy) {
192
- shouldBlock = true;
193
- blockReason = 'VPN detected';
194
- } else if (geoData.isProxy && config.alertOnVpnProxy) {
195
- shouldBlock = true;
196
- blockReason = 'Proxy detected';
197
- } else if (geoData.securityScore < 50) {
198
- shouldBlock = true;
199
- blockReason = `Low security score: ${geoData.securityScore}/100`;
200
- }
201
- }
202
-
203
- // Advanced: Geo-fencing
204
- if (config.enableGeofencing && geoData && geoData.country_code) {
205
- const countryCode = geoData.country_code;
206
-
207
- // Check blocked countries
208
- if (config.blockedCountries && config.blockedCountries.includes(countryCode)) {
209
- shouldBlock = true;
210
- blockReason = `Country ${countryCode} is blocked`;
211
- }
212
-
213
- // Check allowed countries (whitelist)
214
- if (config.allowedCountries && config.allowedCountries.length > 0) {
215
- if (!config.allowedCountries.includes(countryCode)) {
216
- shouldBlock = true;
217
- blockReason = `Country ${countryCode} is not in allowlist`;
218
- }
219
- }
220
- }
221
- } catch (geoErr) {
222
- log.warn('Geolocation check failed:', geoErr.message);
223
- }
224
- }
225
-
226
- // Block if needed
227
- if (shouldBlock) {
228
- log.warn(`[BLOCKED] Blocking login: ${blockReason}`);
229
-
230
- // Don't create session, return error
231
- ctx.status = 403;
232
- ctx.body = {
233
- error: {
234
- status: 403,
235
- message: 'Login blocked for security reasons',
236
- details: { reason: blockReason }
237
- }
238
- };
239
- return; // Stop here
240
- }
241
-
242
- // Create a new session (Strapi v5: Use documentId instead of numeric id)
243
- // If login response doesn't include documentId, fetch it from DB
244
- let userDocId = user.documentId;
245
- if (!userDocId && user.id) {
246
- const fullUser = await strapi.entityService.findOne(USER_UID, user.id);
247
- userDocId = fullUser?.documentId || user.id;
248
- }
249
-
250
- const newSession = await sessionService.createSession({
251
- userId: userDocId,
252
- ip,
253
- userAgent,
254
- token: ctx.body.jwt, // Store Access Token (encrypted)
255
- refreshToken: ctx.body.refreshToken, // Store Refresh Token (encrypted) if exists
256
- });
257
-
258
- log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
259
-
260
- // Advanced: Send notifications
261
- if (geoData && (config.enableEmailAlerts || config.enableWebhooks)) {
262
- try {
263
- const notificationService = strapi.plugin('magic-sessionmanager').service('notifications');
264
-
265
- // Determine if suspicious
266
- const isSuspicious = geoData.isVpn || geoData.isProxy || geoData.isThreat || geoData.securityScore < 70;
267
-
268
- // Email alerts
269
- if (config.enableEmailAlerts && config.alertOnSuspiciousLogin && isSuspicious) {
270
- await notificationService.sendSuspiciousLoginAlert({
271
- user,
272
- session: newSession,
273
- reason: {
274
- isVpn: geoData.isVpn,
275
- isProxy: geoData.isProxy,
276
- isThreat: geoData.isThreat,
277
- securityScore: geoData.securityScore,
278
- },
279
- geoData,
280
- });
281
- }
282
-
283
- // Webhook notifications (Discord/Slack)
284
- if (config.enableWebhooks) {
285
- const webhookData = notificationService.formatDiscordWebhook({
286
- event: isSuspicious ? 'login.suspicious' : 'login.success',
287
- session: newSession,
288
- user,
289
- geoData,
290
- });
291
-
292
- if (config.discordWebhookUrl) {
293
- await notificationService.sendWebhook({
294
- event: 'session.login',
295
- data: webhookData,
296
- webhookUrl: config.discordWebhookUrl,
297
- });
298
- }
299
- }
300
- } catch (notifErr) {
301
- log.warn('Notification failed:', notifErr.message);
302
- }
303
- }
304
- } catch (err) {
305
- log.error('[ERROR] Error creating session:', err);
306
- // Don't throw - login should still succeed even if session creation fails
307
- }
308
- }
309
- });
310
-
311
- log.info('[SUCCESS] Login/Logout interceptor middleware mounted');
312
-
313
- // Middleware to block refresh token requests for terminated sessions
314
- strapi.server.use(async (ctx, next) => {
315
- // Check if this is a refresh token request (Strapi v5: /api/auth/refresh)
316
- const isRefreshToken = ctx.path === '/api/auth/refresh' && ctx.method === 'POST';
317
-
318
- if (isRefreshToken) {
319
- try {
320
- const refreshToken = ctx.request.body?.refreshToken;
321
-
322
- if (refreshToken) {
323
- // Find session with this refresh token
324
- const allSessions = await strapi.documents(SESSION_UID).findMany( {
325
- filters: {
326
- isActive: true,
327
- },
328
- });
329
-
330
- // Find matching session by decrypting and comparing refresh tokens
331
- const matchingSession = allSessions.find(session => {
332
- if (!session.refreshToken) return false;
333
- try {
334
- const decrypted = decryptToken(session.refreshToken);
335
- return decrypted === refreshToken;
336
- } catch (err) {
337
- return false;
338
- }
339
- });
340
-
341
- if (!matchingSession) {
342
- // No active session with this refresh token - Block!
343
- log.warn('[BLOCKED] Blocked refresh token request - no active session');
344
- ctx.status = 401;
345
- ctx.body = {
346
- error: {
347
- status: 401,
348
- message: 'Session terminated. Please login again.',
349
- name: 'UnauthorizedError'
350
- }
351
- };
352
- return; // Don't continue
353
- }
354
-
355
- log.info(`[SUCCESS] Refresh token allowed for session ${matchingSession.documentId}`);
356
- }
357
- } catch (err) {
358
- log.error('Error checking refresh token:', err);
359
- // On error, allow request to continue (fail-open for availability)
360
- }
361
- }
362
-
363
- // Continue with request
364
- await next();
365
-
366
- // AFTER: If refresh token response was successful, update session with new tokens
367
- if (isRefreshToken && ctx.status === 200 && ctx.body && ctx.body.jwt) {
368
- try {
369
- const oldRefreshToken = ctx.request.body?.refreshToken;
370
- const newAccessToken = ctx.body.jwt;
371
- const newRefreshToken = ctx.body.refreshToken;
372
-
373
- if (oldRefreshToken) {
374
- // Find session and update with new tokens
375
- const allSessions = await strapi.documents(SESSION_UID).findMany( {
376
- filters: {
377
- isActive: true,
378
- },
379
- });
380
-
381
- const matchingSession = allSessions.find(session => {
382
- if (!session.refreshToken) return false;
383
- try {
384
- const decrypted = decryptToken(session.refreshToken);
385
- return decrypted === oldRefreshToken;
386
- } catch (err) {
387
- return false;
388
- }
389
- });
390
-
391
- if (matchingSession) {
392
- const encryptedToken = newAccessToken ? encryptToken(newAccessToken) : matchingSession.token;
393
- const encryptedRefreshToken = newRefreshToken ? encryptToken(newRefreshToken) : matchingSession.refreshToken;
394
-
395
- await strapi.documents(SESSION_UID).update({
396
- documentId: matchingSession.documentId,
397
- data: {
398
- token: encryptedToken,
399
- refreshToken: encryptedRefreshToken,
400
- lastActive: new Date(),
401
- },
402
- });
403
-
404
- log.info(`[REFRESH] Tokens refreshed for session ${matchingSession.documentId}`);
405
- }
406
- }
407
- } catch (err) {
408
- log.error('Error updating refreshed tokens:', err);
409
- }
410
- }
411
- });
412
-
413
- log.info('[SUCCESS] Refresh Token interceptor middleware mounted');
414
-
415
- // Mount lastSeen update middleware
416
- strapi.server.use(
417
- require('./middlewares/last-seen')({ strapi, sessionService })
418
- );
419
-
420
- log.info('[SUCCESS] LastSeen middleware mounted');
421
-
422
- // Auto-enable Content-API permissions for authenticated users
423
- await ensureContentApiPermissions(strapi, log);
424
-
425
- log.info('[SUCCESS] Bootstrap complete');
426
- log.info('[READY] Session Manager ready! Sessions stored in plugin::magic-sessionmanager.session');
427
-
428
- } catch (err) {
429
- log.error('[ERROR] Bootstrap error:', err);
430
- }
431
- };
432
-
433
- /**
434
- * Auto-enable Content-API permissions for authenticated users
435
- * This ensures plugin endpoints are accessible after installation
436
- * @param {object} strapi - Strapi instance
437
- * @param {object} log - Logger instance
438
- */
439
- async function ensureContentApiPermissions(strapi, log) {
440
- try {
441
- // Get the authenticated role
442
- const authenticatedRole = await strapi.query('plugin::users-permissions.role').findOne({
443
- where: { type: 'authenticated' },
444
- });
445
-
446
- if (!authenticatedRole) {
447
- log.warn('Authenticated role not found - skipping permission setup');
448
- return;
449
- }
450
-
451
- // Content-API actions that should be enabled for authenticated users
452
- const requiredActions = [
453
- 'plugin::magic-sessionmanager.session.logout',
454
- 'plugin::magic-sessionmanager.session.logoutAll',
455
- 'plugin::magic-sessionmanager.session.getOwnSessions',
456
- 'plugin::magic-sessionmanager.session.getUserSessions',
457
- ];
458
-
459
- // Get existing permissions for this role
460
- const existingPermissions = await strapi.query('plugin::users-permissions.permission').findMany({
461
- where: {
462
- role: authenticatedRole.id,
463
- action: { $in: requiredActions },
464
- },
465
- });
466
-
467
- // Find which actions are missing
468
- const existingActions = existingPermissions.map(p => p.action);
469
- const missingActions = requiredActions.filter(action => !existingActions.includes(action));
470
-
471
- if (missingActions.length === 0) {
472
- log.debug('Content-API permissions already configured');
473
- return;
474
- }
475
-
476
- // Create missing permissions
477
- for (const action of missingActions) {
478
- await strapi.query('plugin::users-permissions.permission').create({
479
- data: {
480
- action,
481
- role: authenticatedRole.id,
482
- },
483
- });
484
- log.info(`[PERMISSION] Enabled ${action} for authenticated users`);
485
- }
486
-
487
- log.info('[SUCCESS] Content-API permissions configured for authenticated users');
488
- } catch (err) {
489
- log.warn('Could not auto-configure permissions:', err.message);
490
- log.warn('Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated');
491
- }
492
- }
@@ -1,23 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- default: {
5
- // Enable debug logging (set to true to see all plugin logs)
6
- debug: false,
7
-
8
- // Rate limit for lastSeen updates (in milliseconds)
9
- lastSeenRateLimit: 30000, // 30 seconds
10
-
11
- // Session inactivity timeout (in milliseconds)
12
- // After this time without activity, a session is considered inactive
13
- inactivityTimeout: 15 * 60 * 1000, // 15 minutes
14
- },
15
- validator: (config) => {
16
- if (config.lastSeenRateLimit && typeof config.lastSeenRateLimit !== 'number') {
17
- throw new Error('lastSeenRateLimit must be a number (milliseconds)');
18
- }
19
- if (config.inactivityTimeout && typeof config.inactivityTimeout !== 'number') {
20
- throw new Error('inactivityTimeout must be a number (milliseconds)');
21
- }
22
- },
23
- };
@@ -1,9 +0,0 @@
1
- 'use strict';
2
-
3
- const session = require('./session/schema.json');
4
-
5
- module.exports = {
6
- session: {
7
- schema: session,
8
- },
9
- };
@@ -1,84 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "magic_sessions",
4
- "info": {
5
- "singularName": "session",
6
- "pluralName": "sessions",
7
- "displayName": "Session",
8
- "description": "User session tracking with IP, device, and activity information"
9
- },
10
- "options": {
11
- "draftAndPublish": false,
12
- "comment": ""
13
- },
14
- "pluginOptions": {
15
- "content-manager": {
16
- "visible": false
17
- },
18
- "content-type-builder": {
19
- "visible": false
20
- }
21
- },
22
- "attributes": {
23
- "sessionId": {
24
- "type": "string",
25
- "unique": true,
26
- "required": true
27
- },
28
- "user": {
29
- "type": "relation",
30
- "relation": "manyToOne",
31
- "target": "plugin::users-permissions.user"
32
- },
33
- "ipAddress": {
34
- "type": "string",
35
- "maxLength": 45,
36
- "required": true
37
- },
38
- "userAgent": {
39
- "type": "text",
40
- "maxLength": 500
41
- },
42
- "token": {
43
- "type": "text",
44
- "private": true
45
- },
46
- "refreshToken": {
47
- "type": "text",
48
- "private": true
49
- },
50
- "loginTime": {
51
- "type": "datetime",
52
- "required": true
53
- },
54
- "logoutTime": {
55
- "type": "datetime"
56
- },
57
- "lastActive": {
58
- "type": "datetime"
59
- },
60
- "isActive": {
61
- "type": "boolean",
62
- "default": true,
63
- "required": true
64
- },
65
- "geoLocation": {
66
- "type": "json"
67
- },
68
- "securityScore": {
69
- "type": "integer",
70
- "min": 0,
71
- "max": 100
72
- },
73
- "deviceType": {
74
- "type": "string"
75
- },
76
- "browserName": {
77
- "type": "string"
78
- },
79
- "osName": {
80
- "type": "string"
81
- }
82
- }
83
- }
84
-
@@ -1,11 +0,0 @@
1
- const controller = ({ strapi }) => ({
2
- index(ctx) {
3
- ctx.body = strapi
4
- .plugin('magic-sessionmanager')
5
- // the name of the service file & the method.
6
- .service('service')
7
- .getWelcomeMessage();
8
- },
9
- });
10
-
11
- export default controller;
@@ -1,11 +0,0 @@
1
- 'use strict';
2
-
3
- const session = require('./session');
4
- const license = require('./license');
5
- const settings = require('./settings');
6
-
7
- module.exports = {
8
- session,
9
- license,
10
- settings,
11
- };