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,122 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Settings controller
5
- * Manages plugin settings stored in Strapi database
6
- */
7
-
8
- module.exports = {
9
- /**
10
- * Get plugin settings
11
- */
12
- async getSettings(ctx) {
13
- try {
14
- const pluginStore = strapi.store({
15
- type: 'plugin',
16
- name: 'magic-sessionmanager',
17
- });
18
-
19
- let settings = await pluginStore.get({ key: 'settings' });
20
-
21
- // If no settings exist, return defaults
22
- if (!settings) {
23
- settings = {
24
- inactivityTimeout: 15,
25
- cleanupInterval: 30,
26
- lastSeenRateLimit: 30,
27
- retentionDays: 90,
28
- enableGeolocation: true,
29
- enableSecurityScoring: true,
30
- blockSuspiciousSessions: false,
31
- maxFailedLogins: 5,
32
- enableEmailAlerts: false,
33
- alertOnSuspiciousLogin: true,
34
- alertOnNewLocation: true,
35
- alertOnVpnProxy: true,
36
- enableWebhooks: false,
37
- discordWebhookUrl: '',
38
- slackWebhookUrl: '',
39
- enableGeofencing: false,
40
- allowedCountries: [],
41
- blockedCountries: [],
42
- emailTemplates: {
43
- suspiciousLogin: { subject: '', html: '', text: '' },
44
- newLocation: { subject: '', html: '', text: '' },
45
- vpnProxy: { subject: '', html: '', text: '' },
46
- },
47
- };
48
- }
49
-
50
- ctx.send({
51
- settings,
52
- success: true
53
- });
54
- } catch (error) {
55
- strapi.log.error('[magic-sessionmanager/settings] Error getting settings:', error);
56
- ctx.badRequest('Error loading settings');
57
- }
58
- },
59
-
60
- /**
61
- * Update plugin settings
62
- */
63
- async updateSettings(ctx) {
64
- try {
65
- const { body } = ctx.request;
66
-
67
- if (!body) {
68
- return ctx.badRequest('Settings data is required');
69
- }
70
-
71
- const pluginStore = strapi.store({
72
- type: 'plugin',
73
- name: 'magic-sessionmanager',
74
- });
75
-
76
- // Validate and sanitize settings
77
- const sanitizedSettings = {
78
- inactivityTimeout: parseInt(body.inactivityTimeout) || 15,
79
- cleanupInterval: parseInt(body.cleanupInterval) || 30,
80
- lastSeenRateLimit: parseInt(body.lastSeenRateLimit) || 30,
81
- retentionDays: parseInt(body.retentionDays) || 90,
82
- enableGeolocation: !!body.enableGeolocation,
83
- enableSecurityScoring: !!body.enableSecurityScoring,
84
- blockSuspiciousSessions: !!body.blockSuspiciousSessions,
85
- maxFailedLogins: parseInt(body.maxFailedLogins) || 5,
86
- enableEmailAlerts: !!body.enableEmailAlerts,
87
- alertOnSuspiciousLogin: !!body.alertOnSuspiciousLogin,
88
- alertOnNewLocation: !!body.alertOnNewLocation,
89
- alertOnVpnProxy: !!body.alertOnVpnProxy,
90
- enableWebhooks: !!body.enableWebhooks,
91
- discordWebhookUrl: String(body.discordWebhookUrl || ''),
92
- slackWebhookUrl: String(body.slackWebhookUrl || ''),
93
- enableGeofencing: !!body.enableGeofencing,
94
- allowedCountries: Array.isArray(body.allowedCountries) ? body.allowedCountries : [],
95
- blockedCountries: Array.isArray(body.blockedCountries) ? body.blockedCountries : [],
96
- emailTemplates: body.emailTemplates || {
97
- suspiciousLogin: { subject: '', html: '', text: '' },
98
- newLocation: { subject: '', html: '', text: '' },
99
- vpnProxy: { subject: '', html: '', text: '' },
100
- },
101
- };
102
-
103
- // Save to database
104
- await pluginStore.set({
105
- key: 'settings',
106
- value: sanitizedSettings
107
- });
108
-
109
- strapi.log.info('[magic-sessionmanager/settings] Settings updated successfully');
110
-
111
- ctx.send({
112
- settings: sanitizedSettings,
113
- success: true,
114
- message: 'Settings saved successfully!'
115
- });
116
- } catch (error) {
117
- strapi.log.error('[magic-sessionmanager/settings] Error updating settings:', error);
118
- ctx.badRequest('Error saving settings');
119
- }
120
- },
121
- };
122
-
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- const { createLogger } = require('./utils/logger');
4
-
5
- module.exports = async ({ strapi }) => {
6
- const log = createLogger(strapi);
7
-
8
- // Stop license pinging
9
- if (strapi.licenseGuard && strapi.licenseGuard.pingInterval) {
10
- clearInterval(strapi.licenseGuard.pingInterval);
11
- log.info('🛑 License pinging stopped');
12
- }
13
-
14
- // Stop cleanup interval
15
- if (strapi.sessionManagerIntervals && strapi.sessionManagerIntervals.cleanup) {
16
- clearInterval(strapi.sessionManagerIntervals.cleanup);
17
- log.info('🛑 Session cleanup interval stopped');
18
- }
19
-
20
- log.info('[SUCCESS] Plugin cleanup completed');
21
- };
22
-
@@ -1,23 +0,0 @@
1
- 'use strict';
2
-
3
- const register = require('./register');
4
- const bootstrap = require('./bootstrap');
5
- const destroy = require('./destroy');
6
- const config = require('./config');
7
- const contentTypes = require('./content-types');
8
- const routes = require('./routes');
9
- const controllers = require('./controllers');
10
- const services = require('./services');
11
- const middlewares = require('./middlewares');
12
-
13
- module.exports = {
14
- register,
15
- bootstrap,
16
- destroy,
17
- config,
18
- contentTypes,
19
- routes,
20
- controllers,
21
- services,
22
- middlewares,
23
- };
@@ -1,5 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- 'last-seen': require('./last-seen'),
5
- };
@@ -1,62 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * lastSeen Middleware
5
- * Updates user lastSeen and session lastActive on each authenticated request
6
- * Rate-limited to prevent DB write noise (default: 30 seconds)
7
- *
8
- * [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
9
- */
10
-
11
- const SESSION_UID = 'plugin::magic-sessionmanager.session';
12
-
13
- module.exports = ({ strapi, sessionService }) => {
14
- return async (ctx, next) => {
15
- // BEFORE processing request: Check if user's sessions are active
16
- // Strapi v5: Use documentId instead of numeric id for Document Service API
17
- if (ctx.state.user && ctx.state.user.documentId) {
18
- try {
19
- const userId = ctx.state.user.documentId;
20
-
21
- // Check if user has ANY active sessions
22
- const activeSessions = await strapi.documents(SESSION_UID).findMany( {
23
- filters: {
24
- user: { documentId: userId },
25
- isActive: true,
26
- },
27
- limit: 1,
28
- });
29
-
30
- // If user has NO active sessions, reject the request
31
- if (!activeSessions || activeSessions.length === 0) {
32
- strapi.log.info(`[magic-sessionmanager] [BLOCKED] Blocked request - User ${userId} has no active sessions`);
33
- return ctx.unauthorized('All sessions have been terminated. Please login again.');
34
- }
35
- } catch (err) {
36
- strapi.log.debug('[magic-sessionmanager] Error checking active sessions:', err.message);
37
- // On error, allow request to continue (fail-open for availability)
38
- }
39
- }
40
-
41
- // Process request
42
- await next();
43
-
44
- // AFTER response: Update activity timestamps if user is authenticated
45
- if (ctx.state.user && ctx.state.user.documentId) {
46
- try {
47
- const userId = ctx.state.user.documentId;
48
-
49
- // Try to find or extract sessionId from context
50
- const sessionId = ctx.state.sessionId;
51
-
52
- // Call touch with rate limiting
53
- await sessionService.touch({
54
- userId,
55
- sessionId,
56
- });
57
- } catch (err) {
58
- strapi.log.debug('[magic-sessionmanager] Error updating lastSeen:', err.message);
59
- }
60
- }
61
- };
62
- };
@@ -1,3 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {};
@@ -1,36 +0,0 @@
1
- 'use strict';
2
-
3
- const { createLogger } = require('./utils/logger');
4
-
5
- /**
6
- * Register hook
7
- * Sessions relation is hidden from UI to keep User interface clean
8
- * Sessions are accessed via the Session Manager plugin UI components
9
- */
10
- module.exports = async ({ strapi }) => {
11
- const log = createLogger(strapi);
12
-
13
- log.info('[START] Plugin registration starting...');
14
-
15
- try {
16
- // Get the user content type
17
- const userCT = strapi.contentType('plugin::users-permissions.user');
18
-
19
- if (!userCT) {
20
- log.error('User content type not found');
21
- return;
22
- }
23
-
24
- // REMOVE sessions relation from User content type to keep UI clean
25
- // Sessions are managed through SessionInfoPanel sidebar instead
26
- if (userCT.attributes && userCT.attributes.sessions) {
27
- delete userCT.attributes.sessions;
28
- log.info('[SUCCESS] Removed sessions field from User content type');
29
- }
30
-
31
- log.info('[SUCCESS] Plugin registered successfully');
32
-
33
- } catch (err) {
34
- log.error('[ERROR] Registration error:', err);
35
- }
36
- };
@@ -1,149 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- type: 'admin',
5
- routes: [
6
- {
7
- method: 'GET',
8
- path: '/sessions',
9
- handler: 'session.getAllSessionsAdmin',
10
- config: {
11
- policies: ['admin::isAuthenticatedAdmin'],
12
- description: 'Get all sessions - active and inactive (admin)',
13
- },
14
- },
15
- {
16
- method: 'GET',
17
- path: '/sessions/active',
18
- handler: 'session.getActiveSessions',
19
- config: {
20
- policies: ['admin::isAuthenticatedAdmin'],
21
- description: 'Get only active sessions (admin)',
22
- },
23
- },
24
- {
25
- method: 'GET',
26
- path: '/user/:userId/sessions',
27
- handler: 'session.getUserSessions',
28
- config: {
29
- policies: ['admin::isAuthenticatedAdmin'],
30
- description: 'Get user sessions (admin)',
31
- },
32
- },
33
- {
34
- method: 'POST',
35
- path: '/sessions/:sessionId/terminate',
36
- handler: 'session.terminateSingleSession',
37
- config: {
38
- policies: ['admin::isAuthenticatedAdmin'],
39
- description: 'Terminate a specific session (admin)',
40
- },
41
- },
42
- {
43
- method: 'DELETE',
44
- path: '/sessions/:sessionId',
45
- handler: 'session.deleteSession',
46
- config: {
47
- policies: ['admin::isAuthenticatedAdmin'],
48
- description: 'Delete a single session permanently (admin)',
49
- },
50
- },
51
- {
52
- method: 'POST',
53
- path: '/sessions/clean-inactive',
54
- handler: 'session.cleanInactiveSessions',
55
- config: {
56
- policies: ['admin::isAuthenticatedAdmin'],
57
- description: 'Delete all inactive sessions from database (admin)',
58
- },
59
- },
60
- {
61
- method: 'POST',
62
- path: '/user/:userId/terminate-all',
63
- handler: 'session.terminateAllUserSessions',
64
- config: {
65
- policies: ['admin::isAuthenticatedAdmin'],
66
- description: 'Terminate all sessions for a user (admin)',
67
- },
68
- },
69
- {
70
- method: 'POST',
71
- path: '/user/:userId/toggle-block',
72
- handler: 'session.toggleUserBlock',
73
- config: {
74
- policies: ['admin::isAuthenticatedAdmin'],
75
- description: 'Toggle user blocked status (admin)',
76
- },
77
- },
78
- // License Management
79
- {
80
- method: 'GET',
81
- path: '/license/status',
82
- handler: 'license.getStatus',
83
- config: {
84
- policies: [],
85
- },
86
- },
87
- {
88
- method: 'POST',
89
- path: '/license/auto-create',
90
- handler: 'license.autoCreate',
91
- config: {
92
- policies: [],
93
- },
94
- },
95
- {
96
- method: 'POST',
97
- path: '/license/create',
98
- handler: 'license.createAndActivate',
99
- config: {
100
- policies: [],
101
- },
102
- },
103
- {
104
- method: 'POST',
105
- path: '/license/ping',
106
- handler: 'license.ping',
107
- config: {
108
- policies: [],
109
- },
110
- },
111
- {
112
- method: 'POST',
113
- path: '/license/store-key',
114
- handler: 'license.storeKey',
115
- config: {
116
- policies: [],
117
- },
118
- },
119
- // Geolocation (Premium Feature)
120
- {
121
- method: 'GET',
122
- path: '/geolocation/:ipAddress',
123
- handler: 'session.getIpGeolocation',
124
- config: {
125
- policies: ['admin::isAuthenticatedAdmin'],
126
- description: 'Get IP geolocation data (Premium feature)',
127
- },
128
- },
129
- // Settings Management
130
- {
131
- method: 'GET',
132
- path: '/settings',
133
- handler: 'settings.getSettings',
134
- config: {
135
- policies: ['admin::isAuthenticatedAdmin'],
136
- description: 'Get plugin settings',
137
- },
138
- },
139
- {
140
- method: 'PUT',
141
- path: '/settings',
142
- handler: 'settings.updateSettings',
143
- config: {
144
- policies: ['admin::isAuthenticatedAdmin'],
145
- description: 'Update plugin settings',
146
- },
147
- },
148
- ],
149
- };
@@ -1,60 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Content API Routes for Magic Session Manager
5
- *
6
- * SECURITY: All routes require authentication
7
- * - User can only access their own sessions
8
- * - Admin routes are in admin.js
9
- */
10
-
11
- module.exports = {
12
- type: 'content-api',
13
- routes: [
14
- // ============================================================
15
- // LOGOUT ENDPOINTS
16
- // ============================================================
17
-
18
- {
19
- method: 'POST',
20
- path: '/logout',
21
- handler: 'session.logout',
22
- config: {
23
- auth: { strategies: ['users-permissions'] },
24
- description: 'Logout current session (requires JWT)',
25
- },
26
- },
27
- {
28
- method: 'POST',
29
- path: '/logout-all',
30
- handler: 'session.logoutAll',
31
- config: {
32
- auth: { strategies: ['users-permissions'] },
33
- description: 'Logout from all devices (requires JWT)',
34
- },
35
- },
36
-
37
- // ============================================================
38
- // SESSION QUERIES
39
- // ============================================================
40
-
41
- {
42
- method: 'GET',
43
- path: '/my-sessions',
44
- handler: 'session.getOwnSessions',
45
- config: {
46
- auth: { strategies: ['users-permissions'] },
47
- description: 'Get own sessions (automatically uses authenticated user)',
48
- },
49
- },
50
- {
51
- method: 'GET',
52
- path: '/user/:userId/sessions',
53
- handler: 'session.getUserSessions',
54
- config: {
55
- auth: { strategies: ['users-permissions'] },
56
- description: 'Get sessions by userId (validates user can only see own sessions)',
57
- },
58
- },
59
- ],
60
- };
@@ -1,9 +0,0 @@
1
- 'use strict';
2
-
3
- const contentApi = require('./content-api');
4
- const admin = require('./admin');
5
-
6
- module.exports = {
7
- admin,
8
- 'content-api': contentApi,
9
- };
@@ -1,182 +0,0 @@
1
- /**
2
- * IP Geolocation Service
3
- * Uses ipapi.co for accurate IP information
4
- * Free tier: 30,000 requests/month
5
- *
6
- * Premium features:
7
- * - Country, City, Region
8
- * - Timezone
9
- * - VPN/Proxy/Threat detection
10
- * - Security scoring
11
- */
12
-
13
- module.exports = ({ strapi }) => ({
14
- /**
15
- * Get IP information from ipapi.co
16
- * @param {string} ipAddress - IP to lookup
17
- * @returns {Promise<Object>} Geolocation data
18
- */
19
- async getIpInfo(ipAddress) {
20
- try {
21
- // Skip localhost/private IPs
22
- if (!ipAddress || ipAddress === '127.0.0.1' || ipAddress === '::1' || ipAddress.startsWith('192.168.') || ipAddress.startsWith('10.')) {
23
- return {
24
- ip: ipAddress,
25
- country: 'Local Network',
26
- country_code: 'XX',
27
- country_flag: '🏠',
28
- city: 'Localhost',
29
- region: 'Private Network',
30
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
31
- latitude: null,
32
- longitude: null,
33
- isVpn: false,
34
- isProxy: false,
35
- isThreat: false,
36
- securityScore: 100,
37
- riskLevel: 'None',
38
- };
39
- }
40
-
41
- // Call ipapi.co API
42
- const response = await fetch(`https://ipapi.co/${ipAddress}/json/`, {
43
- method: 'GET',
44
- headers: {
45
- 'User-Agent': 'Strapi-Magic-SessionManager/1.0',
46
- },
47
- });
48
-
49
- if (!response.ok) {
50
- throw new Error(`API returned ${response.status}`);
51
- }
52
-
53
- const data = await response.json();
54
-
55
- // Check for rate limit
56
- if (data.error) {
57
- strapi.log.warn(`[magic-sessionmanager/geolocation] API Error: ${data.reason}`);
58
- return this.getFallbackData(ipAddress);
59
- }
60
-
61
- // Parse and return structured data
62
- const result = {
63
- ip: data.ip,
64
- country: data.country_name,
65
- country_code: data.country_code,
66
- country_flag: this.getCountryFlag(data.country_code),
67
- city: data.city,
68
- region: data.region,
69
- timezone: data.timezone,
70
- latitude: data.latitude,
71
- longitude: data.longitude,
72
- postal: data.postal,
73
- org: data.org,
74
- asn: data.asn,
75
-
76
- // Security features (available in ipapi.co response)
77
- isVpn: data.threat?.is_vpn || false,
78
- isProxy: data.threat?.is_proxy || false,
79
- isThreat: data.threat?.is_threat || false,
80
- isAnonymous: data.threat?.is_anonymous || false,
81
- isTor: data.threat?.is_tor || false,
82
-
83
- // Calculate security score (0-100, higher is safer)
84
- securityScore: this.calculateSecurityScore({
85
- isVpn: data.threat?.is_vpn,
86
- isProxy: data.threat?.is_proxy,
87
- isThreat: data.threat?.is_threat,
88
- isAnonymous: data.threat?.is_anonymous,
89
- isTor: data.threat?.is_tor,
90
- }),
91
-
92
- riskLevel: this.getRiskLevel({
93
- isVpn: data.threat?.is_vpn,
94
- isProxy: data.threat?.is_proxy,
95
- isThreat: data.threat?.is_threat,
96
- }),
97
- };
98
-
99
- strapi.log.debug(`[magic-sessionmanager/geolocation] IP ${ipAddress}: ${result.city}, ${result.country} (Score: ${result.securityScore})`);
100
-
101
- return result;
102
- } catch (error) {
103
- strapi.log.error(`[magic-sessionmanager/geolocation] Error fetching IP info for ${ipAddress}:`, error.message);
104
- return this.getFallbackData(ipAddress);
105
- }
106
- },
107
-
108
- /**
109
- * Calculate security score based on threat indicators
110
- */
111
- calculateSecurityScore({ isVpn, isProxy, isThreat, isAnonymous, isTor }) {
112
- let score = 100;
113
-
114
- if (isTor) score -= 50; // Tor = sehr verdächtig
115
- if (isThreat) score -= 40; // Known threat
116
- if (isVpn) score -= 20; // VPN = moderate risk
117
- if (isProxy) score -= 15; // Proxy = low-moderate risk
118
- if (isAnonymous) score -= 10; // Anonymous service
119
-
120
- return Math.max(0, score);
121
- },
122
-
123
- /**
124
- * Get risk level based on indicators
125
- */
126
- getRiskLevel({ isVpn, isProxy, isThreat }) {
127
- if (isThreat) return 'High';
128
- if (isVpn && isProxy) return 'Medium-High';
129
- if (isVpn || isProxy) return 'Medium';
130
- return 'Low';
131
- },
132
-
133
- /**
134
- * Get country flag emoji
135
- * @param {string} countryCode - ISO 2-letter country code
136
- * @returns {string} Flag emoji or empty string
137
- */
138
- getCountryFlag(countryCode) {
139
- if (!countryCode) return '';
140
-
141
- // Convert country code to flag emoji
142
- const codePoints = countryCode
143
- .toUpperCase()
144
- .split('')
145
- .map(char => 127397 + char.charCodeAt());
146
-
147
- return String.fromCodePoint(...codePoints);
148
- },
149
-
150
- /**
151
- * Fallback data when API fails
152
- */
153
- getFallbackData(ipAddress) {
154
- return {
155
- ip: ipAddress,
156
- country: 'Unknown',
157
- country_code: 'XX',
158
- country_flag: '[GEO]',
159
- city: 'Unknown',
160
- region: 'Unknown',
161
- timezone: 'Unknown',
162
- latitude: null,
163
- longitude: null,
164
- isVpn: false,
165
- isProxy: false,
166
- isThreat: false,
167
- securityScore: 50,
168
- riskLevel: 'Unknown',
169
- };
170
- },
171
-
172
- /**
173
- * Batch lookup multiple IPs (for efficiency)
174
- */
175
- async batchGetIpInfo(ipAddresses) {
176
- const results = await Promise.all(
177
- ipAddresses.map(ip => this.getIpInfo(ip))
178
- );
179
- return results;
180
- },
181
- });
182
-
@@ -1,13 +0,0 @@
1
- 'use strict';
2
-
3
- const session = require('./session');
4
- const licenseGuard = require('./license-guard');
5
- const geolocation = require('./geolocation');
6
- const notifications = require('./notifications');
7
-
8
- module.exports = {
9
- session,
10
- 'license-guard': licenseGuard,
11
- geolocation,
12
- notifications,
13
- };