strapi-plugin-magic-sessionmanager 4.2.4 → 4.2.6

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 (62) hide show
  1. package/README.md +0 -2
  2. package/dist/server/index.js +1 -1
  3. package/dist/server/index.mjs +1 -1
  4. package/package.json +1 -3
  5. package/admin/jsconfig.json +0 -10
  6. package/admin/src/components/Initializer.jsx +0 -11
  7. package/admin/src/components/LicenseGuard.jsx +0 -591
  8. package/admin/src/components/OnlineUsersWidget.jsx +0 -212
  9. package/admin/src/components/PluginIcon.jsx +0 -8
  10. package/admin/src/components/SessionDetailModal.jsx +0 -449
  11. package/admin/src/components/SessionInfoCard.jsx +0 -151
  12. package/admin/src/components/SessionInfoPanel.jsx +0 -385
  13. package/admin/src/components/index.jsx +0 -5
  14. package/admin/src/hooks/useLicense.js +0 -103
  15. package/admin/src/index.js +0 -149
  16. package/admin/src/pages/ActiveSessions.jsx +0 -12
  17. package/admin/src/pages/Analytics.jsx +0 -735
  18. package/admin/src/pages/App.jsx +0 -12
  19. package/admin/src/pages/HomePage.jsx +0 -1212
  20. package/admin/src/pages/License.jsx +0 -603
  21. package/admin/src/pages/Settings.jsx +0 -1646
  22. package/admin/src/pages/SettingsNew.jsx +0 -1204
  23. package/admin/src/pages/UpgradePage.jsx +0 -448
  24. package/admin/src/pages/index.jsx +0 -3
  25. package/admin/src/pluginId.js +0 -4
  26. package/admin/src/translations/de.json +0 -299
  27. package/admin/src/translations/en.json +0 -299
  28. package/admin/src/translations/es.json +0 -287
  29. package/admin/src/translations/fr.json +0 -287
  30. package/admin/src/translations/pt.json +0 -287
  31. package/admin/src/utils/getTranslation.js +0 -5
  32. package/admin/src/utils/index.js +0 -2
  33. package/admin/src/utils/parseUserAgent.js +0 -79
  34. package/admin/src/utils/theme.js +0 -85
  35. package/server/jsconfig.json +0 -10
  36. package/server/src/bootstrap.js +0 -492
  37. package/server/src/config/index.js +0 -23
  38. package/server/src/content-types/index.js +0 -9
  39. package/server/src/content-types/session/schema.json +0 -84
  40. package/server/src/controllers/controller.js +0 -11
  41. package/server/src/controllers/index.js +0 -11
  42. package/server/src/controllers/license.js +0 -266
  43. package/server/src/controllers/session.js +0 -433
  44. package/server/src/controllers/settings.js +0 -122
  45. package/server/src/destroy.js +0 -22
  46. package/server/src/index.js +0 -23
  47. package/server/src/middlewares/index.js +0 -5
  48. package/server/src/middlewares/last-seen.js +0 -62
  49. package/server/src/policies/index.js +0 -3
  50. package/server/src/register.js +0 -36
  51. package/server/src/routes/admin.js +0 -149
  52. package/server/src/routes/content-api.js +0 -60
  53. package/server/src/routes/index.js +0 -9
  54. package/server/src/services/geolocation.js +0 -182
  55. package/server/src/services/index.js +0 -13
  56. package/server/src/services/license-guard.js +0 -316
  57. package/server/src/services/notifications.js +0 -319
  58. package/server/src/services/service.js +0 -7
  59. package/server/src/services/session.js +0 -393
  60. package/server/src/utils/encryption.js +0 -121
  61. package/server/src/utils/getClientIp.js +0 -118
  62. package/server/src/utils/logger.js +0 -84
