strapi-plugin-magic-mail 2.2.4 → 2.2.6
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 +0 -2
- 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,329 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { encryptCredentials, decryptCredentials } = require('../utils/encryption');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Account Manager Service
|
|
7
|
-
* Manages email accounts (create, update, test, delete)
|
|
8
|
-
* [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const EMAIL_ACCOUNT_UID = 'plugin::magic-mail.email-account';
|
|
12
|
-
|
|
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
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create new email account
|
|
37
|
-
*/
|
|
38
|
-
async createAccount(accountData) {
|
|
39
|
-
const {
|
|
40
|
-
name,
|
|
41
|
-
provider,
|
|
42
|
-
config,
|
|
43
|
-
fromEmail,
|
|
44
|
-
fromName,
|
|
45
|
-
replyTo,
|
|
46
|
-
isPrimary = false,
|
|
47
|
-
priority = 1,
|
|
48
|
-
dailyLimit = 0,
|
|
49
|
-
hourlyLimit = 0,
|
|
50
|
-
} = accountData;
|
|
51
|
-
|
|
52
|
-
console.log('create account', accountData);
|
|
53
|
-
|
|
54
|
-
// Encrypt sensitive config data
|
|
55
|
-
const encryptedConfig = encryptCredentials(config);
|
|
56
|
-
|
|
57
|
-
// If this is primary, unset other primaries
|
|
58
|
-
if (isPrimary) {
|
|
59
|
-
await this.unsetAllPrimary();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const account = await strapi.documents(EMAIL_ACCOUNT_UID).create({
|
|
63
|
-
data: {
|
|
64
|
-
name,
|
|
65
|
-
provider,
|
|
66
|
-
config: encryptedConfig,
|
|
67
|
-
fromEmail,
|
|
68
|
-
fromName,
|
|
69
|
-
replyTo,
|
|
70
|
-
isPrimary,
|
|
71
|
-
priority,
|
|
72
|
-
dailyLimit,
|
|
73
|
-
hourlyLimit,
|
|
74
|
-
isActive: true,
|
|
75
|
-
emailsSentToday: 0,
|
|
76
|
-
emailsSentThisHour: 0,
|
|
77
|
-
totalEmailsSent: 0,
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
strapi.log.info(`[magic-mail] [SUCCESS] Email account created: ${name}`);
|
|
82
|
-
|
|
83
|
-
return account;
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Update email account
|
|
88
|
-
*/
|
|
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
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!existingAccount) {
|
|
100
|
-
throw new Error('Account not found');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const {
|
|
104
|
-
name,
|
|
105
|
-
description,
|
|
106
|
-
provider,
|
|
107
|
-
config,
|
|
108
|
-
fromEmail,
|
|
109
|
-
fromName,
|
|
110
|
-
replyTo,
|
|
111
|
-
isActive,
|
|
112
|
-
isPrimary,
|
|
113
|
-
priority,
|
|
114
|
-
dailyLimit,
|
|
115
|
-
hourlyLimit,
|
|
116
|
-
} = accountData;
|
|
117
|
-
|
|
118
|
-
// Encrypt sensitive config data
|
|
119
|
-
const encryptedConfig = encryptCredentials(config);
|
|
120
|
-
|
|
121
|
-
// If this is being set to primary, unset other primaries
|
|
122
|
-
if (isPrimary && !existingAccount.isPrimary) {
|
|
123
|
-
await this.unsetAllPrimary();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const updatedAccount = await strapi.documents(EMAIL_ACCOUNT_UID).update({
|
|
127
|
-
documentId,
|
|
128
|
-
data: {
|
|
129
|
-
name,
|
|
130
|
-
description,
|
|
131
|
-
provider,
|
|
132
|
-
config: encryptedConfig,
|
|
133
|
-
fromEmail,
|
|
134
|
-
fromName,
|
|
135
|
-
replyTo,
|
|
136
|
-
isActive,
|
|
137
|
-
isPrimary,
|
|
138
|
-
priority,
|
|
139
|
-
dailyLimit,
|
|
140
|
-
hourlyLimit,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
strapi.log.info(`[magic-mail] [SUCCESS] Email account updated: ${name} (Active: ${isActive})`);
|
|
145
|
-
|
|
146
|
-
return updatedAccount;
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Test email account
|
|
151
|
-
*/
|
|
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
|
-
});
|
|
161
|
-
|
|
162
|
-
if (!account) {
|
|
163
|
-
throw new Error('Account not found');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Use provided test email or default to account's own email
|
|
167
|
-
const recipient = testEmail || account.fromEmail;
|
|
168
|
-
|
|
169
|
-
const emailRouter = strapi.plugin('magic-mail').service('email-router');
|
|
170
|
-
|
|
171
|
-
// Extract test options
|
|
172
|
-
const {
|
|
173
|
-
priority = 'normal',
|
|
174
|
-
type = 'transactional',
|
|
175
|
-
unsubscribeUrl = null,
|
|
176
|
-
} = testOptions;
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
await emailRouter.send({
|
|
180
|
-
to: recipient,
|
|
181
|
-
from: account.fromEmail,
|
|
182
|
-
subject: 'MagicMail Test Email',
|
|
183
|
-
text: `This is a test email from MagicMail account: ${account.name}\n\nPriority: ${priority}\nType: ${type}\n\nProvider: ${account.provider}\nFrom: ${account.fromEmail}\n\nIf you receive this, your email account is configured correctly!`,
|
|
184
|
-
html: `
|
|
185
|
-
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
186
|
-
<h2 style="color: #0EA5E9;">MagicMail Test Email</h2>
|
|
187
|
-
<p>This is a test email from account: <strong>${account.name}</strong></p>
|
|
188
|
-
|
|
189
|
-
<div style="background: #F0F9FF; border: 1px solid #0EA5E9; border-radius: 8px; padding: 15px; margin: 20px 0;">
|
|
190
|
-
<h3 style="margin-top: 0; color: #0369A1;">Test Configuration</h3>
|
|
191
|
-
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
192
|
-
<li><strong>Priority:</strong> ${priority}</li>
|
|
193
|
-
<li><strong>Type:</strong> ${type}</li>
|
|
194
|
-
<li><strong>Provider:</strong> ${account.provider}</li>
|
|
195
|
-
<li><strong>From:</strong> ${account.fromEmail}</li>
|
|
196
|
-
${unsubscribeUrl ? `<li><strong>Unsubscribe URL:</strong> ${unsubscribeUrl}</li>` : ''}
|
|
197
|
-
</ul>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
<div style="background: #DCFCE7; border: 1px solid #22C55E; border-radius: 8px; padding: 15px; margin: 20px 0;">
|
|
201
|
-
<h3 style="margin-top: 0; color: #15803D;">Security Features Active</h3>
|
|
202
|
-
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
203
|
-
<li>TLS/SSL Encryption enforced</li>
|
|
204
|
-
<li>Email content validated</li>
|
|
205
|
-
<li>Proper headers included</li>
|
|
206
|
-
<li>Message-ID generated</li>
|
|
207
|
-
${type === 'marketing' && unsubscribeUrl ? '<li>List-Unsubscribe header added (GDPR/CAN-SPAM)</li>' : ''}
|
|
208
|
-
</ul>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
<p style="color: #6B7280; font-size: 14px; margin-top: 30px;">
|
|
212
|
-
Sent at: ${new Date().toLocaleString()}<br>
|
|
213
|
-
Via: MagicMail Email Router<br>
|
|
214
|
-
Version: 1.0
|
|
215
|
-
</p>
|
|
216
|
-
|
|
217
|
-
${unsubscribeUrl ? `<p style="text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #E5E7EB;"><a href="${unsubscribeUrl}" style="color: #6B7280; font-size: 12px;">Unsubscribe</a></p>` : ''}
|
|
218
|
-
</div>
|
|
219
|
-
`,
|
|
220
|
-
accountName: account.name,
|
|
221
|
-
priority,
|
|
222
|
-
type,
|
|
223
|
-
unsubscribeUrl,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
success: true,
|
|
228
|
-
message: `Test email sent successfully to ${recipient}`,
|
|
229
|
-
testConfig: { priority, type, unsubscribeUrl: !!unsubscribeUrl }
|
|
230
|
-
};
|
|
231
|
-
} catch (error) {
|
|
232
|
-
return { success: false, message: error.message };
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Get all accounts
|
|
238
|
-
*/
|
|
239
|
-
async getAllAccounts() {
|
|
240
|
-
const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
241
|
-
sort: [{ priority: 'desc' }],
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Don't return encrypted config in list
|
|
245
|
-
return accounts.map(account => ({
|
|
246
|
-
...account,
|
|
247
|
-
config: account.config ? '***encrypted***' : null,
|
|
248
|
-
}));
|
|
249
|
-
},
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Get single account with decrypted config (for editing)
|
|
253
|
-
*/
|
|
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
|
-
});
|
|
263
|
-
|
|
264
|
-
if (!account) {
|
|
265
|
-
throw new Error('Account not found');
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Decrypt the config for editing
|
|
269
|
-
const decryptedConfig = account.config ? decryptCredentials(account.config) : {};
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
...account,
|
|
273
|
-
config: decryptedConfig,
|
|
274
|
-
};
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Delete account
|
|
279
|
-
*/
|
|
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}`);
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Unset all primary flags
|
|
292
|
-
*/
|
|
293
|
-
async unsetAllPrimary() {
|
|
294
|
-
const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({
|
|
295
|
-
filters: { isPrimary: true },
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
for (const account of accounts) {
|
|
299
|
-
await strapi.documents(EMAIL_ACCOUNT_UID).update({
|
|
300
|
-
documentId: account.documentId,
|
|
301
|
-
data: { isPrimary: false },
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Reset daily/hourly counters (called by cron)
|
|
308
|
-
*/
|
|
309
|
-
async resetCounters(type = 'daily') {
|
|
310
|
-
const accounts = await strapi.documents(EMAIL_ACCOUNT_UID).findMany({});
|
|
311
|
-
|
|
312
|
-
for (const account of accounts) {
|
|
313
|
-
const updateData = {};
|
|
314
|
-
|
|
315
|
-
if (type === 'daily') {
|
|
316
|
-
updateData.emailsSentToday = 0;
|
|
317
|
-
} else if (type === 'hourly') {
|
|
318
|
-
updateData.emailsSentThisHour = 0;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
await strapi.documents(EMAIL_ACCOUNT_UID).update({
|
|
322
|
-
documentId: account.documentId,
|
|
323
|
-
data: updateData,
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
strapi.log.info(`[magic-mail] [SUCCESS] ${type} counters reset`);
|
|
328
|
-
},
|
|
329
|
-
});
|