strapi-plugin-magic-mail 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +15 -17
  2. package/admin/src/index.js +26 -25
  3. package/admin/src/pages/Analytics.jsx +19 -19
  4. package/admin/src/pages/EmailDesigner/EditorPage.jsx +33 -33
  5. package/admin/src/pages/EmailDesigner/TemplateList.jsx +36 -36
  6. package/admin/src/pages/HomePage.jsx +25 -88
  7. package/admin/src/pages/LicensePage.jsx +12 -6
  8. package/admin/src/pages/RoutingRules.jsx +21 -21
  9. package/admin/src/pages/Settings.jsx +3 -3
  10. package/admin/src/utils/theme.js +85 -0
  11. package/dist/_chunks/{App-DYNCyt54.mjs → App-BZaHrE0R.mjs} +184 -200
  12. package/dist/_chunks/{App-DF9vAGXX.js → App-Bze8Ixs_.js} +184 -200
  13. package/dist/_chunks/{LicensePage-CJXwPnEe.js → LicensePage-Bg72gy8w.js} +12 -6
  14. package/dist/_chunks/{LicensePage-Bl02myMx.mjs → LicensePage-ndUhjynY.mjs} +12 -6
  15. package/dist/_chunks/{Settings-zuFQ3pnn.js → Settings-BSFLpt0H.js} +3 -7
  16. package/dist/_chunks/{Settings-C_TmKwcz.mjs → Settings-Ca5UE3c1.mjs} +3 -7
  17. package/dist/admin/index.js +41 -24
  18. package/dist/admin/index.mjs +41 -24
  19. package/dist/server/index.js +602 -665
  20. package/dist/server/index.mjs +602 -665
  21. package/package.json +1 -1
  22. package/server/src/bootstrap.js +16 -16
  23. package/server/src/config/features.js +7 -7
  24. package/server/src/controllers/accounts.js +15 -6
  25. package/server/src/controllers/analytics.js +56 -42
  26. package/server/src/controllers/license.js +9 -7
  27. package/server/src/controllers/oauth.js +9 -9
  28. package/server/src/controllers/routing-rules.js +18 -11
  29. package/server/src/controllers/test.js +111 -193
  30. package/server/src/services/account-manager.js +73 -21
  31. package/server/src/services/analytics.js +88 -72
  32. package/server/src/services/email-designer.js +131 -284
  33. package/server/src/services/email-router.js +69 -43
  34. package/server/src/services/license-guard.js +24 -24
  35. package/server/src/services/oauth.js +11 -11
  36. package/server/src/services/service.js +1 -1
  37. package/server/src/utils/encryption.js +1 -1