@@ -1,151 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { Box, Typography, Flex, Badge } from '@strapi/design-system';
3
- import { useFetchClient } from '@strapi/strapi/admin';
4
-
5
- /**
6
- * Session Info Card - shows user's session status and history
7
- * Injected into Content Manager edit view for Users
8
- */
9
- const SessionInfoCard = ({ id, model }) => {
10
- const { get } = useFetchClient();
11
- const [sessions, setSessions] = useState([]);
12
- const [loading, setLoading] = useState(true);
13
- const [isUserModel, setIsUserModel] = useState(false);
14
-
15
- useEffect(() => {
16
- // Only show for User content type
17
- if (model !== 'plugin::users-permissions.user') {
18
- setIsUserModel(false);
19
- setLoading(false);
20
- return;
21
- }
22
-
23
- setIsUserModel(true);
24
-
25
- // Fetch user's sessions
26
- const fetchSessions = async () => {
27
- if (!id) {
28
- setLoading(false);
29
- return;
30
- }
31
-
32
- try {
33
- const { data } = await get(`/magic-sessionmanager/user/${id}/sessions`);
34
- setSessions(data.data || []);
35
- } catch (err) {
36
- console.error('[SessionInfoCard] Error fetching sessions:', err);
37
- } finally {
38
- setLoading(false);
39
- }
40
- };
41
-
42
- fetchSessions();
43
- }, [id, model, get]);
44
-
45
- // Don't render anything if not a User model
46
- if (!isUserModel) {
47
- return null;
48
- }
49
-
50
- if (loading) {
51
- return (
52
- <Box padding={4} background="neutral100" borderRadius="4px">
53
- <Typography variant="sigma" textColor="neutral600">
54
- Loading sessions...
55
- </Typography>
56
- </Box>
57
- );
58
- }
59
-
60
- const activeSessions = sessions.filter(s => s.isActive);
61
- const isOnline = activeSessions.length > 0;
62
-
63
- return (
64
- <Box
65
- padding={4}
66
- background="neutral0"
67
- shadow="tableShadow"
68
- borderRadius="4px"
69
- marginBottom={4}
70
- >
71
- <Flex direction="column" gap={3}>
72
- {/* Header */}
73
- <Flex justifyContent="space-between" alignItems="center">
74
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase">
75
- Session Status
76
- </Typography>
77
- {isOnline ? (
78
- <Badge active backgroundColor="success500">
79
- Online
80
- </Badge>
81
- ) : (
82
- <Badge backgroundColor="neutral150">
83
- Offline
84
- </Badge>
85
- )}
86
- </Flex>
87
-
88
- {/* Stats */}
89
- <Box>
90
- <Typography variant="omega" fontWeight="bold">
91
- Active Sessions: {activeSessions.length}
92
- </Typography>
93
- <Typography variant="pi" textColor="neutral600">
94
- Total Sessions: {sessions.length}
95
- </Typography>
96
- </Box>
97
-
98
- {/* Active Sessions List */}
99
- {activeSessions.length > 0 && (
100
- <Box>
101
- <Typography variant="pi" fontWeight="bold" marginBottom={2}>
102
- Current Sessions:
103
- </Typography>
104
- {activeSessions.slice(0, 3).map((session) => (
105
- <Box
106
- key={session.id}
107
- padding={2}
108
- background="neutral100"
109
- borderRadius="4px"
110
- marginBottom={2}
111
- >
112
- <Typography variant="pi" textColor="neutral800">
113
- IP: {session.ipAddress}
114
- </Typography>
115
- <Typography variant="pi" textColor="neutral600" fontSize="11px">
116
- {new Date(session.loginTime).toLocaleString()}
117
- </Typography>
118
- </Box>
119
- ))}
120
- {activeSessions.length > 3 && (
121
- <Typography variant="pi" textColor="neutral600">
122
- + {activeSessions.length - 3} more...
123
- </Typography>
124
- )}
125
- </Box>
126
- )}
127
-
128
- {/* Last Activity */}
129
- {sessions.length > 0 && (
130
- <Box>
131
- <Typography variant="pi" textColor="neutral600">
132
- Last Activity: {new Date(sessions[0].lastActive).toLocaleString()}
133
- </Typography>
134
- </Box>
135
- )}
136
-
137
- {/* No sessions message */}
138
- {sessions.length === 0 && (
139
- <Box padding={2} background="neutral100" borderRadius="4px">
140
- <Typography variant="pi" textColor="neutral600">
141
- No session data available
142
- </Typography>
143
- </Box>
144
- )}
145
- </Flex>
146
- </Box>
147
- );
148
- };
149
-
150
- export default SessionInfoCard;
151
-
@@ -1,385 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { useIntl } from 'react-intl';
3
- import { Box, Typography, Flex, Button, Badge, Divider } from '@strapi/design-system';
4
- import { Check, Cross, Monitor, Phone, Server, Clock } from '@strapi/icons';
5
- import { useFetchClient, useNotification } from '@strapi/strapi/admin';
6
- import parseUserAgent from '../utils/parseUserAgent';
7
- import { getTranslation } from '../utils/getTranslation';
8
-
9
- /**
10
- * Session Info Panel - Native Strapi design
11
- * Clean, professional sidebar panel for Content Manager
12
- */
13
- const SessionInfoPanel = ({ documentId, model, document }) => {
14
- const { formatMessage } = useIntl();
15
- const [sessions, setSessions] = useState([]);
16
- const [loading, setLoading] = useState(true);
17
- const [isBlocked, setIsBlocked] = useState(false);
18
- const [actionLoading, setActionLoading] = useState(false);
19
- const { get, post: postRequest } = useFetchClient();
20
- const { toggleNotification } = useNotification();
21
- const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
22
-
23
- // Strapi v5: Use documentId (string UUID) instead of numeric id
24
- const userId = document?.documentId || documentId;
25
-
26
- useEffect(() => {
27
- if (model !== 'plugin::users-permissions.user' || !userId) {
28
- setLoading(false);
29
- return;
30
- }
31
-
32
- const fetchData = async () => {
33
- try {
34
- const { data } = await get(`/magic-sessionmanager/user/${userId}/sessions`);
35
- // Filter by truly active (not just isActive, but also within timeout)
36
- const activeSessions = (data.data || []).filter(s => s.isTrulyActive);
37
- setSessions(activeSessions);
38
-
39
- setIsBlocked(document?.blocked || false);
40
- } catch (err) {
41
- console.error('[SessionInfoPanel] Error:', err);
42
- } finally {
43
- setLoading(false);
44
- }
45
- };
46
-
47
- fetchData();
48
- }, [userId, model, document, get]);
49
-
50
- const handleLogoutAll = async () => {
51
- if (!userId) return;
52
-
53
- setActionLoading(true);
54
- try {
55
- const response = await postRequest(`/magic-sessionmanager/user/${userId}/terminate-all`);
56
-
57
- if (response.data?.success) {
58
- toggleNotification({
59
- type: 'success',
60
- message: t('notifications.success.terminatedAll', 'All sessions terminated successfully'),
61
- });
62
- setSessions([]);
63
- }
64
- } catch (error) {
65
- toggleNotification({
66
- type: 'warning',
67
- message: t('notifications.error.terminateAll', 'Failed to terminate sessions'),
68
- });
69
- console.error('[SessionInfoPanel] Logout all error:', error);
70
- } finally {
71
- setActionLoading(false);
72
- }
73
- };
74
-
75
- const handleToggleBlock = async () => {
76
- if (!userId) return;
77
-
78
- setActionLoading(true);
79
- try {
80
- const response = await postRequest(`/magic-sessionmanager/user/${userId}/toggle-block`);
81
-
82
- if (response.data?.success) {
83
- const newBlockedStatus = response.data.blocked;
84
- setIsBlocked(newBlockedStatus);
85
-
86
- toggleNotification({
87
- type: 'success',
88
- message: newBlockedStatus
89
- ? t('notifications.success.blocked', 'User blocked successfully')
90
- : t('notifications.success.unblocked', 'User unblocked successfully'),
91
- });
92
-
93
- if (newBlockedStatus) {
94
- setSessions([]);
95
- }
96
- }
97
- } catch (error) {
98
- toggleNotification({
99
- type: 'warning',
100
- message: t('notifications.error.block', 'Failed to update user status'),
101
- });
102
- console.error('[SessionInfoPanel] Toggle block error:', error);
103
- } finally {
104
- setActionLoading(false);
105
- }
106
- };
107
-
108
- const getDeviceIcon = (deviceType) => {
109
- if (deviceType === 'Mobile' || deviceType === 'Tablet') return Phone;
110
- if (deviceType === 'Desktop' || deviceType === 'Laptop') return Monitor;
111
- return Server;
112
- };
113
-
114
- // ONLY show for User content type - hide completely for others
115
- if (model !== 'plugin::users-permissions.user') {
116
- return null;
117
- }
118
-
119
- if (loading) {
120
- return {
121
- title: t('panel.title', 'Session Info'),
122
- content: (
123
- <Box padding={4} background="neutral0">
124
- <Typography variant="pi" textColor="neutral600">{t('panel.loading', 'Loading...')}</Typography>
125
- </Box>
126
- ),
127
- };
128
- }
129
-
130
- const isOnline = sessions.length > 0;
131
-
132
- return {
133
- title: t('panel.title', 'Session Info'),
134
- content: (
135
- <Box style={{ width: '100%' }}>
136
- <Flex direction="column" gap={4} alignItems="stretch">
137
- {/* Status Bar */}
138
- <Box
139
- padding={5}
140
- background={isOnline ? 'success100' : 'neutral150'}
141
- hasRadius
142
- style={{
143
- border: isOnline ? '1px solid #c6f6d5' : '1px solid #eaeaef',
144
- transition: 'all 0.2s ease'
145
- }}
146
- >
147
- <Flex direction="column" gap={3} alignItems="center">
148
- <Badge
149
- backgroundColor={isOnline ? 'success600' : 'neutral600'}
150
- textColor="neutral0"
151
- size="M"
152
- style={{ fontSize: '14px', padding: '6px 12px' }}
153
- >
154
- {isOnline ? t('panel.status.active', 'ACTIVE') : t('panel.status.offline', 'OFFLINE')}
155
- </Badge>
156
- <Typography variant="omega" fontWeight="semiBold" textColor={isOnline ? 'success700' : 'neutral700'}>
157
- {t('panel.sessions.count', '{count} active session{count, plural, one {} other {s}}', { count: sessions.length })}
158
- </Typography>
159
- </Flex>
160
- </Box>
161
-
162
- {/* User Blocked Warning */}
163
- {isBlocked && (
164
- <Box
165
- padding={4}
166
- background="danger100"
167
- hasRadius
168
- >
169
- <Typography variant="omega" fontWeight="semiBold" textColor="danger700" marginBottom={1}>
170
- {t('panel.blocked.title', 'User is blocked')}
171
- </Typography>
172
- <Typography variant="pi" textColor="danger600">
173
- {t('panel.blocked.description', 'Authentication disabled')}
174
- </Typography>
175
- </Box>
176
- )}
177
-
178
- {/* Active Sessions List */}
179
- {sessions.length > 0 ? (
180
- <Flex direction="column" gap={3} alignItems="stretch">
181
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{
182
- textAlign: 'left',
183
- letterSpacing: '0.5px',
184
- fontSize: '12px'
185
- }}>
186
- {t('panel.sessions.title', 'Active Sessions')}
187
- </Typography>
188
-
189
- {sessions.slice(0, 5).map((session) => {
190
- const deviceInfo = parseUserAgent(session.userAgent);
191
- const DeviceIcon = getDeviceIcon(deviceInfo.device);
192
-
193
- return (
194
- <Box
195
- key={session.id}
196
- padding={4}
197
- background="neutral0"
198
- hasRadius
199
- style={{
200
- border: '1px solid #e3e8ef',
201
- boxShadow: '0 1px 3px rgba(0, 0, 0, 0.04)',
202
- transition: 'all 0.2s ease'
203
- }}
204
- onMouseEnter={(e) => {
205
- e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.08)';
206
- e.currentTarget.style.borderColor = '#4945FF';
207
- }}
208
- onMouseLeave={(e) => {
209
- e.currentTarget.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.04)';
210
- e.currentTarget.style.borderColor = '#e3e8ef';
211
- }}
212
- >
213
- <Flex direction="column" gap={2} alignItems="flex-start">
214
- {/* Device Name with Icon */}
215
- <Flex gap={2} alignItems="center">
216
- <DeviceIcon width="20px" height="20px" />
217
- <Typography variant="omega" fontWeight="bold" textColor="neutral800">
218
- {deviceInfo.device}
219
- </Typography>
220
- </Flex>
221
-
222
- {/* Status Badge */}
223
- <Badge
224
- backgroundColor="success600"
225
- textColor="neutral0"
226
- size="S"
227
- >
228
- {t('panel.sessions.active', 'Active')}
229
- </Badge>
230
-
231
- {/* Browser & OS */}
232
- <Typography variant="pi" textColor="neutral600">
233
- {deviceInfo.browser} on {deviceInfo.os}
234
- </Typography>
235
-
236
- <Divider />
237
-
238
- {/* IP Address */}
239
- <Flex gap={2} alignItems="center">
240
- <Server width="14px" height="14px" />
241
- <Typography variant="pi" textColor="neutral600">
242
- {session.ipAddress}
243
- </Typography>
244
- </Flex>
245
-
246
- {/* Login Time */}
247
- <Flex gap={2} alignItems="center">
248
- <Clock width="14px" height="14px" />
249
- <Typography variant="pi" textColor="neutral600">
250
- {new Date(session.loginTime).toLocaleString('en-US', {
251
- month: 'short',
252
- day: 'numeric',
253
- hour: '2-digit',
254
- minute: '2-digit'
255
- })}
256
- </Typography>
257
- </Flex>
258
-
259
- {/* Show minutes since last activity */}
260
- {session.minutesSinceActive !== undefined && session.minutesSinceActive < 60 && (
261
- <Typography variant="pi" textColor="success600" fontWeight="semiBold">
262
- {session.minutesSinceActive === 0
263
- ? t('panel.sessions.activeNow', 'Active now')
264
- : t('panel.sessions.activeAgo', 'Active {minutes} min ago', { minutes: session.minutesSinceActive })
265
- }
266
- </Typography>
267
- )}
268
- </Flex>
269
- </Box>
270
- );
271
- })}
272
-
273
- {sessions.length > 5 && (
274
- <Box padding={3} background="primary100" hasRadius textAlign="center">
275
- <Typography variant="pi" textColor="primary600" fontWeight="semiBold">
276
- {t('panel.sessions.more', '+{count} more session{count, plural, one {} other {s}}', { count: sessions.length - 5 })}
277
- </Typography>
278
- </Box>
279
- )}
280
- </Flex>
281
- ) : (
282
- <Box
283
- padding={6}
284
- background="neutral100"
285
- hasRadius
286
- style={{
287
- border: '1px dashed #dcdce4',
288
- textAlign: 'center'
289
- }}
290
- >
291
- <Flex direction="column" alignItems="center" gap={2}>
292
- <Typography
293
- variant="pi"
294
- textColor="neutral600"
295
- style={{ fontSize: '32px', marginBottom: '8px' }}
296
- >
297
- 💤
298
- </Typography>
299
- <Typography variant="omega" fontWeight="semiBold" textColor="neutral700">
300
- {t('panel.empty.title', 'No active sessions')}
301
- </Typography>
302
- <Typography variant="pi" textColor="neutral500" style={{ fontSize: '13px' }}>
303
- {t('panel.empty.description', 'User has not logged in yet')}
304
- </Typography>
305
- </Flex>
306
- </Box>
307
- )}
308
-
309
- {/* Action Buttons - Always at the bottom */}
310
- <Divider />
311
-
312
- <Flex direction="column" gap={3} alignItems="stretch">
313
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{
314
- textAlign: 'left',
315
- letterSpacing: '0.5px',
316
- fontSize: '12px'
317
- }}>
318
- {t('panel.actions.title', 'Actions')}
319
- </Typography>
320
-
321
- <Button
322
- variant="secondary"
323
- size="M"
324
- fullWidth
325
- onClick={handleLogoutAll}
326
- disabled={actionLoading || sessions.length === 0}
327
- startIcon={<Cross />}
328
- style={{
329
- border: '1px solid #dc2626',
330
- color: '#dc2626',
331
- backgroundColor: 'transparent',
332
- transition: 'all 0.2s ease',
333
- }}
334
- onMouseEnter={(e) => {
335
- if (!actionLoading && sessions.length > 0) {
336
- e.currentTarget.style.backgroundColor = '#dc2626';
337
- e.currentTarget.style.color = 'white';
338
- }
339
- }}
340
- onMouseLeave={(e) => {
341
- if (!actionLoading && sessions.length > 0) {
342
- e.currentTarget.style.backgroundColor = 'transparent';
343
- e.currentTarget.style.color = '#dc2626';
344
- }
345
- }}
346
- >
347
- {t('panel.actions.terminateAll', 'Terminate All Sessions')}
348
- </Button>
349
-
350
- <Button
351
- variant="secondary"
352
- size="M"
353
- fullWidth
354
- onClick={handleToggleBlock}
355
- disabled={actionLoading}
356
- startIcon={isBlocked ? <Check /> : <Cross />}
357
- style={{
358
- border: isBlocked ? '1px solid #16a34a' : '1px solid #dc2626',
359
- color: isBlocked ? '#16a34a' : '#dc2626',
360
- backgroundColor: 'transparent',
361
- transition: 'all 0.2s ease',
362
- }}
363
- onMouseEnter={(e) => {
364
- if (!actionLoading) {
365
- e.currentTarget.style.backgroundColor = isBlocked ? '#16a34a' : '#dc2626';
366
- e.currentTarget.style.color = 'white';
367
- }
368
- }}
369
- onMouseLeave={(e) => {
370
- if (!actionLoading) {
371
- e.currentTarget.style.backgroundColor = 'transparent';
372
- e.currentTarget.style.color = isBlocked ? '#16a34a' : '#dc2626';
373
- }
374
- }}
375
- >
376
- {isBlocked ? t('panel.actions.unblockUser', 'Unblock User') : t('panel.actions.blockUser', 'Block User')}
377
- </Button>
378
- </Flex>
379
- </Flex>
380
- </Box>
381
- ),
382
- };
383
- };
384
-
385
- export default SessionInfoPanel;
@@ -1,5 +0,0 @@
1
- export { default as Initializer } from './Initializer';
2
- export { default as PluginIcon } from './PluginIcon';
3
- export { default as SessionInfoCard } from './SessionInfoCard';
4
- export { default as SessionInfoPanel } from './SessionInfoPanel';
5
- export { default as OnlineUsersWidget } from './OnlineUsersWidget';
@@ -1,103 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { useFetchClient } from '@strapi/strapi/admin';
3
- import pluginId from '../pluginId';
4
-
5
- /**
6
- * Hook to check license status
7
- * Returns: { isPremium, loading, error }
8
- */
9
- export const useLicense = () => {
10
- const { get } = useFetchClient();
11
- const [isPremium, setIsPremium] = useState(false);
12
- const [isAdvanced, setIsAdvanced] = useState(false);
13
- const [isEnterprise, setIsEnterprise] = useState(false);
14
- const [loading, setLoading] = useState(true);
15
- const [error, setError] = useState(null);
16
- const [licenseData, setLicenseData] = useState(null);
17
-
18
- useEffect(() => {
19
- checkLicense();
20
-
21
- // Auto-refresh every 1 hour to detect license changes (silent background check)
22
- const interval = setInterval(() => {
23
- checkLicense(true); // Silent refresh - user merkt nichts
24
- }, 60 * 60 * 1000); // 1 hour
25
-
26
- return () => clearInterval(interval);
27
- }, []);
28
-
29
- const checkLicense = async (silent = false) => {
30
- if (!silent) {
31
- setLoading(true);
32
- }
33
-
34
- try {
35
- const response = await get(`/${pluginId}/license/status`);
36
-
37
- // Debug logging with plugin name
38
- if (!silent) {
39
- console.log('[magic-sessionmanager/useLicense] Full API Response:', response.data);
40
- console.log('[magic-sessionmanager/useLicense] License Details:', {
41
- valid: response.data?.valid,
42
- demo: response.data?.demo,
43
- licenseKey: response.data?.data?.licenseKey?.substring(0, 13) + '...',
44
- email: response.data?.data?.email,
45
- features: response.data?.data?.features,
46
- rawFeaturePremium: response.data?.data?.features?.premium,
47
- });
48
- }
49
-
50
- // Check if license is valid AND has premium feature enabled
51
- const isValid = response.data?.valid || false;
52
- const hasPremiumFeature = response.data?.data?.features?.premium || false;
53
- const hasAdvancedFeature = response.data?.data?.features?.advanced || false;
54
- const hasEnterpriseFeature = response.data?.data?.features?.enterprise || false;
55
- const newIsPremium = isValid && hasPremiumFeature;
56
- const newIsAdvanced = isValid && hasAdvancedFeature;
57
- const newIsEnterprise = isValid && hasEnterpriseFeature;
58
-
59
- // Log with plugin name
60
- if ((newIsPremium !== isPremium || !silent) && !silent) {
61
- console.log(`[magic-sessionmanager/useLicense] Premium Status: ${newIsPremium} (valid: ${isValid}, featurePremium: ${hasPremiumFeature})`);
62
- if (!newIsPremium && isValid) {
63
- console.warn('[magic-sessionmanager/useLicense] [WARN] License is valid but Premium feature is not enabled!');
64
- }
65
- }
66
-
67
- setIsPremium(newIsPremium);
68
- setIsAdvanced(newIsAdvanced);
69
- setIsEnterprise(newIsEnterprise);
70
- setLicenseData(response.data?.data || null);
71
- setError(null);
72
- } catch (err) {
73
- if (!silent) {
74
- console.error('[useLicense] Error checking license:', err);
75
- }
76
- setIsPremium(false);
77
- setIsAdvanced(false);
78
- setIsEnterprise(false);
79
- setLicenseData(null);
80
- setError(err);
81
- } finally {
82
- if (!silent) {
83
- setLoading(false);
84
- }
85
- }
86
- };
87
-
88
- return {
89
- isPremium,
90
- isAdvanced,
91
- isEnterprise,
92
- loading,
93
- error,
94
- licenseData,
95
- features: {
96
- premium: isPremium,
97
- advanced: isAdvanced,
98
- enterprise: isEnterprise,
99
- },
100
- refetch: checkLicense
101
- };
102
- };
103
-