strapi-plugin-magic-sessionmanager 3.1.0 → 3.2.0
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 +121 -1
- package/admin/src/pages/Settings.jsx +146 -0
- package/dist/_chunks/{Analytics-Bp1cPJx1.mjs → Analytics-CwyLwdOZ.mjs} +2 -2
- package/dist/_chunks/{Analytics-CjqdGXnQ.js → Analytics-DRzCKaDF.js} +2 -2
- package/dist/_chunks/{App-DONYhluL.mjs → App-Zhs_vt59.mjs} +2 -2
- package/dist/_chunks/{App-DtfZMVae.js → App-nGu2Eb87.js} +2 -2
- package/dist/_chunks/{License-lcVK7rtT.mjs → License-CPI0p_W8.mjs} +1 -1
- package/dist/_chunks/{License-IWH6ClOx.js → License-k5vvhgKr.js} +1 -1
- package/dist/_chunks/{Settings-JaiqQ_7r.mjs → Settings-CL2im8M3.mjs} +154 -6
- package/dist/_chunks/{Settings-JixgQiB_.js → Settings-Lkmxisuv.js} +152 -4
- package/dist/_chunks/{index--JzOiQNw.mjs → index-B-0VPfeF.mjs} +4 -4
- package/dist/_chunks/{index-DqtQaEBL.js → index-W_QbTAYU.js} +4 -4
- package/dist/_chunks/{useLicense-DA-averf.js → useLicense-C_Rneohy.js} +1 -1
- package/dist/_chunks/{useLicense-W1cxUaca.mjs → useLicense-DUGjNbQ9.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,11 +101,33 @@ npm run build
|
|
|
101
101
|
npm run develop
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
### 4.
|
|
104
|
+
### 4. Configure Encryption (Important!) 🔐
|
|
105
|
+
|
|
106
|
+
Generate a secure encryption key for JWT token storage:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Option 1: Use Admin Panel
|
|
110
|
+
# Go to Admin → Sessions → Settings → Security Settings
|
|
111
|
+
# Click "Generate Key" and copy to .env
|
|
112
|
+
|
|
113
|
+
# Option 2: Generate manually
|
|
114
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
115
|
+
|
|
116
|
+
# Add to .env file:
|
|
117
|
+
SESSION_ENCRYPTION_KEY=your-generated-32-char-key-here
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Why this is important:**
|
|
121
|
+
- JWT tokens are encrypted before storing in database
|
|
122
|
+
- Prevents token exposure if database is compromised
|
|
123
|
+
- Uses AES-256-GCM encryption standard
|
|
124
|
+
|
|
125
|
+
### 5. Access Admin Dashboard
|
|
105
126
|
|
|
106
127
|
- Navigate to Strapi Admin: `http://localhost:1337/admin`
|
|
107
128
|
- Find **Sessions** in the left sidebar under plugins
|
|
108
129
|
- Start with the **License** tab to activate your license
|
|
130
|
+
- Go to **Settings → Security** to generate your encryption key
|
|
109
131
|
|
|
110
132
|
---
|
|
111
133
|
|
|
@@ -699,6 +721,104 @@ Available through Admin UI **Settings → Sessions → Settings**:
|
|
|
699
721
|
|
|
700
722
|
---
|
|
701
723
|
|
|
724
|
+
## 🔐 JWT Token Security
|
|
725
|
+
|
|
726
|
+
### Encryption
|
|
727
|
+
|
|
728
|
+
All JWT tokens are **encrypted before storing** in the database using **AES-256-GCM** encryption.
|
|
729
|
+
|
|
730
|
+
#### Why Encrypt Tokens?
|
|
731
|
+
|
|
732
|
+
```
|
|
733
|
+
❌ Without Encryption:
|
|
734
|
+
Database compromised → Attacker sees JWTs → Can impersonate users!
|
|
735
|
+
|
|
736
|
+
✅ With Encryption:
|
|
737
|
+
Database compromised → Attacker sees encrypted data → Useless without key!
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
#### How It Works
|
|
741
|
+
|
|
742
|
+
```
|
|
743
|
+
Login: User gets JWT
|
|
744
|
+
↓
|
|
745
|
+
JWT: "eyJhbGciOiJIUzI1NiIs..."
|
|
746
|
+
↓
|
|
747
|
+
[Encrypt with AES-256-GCM]
|
|
748
|
+
↓
|
|
749
|
+
Encrypted: "a3f7b2c1:8c4d9e2a:f2a5b8c3d4e5f6a7..."
|
|
750
|
+
↓
|
|
751
|
+
Stored in Database (secure!)
|
|
752
|
+
|
|
753
|
+
Logout: User sends JWT
|
|
754
|
+
↓
|
|
755
|
+
[Fetch all active sessions from DB]
|
|
756
|
+
↓
|
|
757
|
+
[Decrypt each token]
|
|
758
|
+
↓
|
|
759
|
+
[Compare with user's JWT]
|
|
760
|
+
↓
|
|
761
|
+
Match found → Terminate session ✅
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
#### Configuration
|
|
765
|
+
|
|
766
|
+
**Generate Encryption Key (Admin Panel):**
|
|
767
|
+
|
|
768
|
+
1. Go to **Admin → Sessions → Settings**
|
|
769
|
+
2. Open **Security Settings** accordion
|
|
770
|
+
3. Find **JWT Encryption Key Generator**
|
|
771
|
+
4. Click **"Generate Key"**
|
|
772
|
+
5. Copy key with **"Copy for .env"** button
|
|
773
|
+
6. Add to your `.env` file
|
|
774
|
+
|
|
775
|
+
**Or generate manually:**
|
|
776
|
+
|
|
777
|
+
```bash
|
|
778
|
+
# Generate secure 32-byte key
|
|
779
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
780
|
+
|
|
781
|
+
# Add to .env
|
|
782
|
+
SESSION_ENCRYPTION_KEY=aBc123XyZ...your-32-char-key
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Fallback Behavior:**
|
|
786
|
+
|
|
787
|
+
If `SESSION_ENCRYPTION_KEY` is not set:
|
|
788
|
+
- Plugin uses `APP_KEYS` or `API_TOKEN_SALT` as fallback
|
|
789
|
+
- ⚠️ Warning logged on startup
|
|
790
|
+
- Still encrypted, but key is derived from Strapi's keys
|
|
791
|
+
|
|
792
|
+
**Production Recommendation:**
|
|
793
|
+
Always use a dedicated `SESSION_ENCRYPTION_KEY` for better security isolation.
|
|
794
|
+
|
|
795
|
+
#### Security Details
|
|
796
|
+
|
|
797
|
+
| Feature | Value |
|
|
798
|
+
|---------|-------|
|
|
799
|
+
| Algorithm | AES-256-GCM |
|
|
800
|
+
| Key Size | 256 bits (32 bytes) |
|
|
801
|
+
| IV Length | 128 bits (16 bytes) |
|
|
802
|
+
| Auth Tag | 128 bits (16 bytes) |
|
|
803
|
+
| Format | `iv:authTag:encryptedData` (hex) |
|
|
804
|
+
|
|
805
|
+
### Unique Session IDs
|
|
806
|
+
|
|
807
|
+
Each session gets a cryptographically unique identifier:
|
|
808
|
+
|
|
809
|
+
```javascript
|
|
810
|
+
sessionId: "sess_lx3k7_4f2a8b3c_a1b2c3d4e5f6"
|
|
811
|
+
// prefix^ ^timestamp ^user-hash ^random-bytes
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
**Benefits:**
|
|
815
|
+
- ✅ No collisions across sessions
|
|
816
|
+
- ✅ Traceable session identifiers
|
|
817
|
+
- ✅ Independent from database IDs
|
|
818
|
+
- ✅ URL-safe for future features
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
702
822
|
## 🔒 Premium Features
|
|
703
823
|
|
|
704
824
|
### IP Geolocation & Threat Detection
|
|
@@ -366,6 +366,20 @@ Login Details:
|
|
|
366
366
|
},
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
+
// ================ HELPER FUNCTIONS ================
|
|
370
|
+
const generateSecureKey = () => {
|
|
371
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
372
|
+
let key = '';
|
|
373
|
+
const array = new Uint8Array(32);
|
|
374
|
+
crypto.getRandomValues(array);
|
|
375
|
+
|
|
376
|
+
for (let i = 0; i < 32; i++) {
|
|
377
|
+
key += chars[array[i] % chars.length];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return key;
|
|
381
|
+
};
|
|
382
|
+
|
|
369
383
|
const SettingsPage = () => {
|
|
370
384
|
const { get, post, put } = useFetchClient();
|
|
371
385
|
const { toggleNotification } = useNotification();
|
|
@@ -375,6 +389,8 @@ const SettingsPage = () => {
|
|
|
375
389
|
const [hasChanges, setHasChanges] = useState(false);
|
|
376
390
|
const [cleaning, setCleaning] = useState(false);
|
|
377
391
|
const [activeTemplateTab, setActiveTemplateTab] = useState('suspiciousLogin');
|
|
392
|
+
const [encryptionKey, setEncryptionKey] = useState('');
|
|
393
|
+
const [showEncryptionKey, setShowEncryptionKey] = useState(false);
|
|
378
394
|
|
|
379
395
|
const [settings, setSettings] = useState({
|
|
380
396
|
inactivityTimeout: 15,
|
|
@@ -772,6 +788,136 @@ const SettingsPage = () => {
|
|
|
772
788
|
<Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '16px', display: 'block', color: theme.colors.neutral[700] }}>
|
|
773
789
|
🔒 SECURITY OPTIONS
|
|
774
790
|
</Typography>
|
|
791
|
+
|
|
792
|
+
{/* Encryption Key Generator */}
|
|
793
|
+
<Box
|
|
794
|
+
background="neutral0"
|
|
795
|
+
padding={6}
|
|
796
|
+
style={{
|
|
797
|
+
borderRadius: theme.borderRadius.lg,
|
|
798
|
+
marginBottom: '32px',
|
|
799
|
+
border: `2px solid ${theme.colors.primary[100]}`,
|
|
800
|
+
background: `linear-gradient(135deg, ${theme.colors.neutral[0]} 0%, ${theme.colors.primary[50]} 100%)`
|
|
801
|
+
}}
|
|
802
|
+
>
|
|
803
|
+
<Flex direction="column" gap={4}>
|
|
804
|
+
<Flex alignItems="center" gap={3}>
|
|
805
|
+
<Shield style={{ width: 24, height: 24, color: theme.colors.primary[600] }} />
|
|
806
|
+
<Typography variant="delta" fontWeight="bold">
|
|
807
|
+
JWT Encryption Key Generator
|
|
808
|
+
</Typography>
|
|
809
|
+
</Flex>
|
|
810
|
+
|
|
811
|
+
<Typography variant="omega" textColor="neutral600" style={{ lineHeight: 1.6 }}>
|
|
812
|
+
Generate a secure 32-character encryption key for JWT token storage.
|
|
813
|
+
This key is used to encrypt tokens before saving them to the database.
|
|
814
|
+
</Typography>
|
|
815
|
+
|
|
816
|
+
<Alert
|
|
817
|
+
variant="default"
|
|
818
|
+
title="Important"
|
|
819
|
+
style={{ marginTop: 8 }}
|
|
820
|
+
>
|
|
821
|
+
Add this key to your <code>.env</code> file as <strong>SESSION_ENCRYPTION_KEY</strong> for production.
|
|
822
|
+
</Alert>
|
|
823
|
+
|
|
824
|
+
<Flex gap={3} alignItems="flex-end">
|
|
825
|
+
<Box style={{ flex: 1 }}>
|
|
826
|
+
<TextInput
|
|
827
|
+
label="Generated Encryption Key"
|
|
828
|
+
value={encryptionKey}
|
|
829
|
+
onChange={(e) => setEncryptionKey(e.target.value)}
|
|
830
|
+
placeholder="Click 'Generate Key' to create a secure key"
|
|
831
|
+
type={showEncryptionKey ? 'text' : 'password'}
|
|
832
|
+
/>
|
|
833
|
+
</Box>
|
|
834
|
+
<Button
|
|
835
|
+
variant="secondary"
|
|
836
|
+
onClick={() => setShowEncryptionKey(!showEncryptionKey)}
|
|
837
|
+
size="L"
|
|
838
|
+
>
|
|
839
|
+
{showEncryptionKey ? 'Hide' : 'Show'}
|
|
840
|
+
</Button>
|
|
841
|
+
</Flex>
|
|
842
|
+
|
|
843
|
+
<Flex gap={3}>
|
|
844
|
+
<Button
|
|
845
|
+
variant="default"
|
|
846
|
+
startIcon={<Code />}
|
|
847
|
+
onClick={() => {
|
|
848
|
+
const key = generateSecureKey();
|
|
849
|
+
setEncryptionKey(key);
|
|
850
|
+
setShowEncryptionKey(true);
|
|
851
|
+
toggleNotification({
|
|
852
|
+
type: 'success',
|
|
853
|
+
message: '32-character encryption key generated!'
|
|
854
|
+
});
|
|
855
|
+
}}
|
|
856
|
+
size="L"
|
|
857
|
+
>
|
|
858
|
+
Generate Key
|
|
859
|
+
</Button>
|
|
860
|
+
|
|
861
|
+
<Button
|
|
862
|
+
variant="tertiary"
|
|
863
|
+
startIcon={<Duplicate />}
|
|
864
|
+
onClick={() => {
|
|
865
|
+
if (encryptionKey) {
|
|
866
|
+
navigator.clipboard.writeText(encryptionKey);
|
|
867
|
+
toggleNotification({
|
|
868
|
+
type: 'success',
|
|
869
|
+
message: 'Encryption key copied to clipboard!'
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}}
|
|
873
|
+
disabled={!encryptionKey}
|
|
874
|
+
size="L"
|
|
875
|
+
>
|
|
876
|
+
Copy to Clipboard
|
|
877
|
+
</Button>
|
|
878
|
+
|
|
879
|
+
<Button
|
|
880
|
+
variant="tertiary"
|
|
881
|
+
startIcon={<Duplicate />}
|
|
882
|
+
onClick={() => {
|
|
883
|
+
if (encryptionKey) {
|
|
884
|
+
const envLine = `SESSION_ENCRYPTION_KEY=${encryptionKey}`;
|
|
885
|
+
navigator.clipboard.writeText(envLine);
|
|
886
|
+
toggleNotification({
|
|
887
|
+
type: 'success',
|
|
888
|
+
message: 'Copied as .env format!'
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
}}
|
|
892
|
+
disabled={!encryptionKey}
|
|
893
|
+
size="L"
|
|
894
|
+
>
|
|
895
|
+
Copy for .env
|
|
896
|
+
</Button>
|
|
897
|
+
</Flex>
|
|
898
|
+
|
|
899
|
+
{encryptionKey && (
|
|
900
|
+
<Box
|
|
901
|
+
padding={4}
|
|
902
|
+
background="neutral100"
|
|
903
|
+
style={{
|
|
904
|
+
borderRadius: theme.borderRadius.md,
|
|
905
|
+
border: '1px solid ' + theme.colors.neutral[200],
|
|
906
|
+
fontFamily: 'monospace',
|
|
907
|
+
fontSize: '12px',
|
|
908
|
+
wordBreak: 'break-all'
|
|
909
|
+
}}
|
|
910
|
+
>
|
|
911
|
+
<Typography variant="omega" fontWeight="bold" style={{ marginBottom: 8, display: 'block' }}>
|
|
912
|
+
Add to .env file:
|
|
913
|
+
</Typography>
|
|
914
|
+
<code style={{ color: theme.colors.primary[700] }}>
|
|
915
|
+
SESSION_ENCRYPTION_KEY={encryptionKey}
|
|
916
|
+
</code>
|
|
917
|
+
</Box>
|
|
918
|
+
)}
|
|
919
|
+
</Flex>
|
|
920
|
+
</Box>
|
|
775
921
|
|
|
776
922
|
{/* Feature Toggles */}
|
|
777
923
|
<Box background="neutral100" padding={5} style={{ borderRadius: theme.borderRadius.md, marginBottom: '32px' }}>
|
|
@@ -4,8 +4,8 @@ import { useFetchClient } from "@strapi/strapi/admin";
|
|
|
4
4
|
import styled, { css, keyframes } from "styled-components";
|
|
5
5
|
import { Loader, Typography, Box, Flex, Badge } from "@strapi/design-system";
|
|
6
6
|
import { ChartBubble, Crown, User, Clock, Monitor } from "@strapi/icons";
|
|
7
|
-
import { a as pluginId } from "./index
|
|
8
|
-
import { u as useLicense } from "./useLicense-
|
|
7
|
+
import { a as pluginId } from "./index-B-0VPfeF.mjs";
|
|
8
|
+
import { u as useLicense } from "./useLicense-DUGjNbQ9.mjs";
|
|
9
9
|
const theme = {
|
|
10
10
|
colors: {
|
|
11
11
|
primary: { 100: "#E0F2FE", 500: "#0EA5E9", 600: "#0284C7" },
|
|
@@ -6,8 +6,8 @@ const admin = require("@strapi/strapi/admin");
|
|
|
6
6
|
const styled = require("styled-components");
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
|
-
const index = require("./index-
|
|
10
|
-
const useLicense = require("./useLicense-
|
|
9
|
+
const index = require("./index-W_QbTAYU.js");
|
|
10
|
+
const useLicense = require("./useLicense-C_Rneohy.js");
|
|
11
11
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
12
12
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
13
13
|
const theme = {
|
|
@@ -4,8 +4,8 @@ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
|
4
4
|
import styled, { css, keyframes } from "styled-components";
|
|
5
5
|
import { Modal, Flex, Box, Typography, Badge, Divider, Button, Loader, SingleSelect, SingleSelectOption, Thead, Tr, Th, Tbody, Td, Table, TextInput } from "@strapi/design-system";
|
|
6
6
|
import { Check, Information, Monitor, Server, Clock, Cross, Earth, Shield, Crown, Phone, Download, User, Eye, Trash, Search, Key } from "@strapi/icons";
|
|
7
|
-
import { p as parseUserAgent, a as pluginId } from "./index
|
|
8
|
-
import { u as useLicense } from "./useLicense-
|
|
7
|
+
import { p as parseUserAgent, a as pluginId } from "./index-B-0VPfeF.mjs";
|
|
8
|
+
import { u as useLicense } from "./useLicense-DUGjNbQ9.mjs";
|
|
9
9
|
import { useNavigate } from "react-router-dom";
|
|
10
10
|
const TwoColumnGrid = styled.div`
|
|
11
11
|
display: grid;
|
|
@@ -6,8 +6,8 @@ const admin = require("@strapi/strapi/admin");
|
|
|
6
6
|
const styled = require("styled-components");
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
|
-
const index = require("./index-
|
|
10
|
-
const useLicense = require("./useLicense-
|
|
9
|
+
const index = require("./index-W_QbTAYU.js");
|
|
10
|
+
const useLicense = require("./useLicense-C_Rneohy.js");
|
|
11
11
|
const reactRouterDom = require("react-router-dom");
|
|
12
12
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
13
13
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
@@ -4,7 +4,7 @@ import { Loader, Box, Alert, Flex, Typography, Button, Badge, Accordion } from "
|
|
|
4
4
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
5
5
|
import { ArrowClockwise, Duplicate, Download, User, Shield, Sparkle, ChartBubble } from "@strapi/icons";
|
|
6
6
|
import styled, { css, keyframes } from "styled-components";
|
|
7
|
-
import { a as pluginId } from "./index
|
|
7
|
+
import { a as pluginId } from "./index-B-0VPfeF.mjs";
|
|
8
8
|
const theme = {
|
|
9
9
|
colors: {
|
|
10
10
|
neutral: { 200: "#E5E7EB" }
|
|
@@ -6,7 +6,7 @@ const designSystem = require("@strapi/design-system");
|
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-W_QbTAYU.js");
|
|
10
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
11
11
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
12
12
|
const theme = {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from "react";
|
|
3
|
-
import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSelect, SingleSelectOption, Divider, Toggle, NumberInput, Checkbox, Tabs
|
|
3
|
+
import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSelect, SingleSelectOption, Divider, Alert, TextInput, Toggle, NumberInput, Checkbox, Tabs } from "@strapi/design-system";
|
|
4
4
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
5
|
-
import { Check, Information, Cog, Trash, Shield,
|
|
5
|
+
import { Check, Information, Cog, Trash, Shield, Code, Duplicate, Mail } from "@strapi/icons";
|
|
6
6
|
import styled, { css, keyframes } from "styled-components";
|
|
7
|
-
import { a as pluginId } from "./index
|
|
8
|
-
import { u as useLicense } from "./useLicense-
|
|
7
|
+
import { a as pluginId } from "./index-B-0VPfeF.mjs";
|
|
8
|
+
import { u as useLicense } from "./useLicense-DUGjNbQ9.mjs";
|
|
9
9
|
const theme = {
|
|
10
10
|
colors: {
|
|
11
|
-
primary: { 600: "#0284C7", 700: "#075985" },
|
|
11
|
+
primary: { 600: "#0284C7", 700: "#075985", 100: "#E0F2FE", 50: "#F0F9FF" },
|
|
12
12
|
success: { 600: "#16A34A", 700: "#15803D" },
|
|
13
13
|
danger: { 600: "#DC2626" },
|
|
14
|
-
neutral: { 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
|
|
14
|
+
neutral: { 0: "#FFFFFF", 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
|
|
15
15
|
},
|
|
16
16
|
shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
|
|
17
17
|
borderRadius: { md: "8px", lg: "12px" }
|
|
@@ -322,6 +322,16 @@ Login Details:
|
|
|
322
322
|
- VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`
|
|
323
323
|
}
|
|
324
324
|
});
|
|
325
|
+
const generateSecureKey = () => {
|
|
326
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?";
|
|
327
|
+
let key = "";
|
|
328
|
+
const array = new Uint8Array(32);
|
|
329
|
+
crypto.getRandomValues(array);
|
|
330
|
+
for (let i = 0; i < 32; i++) {
|
|
331
|
+
key += chars[array[i] % chars.length];
|
|
332
|
+
}
|
|
333
|
+
return key;
|
|
334
|
+
};
|
|
325
335
|
const SettingsPage = () => {
|
|
326
336
|
const { get, post, put } = useFetchClient();
|
|
327
337
|
const { toggleNotification } = useNotification();
|
|
@@ -331,6 +341,8 @@ const SettingsPage = () => {
|
|
|
331
341
|
const [hasChanges, setHasChanges] = useState(false);
|
|
332
342
|
const [cleaning, setCleaning] = useState(false);
|
|
333
343
|
const [activeTemplateTab, setActiveTemplateTab] = useState("suspiciousLogin");
|
|
344
|
+
const [encryptionKey, setEncryptionKey] = useState("");
|
|
345
|
+
const [showEncryptionKey, setShowEncryptionKey] = useState(false);
|
|
334
346
|
const [settings, setSettings] = useState({
|
|
335
347
|
inactivityTimeout: 15,
|
|
336
348
|
cleanupInterval: 30,
|
|
@@ -658,6 +670,142 @@ const SettingsPage = () => {
|
|
|
658
670
|
) }),
|
|
659
671
|
/* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
|
|
660
672
|
/* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🔒 SECURITY OPTIONS" }),
|
|
673
|
+
/* @__PURE__ */ jsx(
|
|
674
|
+
Box,
|
|
675
|
+
{
|
|
676
|
+
background: "neutral0",
|
|
677
|
+
padding: 6,
|
|
678
|
+
style: {
|
|
679
|
+
borderRadius: theme.borderRadius.lg,
|
|
680
|
+
marginBottom: "32px",
|
|
681
|
+
border: `2px solid ${theme.colors.primary[100]}`,
|
|
682
|
+
background: `linear-gradient(135deg, ${theme.colors.neutral[0]} 0%, ${theme.colors.primary[50]} 100%)`
|
|
683
|
+
},
|
|
684
|
+
children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
685
|
+
/* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 3, children: [
|
|
686
|
+
/* @__PURE__ */ jsx(Shield, { style: { width: 24, height: 24, color: theme.colors.primary[600] } }),
|
|
687
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "JWT Encryption Key Generator" })
|
|
688
|
+
] }),
|
|
689
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", style: { lineHeight: 1.6 }, children: "Generate a secure 32-character encryption key for JWT token storage. This key is used to encrypt tokens before saving them to the database." }),
|
|
690
|
+
/* @__PURE__ */ jsxs(
|
|
691
|
+
Alert,
|
|
692
|
+
{
|
|
693
|
+
variant: "default",
|
|
694
|
+
title: "Important",
|
|
695
|
+
style: { marginTop: 8 },
|
|
696
|
+
children: [
|
|
697
|
+
"Add this key to your ",
|
|
698
|
+
/* @__PURE__ */ jsx("code", { children: ".env" }),
|
|
699
|
+
" file as ",
|
|
700
|
+
/* @__PURE__ */ jsx("strong", { children: "SESSION_ENCRYPTION_KEY" }),
|
|
701
|
+
" for production."
|
|
702
|
+
]
|
|
703
|
+
}
|
|
704
|
+
),
|
|
705
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", children: [
|
|
706
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
|
|
707
|
+
TextInput,
|
|
708
|
+
{
|
|
709
|
+
label: "Generated Encryption Key",
|
|
710
|
+
value: encryptionKey,
|
|
711
|
+
onChange: (e) => setEncryptionKey(e.target.value),
|
|
712
|
+
placeholder: "Click 'Generate Key' to create a secure key",
|
|
713
|
+
type: showEncryptionKey ? "text" : "password"
|
|
714
|
+
}
|
|
715
|
+
) }),
|
|
716
|
+
/* @__PURE__ */ jsx(
|
|
717
|
+
Button,
|
|
718
|
+
{
|
|
719
|
+
variant: "secondary",
|
|
720
|
+
onClick: () => setShowEncryptionKey(!showEncryptionKey),
|
|
721
|
+
size: "L",
|
|
722
|
+
children: showEncryptionKey ? "Hide" : "Show"
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
] }),
|
|
726
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
|
|
727
|
+
/* @__PURE__ */ jsx(
|
|
728
|
+
Button,
|
|
729
|
+
{
|
|
730
|
+
variant: "default",
|
|
731
|
+
startIcon: /* @__PURE__ */ jsx(Code, {}),
|
|
732
|
+
onClick: () => {
|
|
733
|
+
const key = generateSecureKey();
|
|
734
|
+
setEncryptionKey(key);
|
|
735
|
+
setShowEncryptionKey(true);
|
|
736
|
+
toggleNotification({
|
|
737
|
+
type: "success",
|
|
738
|
+
message: "32-character encryption key generated!"
|
|
739
|
+
});
|
|
740
|
+
},
|
|
741
|
+
size: "L",
|
|
742
|
+
children: "Generate Key"
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
/* @__PURE__ */ jsx(
|
|
746
|
+
Button,
|
|
747
|
+
{
|
|
748
|
+
variant: "tertiary",
|
|
749
|
+
startIcon: /* @__PURE__ */ jsx(Duplicate, {}),
|
|
750
|
+
onClick: () => {
|
|
751
|
+
if (encryptionKey) {
|
|
752
|
+
navigator.clipboard.writeText(encryptionKey);
|
|
753
|
+
toggleNotification({
|
|
754
|
+
type: "success",
|
|
755
|
+
message: "Encryption key copied to clipboard!"
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
disabled: !encryptionKey,
|
|
760
|
+
size: "L",
|
|
761
|
+
children: "Copy to Clipboard"
|
|
762
|
+
}
|
|
763
|
+
),
|
|
764
|
+
/* @__PURE__ */ jsx(
|
|
765
|
+
Button,
|
|
766
|
+
{
|
|
767
|
+
variant: "tertiary",
|
|
768
|
+
startIcon: /* @__PURE__ */ jsx(Duplicate, {}),
|
|
769
|
+
onClick: () => {
|
|
770
|
+
if (encryptionKey) {
|
|
771
|
+
const envLine = `SESSION_ENCRYPTION_KEY=${encryptionKey}`;
|
|
772
|
+
navigator.clipboard.writeText(envLine);
|
|
773
|
+
toggleNotification({
|
|
774
|
+
type: "success",
|
|
775
|
+
message: "Copied as .env format!"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
disabled: !encryptionKey,
|
|
780
|
+
size: "L",
|
|
781
|
+
children: "Copy for .env"
|
|
782
|
+
}
|
|
783
|
+
)
|
|
784
|
+
] }),
|
|
785
|
+
encryptionKey && /* @__PURE__ */ jsxs(
|
|
786
|
+
Box,
|
|
787
|
+
{
|
|
788
|
+
padding: 4,
|
|
789
|
+
background: "neutral100",
|
|
790
|
+
style: {
|
|
791
|
+
borderRadius: theme.borderRadius.md,
|
|
792
|
+
border: "1px solid " + theme.colors.neutral[200],
|
|
793
|
+
fontFamily: "monospace",
|
|
794
|
+
fontSize: "12px",
|
|
795
|
+
wordBreak: "break-all"
|
|
796
|
+
},
|
|
797
|
+
children: [
|
|
798
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: "Add to .env file:" }),
|
|
799
|
+
/* @__PURE__ */ jsxs("code", { style: { color: theme.colors.primary[700] }, children: [
|
|
800
|
+
"SESSION_ENCRYPTION_KEY=",
|
|
801
|
+
encryptionKey
|
|
802
|
+
] })
|
|
803
|
+
]
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
] })
|
|
807
|
+
}
|
|
808
|
+
),
|
|
661
809
|
/* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
|
|
662
810
|
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsx(
|
|
663
811
|
ToggleCard,
|
|
@@ -6,16 +6,16 @@ const designSystem = require("@strapi/design-system");
|
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
10
|
-
const useLicense = require("./useLicense-
|
|
9
|
+
const index = require("./index-W_QbTAYU.js");
|
|
10
|
+
const useLicense = require("./useLicense-C_Rneohy.js");
|
|
11
11
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
12
12
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
13
13
|
const theme = {
|
|
14
14
|
colors: {
|
|
15
|
-
primary: { 600: "#0284C7", 700: "#075985" },
|
|
15
|
+
primary: { 600: "#0284C7", 700: "#075985", 100: "#E0F2FE", 50: "#F0F9FF" },
|
|
16
16
|
success: { 600: "#16A34A", 700: "#15803D" },
|
|
17
17
|
danger: { 600: "#DC2626" },
|
|
18
|
-
neutral: { 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
|
|
18
|
+
neutral: { 0: "#FFFFFF", 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
|
|
19
19
|
},
|
|
20
20
|
shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
|
|
21
21
|
borderRadius: { md: "8px", lg: "12px" }
|
|
@@ -326,6 +326,16 @@ Login Details:
|
|
|
326
326
|
- VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`
|
|
327
327
|
}
|
|
328
328
|
});
|
|
329
|
+
const generateSecureKey = () => {
|
|
330
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?";
|
|
331
|
+
let key = "";
|
|
332
|
+
const array = new Uint8Array(32);
|
|
333
|
+
crypto.getRandomValues(array);
|
|
334
|
+
for (let i = 0; i < 32; i++) {
|
|
335
|
+
key += chars[array[i] % chars.length];
|
|
336
|
+
}
|
|
337
|
+
return key;
|
|
338
|
+
};
|
|
329
339
|
const SettingsPage = () => {
|
|
330
340
|
const { get, post, put } = admin.useFetchClient();
|
|
331
341
|
const { toggleNotification } = admin.useNotification();
|
|
@@ -335,6 +345,8 @@ const SettingsPage = () => {
|
|
|
335
345
|
const [hasChanges, setHasChanges] = react.useState(false);
|
|
336
346
|
const [cleaning, setCleaning] = react.useState(false);
|
|
337
347
|
const [activeTemplateTab, setActiveTemplateTab] = react.useState("suspiciousLogin");
|
|
348
|
+
const [encryptionKey, setEncryptionKey] = react.useState("");
|
|
349
|
+
const [showEncryptionKey, setShowEncryptionKey] = react.useState(false);
|
|
338
350
|
const [settings, setSettings] = react.useState({
|
|
339
351
|
inactivityTimeout: 15,
|
|
340
352
|
cleanupInterval: 30,
|
|
@@ -662,6 +674,142 @@ const SettingsPage = () => {
|
|
|
662
674
|
) }),
|
|
663
675
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
|
|
664
676
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🔒 SECURITY OPTIONS" }),
|
|
677
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
678
|
+
designSystem.Box,
|
|
679
|
+
{
|
|
680
|
+
background: "neutral0",
|
|
681
|
+
padding: 6,
|
|
682
|
+
style: {
|
|
683
|
+
borderRadius: theme.borderRadius.lg,
|
|
684
|
+
marginBottom: "32px",
|
|
685
|
+
border: `2px solid ${theme.colors.primary[100]}`,
|
|
686
|
+
background: `linear-gradient(135deg, ${theme.colors.neutral[0]} 0%, ${theme.colors.primary[50]} 100%)`
|
|
687
|
+
},
|
|
688
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 4, children: [
|
|
689
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 3, children: [
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Shield, { style: { width: 24, height: 24, color: theme.colors.primary[600] } }),
|
|
691
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "JWT Encryption Key Generator" })
|
|
692
|
+
] }),
|
|
693
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: { lineHeight: 1.6 }, children: "Generate a secure 32-character encryption key for JWT token storage. This key is used to encrypt tokens before saving them to the database." }),
|
|
694
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
695
|
+
designSystem.Alert,
|
|
696
|
+
{
|
|
697
|
+
variant: "default",
|
|
698
|
+
title: "Important",
|
|
699
|
+
style: { marginTop: 8 },
|
|
700
|
+
children: [
|
|
701
|
+
"Add this key to your ",
|
|
702
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: ".env" }),
|
|
703
|
+
" file as ",
|
|
704
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "SESSION_ENCRYPTION_KEY" }),
|
|
705
|
+
" for production."
|
|
706
|
+
]
|
|
707
|
+
}
|
|
708
|
+
),
|
|
709
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", children: [
|
|
710
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
711
|
+
designSystem.TextInput,
|
|
712
|
+
{
|
|
713
|
+
label: "Generated Encryption Key",
|
|
714
|
+
value: encryptionKey,
|
|
715
|
+
onChange: (e) => setEncryptionKey(e.target.value),
|
|
716
|
+
placeholder: "Click 'Generate Key' to create a secure key",
|
|
717
|
+
type: showEncryptionKey ? "text" : "password"
|
|
718
|
+
}
|
|
719
|
+
) }),
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
721
|
+
designSystem.Button,
|
|
722
|
+
{
|
|
723
|
+
variant: "secondary",
|
|
724
|
+
onClick: () => setShowEncryptionKey(!showEncryptionKey),
|
|
725
|
+
size: "L",
|
|
726
|
+
children: showEncryptionKey ? "Hide" : "Show"
|
|
727
|
+
}
|
|
728
|
+
)
|
|
729
|
+
] }),
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, children: [
|
|
731
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
732
|
+
designSystem.Button,
|
|
733
|
+
{
|
|
734
|
+
variant: "default",
|
|
735
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Code, {}),
|
|
736
|
+
onClick: () => {
|
|
737
|
+
const key = generateSecureKey();
|
|
738
|
+
setEncryptionKey(key);
|
|
739
|
+
setShowEncryptionKey(true);
|
|
740
|
+
toggleNotification({
|
|
741
|
+
type: "success",
|
|
742
|
+
message: "32-character encryption key generated!"
|
|
743
|
+
});
|
|
744
|
+
},
|
|
745
|
+
size: "L",
|
|
746
|
+
children: "Generate Key"
|
|
747
|
+
}
|
|
748
|
+
),
|
|
749
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
750
|
+
designSystem.Button,
|
|
751
|
+
{
|
|
752
|
+
variant: "tertiary",
|
|
753
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {}),
|
|
754
|
+
onClick: () => {
|
|
755
|
+
if (encryptionKey) {
|
|
756
|
+
navigator.clipboard.writeText(encryptionKey);
|
|
757
|
+
toggleNotification({
|
|
758
|
+
type: "success",
|
|
759
|
+
message: "Encryption key copied to clipboard!"
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
disabled: !encryptionKey,
|
|
764
|
+
size: "L",
|
|
765
|
+
children: "Copy to Clipboard"
|
|
766
|
+
}
|
|
767
|
+
),
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
769
|
+
designSystem.Button,
|
|
770
|
+
{
|
|
771
|
+
variant: "tertiary",
|
|
772
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {}),
|
|
773
|
+
onClick: () => {
|
|
774
|
+
if (encryptionKey) {
|
|
775
|
+
const envLine = `SESSION_ENCRYPTION_KEY=${encryptionKey}`;
|
|
776
|
+
navigator.clipboard.writeText(envLine);
|
|
777
|
+
toggleNotification({
|
|
778
|
+
type: "success",
|
|
779
|
+
message: "Copied as .env format!"
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
},
|
|
783
|
+
disabled: !encryptionKey,
|
|
784
|
+
size: "L",
|
|
785
|
+
children: "Copy for .env"
|
|
786
|
+
}
|
|
787
|
+
)
|
|
788
|
+
] }),
|
|
789
|
+
encryptionKey && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
790
|
+
designSystem.Box,
|
|
791
|
+
{
|
|
792
|
+
padding: 4,
|
|
793
|
+
background: "neutral100",
|
|
794
|
+
style: {
|
|
795
|
+
borderRadius: theme.borderRadius.md,
|
|
796
|
+
border: "1px solid " + theme.colors.neutral[200],
|
|
797
|
+
fontFamily: "monospace",
|
|
798
|
+
fontSize: "12px",
|
|
799
|
+
wordBreak: "break-all"
|
|
800
|
+
},
|
|
801
|
+
children: [
|
|
802
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: "Add to .env file:" }),
|
|
803
|
+
/* @__PURE__ */ jsxRuntime.jsxs("code", { style: { color: theme.colors.primary[700] }, children: [
|
|
804
|
+
"SESSION_ENCRYPTION_KEY=",
|
|
805
|
+
encryptionKey
|
|
806
|
+
] })
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
] })
|
|
811
|
+
}
|
|
812
|
+
),
|
|
665
813
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
|
|
666
814
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
667
815
|
ToggleCard,
|
|
@@ -403,7 +403,7 @@ const index = {
|
|
|
403
403
|
id: `${pluginId}.plugin.name`,
|
|
404
404
|
defaultMessage: pluginPkg.strapi.displayName
|
|
405
405
|
},
|
|
406
|
-
Component: () => import("./App-
|
|
406
|
+
Component: () => import("./App-Zhs_vt59.mjs")
|
|
407
407
|
});
|
|
408
408
|
app.createSettingSection(
|
|
409
409
|
{
|
|
@@ -419,7 +419,7 @@ const index = {
|
|
|
419
419
|
},
|
|
420
420
|
id: "general",
|
|
421
421
|
to: `/settings/${pluginId}/general`,
|
|
422
|
-
Component: () => import("./Settings-
|
|
422
|
+
Component: () => import("./Settings-CL2im8M3.mjs")
|
|
423
423
|
},
|
|
424
424
|
{
|
|
425
425
|
intlLabel: {
|
|
@@ -428,7 +428,7 @@ const index = {
|
|
|
428
428
|
},
|
|
429
429
|
id: "analytics",
|
|
430
430
|
to: `/settings/${pluginId}/analytics`,
|
|
431
|
-
Component: () => import("./Analytics-
|
|
431
|
+
Component: () => import("./Analytics-CwyLwdOZ.mjs")
|
|
432
432
|
},
|
|
433
433
|
{
|
|
434
434
|
intlLabel: {
|
|
@@ -437,7 +437,7 @@ const index = {
|
|
|
437
437
|
},
|
|
438
438
|
id: "license",
|
|
439
439
|
to: `/settings/${pluginId}/license`,
|
|
440
|
-
Component: () => import("./License-
|
|
440
|
+
Component: () => import("./License-CPI0p_W8.mjs")
|
|
441
441
|
}
|
|
442
442
|
]
|
|
443
443
|
);
|
|
@@ -404,7 +404,7 @@ const index = {
|
|
|
404
404
|
id: `${pluginId}.plugin.name`,
|
|
405
405
|
defaultMessage: pluginPkg.strapi.displayName
|
|
406
406
|
},
|
|
407
|
-
Component: () => Promise.resolve().then(() => require("./App-
|
|
407
|
+
Component: () => Promise.resolve().then(() => require("./App-nGu2Eb87.js"))
|
|
408
408
|
});
|
|
409
409
|
app.createSettingSection(
|
|
410
410
|
{
|
|
@@ -420,7 +420,7 @@ const index = {
|
|
|
420
420
|
},
|
|
421
421
|
id: "general",
|
|
422
422
|
to: `/settings/${pluginId}/general`,
|
|
423
|
-
Component: () => Promise.resolve().then(() => require("./Settings-
|
|
423
|
+
Component: () => Promise.resolve().then(() => require("./Settings-Lkmxisuv.js"))
|
|
424
424
|
},
|
|
425
425
|
{
|
|
426
426
|
intlLabel: {
|
|
@@ -429,7 +429,7 @@ const index = {
|
|
|
429
429
|
},
|
|
430
430
|
id: "analytics",
|
|
431
431
|
to: `/settings/${pluginId}/analytics`,
|
|
432
|
-
Component: () => Promise.resolve().then(() => require("./Analytics-
|
|
432
|
+
Component: () => Promise.resolve().then(() => require("./Analytics-DRzCKaDF.js"))
|
|
433
433
|
},
|
|
434
434
|
{
|
|
435
435
|
intlLabel: {
|
|
@@ -438,7 +438,7 @@ const index = {
|
|
|
438
438
|
},
|
|
439
439
|
id: "license",
|
|
440
440
|
to: `/settings/${pluginId}/license`,
|
|
441
|
-
Component: () => Promise.resolve().then(() => require("./License-
|
|
441
|
+
Component: () => Promise.resolve().then(() => require("./License-k5vvhgKr.js"))
|
|
442
442
|
}
|
|
443
443
|
]
|
|
444
444
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const react = require("react");
|
|
3
3
|
const admin = require("@strapi/strapi/admin");
|
|
4
|
-
const index = require("./index-
|
|
4
|
+
const index = require("./index-W_QbTAYU.js");
|
|
5
5
|
const useLicense = () => {
|
|
6
6
|
const { get } = admin.useFetchClient();
|
|
7
7
|
const [isPremium, setIsPremium] = react.useState(false);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
2
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
3
|
-
import { a as pluginId } from "./index
|
|
3
|
+
import { a as pluginId } from "./index-B-0VPfeF.mjs";
|
|
4
4
|
const useLicense = () => {
|
|
5
5
|
const { get } = useFetchClient();
|
|
6
6
|
const [isPremium, setIsPremium] = useState(false);
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/package.json
CHANGED