strapi-plugin-magic-sessionmanager 2.0.1 → 2.0.3

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 (54) hide show
  1. package/admin/jsconfig.json +10 -0
  2. package/admin/src/components/Initializer.jsx +11 -0
  3. package/admin/src/components/LicenseGuard.jsx +591 -0
  4. package/admin/src/components/OnlineUsersWidget.jsx +208 -0
  5. package/admin/src/components/PluginIcon.jsx +8 -0
  6. package/admin/src/components/SessionDetailModal.jsx +445 -0
  7. package/admin/src/components/SessionInfoCard.jsx +151 -0
  8. package/admin/src/components/SessionInfoPanel.jsx +375 -0
  9. package/admin/src/components/index.jsx +5 -0
  10. package/admin/src/hooks/useLicense.js +103 -0
  11. package/admin/src/index.js +137 -0
  12. package/admin/src/pages/ActiveSessions.jsx +12 -0
  13. package/admin/src/pages/Analytics.jsx +735 -0
  14. package/admin/src/pages/App.jsx +12 -0
  15. package/admin/src/pages/HomePage.jsx +1248 -0
  16. package/admin/src/pages/License.jsx +603 -0
  17. package/admin/src/pages/Settings.jsx +1497 -0
  18. package/admin/src/pages/SettingsNew.jsx +1204 -0
  19. package/admin/src/pages/index.jsx +3 -0
  20. package/admin/src/pluginId.js +3 -0
  21. package/admin/src/translations/de.json +20 -0
  22. package/admin/src/translations/en.json +20 -0
  23. package/admin/src/utils/getTranslation.js +5 -0
  24. package/admin/src/utils/index.js +2 -0
  25. package/admin/src/utils/parseUserAgent.js +79 -0
  26. package/dist/server/index.js +91 -2
  27. package/dist/server/index.mjs +91 -2
  28. package/package.json +3 -1
  29. package/server/jsconfig.json +10 -0
  30. package/server/src/bootstrap.js +297 -0
  31. package/server/src/config/index.js +20 -0
  32. package/server/src/content-types/index.js +9 -0
  33. package/server/src/content-types/session/schema.json +76 -0
  34. package/server/src/controllers/controller.js +11 -0
  35. package/server/src/controllers/index.js +11 -0
  36. package/server/src/controllers/license.js +266 -0
  37. package/server/src/controllers/session.js +362 -0
  38. package/server/src/controllers/settings.js +122 -0
  39. package/server/src/destroy.js +18 -0
  40. package/server/src/index.js +23 -0
  41. package/server/src/middlewares/index.js +5 -0
  42. package/server/src/middlewares/last-seen.js +56 -0
  43. package/server/src/policies/index.js +3 -0
  44. package/server/src/register.js +32 -0
  45. package/server/src/routes/admin.js +149 -0
  46. package/server/src/routes/content-api.js +51 -0
  47. package/server/src/routes/index.js +9 -0
  48. package/server/src/services/geolocation.js +180 -0
  49. package/server/src/services/index.js +13 -0
  50. package/server/src/services/license-guard.js +308 -0
  51. package/server/src/services/notifications.js +319 -0
  52. package/server/src/services/service.js +7 -0
  53. package/server/src/services/session.js +345 -0
  54. package/server/src/utils/getClientIp.js +118 -0
