strapi-plugin-magic-sessionmanager 3.7.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/admin/src/components/LicenseGuard.jsx +6 -6
- package/admin/src/components/SessionDetailModal.jsx +12 -12
- package/admin/src/components/SessionInfoCard.jsx +3 -3
- package/admin/src/components/SessionInfoPanel.jsx +3 -2
- package/admin/src/hooks/useLicense.js +1 -1
- package/admin/src/index.js +2 -2
- package/admin/src/pages/Analytics.jsx +2 -2
- package/admin/src/pages/HomePage.jsx +11 -14
- package/admin/src/pages/License.jsx +2 -2
- package/admin/src/pages/Settings.jsx +24 -25
- package/admin/src/pages/SettingsNew.jsx +21 -21
- package/admin/src/utils/parseUserAgent.js +7 -7
- package/dist/_chunks/{Analytics-Bi-vcT63.js → Analytics-BBdv1I5y.js} +4 -4
- package/dist/_chunks/{Analytics-BM9i88xu.mjs → Analytics-Dv9f_0eZ.mjs} +4 -4
- package/dist/_chunks/{App-DcnJOCL9.mjs → App-CIQ-7sa7.mjs} +26 -31
- package/dist/_chunks/{App-BbiNy_cT.js → App-CJaZPNjt.js} +26 -31
- package/dist/_chunks/{License-kYo8j2yl.js → License-D24rgaZQ.js} +3 -3
- package/dist/_chunks/{License-DsxP-MAL.mjs → License-nrmFxoBm.mjs} +3 -3
- package/dist/_chunks/{Settings-jW0TOE_d.js → Settings-CqxgjU0y.js} +26 -26
- package/dist/_chunks/{Settings-C3sW9eBD.mjs → Settings-D5dLEGc_.mjs} +26 -26
- package/dist/_chunks/{index-DG9XeVSg.mjs → index-Duk1_Wrz.mjs} +15 -15
- package/dist/_chunks/{index-Dr2HT-Dd.js → index-WH04CS1c.js} +15 -15
- package/dist/_chunks/{useLicense-BL_3bX9O.js → useLicense-BwOlCyhc.js} +2 -2
- package/dist/_chunks/{useLicense-DOkJX-tk.mjs → useLicense-Ce8GaxB0.mjs} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +250 -119
- package/dist/server/index.mjs +250 -119
- package/package.json +1 -1
- package/server/src/bootstrap.js +106 -28
- package/server/src/controllers/license.js +4 -4
- package/server/src/controllers/session.js +67 -13
- package/server/src/destroy.js +1 -1
- package/server/src/middlewares/last-seen.js +13 -7
- package/server/src/register.js +4 -4
- package/server/src/routes/content-api.js +11 -2
- package/server/src/services/geolocation.js +4 -2
- package/server/src/services/license-guard.js +13 -10
- package/server/src/services/notifications.js +20 -20
- package/server/src/services/service.js +1 -1
- package/server/src/services/session.js +63 -33
- package/server/src/utils/encryption.js +1 -1
package/README.md
CHANGED
|
@@ -300,6 +300,96 @@ Add to `config/plugins.ts`:
|
|
|
300
300
|
|
|
301
301
|
---
|
|
302
302
|
|
|
303
|
+
## 📧 Email Alerts Setup (Premium)
|
|
304
|
+
|
|
305
|
+
The Session Manager uses **Strapi's Email Plugin** to send notifications. You need to configure an email provider first.
|
|
306
|
+
|
|
307
|
+
### Step 1: Install Email Provider
|
|
308
|
+
|
|
309
|
+
Choose one of these providers:
|
|
310
|
+
|
|
311
|
+
**Option A: Nodemailer (Recommended)**
|
|
312
|
+
```bash
|
|
313
|
+
npm install @strapi/provider-email-nodemailer
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Option B: SendGrid**
|
|
317
|
+
```bash
|
|
318
|
+
npm install @strapi/provider-email-sendgrid
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Option C: Mailgun**
|
|
322
|
+
```bash
|
|
323
|
+
npm install @strapi/provider-email-mailgun
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Step 2: Configure Email Plugin
|
|
327
|
+
|
|
328
|
+
Add to `config/plugins.ts`:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
export default () => ({
|
|
332
|
+
// Email configuration
|
|
333
|
+
email: {
|
|
334
|
+
config: {
|
|
335
|
+
provider: 'nodemailer',
|
|
336
|
+
providerOptions: {
|
|
337
|
+
host: process.env.SMTP_HOST || 'smtp.gmail.com',
|
|
338
|
+
port: process.env.SMTP_PORT || 587,
|
|
339
|
+
auth: {
|
|
340
|
+
user: process.env.SMTP_USER,
|
|
341
|
+
pass: process.env.SMTP_PASSWORD,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
settings: {
|
|
345
|
+
defaultFrom: process.env.SMTP_DEFAULT_FROM || 'noreply@yourapp.com',
|
|
346
|
+
defaultReplyTo: process.env.SMTP_DEFAULT_REPLY_TO || 'support@yourapp.com',
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
// Session Manager configuration
|
|
352
|
+
'magic-sessionmanager': {
|
|
353
|
+
enabled: true,
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Step 3: Add Environment Variables
|
|
359
|
+
|
|
360
|
+
Add to your `.env` file:
|
|
361
|
+
|
|
362
|
+
```env
|
|
363
|
+
SMTP_HOST=smtp.gmail.com
|
|
364
|
+
SMTP_PORT=587
|
|
365
|
+
SMTP_USER=your-email@gmail.com
|
|
366
|
+
SMTP_PASSWORD=your-app-password
|
|
367
|
+
SMTP_DEFAULT_FROM=noreply@yourapp.com
|
|
368
|
+
SMTP_DEFAULT_REPLY_TO=support@yourapp.com
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**For Gmail:**
|
|
372
|
+
- Use an [App Password](https://support.google.com/accounts/answer/185833), not your regular password!
|
|
373
|
+
|
|
374
|
+
### Step 4: Enable in Admin Panel
|
|
375
|
+
|
|
376
|
+
1. Go to **Sessions → Settings**
|
|
377
|
+
2. Scroll to **"Email Notifications"**
|
|
378
|
+
3. Toggle **"Enable Email Alerts"** to ON
|
|
379
|
+
4. Customize email templates (optional)
|
|
380
|
+
5. Click **Save**
|
|
381
|
+
|
|
382
|
+
### Step 5: Test It
|
|
383
|
+
|
|
384
|
+
Trigger a suspicious login (e.g., use a VPN) and check if the email arrives!
|
|
385
|
+
|
|
386
|
+
**Troubleshooting:**
|
|
387
|
+
- Check Strapi logs for email errors
|
|
388
|
+
- Verify SMTP credentials are correct
|
|
389
|
+
- Test SMTP connection with a tool like [smtp-tester](https://www.npmjs.com/package/smtp-tester)
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
303
393
|
## 📋 Simple API Guide
|
|
304
394
|
|
|
305
395
|
### Get Sessions
|
|
@@ -407,12 +407,12 @@ const LicenseGuard = ({ children }) => {
|
|
|
407
407
|
>
|
|
408
408
|
<Typography variant="omega" style={{ fontSize: '13px', lineHeight: '1.6' }}>
|
|
409
409
|
{useExistingKey
|
|
410
|
-
? '
|
|
410
|
+
? 'Enter your email and license key to activate.'
|
|
411
411
|
: useAutoCreate && adminUser && adminUser.email
|
|
412
|
-
?
|
|
412
|
+
? `Click "Activate" to auto-create a license with your account (${adminUser.email})`
|
|
413
413
|
: useAutoCreate
|
|
414
|
-
? '
|
|
415
|
-
: '
|
|
414
|
+
? 'Click "Activate" to auto-create a license with your admin account'
|
|
415
|
+
: 'A license will be created with the details below.'
|
|
416
416
|
}
|
|
417
417
|
</Typography>
|
|
418
418
|
</Box>
|
|
@@ -476,10 +476,10 @@ const LicenseGuard = ({ children }) => {
|
|
|
476
476
|
Ready to activate with your account:
|
|
477
477
|
</Typography>
|
|
478
478
|
<Typography variant="pi" style={{ marginBottom: '4px', display: 'block' }}>
|
|
479
|
-
|
|
479
|
+
{adminUser.firstname || 'Admin'} {adminUser.lastname || 'User'}
|
|
480
480
|
</Typography>
|
|
481
481
|
<Typography variant="pi" textColor="neutral600">
|
|
482
|
-
|
|
482
|
+
{adminUser.email || 'Loading...'}
|
|
483
483
|
</Typography>
|
|
484
484
|
</Box>
|
|
485
485
|
) : (
|
|
@@ -82,7 +82,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
82
82
|
console.error('[SessionDetailModal] Error fetching geolocation:', err);
|
|
83
83
|
// Fallback to mock data if API fails
|
|
84
84
|
setGeoData({
|
|
85
|
-
country_flag: '
|
|
85
|
+
country_flag: '',
|
|
86
86
|
country: 'Unknown',
|
|
87
87
|
city: 'Unknown',
|
|
88
88
|
timezone: 'Unknown',
|
|
@@ -98,7 +98,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
98
98
|
|
|
99
99
|
// Use real data if available, otherwise fallback
|
|
100
100
|
const premiumData = geoData || {
|
|
101
|
-
country_flag: '
|
|
101
|
+
country_flag: '',
|
|
102
102
|
country: 'Loading...',
|
|
103
103
|
city: 'Loading...',
|
|
104
104
|
timezone: 'Loading...',
|
|
@@ -207,7 +207,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
207
207
|
size="M"
|
|
208
208
|
style={{ fontSize: '14px', padding: '8px 20px', fontWeight: '600' }}
|
|
209
209
|
>
|
|
210
|
-
{isOnline ? '
|
|
210
|
+
{isOnline ? 'ONLINE' : 'OFFLINE'}
|
|
211
211
|
</Badge>
|
|
212
212
|
</Flex>
|
|
213
213
|
|
|
@@ -220,7 +220,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
220
220
|
{/* User Information */}
|
|
221
221
|
<Section>
|
|
222
222
|
<SectionTitle>
|
|
223
|
-
|
|
223
|
+
User
|
|
224
224
|
</SectionTitle>
|
|
225
225
|
|
|
226
226
|
<DetailRow compact icon={Check} label="Username" value={session.user?.username || 'N/A'} />
|
|
@@ -231,7 +231,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
231
231
|
{/* Device Information */}
|
|
232
232
|
<Section>
|
|
233
233
|
<SectionTitle>
|
|
234
|
-
|
|
234
|
+
Device
|
|
235
235
|
</SectionTitle>
|
|
236
236
|
|
|
237
237
|
<DetailRow compact icon={DeviceIcon} label="Device" value={deviceInfo.device} />
|
|
@@ -245,7 +245,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
245
245
|
<Box>
|
|
246
246
|
<Section>
|
|
247
247
|
<SectionTitle>
|
|
248
|
-
|
|
248
|
+
Timeline
|
|
249
249
|
</SectionTitle>
|
|
250
250
|
|
|
251
251
|
<DetailRow
|
|
@@ -297,7 +297,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
297
297
|
{isPremium ? (
|
|
298
298
|
<Section>
|
|
299
299
|
<SectionTitle>
|
|
300
|
-
|
|
300
|
+
Location and Security
|
|
301
301
|
</SectionTitle>
|
|
302
302
|
|
|
303
303
|
{geoLoading ? (
|
|
@@ -313,7 +313,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
313
313
|
compact
|
|
314
314
|
icon={Earth}
|
|
315
315
|
label="Country"
|
|
316
|
-
value={`${premiumData.country_flag || '
|
|
316
|
+
value={`${premiumData.country_flag || ''} ${premiumData.country}`.trim()}
|
|
317
317
|
/>
|
|
318
318
|
<DetailRow compact icon={Earth} label="City" value={premiumData.city} />
|
|
319
319
|
<DetailRow compact icon={Clock} label="Timezone" value={premiumData.timezone} />
|
|
@@ -329,13 +329,13 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
329
329
|
compact
|
|
330
330
|
icon={Shield}
|
|
331
331
|
label="VPN"
|
|
332
|
-
value={premiumData.isVpn ? '
|
|
332
|
+
value={premiumData.isVpn ? '[WARNING] Yes' : 'No'}
|
|
333
333
|
/>
|
|
334
334
|
<DetailRow
|
|
335
335
|
compact
|
|
336
336
|
icon={Shield}
|
|
337
337
|
label="Proxy"
|
|
338
|
-
value={premiumData.isProxy ? '
|
|
338
|
+
value={premiumData.isProxy ? '[WARNING] Yes' : 'No'}
|
|
339
339
|
/>
|
|
340
340
|
</Box>
|
|
341
341
|
</TwoColumnGrid>
|
|
@@ -355,7 +355,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
355
355
|
<Flex direction="column" alignItems="center" gap={3}>
|
|
356
356
|
<Crown style={{ width: '40px', height: '40px', color: '#d97706' }} />
|
|
357
357
|
<Typography variant="beta" style={{ color: '#92400e', fontWeight: '700' }}>
|
|
358
|
-
|
|
358
|
+
Location and Security Analysis
|
|
359
359
|
</Typography>
|
|
360
360
|
<Typography variant="omega" style={{ color: '#78350f', fontSize: '14px', lineHeight: '1.6' }}>
|
|
361
361
|
Unlock premium features to get IP geolocation, security scoring, and VPN/Proxy detection for every session
|
|
@@ -384,7 +384,7 @@ const SessionDetailModal = ({ session, onClose, onSessionTerminated }) => {
|
|
|
384
384
|
<Section>
|
|
385
385
|
<Flex justifyContent="space-between" alignItems="center" style={{ marginBottom: '12px' }}>
|
|
386
386
|
<SectionTitle style={{ marginBottom: 0, paddingBottom: 0, border: 'none' }}>
|
|
387
|
-
|
|
387
|
+
Technical Details
|
|
388
388
|
</SectionTitle>
|
|
389
389
|
<Button
|
|
390
390
|
variant="tertiary"
|
|
@@ -76,11 +76,11 @@ const SessionInfoCard = ({ id, model }) => {
|
|
|
76
76
|
</Typography>
|
|
77
77
|
{isOnline ? (
|
|
78
78
|
<Badge active backgroundColor="success500">
|
|
79
|
-
|
|
79
|
+
Online
|
|
80
80
|
</Badge>
|
|
81
81
|
) : (
|
|
82
82
|
<Badge backgroundColor="neutral150">
|
|
83
|
-
|
|
83
|
+
Offline
|
|
84
84
|
</Badge>
|
|
85
85
|
)}
|
|
86
86
|
</Flex>
|
|
@@ -110,7 +110,7 @@ const SessionInfoCard = ({ id, model }) => {
|
|
|
110
110
|
marginBottom={2}
|
|
111
111
|
>
|
|
112
112
|
<Typography variant="pi" textColor="neutral800">
|
|
113
|
-
|
|
113
|
+
IP: {session.ipAddress}
|
|
114
114
|
</Typography>
|
|
115
115
|
<Typography variant="pi" textColor="neutral600" fontSize="11px">
|
|
116
116
|
{new Date(session.loginTime).toLocaleString()}
|
|
@@ -16,7 +16,8 @@ const SessionInfoPanel = ({ documentId, model, document }) => {
|
|
|
16
16
|
const { get, post: postRequest } = useFetchClient();
|
|
17
17
|
const { toggleNotification } = useNotification();
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// Strapi v5: Use documentId (string UUID) instead of numeric id
|
|
20
|
+
const userId = document?.documentId || documentId;
|
|
20
21
|
|
|
21
22
|
useEffect(() => {
|
|
22
23
|
if (model !== 'plugin::users-permissions.user' || !userId) {
|
|
@@ -144,7 +145,7 @@ const SessionInfoPanel = ({ documentId, model, document }) => {
|
|
|
144
145
|
size="M"
|
|
145
146
|
style={{ fontSize: '14px', padding: '6px 12px' }}
|
|
146
147
|
>
|
|
147
|
-
{isOnline ? '
|
|
148
|
+
{isOnline ? 'ACTIVE' : 'OFFLINE'}
|
|
148
149
|
</Badge>
|
|
149
150
|
<Typography variant="omega" fontWeight="semiBold" textColor={isOnline ? 'success700' : 'neutral700'}>
|
|
150
151
|
{sessions.length} active session{sessions.length !== 1 ? 's' : ''}
|
|
@@ -60,7 +60,7 @@ export const useLicense = () => {
|
|
|
60
60
|
if ((newIsPremium !== isPremium || !silent) && !silent) {
|
|
61
61
|
console.log(`[magic-sessionmanager/useLicense] Premium Status: ${newIsPremium} (valid: ${isValid}, featurePremium: ${hasPremiumFeature})`);
|
|
62
62
|
if (!newIsPremium && isValid) {
|
|
63
|
-
console.warn('[magic-sessionmanager/useLicense]
|
|
63
|
+
console.warn('[magic-sessionmanager/useLicense] [WARN] License is valid but Premium feature is not enabled!');
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
package/admin/src/index.js
CHANGED
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
id: 'online-users-widget',
|
|
87
87
|
pluginId: pluginId,
|
|
88
88
|
});
|
|
89
|
-
console.log(`[${pluginId}]
|
|
89
|
+
console.log(`[${pluginId}] [SUCCESS] Online Users Widget registered`);
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
|
|
@@ -99,7 +99,7 @@ export default {
|
|
|
99
99
|
if (contentManagerPlugin && contentManagerPlugin.apis) {
|
|
100
100
|
console.log(`[${pluginId}] Injecting SessionInfoPanel into edit view sidebar...`);
|
|
101
101
|
contentManagerPlugin.apis.addEditViewSidePanel([SessionInfoPanel]);
|
|
102
|
-
console.log(`[${pluginId}]
|
|
102
|
+
console.log(`[${pluginId}] [SUCCESS] SessionInfoPanel injected successfully`);
|
|
103
103
|
} else {
|
|
104
104
|
console.warn(`[${pluginId}] Content Manager plugin or APIs not available`);
|
|
105
105
|
}
|
|
@@ -506,7 +506,7 @@ const AnalyticsPage = () => {
|
|
|
506
506
|
textShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
|
|
507
507
|
}}
|
|
508
508
|
>
|
|
509
|
-
|
|
509
|
+
Analytics Dashboard
|
|
510
510
|
</Typography>
|
|
511
511
|
|
|
512
512
|
<Typography
|
|
@@ -547,7 +547,7 @@ const AnalyticsPage = () => {
|
|
|
547
547
|
e.currentTarget.style.boxShadow = '0 6px 16px rgba(245, 158, 11, 0.4)';
|
|
548
548
|
}}
|
|
549
549
|
>
|
|
550
|
-
|
|
550
|
+
Upgrade to Premium
|
|
551
551
|
</button>
|
|
552
552
|
</div>
|
|
553
553
|
</Box>
|
|
@@ -531,7 +531,7 @@ const HomePage = () => {
|
|
|
531
531
|
};
|
|
532
532
|
|
|
533
533
|
const handleDeleteSession = async (sessionId) => {
|
|
534
|
-
if (!confirm('
|
|
534
|
+
if (!confirm('[WARNING] This will PERMANENTLY delete this session from the database!\n\nThis action cannot be undone.\n\nAre you sure?')) {
|
|
535
535
|
return;
|
|
536
536
|
}
|
|
537
537
|
|
|
@@ -838,7 +838,7 @@ const HomePage = () => {
|
|
|
838
838
|
<Box>
|
|
839
839
|
<Box style={{ marginBottom: theme.spacing.md }}>
|
|
840
840
|
<Typography variant="delta" style={{ marginBottom: theme.spacing.md, color: theme.colors.neutral[700] }}>
|
|
841
|
-
|
|
841
|
+
All Sessions
|
|
842
842
|
</Typography>
|
|
843
843
|
</Box>
|
|
844
844
|
|
|
@@ -864,10 +864,10 @@ const HomePage = () => {
|
|
|
864
864
|
size="S"
|
|
865
865
|
>
|
|
866
866
|
<SingleSelectOption value="all">All Sessions</SingleSelectOption>
|
|
867
|
-
<SingleSelectOption value="active"
|
|
868
|
-
<SingleSelectOption value="idle"
|
|
869
|
-
<SingleSelectOption value="loggedout"
|
|
870
|
-
<SingleSelectOption value="terminated"
|
|
867
|
+
<SingleSelectOption value="active">Active (less than 15 min)</SingleSelectOption>
|
|
868
|
+
<SingleSelectOption value="idle">Idle (more than 15 min)</SingleSelectOption>
|
|
869
|
+
<SingleSelectOption value="loggedout">Logged Out</SingleSelectOption>
|
|
870
|
+
<SingleSelectOption value="terminated">Terminated</SingleSelectOption>
|
|
871
871
|
</SingleSelect>
|
|
872
872
|
</Box>
|
|
873
873
|
|
|
@@ -921,26 +921,26 @@ const HomePage = () => {
|
|
|
921
921
|
active: {
|
|
922
922
|
bg: theme.colors.success[50],
|
|
923
923
|
badgeColor: 'success600',
|
|
924
|
-
label: '
|
|
924
|
+
label: 'Active',
|
|
925
925
|
indicator: true
|
|
926
926
|
},
|
|
927
927
|
idle: {
|
|
928
928
|
bg: theme.colors.warning[50],
|
|
929
929
|
badgeColor: 'warning600',
|
|
930
|
-
label: '
|
|
930
|
+
label: 'Idle',
|
|
931
931
|
indicator: false
|
|
932
932
|
},
|
|
933
933
|
loggedout: {
|
|
934
934
|
bg: theme.colors.danger[50],
|
|
935
935
|
badgeColor: 'danger600',
|
|
936
|
-
label: '
|
|
936
|
+
label: 'Logged Out',
|
|
937
937
|
indicator: false,
|
|
938
938
|
opacity: 0.7
|
|
939
939
|
},
|
|
940
940
|
terminated: {
|
|
941
941
|
bg: theme.colors.neutral[100],
|
|
942
942
|
badgeColor: 'neutral600',
|
|
943
|
-
label: '
|
|
943
|
+
label: 'Terminated',
|
|
944
944
|
indicator: false,
|
|
945
945
|
opacity: 0.6
|
|
946
946
|
},
|
|
@@ -1185,10 +1185,7 @@ const HomePage = () => {
|
|
|
1185
1185
|
}}
|
|
1186
1186
|
/>
|
|
1187
1187
|
|
|
1188
|
-
{/* Floating
|
|
1189
|
-
<FloatingEmoji>
|
|
1190
|
-
💻
|
|
1191
|
-
</FloatingEmoji>
|
|
1188
|
+
{/* Floating Icon (removed emoji) */}
|
|
1192
1189
|
|
|
1193
1190
|
<Flex direction="column" alignItems="center" gap={6} style={{ position: 'relative', zIndex: 1 }}>
|
|
1194
1191
|
<Box
|
|
@@ -465,7 +465,7 @@ Generated: ${new Date().toLocaleString()}
|
|
|
465
465
|
{data.features?.premium && (
|
|
466
466
|
<Box marginBottom={5} padding={5} background="success50" hasRadius style={{ border: '2px solid #dcfce7' }}>
|
|
467
467
|
<Typography variant="delta" fontWeight="bold" textColor="success700" style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
468
|
-
|
|
468
|
+
Premium Features Active
|
|
469
469
|
</Typography>
|
|
470
470
|
<Flex direction="column" gap={2}>
|
|
471
471
|
<Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
@@ -494,7 +494,7 @@ Generated: ${new Date().toLocaleString()}
|
|
|
494
494
|
{data.features?.advanced && (
|
|
495
495
|
<Box marginBottom={5} padding={5} background="primary50" hasRadius style={{ border: '2px solid #bae6fd' }}>
|
|
496
496
|
<Typography variant="delta" fontWeight="bold" textColor="primary700" style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
497
|
-
|
|
497
|
+
Advanced Features Active
|
|
498
498
|
</Typography>
|
|
499
499
|
<Flex direction="column" gap={2}>
|
|
500
500
|
<Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
Checkbox,
|
|
11
11
|
Alert,
|
|
12
12
|
TextInput,
|
|
13
|
-
Textarea,
|
|
14
13
|
Tabs,
|
|
15
14
|
Divider,
|
|
16
15
|
Badge,
|
|
@@ -224,12 +223,12 @@ const validateTemplate = (template, templateType) => {
|
|
|
224
223
|
// Get default email templates
|
|
225
224
|
const getDefaultTemplates = () => ({
|
|
226
225
|
suspiciousLogin: {
|
|
227
|
-
subject: '
|
|
226
|
+
subject: '[ALERT] Suspicious Login Alert - Session Manager',
|
|
228
227
|
html: `
|
|
229
228
|
<html>
|
|
230
229
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
231
230
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
|
|
232
|
-
<h2 style="color: #dc2626;"
|
|
231
|
+
<h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
|
|
233
232
|
<p>A potentially suspicious login was detected for your account.</p>
|
|
234
233
|
|
|
235
234
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -264,7 +263,7 @@ const getDefaultTemplates = () => ({
|
|
|
264
263
|
</div>
|
|
265
264
|
</body>
|
|
266
265
|
</html>`,
|
|
267
|
-
text:
|
|
266
|
+
text: `[ALERT] Suspicious Login Detected
|
|
268
267
|
|
|
269
268
|
A potentially suspicious login was detected for your account.
|
|
270
269
|
|
|
@@ -279,12 +278,12 @@ Login Details:
|
|
|
279
278
|
Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`,
|
|
280
279
|
},
|
|
281
280
|
newLocation: {
|
|
282
|
-
subject: '
|
|
281
|
+
subject: '[LOCATION] New Location Login Detected',
|
|
283
282
|
html: `
|
|
284
283
|
<html>
|
|
285
284
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
286
285
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f0f9ff; border-radius: 10px;">
|
|
287
|
-
<h2 style="color: #0284c7;"
|
|
286
|
+
<h2 style="color: #0284c7;">[LOCATION] Login from New Location</h2>
|
|
288
287
|
<p>Your account was accessed from a new location.</p>
|
|
289
288
|
|
|
290
289
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -307,7 +306,7 @@ Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThre
|
|
|
307
306
|
</div>
|
|
308
307
|
</body>
|
|
309
308
|
</html>`,
|
|
310
|
-
text:
|
|
309
|
+
text: `[LOCATION] Login from New Location
|
|
311
310
|
|
|
312
311
|
Your account was accessed from a new location.
|
|
313
312
|
|
|
@@ -322,12 +321,12 @@ New Location Details:
|
|
|
322
321
|
If this was you, no action is needed.`,
|
|
323
322
|
},
|
|
324
323
|
vpnProxy: {
|
|
325
|
-
subject: '
|
|
324
|
+
subject: '[WARNING] VPN/Proxy Login Detected',
|
|
326
325
|
html: `
|
|
327
326
|
<html>
|
|
328
327
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
329
328
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fffbeb; border-radius: 10px;">
|
|
330
|
-
<h2 style="color: #d97706;"
|
|
329
|
+
<h2 style="color: #d97706;">[WARNING] VPN/Proxy Detected</h2>
|
|
331
330
|
<p>A login from a VPN or proxy service was detected on your account.</p>
|
|
332
331
|
|
|
333
332
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -352,7 +351,7 @@ If this was you, no action is needed.`,
|
|
|
352
351
|
</div>
|
|
353
352
|
</body>
|
|
354
353
|
</html>`,
|
|
355
|
-
text:
|
|
354
|
+
text: `[WARNING] VPN/Proxy Detected
|
|
356
355
|
|
|
357
356
|
A login from a VPN or proxy service was detected on your account.
|
|
358
357
|
|
|
@@ -518,7 +517,7 @@ const SettingsPage = () => {
|
|
|
518
517
|
};
|
|
519
518
|
|
|
520
519
|
const handleCleanInactive = async () => {
|
|
521
|
-
if (!confirm('
|
|
520
|
+
if (!confirm('[WARNING] This will permanently delete ALL inactive sessions.\n\nContinue?')) {
|
|
522
521
|
return;
|
|
523
522
|
}
|
|
524
523
|
|
|
@@ -645,7 +644,7 @@ const SettingsPage = () => {
|
|
|
645
644
|
|
|
646
645
|
{/* Session Timeout */}
|
|
647
646
|
<Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '16px', display: 'block', color: theme.colors.neutral[700] }}>
|
|
648
|
-
|
|
647
|
+
SESSION TIMEOUT
|
|
649
648
|
</Typography>
|
|
650
649
|
<Grid.Root gap={6} style={{ marginBottom: '32px' }}>
|
|
651
650
|
<Grid.Item col={6} s={12}>
|
|
@@ -786,7 +785,7 @@ const SettingsPage = () => {
|
|
|
786
785
|
<Box padding={6}>
|
|
787
786
|
|
|
788
787
|
<Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '16px', display: 'block', color: theme.colors.neutral[700] }}>
|
|
789
|
-
|
|
788
|
+
SECURITY OPTIONS
|
|
790
789
|
</Typography>
|
|
791
790
|
|
|
792
791
|
{/* Encryption Key Generator */}
|
|
@@ -1119,7 +1118,7 @@ const SettingsPage = () => {
|
|
|
1119
1118
|
onChange={() => handleChange('alertOnSuspiciousLogin', !settings.alertOnSuspiciousLogin)}
|
|
1120
1119
|
>
|
|
1121
1120
|
<Typography variant="omega" fontWeight="semiBold" style={{ fontSize: '14px' }}>
|
|
1122
|
-
|
|
1121
|
+
Suspicious Login
|
|
1123
1122
|
</Typography>
|
|
1124
1123
|
</Checkbox>
|
|
1125
1124
|
</Box>
|
|
@@ -1141,7 +1140,7 @@ const SettingsPage = () => {
|
|
|
1141
1140
|
onChange={() => handleChange('alertOnNewLocation', !settings.alertOnNewLocation)}
|
|
1142
1141
|
>
|
|
1143
1142
|
<Typography variant="omega" fontWeight="semiBold" style={{ fontSize: '14px' }}>
|
|
1144
|
-
|
|
1143
|
+
New Location
|
|
1145
1144
|
</Typography>
|
|
1146
1145
|
</Checkbox>
|
|
1147
1146
|
</Box>
|
|
@@ -1163,7 +1162,7 @@ const SettingsPage = () => {
|
|
|
1163
1162
|
onChange={() => handleChange('alertOnVpnProxy', !settings.alertOnVpnProxy)}
|
|
1164
1163
|
>
|
|
1165
1164
|
<Typography variant="omega" fontWeight="semiBold" style={{ fontSize: '14px' }}>
|
|
1166
|
-
|
|
1165
|
+
VPN/Proxy
|
|
1167
1166
|
</Typography>
|
|
1168
1167
|
</Checkbox>
|
|
1169
1168
|
</Box>
|
|
@@ -1173,7 +1172,7 @@ const SettingsPage = () => {
|
|
|
1173
1172
|
{/* Email Templates */}
|
|
1174
1173
|
<Divider style={{ marginBottom: '24px' }} />
|
|
1175
1174
|
<Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '8px', display: 'block', color: theme.colors.neutral[700] }}>
|
|
1176
|
-
|
|
1175
|
+
EMAIL TEMPLATES
|
|
1177
1176
|
</Typography>
|
|
1178
1177
|
<Typography variant="pi" textColor="neutral600" style={{ marginBottom: '20px', display: 'block', fontSize: '12px' }}>
|
|
1179
1178
|
Customize email notification templates with dynamic variables
|
|
@@ -1182,9 +1181,9 @@ const SettingsPage = () => {
|
|
|
1182
1181
|
{/* Template Tabs */}
|
|
1183
1182
|
<Tabs.Root value={activeTemplateTab} onValueChange={setActiveTemplateTab}>
|
|
1184
1183
|
<Tabs.List aria-label="Email Templates">
|
|
1185
|
-
<Tabs.Trigger value="suspiciousLogin"
|
|
1186
|
-
<Tabs.Trigger value="newLocation"
|
|
1187
|
-
<Tabs.Trigger value="vpnProxy"
|
|
1184
|
+
<Tabs.Trigger value="suspiciousLogin">Suspicious Login</Tabs.Trigger>
|
|
1185
|
+
<Tabs.Trigger value="newLocation">New Location</Tabs.Trigger>
|
|
1186
|
+
<Tabs.Trigger value="vpnProxy">VPN/Proxy</Tabs.Trigger>
|
|
1188
1187
|
</Tabs.List>
|
|
1189
1188
|
|
|
1190
1189
|
{Object.keys(settings.emailTemplates).map((templateKey) => (
|
|
@@ -1193,7 +1192,7 @@ const SettingsPage = () => {
|
|
|
1193
1192
|
{/* Subject */}
|
|
1194
1193
|
<Box style={{ marginBottom: '24px' }}>
|
|
1195
1194
|
<Typography variant="pi" fontWeight="bold" style={{ marginBottom: '8px', display: 'block' }}>
|
|
1196
|
-
|
|
1195
|
+
Email Subject
|
|
1197
1196
|
</Typography>
|
|
1198
1197
|
<TextInput
|
|
1199
1198
|
value={settings.emailTemplates[templateKey].subject}
|
|
@@ -1340,7 +1339,7 @@ const SettingsPage = () => {
|
|
|
1340
1339
|
type: validation.isValid ? 'success' : 'warning',
|
|
1341
1340
|
message: validation.isValid
|
|
1342
1341
|
? `✓ Template valid! Found ${validation.foundVars.length}/${validation.totalAvailable} variables.`
|
|
1343
|
-
: '
|
|
1342
|
+
: '[WARNING] No variables found. Add at least one variable.',
|
|
1344
1343
|
});
|
|
1345
1344
|
}}
|
|
1346
1345
|
>
|
|
@@ -1551,7 +1550,7 @@ const SettingsPage = () => {
|
|
|
1551
1550
|
</Box>
|
|
1552
1551
|
<Flex justifyContent="space-between" alignItems="center" style={{ marginTop: '10px' }}>
|
|
1553
1552
|
<Typography variant="pi" textColor="neutral600" style={{ fontSize: '12px' }}>
|
|
1554
|
-
|
|
1553
|
+
Optional: Post session alerts to your Discord channel
|
|
1555
1554
|
</Typography>
|
|
1556
1555
|
{settings.discordWebhookUrl && (
|
|
1557
1556
|
<Typography variant="pi" textColor="primary600" style={{ fontSize: '11px', fontFamily: 'monospace' }}>
|
|
@@ -1597,7 +1596,7 @@ const SettingsPage = () => {
|
|
|
1597
1596
|
</Box>
|
|
1598
1597
|
<Flex justifyContent="space-between" alignItems="center" style={{ marginTop: '10px' }}>
|
|
1599
1598
|
<Typography variant="pi" textColor="neutral600" style={{ fontSize: '12px' }}>
|
|
1600
|
-
|
|
1599
|
+
Optional: Post session alerts to your Slack workspace
|
|
1601
1600
|
</Typography>
|
|
1602
1601
|
{settings.slackWebhookUrl && (
|
|
1603
1602
|
<Typography variant="pi" textColor="primary600" style={{ fontSize: '11px', fontFamily: 'monospace' }}>
|
|
@@ -1623,7 +1622,7 @@ const SettingsPage = () => {
|
|
|
1623
1622
|
<Check style={{ width: '20px', height: '20px', color: theme.colors.success[600], flexShrink: 0, marginTop: '2px' }} />
|
|
1624
1623
|
<Box style={{ flex: 1 }}>
|
|
1625
1624
|
<Typography variant="omega" fontWeight="bold" style={{ marginBottom: '8px', display: 'block', color: theme.colors.primary[700] }}>
|
|
1626
|
-
|
|
1625
|
+
Database-Backed Settings
|
|
1627
1626
|
</Typography>
|
|
1628
1627
|
<Typography variant="pi" textColor="primary700" style={{ fontSize: '13px', lineHeight: '1.8' }}>
|
|
1629
1628
|
All settings are stored in your Strapi database and shared across all admin users.
|