@@ -5,9 +5,33 @@ const { encryptCredentials, decryptCredentials } = require('../utils/encryption'
5
5
  /**
6
6
  * Account Manager Service
7
7
  * Manages email accounts (create, update, test, delete)
8
+ * [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
8
9
  */
9
10
 
11
+ const EMAIL_ACCOUNT_UID = 'plugin::magic-mail.email-account';
12
+
10
13
  module.exports = ({ strapi }) => ({
14
+ /**
15
+ * Resolves account ID to documentId (handles both numeric id and documentId)
16
+ * @param {string|number} idOrDocumentId - Either numeric id or documentId
17
+ * @returns {Promise<string|null>} The documentId or null if not found
18
+ */
19
+ async resolveDocumentId(idOrDocumentId) {
20
+ // If it looks like a documentId (not purely numeric), use directly
21
+ if (idOrDocumentId && !/^\d+$/.test(String(idOrDocumentId))) {
22
+ return String(idOrDocumentId);
23
+ }
24
+
25
+ // Otherwise, find by numeric id
26
+ const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
27
+ filters: { id: Number(idOrDocumentId) },
28
+ fields: ['documentId'],
29
+ limit: 1,
30
+ });
31
+
32
+ return accounts.length > 0 ? accounts[0].documentId : null;
33
+ },
34
+
11
35
  /**
12
36
  * Create new email account
13
37
  */
@@ -35,7 +59,7 @@ module.exports = ({ strapi }) => ({
35
59
  await this.unsetAllPrimary();
36
60
  }
37
61
 
38
- const account = await strapi.entityService.create('plugin::magic-mail.email-account', {
62
+ const account = await strapi.documents(EMAIL_ACCOUNT_UID).create({
39
63
  data: {
40
64
  name,
41
65
  provider,
@@ -54,7 +78,7 @@ module.exports = ({ strapi }) => ({
54
78
  },
55
79
  });
56
80
 
57
- strapi.log.info(`[magic-mail] Email account created: ${name}`);
81
+ strapi.log.info(`[magic-mail] [SUCCESS] Email account created: ${name}`);
58
82
 
59
83
  return account;
60
84
  },
@@ -62,8 +86,15 @@ module.exports = ({ strapi }) => ({
62
86
  /**
63
87
  * Update email account
64
88
  */
65
- async updateAccount(accountId, accountData) {
66
- const existingAccount = await strapi.entityService.findOne('plugin::magic-mail.email-account', accountId);
89
+ async updateAccount(idOrDocumentId, accountData) {
90
+ const documentId = await this.resolveDocumentId(idOrDocumentId);
91
+ if (!documentId) {
92
+ throw new Error('Account not found');
93
+ }
94
+
95
+ const existingAccount = await strapi.documents(EMAIL_ACCOUNT_UID).findOne({
96
+ documentId,
97
+ });
67
98
 
68
99
  if (!existingAccount) {
69
100
  throw new Error('Account not found');
@@ -92,7 +123,8 @@ module.exports = ({ strapi }) => ({
92
123
  await this.unsetAllPrimary();
93
124
  }
94
125
 
95
- const updatedAccount = await strapi.entityService.update('plugin::magic-mail.email-account', accountId, {
126
+ const updatedAccount = await strapi.documents(EMAIL_ACCOUNT_UID).update({
127
+ documentId,
96
128
  data: {
97
129
  name,
98
130
  description,
@@ -109,7 +141,7 @@ module.exports = ({ strapi }) => ({
109
141
  },
110
142
  });
111
143
 
112
- strapi.log.info(`[magic-mail] Email account updated: ${name} (Active: ${isActive})`);
144
+ strapi.log.info(`[magic-mail] [SUCCESS] Email account updated: ${name} (Active: ${isActive})`);
113
145
 
114
146
  return updatedAccount;
115
147
  },
@@ -117,8 +149,15 @@ module.exports = ({ strapi }) => ({
117
149
  /**
118
150
  * Test email account
119
151
  */
120
- async testAccount(accountId, testEmail, testOptions = {}) {
121
- const account = await strapi.entityService.findOne('plugin::magic-mail.email-account', accountId);
152
+ async testAccount(idOrDocumentId, testEmail, testOptions = {}) {
153
+ const documentId = await this.resolveDocumentId(idOrDocumentId);
154
+ if (!documentId) {
155
+ throw new Error('Account not found');
156
+ }
157
+
158
+ const account = await strapi.documents(EMAIL_ACCOUNT_UID).findOne({
159
+ documentId,
160
+ });
122
161
 
123
162
  if (!account) {
124
163
  throw new Error('Account not found');
@@ -198,8 +237,8 @@ module.exports = ({ strapi }) => ({
198
237
  * Get all accounts
199
238
  */
200
239
  async getAllAccounts() {
201
- const accounts = await strapi.entityService.findMany('plugin::magic-mail.email-account', {
202
- sort: { priority: 'desc' },
240
+ const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
241
+ sort: [{ priority: 'desc' }],
203
242
  });
204
243
 
205
244
  // Don't return encrypted config in list
@@ -212,8 +251,15 @@ module.exports = ({ strapi }) => ({
212
251
  /**
213
252
  * Get single account with decrypted config (for editing)
214
253
  */
215
- async getAccountWithDecryptedConfig(accountId) {
216
- const account = await strapi.entityService.findOne('plugin::magic-mail.email-account', accountId);
254
+ async getAccountWithDecryptedConfig(idOrDocumentId) {
255
+ const documentId = await this.resolveDocumentId(idOrDocumentId);
256
+ if (!documentId) {
257
+ throw new Error('Account not found');
258
+ }
259
+
260
+ const account = await strapi.documents(EMAIL_ACCOUNT_UID).findOne({
261
+ documentId,
262
+ });
217
263
 
218
264
  if (!account) {
219
265
  throw new Error('Account not found');
@@ -231,21 +277,27 @@ module.exports = ({ strapi }) => ({
231
277
  /**
232
278
  * Delete account
233
279
  */
234
- async deleteAccount(accountId) {
235
- await strapi.entityService.delete('plugin::magic-mail.email-account', accountId);
236
- strapi.log.info(`[magic-mail] Account deleted: ${accountId}`);
280
+ async deleteAccount(idOrDocumentId) {
281
+ const documentId = await this.resolveDocumentId(idOrDocumentId);
282
+ if (!documentId) {
283
+ throw new Error('Account not found');
284
+ }
285
+
286
+ await strapi.documents(EMAIL_ACCOUNT_UID).delete({ documentId });
287
+ strapi.log.info(`[magic-mail] Account deleted: ${documentId}`);
237
288
  },
238
289
 
239
290
  /**
240
291
  * Unset all primary flags
241
292
  */
242
293
  async unsetAllPrimary() {
243
- const accounts = await strapi.entityService.findMany('plugin::magic-mail.email-account', {
294
+ const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
244
295
  filters: { isPrimary: true },
245
296
  });
246
297
 
247
298
  for (const account of accounts) {
248
- await strapi.entityService.update('plugin::magic-mail.email-account', account.id, {
299
+ await strapi.documents(EMAIL_ACCOUNT_UID).update({
300
+ documentId: account.documentId,
249
301
  data: { isPrimary: false },
250
302
  });
251
303
  }
@@ -255,7 +307,7 @@ module.exports = ({ strapi }) => ({
255
307
  * Reset daily/hourly counters (called by cron)
256
308
  */
257
309
  async resetCounters(type = 'daily') {
258
- const accounts = await strapi.entityService.findMany('plugin::magic-mail.email-account');
310
+ const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({});
259
311
 
260
312
  for (const account of accounts) {
261
313
  const updateData = {};
@@ -266,12 +318,12 @@ module.exports = ({ strapi }) => ({
266
318
  updateData.emailsSentThisHour = 0;
267
319
  }
268
320
 
269
- await strapi.entityService.update('plugin::magic-mail.email-account', account.id, {
321
+ await strapi.documents(EMAIL_ACCOUNT_UID).update({
322
+ documentId: account.documentId,
270
323
  data: updateData,
271
324
  });
272
325
  }
273
326
 
274
- strapi.log.info(`[magic-mail] ${type} counters reset`);
327
+ strapi.log.info(`[magic-mail] [SUCCESS] ${type} counters reset`);
275
328
  },
276
329
  });
277
-
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * Analytics Service
3
3
  * Handles email tracking, statistics, and user activity
4
+ *
5
+ * [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
4
6
  */
5
7
 
6
8
  'use strict';
7
9
 
8
10
  const crypto = require('crypto');
9
11
 
12
+ // Content Type UIDs
13
+ const EMAIL_LOG_UID = 'plugin::magic-mail.email-log';
14
+ const EMAIL_EVENT_UID = 'plugin::magic-mail.email-event';
15
+ const EMAIL_LINK_UID = 'plugin::magic-mail.email-link';
16
+
10
17
  module.exports = ({ strapi }) => ({
11
18
  /**
12
19
  * Generate unique email ID for tracking
@@ -32,7 +39,7 @@ module.exports = ({ strapi }) => ({
32
39
  async createEmailLog(data) {
33
40
  const emailId = this.generateEmailId();
34
41
 
35
- const logEntry = await strapi.db.query('plugin::magic-mail.email-log').create({
42
+ const logEntry = await strapi.documents(EMAIL_LOG_UID).create({
36
43
  data: {
37
44
  emailId,
38
45
  user: data.userId || null,
@@ -48,9 +55,9 @@ module.exports = ({ strapi }) => ({
48
55
  },
49
56
  });
50
57
 
51
- strapi.log.info(`[magic-mail] Email log created: ${emailId}`);
58
+ strapi.log.info(`[magic-mail] [SUCCESS] Email log created: ${emailId}`);
52
59
  if (data.templateId) {
53
- strapi.log.info(`[magic-mail] 📋 Template tracked: ${data.templateName || 'Unknown'} (ID: ${data.templateId})`);
60
+ strapi.log.info(`[magic-mail] [INFO] Template tracked: ${data.templateName || 'Unknown'} (ID: ${data.templateId})`);
54
61
  }
55
62
  return logEntry;
56
63
  },
@@ -60,9 +67,9 @@ module.exports = ({ strapi }) => ({
60
67
  */
61
68
  async recordOpen(emailId, recipientHash, req) {
62
69
  try {
63
- // Find email log
64
- const emailLog = await strapi.db.query('plugin::magic-mail.email-log').findOne({
65
- where: { emailId },
70
+ // Find email log using Document Service
71
+ const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
72
+ filters: { emailId },
66
73
  });
67
74
 
68
75
  if (!emailLog) {
@@ -79,20 +86,20 @@ module.exports = ({ strapi }) => ({
79
86
 
80
87
  const now = new Date();
81
88
 
82
- // Update email log counters
83
- await strapi.db.query('plugin::magic-mail.email-log').update({
84
- where: { id: emailLog.id },
89
+ // Update email log counters using Document Service
90
+ await strapi.documents(EMAIL_LOG_UID).update({
91
+ documentId: emailLog.documentId,
85
92
  data: {
86
- openCount: emailLog.openCount + 1,
93
+ openCount: (emailLog.openCount || 0) + 1,
87
94
  firstOpenedAt: emailLog.firstOpenedAt || now,
88
95
  lastOpenedAt: now,
89
96
  },
90
97
  });
91
98
 
92
99
  // Create event record
93
- const event = await strapi.db.query('plugin::magic-mail.email-event').create({
100
+ const event = await strapi.documents(EMAIL_EVENT_UID).create({
94
101
  data: {
95
- emailLog: emailLog.id,
102
+ emailLog: emailLog.documentId,
96
103
  type: 'open',
97
104
  timestamp: now,
98
105
  ipAddress: req.ip || req.headers['x-forwarded-for'] || null,
@@ -101,7 +108,7 @@ module.exports = ({ strapi }) => ({
101
108
  },
102
109
  });
103
110
 
104
- strapi.log.info(`[magic-mail] 📧 Email opened: ${emailId} (count: ${emailLog.openCount + 1})`);
111
+ strapi.log.info(`[magic-mail] [EMAIL] Email opened: ${emailId} (count: ${(emailLog.openCount || 0) + 1})`);
105
112
  return event;
106
113
  } catch (error) {
107
114
  strapi.log.error('[magic-mail] Error recording open:', error);
@@ -114,9 +121,9 @@ module.exports = ({ strapi }) => ({
114
121
  */
115
122
  async recordClick(emailId, linkHash, recipientHash, targetUrl, req) {
116
123
  try {
117
- // Find email log
118
- const emailLog = await strapi.db.query('plugin::magic-mail.email-log').findOne({
119
- where: { emailId },
124
+ // Find email log using Document Service
125
+ const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
126
+ filters: { emailId },
120
127
  });
121
128
 
122
129
  if (!emailLog) {
@@ -132,17 +139,17 @@ module.exports = ({ strapi }) => ({
132
139
  const now = new Date();
133
140
 
134
141
  // Update click count
135
- await strapi.db.query('plugin::magic-mail.email-log').update({
136
- where: { id: emailLog.id },
142
+ await strapi.documents(EMAIL_LOG_UID).update({
143
+ documentId: emailLog.documentId,
137
144
  data: {
138
- clickCount: emailLog.clickCount + 1,
145
+ clickCount: (emailLog.clickCount || 0) + 1,
139
146
  },
140
147
  });
141
148
 
142
149
  // Create event record
143
- const event = await strapi.db.query('plugin::magic-mail.email-event').create({
150
+ const event = await strapi.documents(EMAIL_EVENT_UID).create({
144
151
  data: {
145
- emailLog: emailLog.id,
152
+ emailLog: emailLog.documentId,
146
153
  type: 'click',
147
154
  timestamp: now,
148
155
  ipAddress: req.ip || req.headers['x-forwarded-for'] || null,
@@ -152,7 +159,7 @@ module.exports = ({ strapi }) => ({
152
159
  },
153
160
  });
154
161
 
155
- strapi.log.info(`[magic-mail] 🖱️ Link clicked: ${emailId} ${targetUrl}`);
162
+ strapi.log.info(`[magic-mail] [CLICK] Link clicked: ${emailId} -> ${targetUrl}`);
156
163
  return event;
157
164
  } catch (error) {
158
165
  strapi.log.error('[magic-mail] Error recording click:', error);
@@ -162,36 +169,41 @@ module.exports = ({ strapi }) => ({
162
169
 
163
170
  /**
164
171
  * Get analytics statistics
172
+ * Note: Document Service doesn't have count() - using findMany for counting
165
173
  */
166
174
  async getStats(filters = {}) {
167
- const where = {};
175
+ const baseFilters = {};
168
176
 
177
+ // Filter by user relation - use documentId for Strapi v5
169
178
  if (filters.userId) {
170
- where.user = filters.userId;
179
+ baseFilters.user = { documentId: filters.userId };
171
180
  }
172
181
  if (filters.templateId) {
173
- where.templateId = filters.templateId;
182
+ baseFilters.templateId = filters.templateId;
174
183
  }
175
184
  if (filters.accountId) {
176
- where.accountId = filters.accountId;
185
+ baseFilters.accountId = filters.accountId;
177
186
  }
178
187
  if (filters.dateFrom) {
179
- where.sentAt = { $gte: new Date(filters.dateFrom) };
188
+ baseFilters.sentAt = { $gte: new Date(filters.dateFrom) };
180
189
  }
181
190
  if (filters.dateTo) {
182
- where.sentAt = { ...where.sentAt, $lte: new Date(filters.dateTo) };
191
+ baseFilters.sentAt = { ...baseFilters.sentAt, $lte: new Date(filters.dateTo) };
183
192
  }
184
193
 
194
+ // Use native count() method for efficient counting with filters
185
195
  const [totalSent, totalOpened, totalClicked, totalBounced] = await Promise.all([
186
- strapi.db.query('plugin::magic-mail.email-log').count({ where }),
187
- strapi.db.query('plugin::magic-mail.email-log').count({
188
- where: { ...where, openCount: { $gt: 0 } },
196
+ strapi.documents(EMAIL_LOG_UID).count({
197
+ filters: baseFilters,
189
198
  }),
190
- strapi.db.query('plugin::magic-mail.email-log').count({
191
- where: { ...where, clickCount: { $gt: 0 } },
199
+ strapi.documents(EMAIL_LOG_UID).count({
200
+ filters: { ...baseFilters, openCount: { $gt: 0 } },
192
201
  }),
193
- strapi.db.query('plugin::magic-mail.email-log').count({
194
- where: { ...where, bounced: true },
202
+ strapi.documents(EMAIL_LOG_UID).count({
203
+ filters: { ...baseFilters, clickCount: { $gt: 0 } },
204
+ }),
205
+ strapi.documents(EMAIL_LOG_UID).count({
206
+ filters: { ...baseFilters, bounced: true },
195
207
  }),
196
208
  ]);
197
209
 
@@ -216,8 +228,9 @@ module.exports = ({ strapi }) => ({
216
228
  async getEmailLogs(filters = {}, pagination = {}) {
217
229
  const where = {};
218
230
 
231
+ // Filter by user relation - use documentId for Strapi v5
219
232
  if (filters.userId) {
220
- where.user = filters.userId;
233
+ where.user = { documentId: filters.userId };
221
234
  }
222
235
  if (filters.templateId) {
223
236
  where.templateId = filters.templateId;
@@ -234,14 +247,17 @@ module.exports = ({ strapi }) => ({
234
247
  const pageSize = pagination.pageSize || 25;
235
248
 
236
249
  const [logs, total] = await Promise.all([
237
- strapi.db.query('plugin::magic-mail.email-log').findMany({
238
- where,
239
- orderBy: { sentAt: 'desc' },
250
+ strapi.documents(EMAIL_LOG_UID).findMany({
251
+ filters: where,
252
+ sort: [{ sentAt: 'desc' }],
240
253
  limit: pageSize,
241
254
  offset: (page - 1) * pageSize,
242
255
  populate: ['user'],
243
256
  }),
244
- strapi.db.query('plugin::magic-mail.email-log').count({ where }),
257
+ // Get total count using native count() method
258
+ strapi.documents(EMAIL_LOG_UID).count({
259
+ filters: where,
260
+ }),
245
261
  ]);
246
262
 
247
263
  return {
@@ -259,8 +275,8 @@ module.exports = ({ strapi }) => ({
259
275
  * Get email log details with events
260
276
  */
261
277
  async getEmailLogDetails(emailId) {
262
- const emailLog = await strapi.db.query('plugin::magic-mail.email-log').findOne({
263
- where: { emailId },
278
+ const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
279
+ filters: { emailId },
264
280
  populate: ['user', 'events'],
265
281
  });
266
282
 
@@ -271,9 +287,10 @@ module.exports = ({ strapi }) => ({
271
287
  * Get user email activity
272
288
  */
273
289
  async getUserActivity(userId) {
274
- const emailLogs = await strapi.db.query('plugin::magic-mail.email-log').findMany({
275
- where: { user: userId },
276
- orderBy: { sentAt: 'desc' },
290
+ // Filter by user relation - use documentId for Strapi v5
291
+ const emailLogs = await strapi.documents(EMAIL_LOG_UID).findMany({
292
+ filters: { user: { documentId: userId } },
293
+ sort: [{ sentAt: 'desc' }],
277
294
  limit: 50,
278
295
  });
279
296
 
@@ -326,8 +343,8 @@ module.exports = ({ strapi }) => ({
326
343
  const baseUrl = strapi.config.get('server.url') || 'http://localhost:1337';
327
344
 
328
345
  // Get the email log for storing link associations
329
- const emailLog = await strapi.db.query('plugin::magic-mail.email-log').findOne({
330
- where: { emailId },
346
+ const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
347
+ filters: { emailId },
331
348
  });
332
349
 
333
350
  if (!emailLog) {
@@ -351,17 +368,17 @@ module.exports = ({ strapi }) => ({
351
368
  const originalUrl = match[1];
352
369
 
353
370
  // Debug: Log what we found
354
- strapi.log.debug(`[magic-mail] 🔍 Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? '...' : ''}`);
371
+ strapi.log.debug(`[magic-mail] [CHECK] Found link: ${originalUrl.substring(0, 100)}${originalUrl.length > 100 ? '...' : ''}`);
355
372
 
356
373
  // Skip if already a tracking link or anchor
357
374
  if (originalUrl.startsWith('#') || originalUrl.includes('/track/click/')) {
358
- strapi.log.debug(`[magic-mail] ⏭️ Skipping (anchor or already tracked)`);
375
+ strapi.log.debug(`[magic-mail] [SKIP] Skipping (anchor or already tracked)`);
359
376
  continue;
360
377
  }
361
378
 
362
379
  // Skip relative URLs without protocol (internal anchors, relative paths)
363
380
  if (!originalUrl.match(/^https?:\/\//i) && !originalUrl.startsWith('/')) {
364
- strapi.log.debug(`[magic-mail] ⏭️ Skipping relative URL: ${originalUrl}`);
381
+ strapi.log.debug(`[magic-mail] [SKIP] Skipping relative URL: ${originalUrl}`);
365
382
  continue;
366
383
  }
367
384
 
@@ -378,7 +395,7 @@ module.exports = ({ strapi }) => ({
378
395
  const trackingUrl = `${baseUrl}/api/magic-mail/track/click/${emailId}/${linkHash}/${recipientHash}`;
379
396
 
380
397
  linkCount++;
381
- strapi.log.info(`[magic-mail] 🔗 Link ${linkCount}: ${originalUrl} → ${trackingUrl}`);
398
+ strapi.log.info(`[magic-mail] [LINK] Link ${linkCount}: ${originalUrl} → ${trackingUrl}`);
382
399
 
383
400
  // Store replacement info
384
401
  replacements.push({
@@ -390,7 +407,7 @@ module.exports = ({ strapi }) => ({
390
407
  // Store all link mappings in database
391
408
  for (const mapping of linkMappings) {
392
409
  try {
393
- await this.storeLinkMapping(emailLog.id, mapping.linkHash, mapping.originalUrl);
410
+ await this.storeLinkMapping(emailLog.documentId, mapping.linkHash, mapping.originalUrl);
394
411
  } catch (err) {
395
412
  strapi.log.error('[magic-mail] Error storing link mapping:', err);
396
413
  }
@@ -403,9 +420,9 @@ module.exports = ({ strapi }) => ({
403
420
  }
404
421
 
405
422
  if (linkCount > 0) {
406
- strapi.log.info(`[magic-mail] Rewrote ${linkCount} links for click tracking`);
423
+ strapi.log.info(`[magic-mail] [SUCCESS] Rewrote ${linkCount} links for click tracking`);
407
424
  } else {
408
- strapi.log.warn(`[magic-mail] ⚠️ No links found in email HTML for tracking!`);
425
+ strapi.log.warn(`[magic-mail] [WARNING] No links found in email HTML for tracking!`);
409
426
  }
410
427
 
411
428
  return result;
@@ -414,12 +431,12 @@ module.exports = ({ strapi }) => ({
414
431
  /**
415
432
  * Store link mapping in database
416
433
  */
417
- async storeLinkMapping(emailLogId, linkHash, originalUrl) {
434
+ async storeLinkMapping(emailLogDocId, linkHash, originalUrl) {
418
435
  try {
419
- // Check if link already exists
420
- const existing = await strapi.db.query('plugin::magic-mail.email-link').findOne({
421
- where: {
422
- emailLog: emailLogId,
436
+ // Check if link already exists - filter relation with documentId object
437
+ const existing = await strapi.documents(EMAIL_LINK_UID).findFirst({
438
+ filters: {
439
+ emailLog: { documentId: emailLogDocId },
423
440
  linkHash,
424
441
  },
425
442
  });
@@ -430,16 +447,16 @@ module.exports = ({ strapi }) => ({
430
447
  }
431
448
 
432
449
  // Create new link mapping
433
- const linkMapping = await strapi.db.query('plugin::magic-mail.email-link').create({
450
+ const linkMapping = await strapi.documents(EMAIL_LINK_UID).create({
434
451
  data: {
435
- emailLog: emailLogId,
452
+ emailLog: emailLogDocId,
436
453
  linkHash,
437
454
  originalUrl,
438
455
  clickCount: 0,
439
456
  },
440
457
  });
441
458
 
442
- strapi.log.debug(`[magic-mail] 💾 Stored link mapping: ${linkHash} → ${originalUrl}`);
459
+ strapi.log.debug(`[magic-mail] [SAVE] Stored link mapping: ${linkHash} → ${originalUrl}`);
443
460
  return linkMapping;
444
461
  } catch (error) {
445
462
  strapi.log.error('[magic-mail] Error storing link mapping:', error);
@@ -453,8 +470,8 @@ module.exports = ({ strapi }) => ({
453
470
  async getOriginalUrlFromHash(emailId, linkHash) {
454
471
  try {
455
472
  // Find the email log
456
- const emailLog = await strapi.db.query('plugin::magic-mail.email-log').findOne({
457
- where: { emailId },
473
+ const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
474
+ filters: { emailId },
458
475
  });
459
476
 
460
477
  if (!emailLog) {
@@ -462,10 +479,10 @@ module.exports = ({ strapi }) => ({
462
479
  return null;
463
480
  }
464
481
 
465
- // Find the link mapping
466
- const linkMapping = await strapi.db.query('plugin::magic-mail.email-link').findOne({
467
- where: {
468
- emailLog: emailLog.id,
482
+ // Find the link mapping - filter relation with documentId object (Strapi v5)
483
+ const linkMapping = await strapi.documents(EMAIL_LINK_UID).findFirst({
484
+ filters: {
485
+ emailLog: { documentId: emailLog.documentId },
469
486
  linkHash,
470
487
  },
471
488
  });
@@ -477,10 +494,10 @@ module.exports = ({ strapi }) => ({
477
494
 
478
495
  // Update click tracking on the link itself
479
496
  const now = new Date();
480
- await strapi.db.query('plugin::magic-mail.email-link').update({
481
- where: { id: linkMapping.id },
497
+ await strapi.documents(EMAIL_LINK_UID).update({
498
+ documentId: linkMapping.documentId,
482
499
  data: {
483
- clickCount: linkMapping.clickCount + 1,
500
+ clickCount: (linkMapping.clickCount || 0) + 1,
484
501
  firstClickedAt: linkMapping.firstClickedAt || now,
485
502
  lastClickedAt: now,
486
503
  },
@@ -493,4 +510,3 @@ module.exports = ({ strapi }) => ({
493
510
  }
494
511
  },
495
512
  });
496
-