@@ -0,0 +1,149 @@
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
+ };
@@ -0,0 +1,51 @@
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: '/user/:userId/sessions',
44
+ handler: 'session.getUserSessions',
45
+ config: {
46
+ auth: { strategies: ['users-permissions'] },
47
+ description: 'Get own sessions (controller validates user can only see own sessions)',
48
+ },
49
+ },
50
+ ],
51
+ };
@@ -0,0 +1,9 @@
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
+ };
@@ -0,0 +1,180 @@
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
+ */
136
+ getCountryFlag(countryCode) {
137
+ if (!countryCode) return '🏳️';
138
+
139
+ // Convert country code to flag emoji
140
+ const codePoints = countryCode
141
+ .toUpperCase()
142
+ .split('')
143
+ .map(char => 127397 + char.charCodeAt());
144
+
145
+ return String.fromCodePoint(...codePoints);
146
+ },
147
+
148
+ /**
149
+ * Fallback data when API fails
150
+ */
151
+ getFallbackData(ipAddress) {
152
+ return {
153
+ ip: ipAddress,
154
+ country: 'Unknown',
155
+ country_code: 'XX',
156
+ country_flag: '🌍',
157
+ city: 'Unknown',
158
+ region: 'Unknown',
159
+ timezone: 'Unknown',
160
+ latitude: null,
161
+ longitude: null,
162
+ isVpn: false,
163
+ isProxy: false,
164
+ isThreat: false,
165
+ securityScore: 50,
166
+ riskLevel: 'Unknown',
167
+ };
168
+ },
169
+
170
+ /**
171
+ * Batch lookup multiple IPs (for efficiency)
172
+ */
173
+ async batchGetIpInfo(ipAddresses) {
174
+ const results = await Promise.all(
175
+ ipAddresses.map(ip => this.getIpInfo(ip))
176
+ );
177
+ return results;
178
+ },
179
+ });
180
+
@@ -0,0 +1,13 @@
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
+ };
@@ -0,0 +1,308 @@
1
+ /**
2
+ * License Guard Service for Magic Session Manager
3
+ * Handles license creation, verification, and ping tracking
4
+ */
5
+
6
+ const crypto = require('crypto');
7
+ const os = require('os');
8
+
9
+ // FIXED LICENSE SERVER URL
10
+ const LICENSE_SERVER_URL = 'https://magicapi.fitlex.me';
11
+
12
+ module.exports = ({ strapi }) => ({
13
+ /**
14
+ * Get license server URL
15
+ */
16
+ getLicenseServerUrl() {
17
+ return LICENSE_SERVER_URL;
18
+ },
19
+
20
+ /**
21
+ * Generate device ID
22
+ */
23
+ generateDeviceId() {
24
+ try {
25
+ const networkInterfaces = os.networkInterfaces();
26
+ const macAddresses = [];
27
+
28
+ Object.values(networkInterfaces).forEach(interfaces => {
29
+ interfaces?.forEach(iface => {
30
+ if (iface.mac && iface.mac !== '00:00:00:00:00:00') {
31
+ macAddresses.push(iface.mac);
32
+ }
33
+ });
34
+ });
35
+
36
+ const identifier = `${macAddresses.join('-')}-${os.hostname()}`;
37
+ return crypto.createHash('sha256').update(identifier).digest('hex').substring(0, 32);
38
+ } catch (error) {
39
+ return crypto.randomBytes(16).toString('hex');
40
+ }
41
+ },
42
+
43
+ getDeviceName() {
44
+ try {
45
+ return os.hostname() || 'Unknown Device';
46
+ } catch (error) {
47
+ return 'Unknown Device';
48
+ }
49
+ },
50
+
51
+ getIpAddress() {
52
+ try {
53
+ const networkInterfaces = os.networkInterfaces();
54
+ for (const name of Object.keys(networkInterfaces)) {
55
+ const interfaces = networkInterfaces[name];
56
+ if (interfaces) {
57
+ for (const iface of interfaces) {
58
+ if (iface.family === 'IPv4' && !iface.internal) {
59
+ return iface.address;
60
+ }
61
+ }
62
+ }
63
+ }
64
+ return '127.0.0.1';
65
+ } catch (error) {
66
+ return '127.0.0.1';
67
+ }
68
+ },
69
+
70
+ getUserAgent() {
71
+ return `Strapi/${strapi.config.get('info.strapi') || '5.0.0'} Node/${process.version} ${os.platform()}/${os.release()}`;
72
+ },
73
+
74
+ async createLicense({ email, firstName, lastName }) {
75
+ try {
76
+ const deviceId = this.generateDeviceId();
77
+ const deviceName = this.getDeviceName();
78
+ const ipAddress = this.getIpAddress();
79
+ const userAgent = this.getUserAgent();
80
+
81
+ const licenseServerUrl = this.getLicenseServerUrl();
82
+ const response = await fetch(`${licenseServerUrl}/api/licenses/create`, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json' },
85
+ body: JSON.stringify({
86
+ email,
87
+ firstName,
88
+ lastName,
89
+ deviceName,
90
+ deviceId,
91
+ ipAddress,
92
+ userAgent,
93
+ pluginName: 'magic-sessionmanager',
94
+ productName: 'Magic Session Manager - Premium Session Tracking',
95
+ }),
96
+ });
97
+
98
+ const data = await response.json();
99
+
100
+ if (data.success) {
101
+ strapi.log.info('[magic-sessionmanager] ✅ License created:', data.data.licenseKey);
102
+ return data.data;
103
+ } else {
104
+ strapi.log.error('[magic-sessionmanager] ❌ License creation failed:', data);
105
+ return null;
106
+ }
107
+ } catch (error) {
108
+ strapi.log.error('[magic-sessionmanager] ❌ Error creating license:', error);
109
+ return null;
110
+ }
111
+ },
112
+
113
+ async verifyLicense(licenseKey, allowGracePeriod = false) {
114
+ try {
115
+ const controller = new AbortController();
116
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
117
+
118
+ const licenseServerUrl = this.getLicenseServerUrl();
119
+ const response = await fetch(`${licenseServerUrl}/api/licenses/verify`, {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({
123
+ licenseKey,
124
+ pluginName: 'magic-sessionmanager',
125
+ productName: 'Magic Session Manager - Premium Session Tracking',
126
+ }),
127
+ signal: controller.signal,
128
+ });
129
+
130
+ clearTimeout(timeoutId);
131
+ const data = await response.json();
132
+
133
+ if (data.success && data.data) {
134
+ return { valid: true, data: data.data, gracePeriod: false };
135
+ } else {
136
+ return { valid: false, data: null };
137
+ }
138
+ } catch (error) {
139
+ if (allowGracePeriod) {
140
+ return { valid: true, data: null, gracePeriod: true };
141
+ }
142
+ return { valid: false, data: null };
143
+ }
144
+ },
145
+
146
+ async getLicenseByKey(licenseKey) {
147
+ try {
148
+ const licenseServerUrl = this.getLicenseServerUrl();
149
+ const url = `${licenseServerUrl}/api/licenses/key/${licenseKey}`;
150
+
151
+ strapi.log.debug(`[magic-sessionmanager/license-guard] Fetching license from: ${url}`);
152
+
153
+ const response = await fetch(url, {
154
+ method: 'GET',
155
+ headers: { 'Content-Type': 'application/json' },
156
+ });
157
+
158
+ const data = await response.json();
159
+
160
+ if (data.success && data.data) {
161
+ strapi.log.debug(`[magic-sessionmanager/license-guard] License fetched: ${data.data.email}, featurePremium: ${data.data.featurePremium}`);
162
+ return data.data;
163
+ }
164
+
165
+ strapi.log.warn(`[magic-sessionmanager/license-guard] License API returned no data`);
166
+ return null;
167
+ } catch (error) {
168
+ strapi.log.error('[magic-sessionmanager/license-guard] Error fetching license by key:', error);
169
+ return null;
170
+ }
171
+ },
172
+
173
+ async pingLicense(licenseKey) {
174
+ try {
175
+ const deviceId = this.generateDeviceId();
176
+ const deviceName = this.getDeviceName();
177
+ const ipAddress = this.getIpAddress();
178
+ const userAgent = this.getUserAgent();
179
+
180
+ const licenseServerUrl = this.getLicenseServerUrl();
181
+ const response = await fetch(`${licenseServerUrl}/api/licenses/ping`, {
182
+ method: 'POST',
183
+ headers: { 'Content-Type': 'application/json' },
184
+ body: JSON.stringify({
185
+ licenseKey,
186
+ deviceId,
187
+ deviceName,
188
+ ipAddress,
189
+ userAgent,
190
+ pluginName: 'magic-sessionmanager',
191
+ }),
192
+ });
193
+
194
+ const data = await response.json();
195
+ return data.success ? data.data : null;
196
+ } catch (error) {
197
+ return null;
198
+ }
199
+ },
200
+
201
+ async storeLicenseKey(licenseKey) {
202
+ const pluginStore = strapi.store({
203
+ type: 'plugin',
204
+ name: 'magic-sessionmanager'
205
+ });
206
+ await pluginStore.set({ key: 'licenseKey', value: licenseKey });
207
+ strapi.log.info(`[magic-sessionmanager] ✅ License key stored: ${licenseKey.substring(0, 8)}...`);
208
+ },
209
+
210
+ startPinging(licenseKey, intervalMinutes = 15) {
211
+ strapi.log.info(`[magic-sessionmanager] ⏰ Starting license pings every ${intervalMinutes} minutes`);
212
+
213
+ // Immediate ping
214
+ this.pingLicense(licenseKey);
215
+
216
+ const interval = setInterval(async () => {
217
+ try {
218
+ await this.pingLicense(licenseKey);
219
+ } catch (error) {
220
+ strapi.log.error('[magic-sessionmanager] Ping error:', error);
221
+ }
222
+ }, intervalMinutes * 60 * 1000);
223
+
224
+ return interval;
225
+ },
226
+
227
+ /**
228
+ * Initialize license guard
229
+ * Checks for existing license and starts pinging
230
+ */
231
+ async initialize() {
232
+ try {
233
+ strapi.log.info('[magic-sessionmanager] 🔐 Initializing License Guard...');
234
+
235
+ // Check if license key exists in plugin store
236
+ const pluginStore = strapi.store({
237
+ type: 'plugin',
238
+ name: 'magic-sessionmanager'
239
+ });
240
+ const licenseKey = await pluginStore.get({ key: 'licenseKey' });
241
+
242
+ if (!licenseKey) {
243
+ strapi.log.info('[magic-sessionmanager] ℹ️ No license found - Running in demo mode');
244
+ return {
245
+ valid: false,
246
+ demo: true,
247
+ data: null,
248
+ };
249
+ }
250
+
251
+ // Check last validation timestamp for grace period
252
+ const lastValidated = await pluginStore.get({ key: 'lastValidated' });
253
+ const now = new Date();
254
+ const gracePeriodHours = 24;
255
+ let withinGracePeriod = false;
256
+
257
+ if (lastValidated) {
258
+ const lastValidatedDate = new Date(lastValidated);
259
+ const hoursSinceValidation = (now.getTime() - lastValidatedDate.getTime()) / (1000 * 60 * 60);
260
+ withinGracePeriod = hoursSinceValidation < gracePeriodHours;
261
+ }
262
+
263
+ // Verify license (allow grace period if we have a last validation)
264
+ const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
265
+
266
+ if (verification.valid) {
267
+ // Update last validated timestamp
268
+ await pluginStore.set({
269
+ key: 'lastValidated',
270
+ value: now.toISOString()
271
+ });
272
+
273
+ // Start automatic pinging
274
+ const pingInterval = this.startPinging(licenseKey, 15);
275
+
276
+ // Store interval globally so we can clean it up
277
+ strapi.licenseGuard = {
278
+ licenseKey,
279
+ pingInterval,
280
+ data: verification.data,
281
+ };
282
+
283
+ return {
284
+ valid: true,
285
+ demo: false,
286
+ data: verification.data,
287
+ gracePeriod: verification.gracePeriod || false,
288
+ };
289
+ } else {
290
+ strapi.log.error('[magic-sessionmanager] ❌ License validation failed');
291
+ return {
292
+ valid: false,
293
+ demo: true,
294
+ error: 'Invalid or expired license',
295
+ data: null,
296
+ };
297
+ }
298
+ } catch (error) {
299
+ strapi.log.error('[magic-sessionmanager] ❌ Error initializing License Guard:', error);
300
+ return {
301
+ valid: false,
302
+ demo: true,
303
+ error: error.message,
304
+ data: null,
305
+ };
306
+ }
307
+ },
308
+ });