strapi-plugin-magic-link-v5 4.0.15 → 4.0.17

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.
@@ -1,7 +1,7 @@
1
1
  import pluginPkg from '../../package.json';
2
2
  import pluginId from './pluginId';
3
3
  import { Initializer } from './components/Initializer';
4
- import { PluginIcon } from './components/PluginIcon';
4
+ import { Faders, Key } from '@strapi/icons';
5
5
  import pluginPermissions from './permissions';
6
6
  import getTrad from './utils/getTrad';
7
7
 
@@ -11,10 +11,10 @@ export default {
11
11
  register(app) {
12
12
  app.addMenuLink({
13
13
  to: `/plugins/${pluginId}`,
14
- icon: PluginIcon,
14
+ icon: Faders,
15
15
  intlLabel: {
16
16
  id: `${pluginId}.plugin.name`,
17
- defaultMessage: name,
17
+ defaultMessage: 'Magic Link Dashboard',
18
18
  },
19
19
  Component: () => import('./pages/HomePage').then(module => ({
20
20
  default: module.default
@@ -23,7 +23,7 @@ export default {
23
23
 
24
24
  app.addMenuLink({
25
25
  to: `/plugins/${pluginId}/tokens`,
26
- icon: PluginIcon,
26
+ icon: Key,
27
27
  intlLabel: {
28
28
  id: getTrad('tokens.title'),
29
29
  defaultMessage: 'Magic Link Tokens',
@@ -22,6 +22,7 @@ import {
22
22
  Modal,
23
23
  Field,
24
24
  useField,
25
+ Grid,
25
26
  } from '@strapi/design-system';
26
27
  import { useFetchClient, useNotification, useFetchClient as useFetchClientHelper } from '@strapi/strapi/admin';
27
28
  import {
@@ -178,19 +179,22 @@ const TokensPage = () => {
178
179
  // Finde Benutzer anhand der E-Mail
179
180
  const findUserByEmail = async (email) => {
180
181
  try {
181
- const response = await get('/magic-link/user-by-email', {
182
+ // Verwende die Plugin-eigene Route statt der geschützten API
183
+ const response = await get('/magic-link/validate-email', {
182
184
  params: { email }
183
185
  });
184
186
 
185
- if (response && response.data) {
187
+ if (response && response.data && response.data.exists) {
186
188
  return {
187
189
  id: response.data.id,
188
- documentId: response.data.documentId
190
+ documentId: response.data.documentId || response.data.id
189
191
  };
190
192
  }
191
193
  return null;
192
194
  } catch (error) {
193
- console.error("Fehler beim Abrufen des Benutzers:", error);
195
+ console.error("Fehler beim Validieren der E-Mail:", error);
196
+ // Nur Log-Ausgabe, kein Fehlerwerfen oder Benachrichtigung
197
+ // Toggle-Notification wird von der aufrufenden Funktion gehandhabt
194
198
  return null;
195
199
  }
196
200
  };
@@ -235,27 +239,61 @@ const TokensPage = () => {
235
239
  setEmailValidationStatus(null);
236
240
 
237
241
  try {
238
- // Prüfe, ob die E-Mail in der Datenbank existiert
239
- const user = await findUserByEmail(email);
242
+ // Prüfe zuerst die Plugin-Einstellungen
243
+ const settingsResponse = await get('/magic-link/settings');
240
244
 
241
- // Wenn der Benutzer existiert, ist alles in Ordnung
242
- if (user) {
245
+ // Berücksichtige verschiedene Antwortstrukturen:
246
+ // 1. Direkte Einstellungen: settingsResponse.data
247
+ // 2. Verschachtelte Einstellungen: settingsResponse.data.settings
248
+ // 3. Zusätzliche Struktur: settingsResponse.data.data oder settingsResponse.data.data.settings
249
+ const rawSettings = settingsResponse.data || {};
250
+ let settings = { ...rawSettings };
251
+
252
+ // Entpacke verschachtelte Strukturen
253
+ if (settings.settings) settings = { ...settings, ...settings.settings };
254
+ if (settings.data) {
255
+ settings = { ...settings, ...settings.data };
256
+ if (settings.data.settings) settings = { ...settings, ...settings.data.settings };
257
+ }
258
+
259
+ // Debug-Ausgaben
260
+ console.log('Rohe API-Antwort:', settingsResponse);
261
+ console.log('Verarbeitete Einstellungen:', settings);
262
+ console.log('createUserIfNotExists Wert:', settings.createUserIfNotExists);
263
+ console.log('Typ von createUserIfNotExists:', typeof settings.createUserIfNotExists);
264
+
265
+ // Prüfe auf mehrere Arten ob die Benutzererstelling aktiviert ist
266
+ // 1. Boolean-Wert direkt
267
+ // 2. String-Wert "true"
268
+ // 3. Objekt mit type="boolean" und value=true
269
+ const canCreateUser =
270
+ (typeof settings.createUserIfNotExists === 'boolean' && settings.createUserIfNotExists) ||
271
+ (typeof settings.createUserIfNotExists === 'string' && settings.createUserIfNotExists === 'true') ||
272
+ (settings.createUserIfNotExists?.type === 'boolean' && settings.createUserIfNotExists.value) ||
273
+ (typeof settings.create_new_user === 'boolean' && settings.create_new_user) ||
274
+ (typeof settings.create_new_user === 'string' && settings.create_new_user === 'true') ||
275
+ (settings.create_new_user?.type === 'boolean' && settings.create_new_user.value);
276
+
277
+ console.log('canCreateUser berechnet:', canCreateUser);
278
+
279
+ // Wenn automatische Benutzererstellung aktiviert ist, brauchen wir nicht zu prüfen,
280
+ // ob der Benutzer existiert - wir können immer einen Token erstellen
281
+ if (canCreateUser) {
243
282
  setEmailValidationStatus({
244
283
  valid: true,
245
- message: 'Benutzer gefunden. Token kann erstellt werden.'
284
+ message: 'Token kann erstellt werden. Benutzer wird bei Bedarf automatisch erstellt.'
246
285
  });
247
286
  return true;
248
287
  }
249
-
250
- // Wenn der Benutzer nicht existiert, müssen wir die Plugin-Einstellungen prüfen
251
- const settingsResponse = await get('/magic-link/settings');
252
- const settings = settingsResponse.data || {};
253
288
 
254
- // Wenn "create_new_user" aktiviert ist, kann trotzdem ein Token erstellt werden
255
- if (settings.create_new_user) {
289
+ // Rest der Funktion bleibt gleich
290
+ const user = await findUserByEmail(email);
291
+
292
+ // Wenn der Benutzer existiert, ist alles in Ordnung
293
+ if (user) {
256
294
  setEmailValidationStatus({
257
295
  valid: true,
258
- message: 'Neuer Benutzer wird beim Login erstellt.'
296
+ message: 'Benutzer gefunden. Token kann erstellt werden.'
259
297
  });
260
298
  return true;
261
299
  } else {
@@ -634,6 +672,13 @@ const TokensPage = () => {
634
672
  // Validiere zuerst die E-Mail
635
673
  const isValid = await validateEmail(emailToCreate);
636
674
  if (!isValid) {
675
+ // Zeige die Validierungsfehlermeldung, falls vorhanden
676
+ if (emailValidationStatus && emailValidationStatus.message) {
677
+ toggleNotification({
678
+ type: 'warning',
679
+ message: emailValidationStatus.message
680
+ });
681
+ }
637
682
  return; // Beende die Funktion, wenn die E-Mail nicht gültig ist
638
683
  }
639
684
 
@@ -675,9 +720,14 @@ const TokensPage = () => {
675
720
  fetchTokens(); // Token-Liste aktualisieren
676
721
  } catch (error) {
677
722
  console.error('Fehler beim Erstellen des Tokens:', error);
723
+ // Zeige die spezifische Fehlermeldung vom Backend an
724
+ const errorMessage = error.response?.data?.error?.message ||
725
+ error.response?.data?.message ||
726
+ 'Fehler beim Erstellen des Tokens';
678
727
  toggleNotification({
679
728
  type: 'danger',
680
- message: error.response?.data?.message || 'Fehler beim Erstellen des Tokens',
729
+ // message: error.response?.data?.message || 'Fehler beim Erstellen des Tokens',
730
+ message: errorMessage,
681
731
  });
682
732
  } finally {
683
733
  setIsCreating(false);
@@ -1900,7 +1950,7 @@ const TokensPage = () => {
1900
1950
  value={emailToCreate}
1901
1951
  required
1902
1952
  aria-label="E-Mail-Adresse"
1903
- error={emailValidationStatus && !emailValidationStatus.valid}
1953
+ error={emailValidationStatus && !emailValidationStatus.valid ? emailValidationStatus.message : undefined}
1904
1954
  />
1905
1955
  {emailValidationStatus && emailValidationStatus.message && (
1906
1956
  <Field.Hint>{emailValidationStatus.message}</Field.Hint>
@@ -1989,6 +2039,7 @@ const TokensPage = () => {
1989
2039
  <Modal.Title>Token Details</Modal.Title>
1990
2040
  </Modal.Header>
1991
2041
  <Modal.Body>
2042
+ {/* Fallback auf Flex-Layout, da Grid Probleme verursacht */}
1992
2043
  <Box background="neutral100" padding={4} hasRadius style={{ marginBottom: '24px' }}>
1993
2044
  <Typography variant="beta" textColor="primary600" style={{ marginBottom: '12px' }}>
1994
2045
  Allgemeine Informationen
@@ -2135,34 +2186,32 @@ const TokensPage = () => {
2135
2186
  </Box>
2136
2187
  )}
2137
2188
 
2138
- {/* Context JSON Feld */}
2139
- {selectedToken.context && (
2140
- <Box background="neutral100" padding={4} hasRadius>
2141
- <Typography variant="beta" textColor="primary600" style={{ marginBottom: '12px' }}>
2142
- Context Daten
2143
- </Typography>
2144
- <Box padding={3} background="neutral0" hasRadius shadow="filterShadow">
2145
- <Typography variant="delta" fontWeight="bold" textColor="neutral800">Context JSON</Typography>
2146
- <Box
2147
- marginTop={2}
2148
- background="neutral150"
2149
- padding={3}
2150
- hasRadius
2151
- style={{
2152
- wordBreak: 'break-all',
2153
- maxHeight: '200px',
2154
- overflow: 'auto'
2155
- }}
2156
- >
2157
- <Typography variant="pi" fontFamily="monospace">
2158
- {typeof selectedToken.context === 'object'
2159
- ? JSON.stringify(selectedToken.context, null, 2)
2160
- : selectedToken.context || '{}'
2161
- }
2162
- </Typography>
2189
+ {selectedToken.context && Object.keys(selectedToken.context).length > 0 && (
2190
+ <Box background="neutral100" padding={4} hasRadius>
2191
+ <Typography variant="beta" textColor="primary600" style={{ marginBottom: '12px' }}>
2192
+ Context Daten
2193
+ </Typography>
2194
+ <Box padding={3} background="neutral0" hasRadius shadow="filterShadow">
2195
+ <Typography variant="delta" fontWeight="bold" textColor="neutral800">Context JSON</Typography>
2196
+ <Box
2197
+ background="neutral150"
2198
+ padding={3}
2199
+ hasRadius
2200
+ style={{
2201
+ wordBreak: 'break-all',
2202
+ maxHeight: '200px',
2203
+ overflow: 'auto'
2204
+ }}
2205
+ >
2206
+ <Typography variant="pi" fontFamily="monospace">
2207
+ {typeof selectedToken.context === 'object'
2208
+ ? JSON.stringify(selectedToken.context, null, 2)
2209
+ : selectedToken.context
2210
+ }
2211
+ </Typography>
2212
+ </Box>
2163
2213
  </Box>
2164
2214
  </Box>
2165
- </Box>
2166
2215
  )}
2167
2216
  </Modal.Body>
2168
2217
  <Modal.Footer>
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.15",
2
+ "version": "4.0.17",
3
3
  "keywords": [],
4
4
  "type": "commonjs",
5
5
  "exports": {
@@ -68,14 +68,17 @@ module.exports = {
68
68
  const { body } = ctx.request;
69
69
 
70
70
  try {
71
- // Stelle sicher, dass alle Boolean-Werte korrekt formatiert sind
72
- const processedBody = { ...body };
71
+ // Extrahiere die tatsächlichen Einstellungen, prüfe ob sie direkt im Body oder in body.settings liegen
72
+ let settingsToProcess = body.settings ? { ...body.settings } : { ...body };
73
73
 
74
- // Entferne verschachtelte settings-Objekte, falls vorhanden
75
- if (processedBody && processedBody.settings) {
76
- delete processedBody.settings;
74
+ // Entferne vorsichtshalber nochmals verschachtelte settings
75
+ if (settingsToProcess.settings) {
76
+ delete settingsToProcess.settings;
77
77
  }
78
78
 
79
+ // Stelle sicher, dass alle Boolean-Werte korrekt formatiert sind
80
+ const processedBody = { ...settingsToProcess };
81
+
79
82
  // Verarbeite alle Eigenschaften, die als Boolean behandelt werden sollten
80
83
  const booleanFields = [
81
84
  'enabled', 'createUserIfNotExists', 'stays_valid', 'verify_email',
@@ -97,19 +97,40 @@ module.exports = {
97
97
 
98
98
  const settings = await pluginStore.get({ key: 'settings' });
99
99
 
100
+ // --- DEBUG LOGGING START ---
101
+ strapi.log.info('[MagicLink Controller - create function] Loaded settings from store:', settings);
102
+ // --- DEBUG LOGGING END ---
103
+
100
104
  // Find the user
101
105
  let user = await strapi.db.query('plugin::users-permissions.user').findOne({
102
106
  where: { email },
103
107
  select: ['id', 'username', 'email'],
104
108
  });
105
109
 
106
- // If user doesn't exist and create_new_user is not enabled, return error
107
- if (!user && !settings.create_new_user) {
110
+ // --- DEBUG LOGGING START ---
111
+ strapi.log.info(`[MagicLink Controller - create function] Checking user existence for: ${email}`);
112
+ strapi.log.info(`[MagicLink Controller - create function] User found: ${!!user}`);
113
+ strapi.log.info(`[MagicLink Controller - create function] Value of createUserIfNotExists from settings: ${settings.createUserIfNotExists}`);
114
+ strapi.log.info(`[MagicLink Controller - create function] Value of create_new_user from settings: ${settings.create_new_user}`);
115
+ // --- DEBUG LOGGING END ---
116
+
117
+ // Verwende den richtigen Einstellungsnamen: createUserIfNotExists anstatt create_new_user
118
+ // Prüfe sowohl auf den neuen als auch auf den alten Namen für Abwärtskompatibilität
119
+ const canCreateUser = settings.createUserIfNotExists || settings.create_new_user;
120
+
121
+ // If user doesn't exist and automatic creation is not enabled, return error
122
+ if (!user && !canCreateUser) {
123
+ // --- DEBUG LOGGING START ---
124
+ strapi.log.warn(`[MagicLink Controller - create function] User does not exist AND canCreateUser is false. Returning Bad Request.`);
125
+ // --- DEBUG LOGGING END ---
108
126
  return ctx.badRequest('User does not exist and automatic user creation is disabled');
109
127
  }
110
128
 
111
129
  // If user doesn't exist, create a new one
112
- if (!user && settings.create_new_user) {
130
+ if (!user && canCreateUser) {
131
+ // --- DEBUG LOGGING START ---
132
+ strapi.log.info(`[MagicLink Controller - create function] User does not exist BUT canCreateUser is true. Attempting to create user...`);
133
+ // --- DEBUG LOGGING END ---
113
134
  // Generate a random username based on the email
114
135
  const username = email.split('@')[0] + Math.floor(Math.random() * 10000);
115
136
 
@@ -426,22 +447,60 @@ module.exports = {
426
447
  return ctx.badRequest('Email is required');
427
448
  }
428
449
 
429
- // Find the user
450
+ // Überprüfe, ob die Plugin-Einstellungen das Erstellen neuer Benutzer erlauben
451
+ const pluginStore = strapi.store({
452
+ environment: '',
453
+ type: 'plugin',
454
+ name: 'magic-link',
455
+ });
456
+
457
+ const settings = await pluginStore.get({ key: 'settings' });
458
+
459
+ // Find the user - Entferne UUID aus der Abfrage, damit es in Strapi v5 funktioniert
430
460
  const user = await strapi.db.query('plugin::users-permissions.user').findOne({
431
461
  where: { email },
432
- select: ['id', 'username', 'email', 'confirmed', 'blocked', 'documentId', 'UUID'],
462
+ select: ['id', 'username', 'email', 'confirmed', 'blocked', 'documentId'],
433
463
  });
434
464
 
435
465
  if (!user) {
436
- return ctx.notFound('User not found');
466
+ // Mit createUserIfNotExists-Option kann die API weiterhin true zurückgeben
467
+ if (settings && settings.createUserIfNotExists) {
468
+ return {
469
+ exists: false,
470
+ canBeCreated: true,
471
+ autoCreationEnabled: true
472
+ };
473
+ }
474
+
475
+ return {
476
+ exists: false,
477
+ canBeCreated: false,
478
+ autoCreationEnabled: false
479
+ };
437
480
  }
438
481
 
439
- return user;
482
+ // Sicheres Benutzerobjekt ohne sensible Daten
483
+ return {
484
+ id: user.id,
485
+ username: user.username,
486
+ email: user.email,
487
+ documentId: user.documentId || null,
488
+ exists: true
489
+ };
440
490
  } catch (error) {
491
+ console.error("Error finding user by email:", error);
441
492
  ctx.throw(500, error);
442
493
  }
443
494
  },
444
495
 
496
+ /**
497
+ * Validiert eine E-Mail (Alias für Frontend-Aufrufe)
498
+ * @param {Object} ctx - The request context
499
+ */
500
+ async validateEmail(ctx) {
501
+ return this.findUserByEmail(ctx);
502
+ },
503
+
445
504
  /**
446
505
  * Ban an IP address
447
506
  * @param {Object} ctx - The request context
@@ -589,7 +648,8 @@ module.exports = {
589
648
  let configPoints = 0;
590
649
 
591
650
  // Ist das Auto-Create-User Feature deaktiviert? (sicherer)
592
- if (settings.create_new_user === false) configPoints += 10;
651
+ // Prüfe sowohl auf den neuen als auch auf den alten Namen für Abwärtskompatibilität
652
+ if (settings.createUserIfNotExists === false && settings.create_new_user === false) configPoints += 10;
593
653
 
594
654
  // Ist das Email-Send Feature aktiviert? (sicherer)
595
655
  if (settings.enabled === true) configPoints += 5;
@@ -50,6 +50,14 @@ module.exports = {
50
50
  policies: [],
51
51
  },
52
52
  },
53
+ {
54
+ method: 'GET',
55
+ path: '/validate-email',
56
+ handler: 'tokens.validateEmail',
57
+ config: {
58
+ policies: [],
59
+ },
60
+ },
53
61
  {
54
62
  method: 'POST',
55
63
  path: '/tokens/:id/block',
@@ -62,16 +62,31 @@ module.exports = ({ strapi }) => ({
62
62
  async user(email, username) {
63
63
  const settings = await this.settings();
64
64
  const createUserIfNotExists = settings.createUserIfNotExists;
65
+
66
+ // --- DEBUG LOGGING START ---
67
+ strapi.log.info(`[MagicLink Service - user function] Checking user: ${email || username}`);
68
+ strapi.log.info(`[MagicLink Service - user function] createUserIfNotExists setting value: ${createUserIfNotExists} (Type: ${typeof createUserIfNotExists})`);
69
+ // --- DEBUG LOGGING END ---
65
70
 
66
71
  const user = await strapi.query('plugin::users-permissions.user').findOne({
67
72
  where: email ? { email } : { username },
68
73
  });
74
+
75
+ // --- DEBUG LOGGING START ---
76
+ strapi.log.info(`[MagicLink Service - user function] User found: ${!!user}`);
77
+ // --- DEBUG LOGGING END ---
69
78
 
70
79
  if (!user && !createUserIfNotExists) {
71
- throw new Error('User not found');
80
+ // --- DEBUG LOGGING START ---
81
+ strapi.log.warn(`[MagicLink Service - user function] User not found AND createUserIfNotExists is false. Throwing error.`);
82
+ // --- DEBUG LOGGING END ---
83
+ throw new Error('User not found and auto-creation disabled.'); // Slightly more specific error
72
84
  }
73
85
 
74
86
  if (!user && createUserIfNotExists) {
87
+ // --- DEBUG LOGGING START ---
88
+ strapi.log.info(`[MagicLink Service - user function] User not found BUT createUserIfNotExists is true. Creating user...`);
89
+ // --- DEBUG LOGGING END ---
75
90
  const newUser = await this.createUser({
76
91
  email,
77
92
  username: username || email.split('@')[0],