strapi-plugin-magic-mail 2.2.4 → 2.2.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/dist/server/index.js +1 -1
- package/dist/server/index.mjs +1 -1
- package/package.json +1 -3
- package/admin/jsconfig.json +0 -10
- package/admin/src/components/AddAccountModal.jsx +0 -1943
- package/admin/src/components/Initializer.jsx +0 -14
- package/admin/src/components/LicenseGuard.jsx +0 -475
- package/admin/src/components/PluginIcon.jsx +0 -5
- package/admin/src/hooks/useAuthRefresh.js +0 -44
- package/admin/src/hooks/useLicense.js +0 -158
- package/admin/src/index.js +0 -87
- package/admin/src/pages/Analytics.jsx +0 -762
- package/admin/src/pages/App.jsx +0 -111
- package/admin/src/pages/EmailDesigner/EditorPage.jsx +0 -1424
- package/admin/src/pages/EmailDesigner/TemplateList.jsx +0 -1807
- package/admin/src/pages/HomePage.jsx +0 -1170
- package/admin/src/pages/LicensePage.jsx +0 -430
- package/admin/src/pages/RoutingRules.jsx +0 -1141
- package/admin/src/pages/Settings.jsx +0 -603
- package/admin/src/pluginId.js +0 -3
- package/admin/src/translations/de.json +0 -71
- package/admin/src/translations/en.json +0 -70
- package/admin/src/translations/es.json +0 -71
- package/admin/src/translations/fr.json +0 -71
- package/admin/src/translations/pt.json +0 -71
- package/admin/src/utils/fetchWithRetry.js +0 -123
- package/admin/src/utils/getTranslation.js +0 -5
- package/admin/src/utils/theme.js +0 -85
- package/server/jsconfig.json +0 -10
- package/server/src/bootstrap.js +0 -157
- package/server/src/config/features.js +0 -260
- package/server/src/config/index.js +0 -9
- package/server/src/content-types/email-account/schema.json +0 -93
- package/server/src/content-types/email-event/index.js +0 -8
- package/server/src/content-types/email-event/schema.json +0 -57
- package/server/src/content-types/email-link/index.js +0 -8
- package/server/src/content-types/email-link/schema.json +0 -49
- package/server/src/content-types/email-log/index.js +0 -8
- package/server/src/content-types/email-log/schema.json +0 -106
- package/server/src/content-types/email-template/schema.json +0 -74
- package/server/src/content-types/email-template-version/schema.json +0 -60
- package/server/src/content-types/index.js +0 -33
- package/server/src/content-types/routing-rule/schema.json +0 -59
- package/server/src/controllers/accounts.js +0 -229
- package/server/src/controllers/analytics.js +0 -361
- package/server/src/controllers/controller.js +0 -26
- package/server/src/controllers/email-designer.js +0 -474
- package/server/src/controllers/index.js +0 -21
- package/server/src/controllers/license.js +0 -269
- package/server/src/controllers/oauth.js +0 -474
- package/server/src/controllers/routing-rules.js +0 -129
- package/server/src/controllers/test.js +0 -301
- package/server/src/destroy.js +0 -27
- package/server/src/index.js +0 -25
- package/server/src/middlewares/index.js +0 -3
- package/server/src/policies/index.js +0 -3
- package/server/src/register.js +0 -5
- package/server/src/routes/admin.js +0 -469
- package/server/src/routes/content-api.js +0 -37
- package/server/src/routes/index.js +0 -9
- package/server/src/services/account-manager.js +0 -329
- package/server/src/services/analytics.js +0 -512
- package/server/src/services/email-designer.js +0 -717
- package/server/src/services/email-router.js +0 -1446
- package/server/src/services/index.js +0 -17
- package/server/src/services/license-guard.js +0 -423
- package/server/src/services/oauth.js +0 -515
- package/server/src/services/service.js +0 -7
- package/server/src/utils/encryption.js +0 -81
- package/server/src/utils/logger.js +0 -84
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analytics Controller
|
|
3
|
-
* Handles tracking endpoints and analytics API
|
|
4
|
-
* [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
const EMAIL_LOG_UID = 'plugin::magic-mail.email-log';
|
|
10
|
-
const EMAIL_EVENT_UID = 'plugin::magic-mail.email-event';
|
|
11
|
-
const EMAIL_ACCOUNT_UID = 'plugin::magic-mail.email-account';
|
|
12
|
-
|
|
13
|
-
module.exports = ({ strapi }) => ({
|
|
14
|
-
/**
|
|
15
|
-
* Tracking pixel endpoint
|
|
16
|
-
* GET /magic-mail/track/open/:emailId/:recipientHash
|
|
17
|
-
*/
|
|
18
|
-
async trackOpen(ctx) {
|
|
19
|
-
const { emailId, recipientHash } = ctx.params;
|
|
20
|
-
|
|
21
|
-
// Record the open event
|
|
22
|
-
await strapi.plugin('magic-mail').service('analytics').recordOpen(emailId, recipientHash, ctx.request);
|
|
23
|
-
|
|
24
|
-
// Return 1x1 transparent GIF
|
|
25
|
-
const pixel = Buffer.from(
|
|
26
|
-
'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
|
|
27
|
-
'base64'
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
ctx.type = 'image/gif';
|
|
31
|
-
ctx.body = pixel;
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Click tracking endpoint
|
|
36
|
-
* GET /magic-mail/track/click/:emailId/:linkHash/:recipientHash
|
|
37
|
-
*/
|
|
38
|
-
async trackClick(ctx) {
|
|
39
|
-
const { emailId, linkHash, recipientHash } = ctx.params;
|
|
40
|
-
let { url } = ctx.query;
|
|
41
|
-
|
|
42
|
-
// Try to get URL from database if not in query string
|
|
43
|
-
if (!url) {
|
|
44
|
-
const analyticsService = strapi.plugin('magic-mail').service('analytics');
|
|
45
|
-
url = await analyticsService.getOriginalUrlFromHash(emailId, linkHash);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!url) {
|
|
49
|
-
return ctx.badRequest('Missing target URL');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Record the click event
|
|
53
|
-
await strapi
|
|
54
|
-
.plugin('magic-mail')
|
|
55
|
-
.service('analytics')
|
|
56
|
-
.recordClick(emailId, linkHash, recipientHash, url, ctx.request);
|
|
57
|
-
|
|
58
|
-
// Redirect to the original URL
|
|
59
|
-
ctx.redirect(url);
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get analytics statistics
|
|
64
|
-
* GET /magic-mail/analytics/stats
|
|
65
|
-
*/
|
|
66
|
-
async getStats(ctx) {
|
|
67
|
-
try {
|
|
68
|
-
const filters = {
|
|
69
|
-
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
70
|
-
userId: ctx.query.userId || null,
|
|
71
|
-
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
72
|
-
accountId: ctx.query.accountId ? parseInt(ctx.query.accountId) : null,
|
|
73
|
-
dateFrom: ctx.query.dateFrom || null,
|
|
74
|
-
dateTo: ctx.query.dateTo || null,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Remove null values
|
|
78
|
-
Object.keys(filters).forEach(key => filters[key] === null && delete filters[key]);
|
|
79
|
-
|
|
80
|
-
const stats = await strapi.plugin('magic-mail').service('analytics').getStats(filters);
|
|
81
|
-
|
|
82
|
-
return ctx.send({
|
|
83
|
-
success: true,
|
|
84
|
-
data: stats,
|
|
85
|
-
});
|
|
86
|
-
} catch (error) {
|
|
87
|
-
ctx.throw(500, error);
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Get email logs
|
|
93
|
-
* GET /magic-mail/analytics/emails
|
|
94
|
-
*/
|
|
95
|
-
async getEmailLogs(ctx) {
|
|
96
|
-
try {
|
|
97
|
-
const filters = {
|
|
98
|
-
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
99
|
-
userId: ctx.query.userId || null,
|
|
100
|
-
templateId: ctx.query.templateId ? parseInt(ctx.query.templateId) : null,
|
|
101
|
-
search: ctx.query.search || null,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const pagination = {
|
|
105
|
-
page: ctx.query.page ? parseInt(ctx.query.page) : 1,
|
|
106
|
-
pageSize: ctx.query.pageSize ? parseInt(ctx.query.pageSize) : 25,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// Remove null values
|
|
110
|
-
Object.keys(filters).forEach(key => filters[key] === null && delete filters[key]);
|
|
111
|
-
|
|
112
|
-
const result = await strapi
|
|
113
|
-
.plugin('magic-mail')
|
|
114
|
-
.service('analytics')
|
|
115
|
-
.getEmailLogs(filters, pagination);
|
|
116
|
-
|
|
117
|
-
return ctx.send({
|
|
118
|
-
success: true,
|
|
119
|
-
...result,
|
|
120
|
-
});
|
|
121
|
-
} catch (error) {
|
|
122
|
-
ctx.throw(500, error);
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get email log details
|
|
128
|
-
* GET /magic-mail/analytics/emails/:emailId
|
|
129
|
-
*/
|
|
130
|
-
async getEmailDetails(ctx) {
|
|
131
|
-
try {
|
|
132
|
-
const { emailId } = ctx.params;
|
|
133
|
-
|
|
134
|
-
const emailLog = await strapi
|
|
135
|
-
.plugin('magic-mail')
|
|
136
|
-
.service('analytics')
|
|
137
|
-
.getEmailLogDetails(emailId);
|
|
138
|
-
|
|
139
|
-
if (!emailLog) {
|
|
140
|
-
return ctx.notFound('Email log not found');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return ctx.send({
|
|
144
|
-
success: true,
|
|
145
|
-
data: emailLog,
|
|
146
|
-
});
|
|
147
|
-
} catch (error) {
|
|
148
|
-
ctx.throw(500, error);
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get user email activity
|
|
154
|
-
* GET /magic-mail/analytics/users/:userId
|
|
155
|
-
* Note: userId is documentId (string) in Strapi v5
|
|
156
|
-
*/
|
|
157
|
-
async getUserActivity(ctx) {
|
|
158
|
-
try {
|
|
159
|
-
const { userId } = ctx.params;
|
|
160
|
-
|
|
161
|
-
// userId is documentId (string) in Strapi v5, NOT parseInt!
|
|
162
|
-
const activity = await strapi
|
|
163
|
-
.plugin('magic-mail')
|
|
164
|
-
.service('analytics')
|
|
165
|
-
.getUserActivity(userId);
|
|
166
|
-
|
|
167
|
-
return ctx.send({
|
|
168
|
-
success: true,
|
|
169
|
-
data: activity,
|
|
170
|
-
});
|
|
171
|
-
} catch (error) {
|
|
172
|
-
ctx.throw(500, error);
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Debug Analytics - Check database state
|
|
178
|
-
* GET /magic-mail/analytics/debug
|
|
179
|
-
*/
|
|
180
|
-
async debug(ctx) {
|
|
181
|
-
try {
|
|
182
|
-
strapi.log.info('[magic-mail] [CHECK] Running Analytics Debug...');
|
|
183
|
-
|
|
184
|
-
// Get email logs using Document Service
|
|
185
|
-
const emailLogs = await strapi.documents(EMAIL_LOG_UID).findMany({
|
|
186
|
-
limit: 10,
|
|
187
|
-
sort: [{ sentAt: 'desc' }],
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Get email events using Document Service
|
|
191
|
-
const emailEvents = await strapi.documents(EMAIL_EVENT_UID).findMany({
|
|
192
|
-
limit: 20,
|
|
193
|
-
sort: [{ timestamp: 'desc' }],
|
|
194
|
-
populate: ['emailLog'],
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Get stats
|
|
198
|
-
const analyticsService = strapi.plugin('magic-mail').service('analytics');
|
|
199
|
-
const stats = await analyticsService.getStats();
|
|
200
|
-
|
|
201
|
-
// Get active accounts using Document Service
|
|
202
|
-
const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
203
|
-
filters: { isActive: true },
|
|
204
|
-
fields: ['id', 'name', 'provider', 'fromEmail', 'emailsSentToday', 'totalEmailsSent'],
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Generate sample tracking URLs
|
|
208
|
-
let sampleTrackingUrls = null;
|
|
209
|
-
if (emailLogs.length > 0) {
|
|
210
|
-
const testLog = emailLogs[0];
|
|
211
|
-
const testHash = analyticsService.generateRecipientHash(testLog.emailId, testLog.recipient);
|
|
212
|
-
|
|
213
|
-
const baseUrl = strapi.config.get('server.url') || 'http://localhost:1337';
|
|
214
|
-
sampleTrackingUrls = {
|
|
215
|
-
trackingPixel: `${baseUrl}/api/magic-mail/track/open/${testLog.emailId}/${testHash}`,
|
|
216
|
-
clickTracking: `${baseUrl}/api/magic-mail/track/click/${testLog.emailId}/test/${testHash}?url=https://example.com`,
|
|
217
|
-
emailId: testLog.emailId,
|
|
218
|
-
recipient: testLog.recipient,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return ctx.send({
|
|
223
|
-
success: true,
|
|
224
|
-
debug: {
|
|
225
|
-
timestamp: new Date().toISOString(),
|
|
226
|
-
stats,
|
|
227
|
-
emailLogsCount: emailLogs.length,
|
|
228
|
-
emailEventsCount: emailEvents.length,
|
|
229
|
-
activeAccountsCount: accounts.length,
|
|
230
|
-
recentEmailLogs: emailLogs.map(log => ({
|
|
231
|
-
emailId: log.emailId,
|
|
232
|
-
recipient: log.recipient,
|
|
233
|
-
subject: log.subject,
|
|
234
|
-
sentAt: log.sentAt,
|
|
235
|
-
openCount: log.openCount,
|
|
236
|
-
clickCount: log.clickCount,
|
|
237
|
-
firstOpenedAt: log.firstOpenedAt,
|
|
238
|
-
accountName: log.accountName,
|
|
239
|
-
templateName: log.templateName,
|
|
240
|
-
})),
|
|
241
|
-
recentEvents: emailEvents.map(event => ({
|
|
242
|
-
type: event.type,
|
|
243
|
-
timestamp: event.timestamp,
|
|
244
|
-
emailId: event.emailLog?.emailId,
|
|
245
|
-
ipAddress: event.ipAddress,
|
|
246
|
-
linkUrl: event.linkUrl,
|
|
247
|
-
})),
|
|
248
|
-
accounts,
|
|
249
|
-
sampleTrackingUrls,
|
|
250
|
-
notes: [
|
|
251
|
-
'If emailLogsCount is 0: Emails are not being tracked (check if enableTracking=true)',
|
|
252
|
-
'If openCount is 0: Tracking pixel not being loaded (check email HTML source)',
|
|
253
|
-
'Test tracking URLs should be publicly accessible without authentication',
|
|
254
|
-
'Check Strapi console logs for tracking events when opening emails',
|
|
255
|
-
],
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
} catch (error) {
|
|
259
|
-
strapi.log.error('[magic-mail] Debug error:', error);
|
|
260
|
-
ctx.throw(500, error);
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Delete single email log
|
|
266
|
-
* DELETE /magic-mail/analytics/emails/:emailId
|
|
267
|
-
*/
|
|
268
|
-
async deleteEmailLog(ctx) {
|
|
269
|
-
try {
|
|
270
|
-
const { emailId } = ctx.params;
|
|
271
|
-
|
|
272
|
-
// Find email log using Document Service
|
|
273
|
-
const emailLog = await strapi.documents(EMAIL_LOG_UID).findFirst({
|
|
274
|
-
filters: { emailId },
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
if (!emailLog) {
|
|
278
|
-
return ctx.notFound('Email log not found');
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Delete associated events - filter relation with documentId object (Strapi v5)
|
|
282
|
-
const events = await strapi.documents(EMAIL_EVENT_UID).findMany({
|
|
283
|
-
filters: { emailLog: { documentId: emailLog.documentId } },
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
for (const event of events) {
|
|
287
|
-
await strapi.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Delete email log
|
|
291
|
-
await strapi.documents(EMAIL_LOG_UID).delete({ documentId: emailLog.documentId });
|
|
292
|
-
|
|
293
|
-
strapi.log.info(`[magic-mail] [DELETE] Deleted email log: ${emailId}`);
|
|
294
|
-
|
|
295
|
-
return ctx.send({
|
|
296
|
-
success: true,
|
|
297
|
-
message: 'Email log deleted successfully',
|
|
298
|
-
});
|
|
299
|
-
} catch (error) {
|
|
300
|
-
strapi.log.error('[magic-mail] Error deleting email log:', error);
|
|
301
|
-
ctx.throw(500, error);
|
|
302
|
-
}
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Clear all email logs
|
|
307
|
-
* DELETE /magic-mail/analytics/emails
|
|
308
|
-
*/
|
|
309
|
-
async clearAllEmailLogs(ctx) {
|
|
310
|
-
try {
|
|
311
|
-
// Optional: Add query params for filtered deletion
|
|
312
|
-
const { olderThan } = ctx.query; // e.g., ?olderThan=2024-01-01
|
|
313
|
-
|
|
314
|
-
const filters = {};
|
|
315
|
-
if (olderThan) {
|
|
316
|
-
filters.sentAt = { $lt: new Date(olderThan) };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Get all email logs to delete using Document Service
|
|
320
|
-
const emailLogs = await strapi.documents(EMAIL_LOG_UID).findMany({
|
|
321
|
-
filters,
|
|
322
|
-
fields: ['id', 'documentId'],
|
|
323
|
-
limit: 100000,
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
if (emailLogs.length === 0) {
|
|
327
|
-
return ctx.send({
|
|
328
|
-
success: true,
|
|
329
|
-
message: 'No email logs to delete',
|
|
330
|
-
deletedCount: 0,
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Delete all associated events and logs
|
|
335
|
-
for (const log of emailLogs) {
|
|
336
|
-
// Delete events for this log - filter relation with documentId object (Strapi v5)
|
|
337
|
-
const events = await strapi.documents(EMAIL_EVENT_UID).findMany({
|
|
338
|
-
filters: { emailLog: { documentId: log.documentId } },
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
for (const event of events) {
|
|
342
|
-
await strapi.documents(EMAIL_EVENT_UID).delete({ documentId: event.documentId });
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Delete the log itself
|
|
346
|
-
await strapi.documents(EMAIL_LOG_UID).delete({ documentId: log.documentId });
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
strapi.log.info(`[magic-mail] [DELETE] Cleared ${emailLogs.length} email logs`);
|
|
350
|
-
|
|
351
|
-
return ctx.send({
|
|
352
|
-
success: true,
|
|
353
|
-
message: `Successfully deleted ${emailLogs.length} email log(s)`,
|
|
354
|
-
deletedCount: emailLogs.length,
|
|
355
|
-
});
|
|
356
|
-
} catch (error) {
|
|
357
|
-
strapi.log.error('[magic-mail] Error clearing email logs:', error);
|
|
358
|
-
ctx.throw(500, error);
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Main Controller
|
|
5
|
-
* Handles email sending requests
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
/**
|
|
10
|
-
* Send email via MagicMail router
|
|
11
|
-
*/
|
|
12
|
-
async send(ctx) {
|
|
13
|
-
try {
|
|
14
|
-
const emailRouter = strapi.plugin('magic-mail').service('email-router');
|
|
15
|
-
const result = await emailRouter.send(ctx.request.body);
|
|
16
|
-
|
|
17
|
-
ctx.body = {
|
|
18
|
-
success: true,
|
|
19
|
-
...result,
|
|
20
|
-
};
|
|
21
|
-
} catch (err) {
|
|
22
|
-
strapi.log.error('[magic-mail] Error sending email:', err);
|
|
23
|
-
ctx.throw(500, err.message || 'Failed to send email');
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
};
|