strapi-plugin-magic-link-v5 4.0.4 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.externals.json +28 -0
- package/README.md +2 -332
- package/admin/src/index.js +48 -64
- package/admin/src/pages/App/index.jsx +28 -40
- package/admin/src/pages/HomePage.jsx +31 -14
- package/admin/src/pages/Settings/index.jsx +24 -31
- package/admin/src/pages/Settings/utils/api.js +7 -5
- package/admin/src/pages/Tokens/index.jsx +60 -87
- package/build.js +50 -0
- package/jsconfig.json +13 -0
- package/package.json +6 -9
- package/server/bootstrap.js +27 -13
- package/server/controllers/settings.js +1 -1
- package/server/src/bootstrap.js +1 -1
- package/server/src/controllers/auth.js +2 -2
- package/server/src/controllers/controller.js +35 -41
- package/server/src/controllers/jwt.js +42 -9
- package/server/src/controllers/tokens.js +72 -49
- package/server/src/services/magic-link.js +51 -28
- package/server/utils/index.js +8 -6
- package/strapi-admin.js +2 -2
- package/vite.config.js +1 -6
package/.externals.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"externals": [
|
|
3
|
+
"react",
|
|
4
|
+
"react-dom",
|
|
5
|
+
"react-router-dom",
|
|
6
|
+
"react-intl",
|
|
7
|
+
"styled-components",
|
|
8
|
+
"formik",
|
|
9
|
+
"react-query",
|
|
10
|
+
"prop-types",
|
|
11
|
+
"yup",
|
|
12
|
+
"@strapi/strapi",
|
|
13
|
+
"@strapi/design-system",
|
|
14
|
+
"@strapi/icons"
|
|
15
|
+
],
|
|
16
|
+
"output": {
|
|
17
|
+
"globals": {
|
|
18
|
+
"react": "React",
|
|
19
|
+
"react-dom": "ReactDOM",
|
|
20
|
+
"react-router-dom": "ReactRouterDom",
|
|
21
|
+
"react-intl": "ReactIntl",
|
|
22
|
+
"styled-components": "styled",
|
|
23
|
+
"@strapi/strapi": "Strapi",
|
|
24
|
+
"@strapi/design-system": "StrapiDesignSystem",
|
|
25
|
+
"@strapi/icons": "StrapiIcons"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/README.md
CHANGED
|
@@ -1,333 +1,3 @@
|
|
|
1
|
-
#
|
|
1
|
+
# strapi-plugin-magic-link-v5
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
## Core Features
|
|
8
|
-
|
|
9
|
-
- **Passwordless Authentication**: Login via secure email links
|
|
10
|
-
- **Token Management**: Admin dashboard for managing and monitoring tokens
|
|
11
|
-
- **JWT Session Tracking**: Monitor and manage active sessions
|
|
12
|
-
- **Security Features**: IP banning, token expiration controls
|
|
13
|
-
- **Admin Interface**: Statistics dashboard and configuration options
|
|
14
|
-
|
|
15
|
-

|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
# Using npm
|
|
21
|
-
npm install strapi-plugin-magic-link-v5
|
|
22
|
-
|
|
23
|
-
# Using yarn
|
|
24
|
-
yarn add strapi-plugin-magic-link-v5
|
|
25
|
-
|
|
26
|
-
# Install directly from GitHub
|
|
27
|
-
npm install begservice/strapi-magic-link
|
|
28
|
-
# or
|
|
29
|
-
yarn add begservice/strapi-magic-link
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
After installation, restart your Strapi server and the plugin will be available in the admin panel.
|
|
33
|
-
|
|
34
|
-
## How It Works
|
|
35
|
-
|
|
36
|
-
### Email User with Login Link
|
|
37
|
-
|
|
38
|
-
1. **Request Process**:
|
|
39
|
-
|
|
40
|
-
- User requests a login link by entering their email
|
|
41
|
-
- System generates a secure token and sends an email
|
|
42
|
-
- Email contains a magic link with the token
|
|
43
|
-
|
|
44
|
-
2. **Token Details**:
|
|
45
|
-
- Cryptographically secure random tokens
|
|
46
|
-
- Configurable expiration time
|
|
47
|
-
- Option for one-time use or reusable tokens
|
|
48
|
-
- Tracks IP address and user agent information
|
|
49
|
-
|
|
50
|
-
### Login with Token
|
|
51
|
-
|
|
52
|
-
1. **Authentication Process**:
|
|
53
|
-
|
|
54
|
-
- User clicks the link in their email
|
|
55
|
-
- System verifies the token is valid and not expired
|
|
56
|
-
- User is automatically authenticated
|
|
57
|
-
- JWT token is generated for the session
|
|
58
|
-
|
|
59
|
-
2. **Security Measures**:
|
|
60
|
-
- IP address validation (optional)
|
|
61
|
-
- Token expiration
|
|
62
|
-
- One-time use tokens (configurable)
|
|
63
|
-
- Automatic blocking after failed attempts
|
|
64
|
-
|
|
65
|
-

|
|
66
|
-
|
|
67
|
-
## Configuration
|
|
68
|
-
|
|
69
|
-
Configure the plugin through **Settings > Magic Link** in the Strapi admin panel:
|
|
70
|
-
|
|
71
|
-

|
|
72
|
-
|
|
73
|
-
### General Settings
|
|
74
|
-
|
|
75
|
-
- **Enable Magic Link**: Turn the feature on/off
|
|
76
|
-
- **Create New Users**: Automatically create users if they don't exist
|
|
77
|
-
- **Token Stays Valid**: Configure one-time use or reusable tokens
|
|
78
|
-
- **Expiration Period**: Set how long tokens remain valid
|
|
79
|
-
- **Token Length**: Configure the security level of tokens
|
|
80
|
-
|
|
81
|
-
### Authentication Settings
|
|
82
|
-
|
|
83
|
-
- **Default Role**: Select which user role is assigned to new users
|
|
84
|
-
- **JWT Token Expiration**: Define how long JWT tokens remain valid (e.g., 30d, 24h)
|
|
85
|
-
- **Store Login Info**: Enable tracking of user agents and IP addresses
|
|
86
|
-
- **Remember Me**: Allow users to stay logged in for extended periods
|
|
87
|
-
|
|
88
|
-
### Email Settings
|
|
89
|
-
|
|
90
|
-
- **Sender Information**: Configure the email sender details (name, email)
|
|
91
|
-
- **Reply-To Address**: Set a reply-to email address for support inquiries
|
|
92
|
-
- **Email Subject**: Customize the subject line of the magic link emails
|
|
93
|
-
- **Email Templates**: Customize HTML and text templates
|
|
94
|
-
- **Email Designer Integration**: Use with Email Designer 5 if installed
|
|
95
|
-
|
|
96
|
-
## Dashboard & Admin Interface
|
|
97
|
-
|
|
98
|
-
Magic Link provides a comprehensive admin interface with several key sections:
|
|
99
|
-
|
|
100
|
-

|
|
101
|
-
|
|
102
|
-
### Dashboard Overview
|
|
103
|
-
|
|
104
|
-
- **Security Score**: Dynamic score (0-100) showing your current security configuration
|
|
105
|
-
- **Active Tokens**: Count and management of currently active tokens
|
|
106
|
-
- **Token Usage**: Metrics on token creation and usage patterns
|
|
107
|
-
- **Users Using Magic Link**: Number of unique users authenticating via magic links
|
|
108
|
-
- **Tokens About to Expire**: Warning system for tokens expiring soon
|
|
109
|
-
|
|
110
|
-
### Token Management
|
|
111
|
-
|
|
112
|
-
- **Token List View**: Sortable and filterable list of all tokens
|
|
113
|
-
- **Token Status Indicators**: Visual indicators showing token status (active, expired, used)
|
|
114
|
-
- **Token Details**: Inspect complete token information including:
|
|
115
|
-
- Creation and expiration dates
|
|
116
|
-
- IP address and user agent information
|
|
117
|
-
- Usage history and context data
|
|
118
|
-
- **Bulk Actions**: Select multiple tokens for batch operations
|
|
119
|
-
- **Search & Filter**: Find tokens by email, status, or creation date
|
|
120
|
-
- **Token Operations**:
|
|
121
|
-
- Block/deactivate tokens
|
|
122
|
-
- Extend token expiration
|
|
123
|
-
- Delete tokens
|
|
124
|
-
|
|
125
|
-
### JWT Session Management
|
|
126
|
-
|
|
127
|
-
- **Active Sessions**: Monitor all active JWT sessions across your application
|
|
128
|
-
- **Session Revocation**: Ability to revoke any active session immediately
|
|
129
|
-
- **Session Details**: View complete session information including:
|
|
130
|
-
- User details
|
|
131
|
-
- Creation and expiration time
|
|
132
|
-
- Last activity
|
|
133
|
-
- IP address and device information
|
|
134
|
-
|
|
135
|
-
### Security Features
|
|
136
|
-
|
|
137
|
-
- **IP Ban Management**: View and manage banned IP addresses
|
|
138
|
-
- **IP Ban Controls**: Ban suspicious IPs and view associated tokens
|
|
139
|
-
- **Security Audit**: Track login attempts and security events
|
|
140
|
-
- **System Status**: Monitor the health of the plugin and its dependencies
|
|
141
|
-
|
|
142
|
-
### Token Creation Interface
|
|
143
|
-
|
|
144
|
-
- **Manual Token Creation**: Generate tokens for specific users
|
|
145
|
-
- **Email Control**: Option to send or not send the email with the token
|
|
146
|
-
- **Context Injection**: Add custom JSON context data to tokens
|
|
147
|
-
- **Email Validation**: Verify email existence before token creation
|
|
148
|
-
|
|
149
|
-
## Admin Features
|
|
150
|
-
|
|
151
|
-
### Token Management
|
|
152
|
-
|
|
153
|
-
- View all active/inactive tokens
|
|
154
|
-
- Block or activate individual tokens
|
|
155
|
-
- Delete expired tokens
|
|
156
|
-
- See token usage statistics
|
|
157
|
-
|
|
158
|
-
### Security Dashboard
|
|
159
|
-
|
|
160
|
-
- Security score based on your configuration
|
|
161
|
-
- IP banning for suspicious activity
|
|
162
|
-
- JWT session monitoring and management
|
|
163
|
-
- Token expiration warnings
|
|
164
|
-
|
|
165
|
-
## API Endpoints
|
|
166
|
-
|
|
167
|
-
- `POST /api/magic-link/send` - Generate and send a magic link
|
|
168
|
-
- `GET /api/magic-link/login?loginToken=xxx` - Authenticate with token
|
|
169
|
-
- `GET /api/magic-link/tokens` - List tokens (admin only)
|
|
170
|
-
- `GET /api/magic-link/jwt-sessions` - List active sessions (admin only)
|
|
171
|
-
- `POST /api/magic-link/tokens/:id/block` - Block a specific token
|
|
172
|
-
- `POST /api/magic-link/ban-ip` - Ban an IP address
|
|
173
|
-
|
|
174
|
-
## Frontend Implementation
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
// Request a magic link
|
|
178
|
-
const requestLogin = async (email) => {
|
|
179
|
-
try {
|
|
180
|
-
await axios.post("/api/magic-link/send", { email });
|
|
181
|
-
// Show success message
|
|
182
|
-
} catch (error) {
|
|
183
|
-
// Handle error
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Verify token on the callback page
|
|
188
|
-
const verifyToken = async () => {
|
|
189
|
-
const token = new URLSearchParams(window.location.search).get("loginToken");
|
|
190
|
-
if (token) {
|
|
191
|
-
try {
|
|
192
|
-
const response = await axios.get(
|
|
193
|
-
`/api/magic-link/login?loginToken=${token}`
|
|
194
|
-
);
|
|
195
|
-
// Store JWT and redirect user
|
|
196
|
-
localStorage.setItem("token", response.data.jwt);
|
|
197
|
-
window.location.href = "/dashboard";
|
|
198
|
-
} catch (error) {
|
|
199
|
-
// Handle invalid token
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Context Data
|
|
206
|
-
|
|
207
|
-
You can include additional context when sending a magic link:
|
|
208
|
-
|
|
209
|
-
```javascript
|
|
210
|
-
await axios.post("/api/magic-link/send", {
|
|
211
|
-
email: "user@example.com",
|
|
212
|
-
context: {
|
|
213
|
-
redirectUrl: "/dashboard",
|
|
214
|
-
source: "registration",
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## Security Best Practices
|
|
220
|
-
|
|
221
|
-
- Set reasonable token expiration times
|
|
222
|
-
- Use one-time tokens for sensitive operations
|
|
223
|
-
- Regularly monitor the security dashboard
|
|
224
|
-
- Ban suspicious IP addresses promptly
|
|
225
|
-
|
|
226
|
-
## Troubleshooting
|
|
227
|
-
|
|
228
|
-
| Issue | Solution |
|
|
229
|
-
| ---------------------- | ---------------------------------------------- |
|
|
230
|
-
| Emails not sending | Check your Strapi email provider configuration |
|
|
231
|
-
| Token validation fails | Verify token expiration settings |
|
|
232
|
-
| User not found errors | Check "Create New Users" setting |
|
|
233
|
-
|
|
234
|
-
## License
|
|
235
|
-
|
|
236
|
-
[MIT](LICENSE)
|
|
237
|
-
|
|
238
|
-
## Development & Contributing
|
|
239
|
-
|
|
240
|
-
### Release Process
|
|
241
|
-
|
|
242
|
-
This project uses [semantic-release](https://github.com/semantic-release/semantic-release) to automate version management and package publishing.
|
|
243
|
-
|
|
244
|
-
To create a new release:
|
|
245
|
-
|
|
246
|
-
```bash
|
|
247
|
-
npx semantic-release
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
This will automatically:
|
|
251
|
-
|
|
252
|
-
1. Analyze commit messages since the last release
|
|
253
|
-
2. Determine the appropriate version bump (major, minor, or patch)
|
|
254
|
-
3. Generate release notes
|
|
255
|
-
4. Update the version in package.json
|
|
256
|
-
5. Create a new Git tag
|
|
257
|
-
6. Publish the package to npm
|
|
258
|
-
|
|
259
|
-
### Commit Guidelines
|
|
260
|
-
|
|
261
|
-
To ensure semantic-release can properly determine the next version number, please follow these commit message conventions:
|
|
262
|
-
|
|
263
|
-
#### Commit Message Format
|
|
264
|
-
|
|
265
|
-
Each commit message consists of a **header**, an optional **body**, and an optional **footer**:
|
|
266
|
-
|
|
267
|
-
```
|
|
268
|
-
<type>(<scope>): <subject>
|
|
269
|
-
<BLANK LINE>
|
|
270
|
-
<body>
|
|
271
|
-
<BLANK LINE>
|
|
272
|
-
<footer>
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
The **header** is mandatory and must conform to the following format:
|
|
276
|
-
|
|
277
|
-
- **type**: What type of change this commit is making. Must be one of:
|
|
278
|
-
|
|
279
|
-
- `feat`: A new feature (triggers a minor release)
|
|
280
|
-
- `fix`: A bug fix (triggers a patch release)
|
|
281
|
-
- `docs`: Documentation changes only
|
|
282
|
-
- `style`: Changes that don't affect code functionality (formatting, etc.)
|
|
283
|
-
- `refactor`: Code changes that neither fix a bug nor add a feature
|
|
284
|
-
- `perf`: Performance improvements
|
|
285
|
-
- `test`: Adding or updating tests
|
|
286
|
-
- `chore`: Changes to build process or auxiliary tools
|
|
287
|
-
- `ci`: Changes to CI configuration files and scripts
|
|
288
|
-
|
|
289
|
-
- **scope**: Optional, can be anything specifying the place of the commit change (e.g., `admin`, `api`, `auth`)
|
|
290
|
-
|
|
291
|
-
- **subject**: Brief description of the change
|
|
292
|
-
|
|
293
|
-
#### Breaking Changes
|
|
294
|
-
|
|
295
|
-
For breaking changes, add `BREAKING CHANGE:` in the footer of the commit message or append a `!` after the type/scope:
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
feat(api)!: completely restructure API endpoints
|
|
299
|
-
|
|
300
|
-
BREAKING CHANGE: The API endpoints have been completely restructured.
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
This will trigger a major version bump.
|
|
304
|
-
|
|
305
|
-
#### Examples
|
|
306
|
-
|
|
307
|
-
```
|
|
308
|
-
feat(auth): add support for multiple roles
|
|
309
|
-
|
|
310
|
-
fix(email): correct template rendering issue
|
|
311
|
-
|
|
312
|
-
docs: update API documentation
|
|
313
|
-
|
|
314
|
-
chore(deps): update dependencies
|
|
315
|
-
|
|
316
|
-
fix!: critical security vulnerability in token validation
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Branching Strategy
|
|
320
|
-
|
|
321
|
-
- `main`: Production-ready code
|
|
322
|
-
- `develop`: Integration branch for features
|
|
323
|
-
- `feature/*`: New features
|
|
324
|
-
- `fix/*`: Bug fixes
|
|
325
|
-
- `docs/*`: Documentation changes
|
|
326
|
-
|
|
327
|
-
When creating PRs, always merge feature branches into `develop` first. The `develop` branch is periodically merged into `main` for releases.
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
## Support
|
|
332
|
-
|
|
333
|
-
If you encounter issues or have questions, please check the admin documentation or open an issue.
|
|
3
|
+
Magic Link Authentication for Strapi v5
|
package/admin/src/index.js
CHANGED
|
@@ -1,98 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import Initializer from './components/Initializer';
|
|
5
|
-
import PluginIcon from './components/PluginIcon';
|
|
6
|
-
import pluginPermissions from './permissions';
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
|
3
|
+
import { PLUGIN_ID } from './pluginId';
|
|
7
4
|
import getTrad from './utils/getTrad';
|
|
5
|
+
import reducers from './reducers';
|
|
6
|
+
import permissions from './permissions';
|
|
7
|
+
import PluginIcon from './components/PluginIcon/index.jsx';
|
|
8
|
+
import pluginId from './pluginId';
|
|
8
9
|
|
|
9
|
-
const name =
|
|
10
|
+
const name = 'strapi-plugin-magic-link-v5';
|
|
11
|
+
|
|
12
|
+
const icon = PluginIcon;
|
|
10
13
|
|
|
11
14
|
export default {
|
|
12
15
|
register(app) {
|
|
13
|
-
app.
|
|
14
|
-
to: `/plugins/${pluginId}`,
|
|
15
|
-
icon: PluginIcon,
|
|
16
|
-
intlLabel: {
|
|
17
|
-
id: `${pluginId}.plugin.name`,
|
|
18
|
-
defaultMessage: name,
|
|
19
|
-
},
|
|
20
|
-
Component: () => import('./pages/HomePage').then(module => ({
|
|
21
|
-
default: module.default
|
|
22
|
-
}))
|
|
23
|
-
});
|
|
16
|
+
app.addReducers(reducers);
|
|
24
17
|
|
|
25
18
|
app.addMenuLink({
|
|
26
|
-
to: `/plugins/${
|
|
27
|
-
icon
|
|
19
|
+
to: `/plugins/${PLUGIN_ID}`,
|
|
20
|
+
icon,
|
|
28
21
|
intlLabel: {
|
|
29
|
-
id:
|
|
30
|
-
defaultMessage: 'Magic Link
|
|
22
|
+
id: `${PLUGIN_ID}.plugin.name`,
|
|
23
|
+
defaultMessage: 'Magic Link',
|
|
31
24
|
},
|
|
32
|
-
Component:
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
Component: async () => {
|
|
26
|
+
const component = await import('./pages/App');
|
|
27
|
+
return component;
|
|
28
|
+
},
|
|
29
|
+
permissions: [
|
|
30
|
+
{
|
|
31
|
+
action: `plugin::${pluginId}.read`,
|
|
32
|
+
subject: null,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
app.createSettingSection(
|
|
38
38
|
{
|
|
39
39
|
id: pluginId,
|
|
40
40
|
intlLabel: {
|
|
41
|
-
id: getTrad('
|
|
41
|
+
id: getTrad('Settings.header'),
|
|
42
42
|
defaultMessage: 'Magic Link',
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
45
|
[
|
|
46
46
|
{
|
|
47
|
+
id: 'strapi-plugin-magic-link-v5-settings',
|
|
47
48
|
intlLabel: {
|
|
48
|
-
id: getTrad('
|
|
49
|
+
id: getTrad('Settings.subHeader'),
|
|
49
50
|
defaultMessage: 'Settings',
|
|
50
51
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
permissions: pluginPermissions.readSettings,
|
|
52
|
+
Component: async () => {
|
|
53
|
+
const component = await import('./pages/Settings');
|
|
54
|
+
return component;
|
|
55
|
+
},
|
|
56
|
+
permissions: permissions.accessSettings,
|
|
57
57
|
},
|
|
58
58
|
]
|
|
59
59
|
);
|
|
60
|
-
|
|
61
|
-
app.registerPlugin({
|
|
62
|
-
id: pluginId,
|
|
63
|
-
initializer: Initializer,
|
|
64
|
-
isReady: false,
|
|
65
|
-
name,
|
|
66
|
-
});
|
|
67
60
|
},
|
|
68
61
|
|
|
69
|
-
bootstrap() {
|
|
70
|
-
// Nothing to do here
|
|
71
|
-
},
|
|
62
|
+
bootstrap() {},
|
|
72
63
|
|
|
73
64
|
async registerTrads({ locales }) {
|
|
74
65
|
const importedTrads = await Promise.all(
|
|
75
|
-
locales.map(locale => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
} catch (error) {
|
|
91
|
-
return {
|
|
92
|
-
data: {},
|
|
93
|
-
locale,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
66
|
+
locales.map((locale) => {
|
|
67
|
+
return import(`./translations/${locale}.json`)
|
|
68
|
+
.then(({ default: data }) => {
|
|
69
|
+
return {
|
|
70
|
+
data: prefixPluginTranslations(data, pluginId),
|
|
71
|
+
locale,
|
|
72
|
+
};
|
|
73
|
+
})
|
|
74
|
+
.catch(() => {
|
|
75
|
+
return {
|
|
76
|
+
data: {},
|
|
77
|
+
locale,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
96
80
|
})
|
|
97
81
|
);
|
|
98
82
|
|
|
@@ -1,50 +1,38 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Switch, Route, useLocation } from 'react-router-dom';
|
|
3
|
+
import { SkipToContent } from '@strapi/design-system';
|
|
4
|
+
import { useIntl } from 'react-intl';
|
|
5
|
+
import { AnErrorOccurred } from '@strapi/helper-plugin';
|
|
4
6
|
import HomePage from '../HomePage';
|
|
7
|
+
import PLUGIN_ID from '../../pluginId';
|
|
8
|
+
import { Helmet } from 'react-helmet';
|
|
9
|
+
import SettingsPage from '../Settings';
|
|
5
10
|
import TokensPage from '../Tokens';
|
|
11
|
+
import getTrad from '../../utils/getTrad';
|
|
6
12
|
|
|
7
13
|
const App = () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Einfachere Bedingungen, die nur prüfen, ob der Pfad bestimmte Segmente enthält
|
|
16
|
-
const isTokensPage = currentPath.includes(`/plugins/${pluginId}/tokens`) ||
|
|
17
|
-
currentPath.includes(`/plugins/magic-link/tokens`);
|
|
18
|
-
const isHomePage = (currentPath.includes(`/plugins/${pluginId}`) ||
|
|
19
|
-
currentPath.includes(`/plugins/magic-link`)) && !isTokensPage;
|
|
14
|
+
const { formatMessage } = useIntl();
|
|
15
|
+
const location = useLocation();
|
|
16
|
+
const currentPath = location.pathname;
|
|
17
|
+
const isTokensPage =
|
|
18
|
+
currentPath.includes(`/plugins/strapi-plugin-magic-link-v5/tokens`);
|
|
19
|
+
const isSettingsPage = (
|
|
20
|
+
currentPath.includes(`/plugins/strapi-plugin-magic-link-v5`)) && !isTokensPage;
|
|
20
21
|
|
|
21
|
-
console.log("isTokensPage:", isTokensPage);
|
|
22
|
-
console.log("isHomePage:", isHomePage);
|
|
23
|
-
|
|
24
|
-
let content;
|
|
25
|
-
if (isTokensPage) {
|
|
26
|
-
console.log("Zeige TokensPage an");
|
|
27
|
-
content = <TokensPage />;
|
|
28
|
-
} else if (isHomePage) {
|
|
29
|
-
console.log("Zeige HomePage an");
|
|
30
|
-
content = <HomePage />;
|
|
31
|
-
} else {
|
|
32
|
-
console.log("Zeige 404 Seite an");
|
|
33
|
-
content = (
|
|
34
|
-
<Box padding={8} background="neutral100">
|
|
35
|
-
<Typography variant="alpha">
|
|
36
|
-
404 - Route nicht gefunden: {currentPath}
|
|
37
|
-
</Typography>
|
|
38
|
-
</Box>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
22
|
return (
|
|
43
|
-
<
|
|
44
|
-
{
|
|
45
|
-
|
|
23
|
+
<div>
|
|
24
|
+
<Helmet title={formatMessage({ id: getTrad('Header.title'), defaultMessage: 'Magic Link' })} />
|
|
25
|
+
<SkipToContent>
|
|
26
|
+
{formatMessage({ id: getTrad('Header.title'), defaultMessage: 'Magic Link' })}
|
|
27
|
+
</SkipToContent>
|
|
28
|
+
<Switch>
|
|
29
|
+
<Route path={`/plugins/${PLUGIN_ID}/tokens`} component={TokensPage} exact />
|
|
30
|
+
<Route path={`/settings/${PLUGIN_ID}`} component={SettingsPage} exact />
|
|
31
|
+
<Route path={`/plugins/${PLUGIN_ID}`} component={HomePage} exact />
|
|
32
|
+
<Route component={AnErrorOccurred} />
|
|
33
|
+
</Switch>
|
|
34
|
+
</div>
|
|
46
35
|
);
|
|
47
36
|
};
|
|
48
37
|
|
|
49
|
-
export { App };
|
|
50
38
|
export default App;
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
Information,
|
|
24
24
|
Calendar
|
|
25
25
|
} from '@strapi/icons';
|
|
26
|
-
import { useFetchClient } from
|
|
26
|
+
import { useFetchClient } from "@strapi/strapi/admin";
|
|
27
27
|
|
|
28
28
|
const HomePage = () => {
|
|
29
29
|
const { get } = useFetchClient();
|
|
@@ -37,13 +37,30 @@ const HomePage = () => {
|
|
|
37
37
|
tokenExpiresNextDays: 0
|
|
38
38
|
});
|
|
39
39
|
const [isLoading, setIsLoading] = useState(true);
|
|
40
|
+
const [tokens, setTokens] = useState([]);
|
|
41
|
+
|
|
42
|
+
const fetchTokens = async () => {
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
try {
|
|
45
|
+
const response = await get('/strapi-plugin-magic-link-v5/tokens');
|
|
46
|
+
setTokens(response.data);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Failed to fetch tokens', error);
|
|
49
|
+
toggleNotification({
|
|
50
|
+
type: 'warning',
|
|
51
|
+
message: 'Error loading tokens',
|
|
52
|
+
});
|
|
53
|
+
} finally {
|
|
54
|
+
setIsLoading(false);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
40
57
|
|
|
41
58
|
// API-Anfrage, um echte Token-Daten zu laden
|
|
42
59
|
useEffect(() => {
|
|
43
60
|
const fetchTokenData = async () => {
|
|
44
61
|
try {
|
|
45
62
|
setIsLoading(true);
|
|
46
|
-
const response = await get('/magic-link/tokens');
|
|
63
|
+
const response = await get('/strapi-plugin-magic-link-v5/tokens');
|
|
47
64
|
const { data, meta } = response.data || { data: [], meta: {} };
|
|
48
65
|
|
|
49
66
|
if (data && Array.isArray(data)) {
|
|
@@ -101,6 +118,14 @@ const HomePage = () => {
|
|
|
101
118
|
return () => clearInterval(interval);
|
|
102
119
|
}, [get]);
|
|
103
120
|
|
|
121
|
+
const goToTokensPage = () => {
|
|
122
|
+
window.location.href = '/admin/plugins/strapi-plugin-magic-link-v5/tokens';
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const goToSettings = () => {
|
|
126
|
+
window.location.href = '/admin/settings/strapi-plugin-magic-link-v5';
|
|
127
|
+
};
|
|
128
|
+
|
|
104
129
|
return (
|
|
105
130
|
<Main>
|
|
106
131
|
<Box
|
|
@@ -353,9 +378,7 @@ const HomePage = () => {
|
|
|
353
378
|
<Button
|
|
354
379
|
variant="default"
|
|
355
380
|
endIcon={<ArrowRight />}
|
|
356
|
-
onClick={
|
|
357
|
-
window.location.href = '/admin/plugins/magic-link/tokens';
|
|
358
|
-
}}
|
|
381
|
+
onClick={goToTokensPage}
|
|
359
382
|
>
|
|
360
383
|
Öffnen
|
|
361
384
|
</Button>
|
|
@@ -379,9 +402,7 @@ const HomePage = () => {
|
|
|
379
402
|
<Button
|
|
380
403
|
variant="default"
|
|
381
404
|
endIcon={<ArrowRight />}
|
|
382
|
-
onClick={
|
|
383
|
-
window.location.href = '/admin/settings/magic-link';
|
|
384
|
-
}}
|
|
405
|
+
onClick={goToSettings}
|
|
385
406
|
>
|
|
386
407
|
Öffnen
|
|
387
408
|
</Button>
|
|
@@ -487,9 +508,7 @@ const HomePage = () => {
|
|
|
487
508
|
variant="secondary"
|
|
488
509
|
startIcon={<Key />}
|
|
489
510
|
endIcon={<ArrowRight />}
|
|
490
|
-
onClick={
|
|
491
|
-
window.location.href = '/admin/plugins/magic-link/tokens';
|
|
492
|
-
}}
|
|
511
|
+
onClick={goToTokensPage}
|
|
493
512
|
>
|
|
494
513
|
Tokens verwalten
|
|
495
514
|
</Button>
|
|
@@ -529,9 +548,7 @@ const HomePage = () => {
|
|
|
529
548
|
variant="secondary"
|
|
530
549
|
startIcon={<Shield />}
|
|
531
550
|
endIcon={<ArrowRight />}
|
|
532
|
-
onClick={
|
|
533
|
-
window.location.href = '/admin/settings/magic-link';
|
|
534
|
-
}}
|
|
551
|
+
onClick={goToSettings}
|
|
535
552
|
>
|
|
536
553
|
Einstellungen öffnen
|
|
537
554
|
</Button>
|