strapi-plugin-magic-mail 1.0.1
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/COPYRIGHT_NOTICE.txt +13 -0
- package/LICENSE +22 -0
- package/README.md +1420 -0
- package/admin/jsconfig.json +10 -0
- package/admin/src/components/AddAccountModal.jsx +1943 -0
- package/admin/src/components/Initializer.jsx +14 -0
- package/admin/src/components/LicenseGuard.jsx +475 -0
- package/admin/src/components/PluginIcon.jsx +5 -0
- package/admin/src/hooks/useAuthRefresh.js +44 -0
- package/admin/src/hooks/useLicense.js +158 -0
- package/admin/src/index.js +86 -0
- package/admin/src/pages/Analytics.jsx +762 -0
- package/admin/src/pages/App.jsx +111 -0
- package/admin/src/pages/EmailDesigner/EditorPage.jsx +1405 -0
- package/admin/src/pages/EmailDesigner/TemplateList.jsx +1807 -0
- package/admin/src/pages/HomePage.jsx +1233 -0
- package/admin/src/pages/LicensePage.jsx +424 -0
- package/admin/src/pages/RoutingRules.jsx +1141 -0
- package/admin/src/pages/Settings.jsx +603 -0
- package/admin/src/pluginId.js +3 -0
- package/admin/src/translations/de.json +71 -0
- package/admin/src/translations/en.json +70 -0
- package/admin/src/translations/es.json +71 -0
- package/admin/src/translations/fr.json +71 -0
- package/admin/src/translations/pt.json +71 -0
- package/admin/src/utils/fetchWithRetry.js +123 -0
- package/admin/src/utils/getTranslation.js +5 -0
- package/dist/_chunks/App-B-Gp4Vbr.js +7568 -0
- package/dist/_chunks/App-BymMjoGM.mjs +7543 -0
- package/dist/_chunks/LicensePage-Bl02myMx.mjs +342 -0
- package/dist/_chunks/LicensePage-CJXwPnEe.js +344 -0
- package/dist/_chunks/Settings-C_TmKwcz.mjs +400 -0
- package/dist/_chunks/Settings-zuFQ3pnn.js +402 -0
- package/dist/_chunks/de-CN-G9j1S.js +64 -0
- package/dist/_chunks/de-DS04rP54.mjs +64 -0
- package/dist/_chunks/en-BDc7Jk8u.js +64 -0
- package/dist/_chunks/en-BEFQJXvR.mjs +64 -0
- package/dist/_chunks/es-BpV1MIdm.js +64 -0
- package/dist/_chunks/es-DQHwzPpP.mjs +64 -0
- package/dist/_chunks/fr-BG1WfEVm.mjs +64 -0
- package/dist/_chunks/fr-vpziIpRp.js +64 -0
- package/dist/_chunks/pt-CMoGrOib.mjs +64 -0
- package/dist/_chunks/pt-ODpAhDNa.js +64 -0
- package/dist/admin/index.js +89 -0
- package/dist/admin/index.mjs +90 -0
- package/dist/server/index.js +6214 -0
- package/dist/server/index.mjs +6208 -0
- package/package.json +113 -0
- package/server/jsconfig.json +10 -0
- package/server/src/bootstrap.js +153 -0
- package/server/src/config/features.js +260 -0
- package/server/src/config/index.js +6 -0
- package/server/src/content-types/email-account/schema.json +93 -0
- package/server/src/content-types/email-event/index.js +8 -0
- package/server/src/content-types/email-event/schema.json +57 -0
- package/server/src/content-types/email-link/index.js +8 -0
- package/server/src/content-types/email-link/schema.json +49 -0
- package/server/src/content-types/email-log/index.js +8 -0
- package/server/src/content-types/email-log/schema.json +106 -0
- package/server/src/content-types/email-template/schema.json +74 -0
- package/server/src/content-types/email-template-version/schema.json +60 -0
- package/server/src/content-types/index.js +33 -0
- package/server/src/content-types/routing-rule/schema.json +59 -0
- package/server/src/controllers/accounts.js +220 -0
- package/server/src/controllers/analytics.js +347 -0
- package/server/src/controllers/controller.js +26 -0
- package/server/src/controllers/email-designer.js +474 -0
- package/server/src/controllers/index.js +21 -0
- package/server/src/controllers/license.js +267 -0
- package/server/src/controllers/oauth.js +474 -0
- package/server/src/controllers/routing-rules.js +122 -0
- package/server/src/controllers/test.js +383 -0
- package/server/src/destroy.js +23 -0
- package/server/src/index.js +25 -0
- package/server/src/middlewares/index.js +3 -0
- package/server/src/policies/index.js +3 -0
- package/server/src/register.js +5 -0
- package/server/src/routes/admin.js +469 -0
- package/server/src/routes/content-api.js +37 -0
- package/server/src/routes/index.js +9 -0
- package/server/src/services/account-manager.js +277 -0
- package/server/src/services/analytics.js +496 -0
- package/server/src/services/email-designer.js +870 -0
- package/server/src/services/email-router.js +1420 -0
- package/server/src/services/index.js +17 -0
- package/server/src/services/license-guard.js +418 -0
- package/server/src/services/oauth.js +515 -0
- package/server/src/services/service.js +7 -0
- package/server/src/utils/encryption.js +81 -0
- package/strapi-admin.js +4 -0
- package/strapi-server.js +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.1",
|
|
3
|
+
"keywords": [
|
|
4
|
+
"strapi",
|
|
5
|
+
"strapi-plugin",
|
|
6
|
+
"strapi-v5",
|
|
7
|
+
"email",
|
|
8
|
+
"multi-account",
|
|
9
|
+
"smtp",
|
|
10
|
+
"nodemailer",
|
|
11
|
+
"email-routing",
|
|
12
|
+
"email-analytics"
|
|
13
|
+
],
|
|
14
|
+
"type": "commonjs",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./package.json": "./package.json",
|
|
17
|
+
"./strapi-admin": {
|
|
18
|
+
"source": "./admin/src/index.js",
|
|
19
|
+
"import": "./dist/admin/index.mjs",
|
|
20
|
+
"require": "./dist/admin/index.js",
|
|
21
|
+
"default": "./dist/admin/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./strapi-server": {
|
|
24
|
+
"source": "./server/src/index.js",
|
|
25
|
+
"import": "./dist/server/index.mjs",
|
|
26
|
+
"require": "./dist/server/index.js",
|
|
27
|
+
"default": "./dist/server/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"server",
|
|
33
|
+
"admin",
|
|
34
|
+
"strapi-admin.js",
|
|
35
|
+
"strapi-server.js",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"COPYRIGHT_NOTICE.txt",
|
|
39
|
+
"pics"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "strapi-plugin build",
|
|
43
|
+
"watch": "strapi-plugin watch",
|
|
44
|
+
"watch:link": "strapi-plugin watch:link",
|
|
45
|
+
"verify": "strapi-plugin verify",
|
|
46
|
+
"test": "node test-magic-mail.js --quick",
|
|
47
|
+
"test:full": "node test-magic-mail.js --full"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@heroicons/react": "^2.2.0",
|
|
51
|
+
"@strapi/design-system": "^2.0.0-rc.30",
|
|
52
|
+
"@strapi/icons": "^2.0.0-rc.30",
|
|
53
|
+
"decode-html": "^2.0.0",
|
|
54
|
+
"form-data": "^4.0.4",
|
|
55
|
+
"html-to-text": "^9.0.5",
|
|
56
|
+
"mustache": "^4.2.0",
|
|
57
|
+
"nodemailer": "^7.0.10",
|
|
58
|
+
"react-email-editor": "^1.7.11",
|
|
59
|
+
"react-intl": "^7.1.14",
|
|
60
|
+
"striptags": "^3.2.0",
|
|
61
|
+
"yup": "^1.7.1"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
65
|
+
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
66
|
+
"@semantic-release/git": "^10.0.1",
|
|
67
|
+
"@semantic-release/github": "^11.0.2",
|
|
68
|
+
"@semantic-release/npm": "^13.1.2",
|
|
69
|
+
"@semantic-release/release-notes-generator": "^14.0.1",
|
|
70
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
71
|
+
"@strapi/strapi": "^5.30.1",
|
|
72
|
+
"prettier": "^3.6.2",
|
|
73
|
+
"react": "^18.3.1",
|
|
74
|
+
"react-dom": "^18.3.1",
|
|
75
|
+
"react-router-dom": "^6.30.1",
|
|
76
|
+
"semantic-release": "^24.2.3",
|
|
77
|
+
"styled-components": "^6.1.19"
|
|
78
|
+
},
|
|
79
|
+
"peerDependencies": {
|
|
80
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
81
|
+
"@strapi/strapi": "^5.30.1",
|
|
82
|
+
"react": "^18.3.1",
|
|
83
|
+
"react-dom": "^18.3.1",
|
|
84
|
+
"react-router-dom": "^6.30.1",
|
|
85
|
+
"styled-components": "^6.1.19"
|
|
86
|
+
},
|
|
87
|
+
"overrides": {
|
|
88
|
+
"glob": "^13.0.0",
|
|
89
|
+
"esbuild": "^0.27.0",
|
|
90
|
+
"tmp": "^0.2.3",
|
|
91
|
+
"vite": "^6.1.7",
|
|
92
|
+
"koa": "^2.16.1"
|
|
93
|
+
},
|
|
94
|
+
"strapi": {
|
|
95
|
+
"kind": "plugin",
|
|
96
|
+
"name": "magic-mail",
|
|
97
|
+
"displayName": "MagicMail - Email Business Suite",
|
|
98
|
+
"description": "Multi-account email management with smart routing, failover, and analytics for Strapi v5"
|
|
99
|
+
},
|
|
100
|
+
"name": "strapi-plugin-magic-mail",
|
|
101
|
+
"description": "Multi-account email management with smart routing, failover, and analytics for Strapi v5",
|
|
102
|
+
"license": "MIT",
|
|
103
|
+
"author": "Schero D.",
|
|
104
|
+
"repository": {
|
|
105
|
+
"type": "git",
|
|
106
|
+
"url": "https://github.com/Schero94/Magic-Mail.git"
|
|
107
|
+
},
|
|
108
|
+
"bugs": {
|
|
109
|
+
"url": "https://github.com/Schero94/Magic-Mail/issues"
|
|
110
|
+
},
|
|
111
|
+
"homepage": "https://github.com/Schero94/Magic-Mail#readme",
|
|
112
|
+
"private": false
|
|
113
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bootstrap: Initialize MagicMail plugin
|
|
5
|
+
* Sets up email account counter resets and health checks
|
|
6
|
+
* OVERRIDES Strapi's native email service with MagicMail router
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
module.exports = async ({ strapi }) => {
|
|
10
|
+
strapi.log.info('🚀 [magic-mail] Bootstrap starting...');
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
// Initialize License Guard
|
|
14
|
+
const licenseGuardService = strapi.plugin('magic-mail').service('license-guard');
|
|
15
|
+
|
|
16
|
+
// Wait a bit for all services to be ready
|
|
17
|
+
setTimeout(async () => {
|
|
18
|
+
const licenseStatus = await licenseGuardService.initialize();
|
|
19
|
+
|
|
20
|
+
if (!licenseStatus.valid && licenseStatus.demo) {
|
|
21
|
+
strapi.log.error('╔════════════════════════════════════════════════════════════════╗');
|
|
22
|
+
strapi.log.error('║ ❌ MAGICMAIL - NO VALID LICENSE ║');
|
|
23
|
+
strapi.log.error('║ ║');
|
|
24
|
+
strapi.log.error('║ This plugin requires a valid license to operate. ║');
|
|
25
|
+
strapi.log.error('║ Please activate your license via Admin UI: ║');
|
|
26
|
+
strapi.log.error('║ Go to MagicMail → License tab ║');
|
|
27
|
+
strapi.log.error('║ ║');
|
|
28
|
+
strapi.log.error('║ Click "Generate Free License" to get started! ║');
|
|
29
|
+
strapi.log.error('╚════════════════════════════════════════════════════════════════╝');
|
|
30
|
+
} else if (licenseStatus.gracePeriod) {
|
|
31
|
+
strapi.log.warn('⚠️ Running on grace period (license server unreachable)');
|
|
32
|
+
}
|
|
33
|
+
// No additional log here, as initialize() already outputs the license box
|
|
34
|
+
}, 2000);
|
|
35
|
+
|
|
36
|
+
const accountManager = strapi.plugin('magic-mail').service('account-manager');
|
|
37
|
+
const emailRouter = strapi.plugin('magic-mail').service('email-router');
|
|
38
|
+
|
|
39
|
+
// ============================================================
|
|
40
|
+
// OVERRIDE STRAPI'S NATIVE EMAIL SERVICE
|
|
41
|
+
// ============================================================
|
|
42
|
+
|
|
43
|
+
// Try to get email service (support both v4 and v5 APIs)
|
|
44
|
+
const originalEmailService = strapi.plugin('email')?.service?.('email') ||
|
|
45
|
+
strapi.plugins?.email?.services?.email;
|
|
46
|
+
|
|
47
|
+
if (originalEmailService && originalEmailService.send) {
|
|
48
|
+
const originalSend = originalEmailService.send.bind(originalEmailService);
|
|
49
|
+
|
|
50
|
+
// Override the send method
|
|
51
|
+
originalEmailService.send = async (emailData) => {
|
|
52
|
+
strapi.log.info('[magic-mail] 📧 Email intercepted from native Strapi service');
|
|
53
|
+
strapi.log.debug('[magic-mail] Email data:', {
|
|
54
|
+
to: emailData.to,
|
|
55
|
+
subject: emailData.subject,
|
|
56
|
+
templateId: emailData.templateId,
|
|
57
|
+
hasHtml: !!emailData.html,
|
|
58
|
+
hasText: !!emailData.text,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// Map 'data' to 'templateData' for backward compatibility
|
|
63
|
+
if (emailData.data && !emailData.templateData) {
|
|
64
|
+
emailData.templateData = emailData.data;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Route through MagicMail
|
|
68
|
+
const result = await emailRouter.send(emailData);
|
|
69
|
+
|
|
70
|
+
strapi.log.info('[magic-mail] ✅ Email routed successfully through MagicMail');
|
|
71
|
+
return result;
|
|
72
|
+
} catch (magicMailError) {
|
|
73
|
+
strapi.log.warn('[magic-mail] ⚠️ MagicMail routing failed, falling back to original service');
|
|
74
|
+
strapi.log.error('[magic-mail] Error:', magicMailError.message);
|
|
75
|
+
|
|
76
|
+
// Fallback to original Strapi email service
|
|
77
|
+
return await originalSend(emailData);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
strapi.log.info('[magic-mail] ✅ Native email service overridden!');
|
|
82
|
+
strapi.log.info('[magic-mail] 💡 All strapi.plugins.email.services.email.send() calls will route through MagicMail');
|
|
83
|
+
} else {
|
|
84
|
+
strapi.log.warn('[magic-mail] ⚠️ Native email service not found - MagicMail will work standalone');
|
|
85
|
+
strapi.log.warn('[magic-mail] 💡 Make sure @strapi/plugin-email is installed');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================
|
|
89
|
+
// COUNTER RESET SCHEDULES
|
|
90
|
+
// ============================================================
|
|
91
|
+
|
|
92
|
+
// Reset hourly counters every hour
|
|
93
|
+
const hourlyResetInterval = setInterval(async () => {
|
|
94
|
+
try {
|
|
95
|
+
if (!strapi || !strapi.plugin) {
|
|
96
|
+
console.warn('[magic-mail] Strapi not available for hourly reset');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
100
|
+
await accountMgr.resetCounters('hourly');
|
|
101
|
+
strapi.log.info('[magic-mail] ✅ Hourly counters reset');
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('[magic-mail] Hourly reset error:', err.message);
|
|
104
|
+
}
|
|
105
|
+
}, 60 * 60 * 1000); // Every hour
|
|
106
|
+
|
|
107
|
+
// Store interval for cleanup
|
|
108
|
+
if (!global.magicMailIntervals) global.magicMailIntervals = {};
|
|
109
|
+
global.magicMailIntervals.hourly = hourlyResetInterval;
|
|
110
|
+
|
|
111
|
+
// Reset daily counters at midnight
|
|
112
|
+
const now = new Date();
|
|
113
|
+
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0);
|
|
114
|
+
const msUntilMidnight = midnight - now;
|
|
115
|
+
|
|
116
|
+
setTimeout(async () => {
|
|
117
|
+
try {
|
|
118
|
+
if (!strapi || !strapi.plugin) {
|
|
119
|
+
console.warn('[magic-mail] Strapi not available for daily reset');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
123
|
+
await accountMgr.resetCounters('daily');
|
|
124
|
+
strapi.log.info('[magic-mail] ✅ Daily counters reset');
|
|
125
|
+
|
|
126
|
+
// Then set daily interval
|
|
127
|
+
const dailyResetInterval = setInterval(async () => {
|
|
128
|
+
try {
|
|
129
|
+
if (!strapi || !strapi.plugin) {
|
|
130
|
+
console.warn('[magic-mail] Strapi not available for daily reset');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
134
|
+
await accountMgr.resetCounters('daily');
|
|
135
|
+
strapi.log.info('[magic-mail] ✅ Daily counters reset');
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.error('[magic-mail] Daily reset error:', err.message);
|
|
138
|
+
}
|
|
139
|
+
}, 24 * 60 * 60 * 1000); // Every 24 hours
|
|
140
|
+
|
|
141
|
+
// Store interval for cleanup
|
|
142
|
+
global.magicMailIntervals.daily = dailyResetInterval;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error('[magic-mail] Initial daily reset error:', err.message);
|
|
145
|
+
}
|
|
146
|
+
}, msUntilMidnight);
|
|
147
|
+
|
|
148
|
+
strapi.log.info('[magic-mail] ✅ Counter reset schedules initialized');
|
|
149
|
+
strapi.log.info('[magic-mail] ✅ Bootstrap complete');
|
|
150
|
+
} catch (err) {
|
|
151
|
+
strapi.log.error('[magic-mail] ❌ Bootstrap error:', err);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicMail Feature Definitions
|
|
3
|
+
* Defines which features are available for each license tier
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
// FREE/DEMO Features
|
|
8
|
+
free: {
|
|
9
|
+
maxAccounts: 3, // 3 Accounts (can be OAuth!)
|
|
10
|
+
maxRoutingRules: 5,
|
|
11
|
+
maxEmailTemplates: 25, // 25 Templates - Genug zum Testen & kleine Projekte!
|
|
12
|
+
providers: ['smtp', 'gmail-oauth', 'microsoft-oauth', 'yahoo-oauth'], // Alle Provider erlaubt!
|
|
13
|
+
features: [
|
|
14
|
+
'basic-smtp',
|
|
15
|
+
'oauth-gmail',
|
|
16
|
+
'oauth-microsoft',
|
|
17
|
+
'oauth-yahoo',
|
|
18
|
+
'basic-routing',
|
|
19
|
+
'email-logging',
|
|
20
|
+
'account-testing',
|
|
21
|
+
'strapi-service-override',
|
|
22
|
+
'email-designer-basic', // Basic Email Designer
|
|
23
|
+
'email-designer-import-export', // Import/Export auch in Free! (Community-freundlich)
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// PREMIUM Features
|
|
28
|
+
premium: {
|
|
29
|
+
maxAccounts: 10, // 10 Accounts - Perfekt für kleine Teams
|
|
30
|
+
maxRoutingRules: 20,
|
|
31
|
+
maxEmailTemplates: 100, // 100 Templates - Mehr als genug für die meisten Projekte
|
|
32
|
+
providers: ['smtp', 'gmail-oauth', 'microsoft-oauth', 'yahoo-oauth'],
|
|
33
|
+
features: [
|
|
34
|
+
'basic-smtp',
|
|
35
|
+
'basic-routing',
|
|
36
|
+
'email-logging',
|
|
37
|
+
'oauth-gmail',
|
|
38
|
+
'oauth-microsoft',
|
|
39
|
+
'oauth-yahoo',
|
|
40
|
+
'account-testing',
|
|
41
|
+
'strapi-service-override',
|
|
42
|
+
'email-designer-basic',
|
|
43
|
+
'email-designer-templates',
|
|
44
|
+
'email-designer-import-export',
|
|
45
|
+
'email-designer-versioning', // NEU in Premium: Versionierung!
|
|
46
|
+
'analytics-basic', // Basic Analytics
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// ADVANCED Features
|
|
51
|
+
advanced: {
|
|
52
|
+
maxAccounts: -1, // Unlimited
|
|
53
|
+
maxRoutingRules: -1, // Unlimited
|
|
54
|
+
maxEmailTemplates: 500, // 500 Templates - Für größere Projekte
|
|
55
|
+
providers: ['smtp', 'gmail-oauth', 'microsoft-oauth', 'yahoo-oauth', 'sendgrid', 'mailgun'],
|
|
56
|
+
features: [
|
|
57
|
+
'basic-smtp',
|
|
58
|
+
'basic-routing',
|
|
59
|
+
'email-logging',
|
|
60
|
+
'oauth-gmail',
|
|
61
|
+
'oauth-microsoft',
|
|
62
|
+
'oauth-yahoo',
|
|
63
|
+
'sendgrid',
|
|
64
|
+
'mailgun',
|
|
65
|
+
'dkim-signing',
|
|
66
|
+
'priority-headers',
|
|
67
|
+
'list-unsubscribe',
|
|
68
|
+
'security-validation',
|
|
69
|
+
'analytics-dashboard',
|
|
70
|
+
'advanced-routing',
|
|
71
|
+
'account-testing',
|
|
72
|
+
'strapi-service-override',
|
|
73
|
+
'email-designer-basic', // NEW
|
|
74
|
+
'email-designer-templates', // NEW
|
|
75
|
+
'email-designer-versioning', // NEW: Template Versioning
|
|
76
|
+
'email-designer-import-export', // NEW: Import/Export
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// ENTERPRISE Features
|
|
81
|
+
enterprise: {
|
|
82
|
+
maxAccounts: -1, // Unlimited
|
|
83
|
+
maxRoutingRules: -1, // Unlimited
|
|
84
|
+
maxEmailTemplates: -1, // Unlimited Templates - Keine Limits!
|
|
85
|
+
providers: ['smtp', 'gmail-oauth', 'microsoft-oauth', 'yahoo-oauth', 'sendgrid', 'mailgun'],
|
|
86
|
+
features: [
|
|
87
|
+
'basic-smtp',
|
|
88
|
+
'basic-routing',
|
|
89
|
+
'email-logging',
|
|
90
|
+
'oauth-gmail',
|
|
91
|
+
'oauth-microsoft',
|
|
92
|
+
'oauth-yahoo',
|
|
93
|
+
'sendgrid',
|
|
94
|
+
'mailgun',
|
|
95
|
+
'dkim-signing',
|
|
96
|
+
'priority-headers',
|
|
97
|
+
'list-unsubscribe',
|
|
98
|
+
'security-validation',
|
|
99
|
+
'analytics-dashboard',
|
|
100
|
+
'advanced-routing',
|
|
101
|
+
'multi-tenant',
|
|
102
|
+
'compliance-reports',
|
|
103
|
+
'custom-security-rules',
|
|
104
|
+
'priority-support',
|
|
105
|
+
'account-testing',
|
|
106
|
+
'strapi-service-override',
|
|
107
|
+
'email-designer-basic', // NEW
|
|
108
|
+
'email-designer-templates', // NEW
|
|
109
|
+
'email-designer-versioning', // NEW
|
|
110
|
+
'email-designer-import-export', // NEW
|
|
111
|
+
'email-designer-custom-blocks', // NEW: Custom Blocks
|
|
112
|
+
'email-designer-team-library', // NEW: Team Library
|
|
113
|
+
'email-designer-a-b-testing', // NEW: A/B Testing
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a feature is available for given license tier
|
|
119
|
+
*/
|
|
120
|
+
hasFeature(licenseData, featureName) {
|
|
121
|
+
/** console.log(`[features.js] 🔍 hasFeature called with:`, {
|
|
122
|
+
featureName,
|
|
123
|
+
hasLicenseData: !!licenseData,
|
|
124
|
+
licenseDataKeys: licenseData ? Object.keys(licenseData) : [],
|
|
125
|
+
featuresObject: licenseData?.features,
|
|
126
|
+
tier: licenseData?.tier,
|
|
127
|
+
// Show actual feature flag values
|
|
128
|
+
featureAdvanced: licenseData?.featureAdvanced,
|
|
129
|
+
featureEnterprise: licenseData?.featureEnterprise,
|
|
130
|
+
featurePremium: licenseData?.featurePremium,
|
|
131
|
+
});
|
|
132
|
+
*/
|
|
133
|
+
if (!licenseData) {
|
|
134
|
+
console.log(`[features.js] ⚠️ No license data → using FREE tier`);
|
|
135
|
+
// Demo mode - only free features
|
|
136
|
+
return this.free.features.includes(featureName);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Determine tier: check multiple possible formats
|
|
140
|
+
let isEnterprise = false;
|
|
141
|
+
let isAdvanced = false;
|
|
142
|
+
let isPremium = false;
|
|
143
|
+
|
|
144
|
+
// Check tier field directly
|
|
145
|
+
if (licenseData.tier) {
|
|
146
|
+
isEnterprise = licenseData.tier === 'enterprise';
|
|
147
|
+
isAdvanced = licenseData.tier === 'advanced';
|
|
148
|
+
isPremium = licenseData.tier === 'premium';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check features object (for backward compatibility)
|
|
152
|
+
if (licenseData.features) {
|
|
153
|
+
isEnterprise = isEnterprise || licenseData.features.enterprise === true;
|
|
154
|
+
isAdvanced = isAdvanced || licenseData.features.advanced === true;
|
|
155
|
+
isPremium = isPremium || licenseData.features.premium === true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check feature flags directly on licenseData (magicapi format)
|
|
159
|
+
// These are boolean fields from the API
|
|
160
|
+
if (licenseData.featureEnterprise === true) {
|
|
161
|
+
isEnterprise = true;
|
|
162
|
+
}
|
|
163
|
+
if (licenseData.featureAdvanced === true) {
|
|
164
|
+
isAdvanced = true;
|
|
165
|
+
}
|
|
166
|
+
if (licenseData.featurePremium === true) {
|
|
167
|
+
isPremium = true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* console.log(`[features.js] 🔍 Tier detection:`, {
|
|
171
|
+
isEnterprise,
|
|
172
|
+
isAdvanced,
|
|
173
|
+
isPremium,
|
|
174
|
+
tierField: licenseData.tier,
|
|
175
|
+
featureAdvanced: licenseData.featureAdvanced,
|
|
176
|
+
featureEnterprise: licenseData.featureEnterprise,
|
|
177
|
+
featurePremium: licenseData.featurePremium,
|
|
178
|
+
featuresAdvanced: licenseData.features?.advanced,
|
|
179
|
+
featuresEnterprise: licenseData.features?.enterprise,
|
|
180
|
+
featuresPremium: licenseData.features?.premium,
|
|
181
|
+
});
|
|
182
|
+
*/
|
|
183
|
+
// Check tiers in order: enterprise > advanced > premium > free
|
|
184
|
+
if (isEnterprise && this.enterprise.features.includes(featureName)) {
|
|
185
|
+
console.log(`[features.js] ✅ ENTERPRISE tier has feature "${featureName}"`);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
if (isAdvanced && this.advanced.features.includes(featureName)) {
|
|
189
|
+
console.log(`[features.js] ✅ ADVANCED tier has feature "${featureName}"`);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
if (isPremium && this.premium.features.includes(featureName)) {
|
|
193
|
+
console.log(`[features.js] ✅ PREMIUM tier has feature "${featureName}"`);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const inFree = this.free.features.includes(featureName);
|
|
198
|
+
console.log(`[features.js] ${inFree ? '✅' : '❌'} FREE tier check for "${featureName}": ${inFree}`);
|
|
199
|
+
return inFree;
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get max allowed accounts for license tier
|
|
204
|
+
*/
|
|
205
|
+
getMaxAccounts(licenseData) {
|
|
206
|
+
if (!licenseData) return this.free.maxAccounts;
|
|
207
|
+
|
|
208
|
+
// Check both formats: features object and direct feature flags
|
|
209
|
+
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxAccounts;
|
|
210
|
+
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxAccounts;
|
|
211
|
+
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxAccounts;
|
|
212
|
+
|
|
213
|
+
return this.free.maxAccounts;
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get max allowed routing rules for license tier
|
|
218
|
+
*/
|
|
219
|
+
getMaxRoutingRules(licenseData) {
|
|
220
|
+
if (!licenseData) return this.free.maxRoutingRules;
|
|
221
|
+
|
|
222
|
+
// Check both formats: features object and direct feature flags
|
|
223
|
+
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxRoutingRules;
|
|
224
|
+
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxRoutingRules;
|
|
225
|
+
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxRoutingRules;
|
|
226
|
+
|
|
227
|
+
return this.free.maxRoutingRules;
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Check if provider is allowed for license tier
|
|
232
|
+
*/
|
|
233
|
+
isProviderAllowed(licenseData, provider) {
|
|
234
|
+
if (!licenseData) {
|
|
235
|
+
return this.free.providers.includes(provider);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Check both formats: features object and direct feature flags
|
|
239
|
+
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.providers.includes(provider);
|
|
240
|
+
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.providers.includes(provider);
|
|
241
|
+
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.providers.includes(provider);
|
|
242
|
+
|
|
243
|
+
return this.free.providers.includes(provider);
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get max allowed email templates for license tier
|
|
248
|
+
*/
|
|
249
|
+
getMaxEmailTemplates(licenseData) {
|
|
250
|
+
if (!licenseData) return this.free.maxEmailTemplates;
|
|
251
|
+
|
|
252
|
+
// Check both formats: features object and direct feature flags
|
|
253
|
+
if (licenseData.featureEnterprise === true || licenseData.features?.enterprise === true) return this.enterprise.maxEmailTemplates;
|
|
254
|
+
if (licenseData.featureAdvanced === true || licenseData.features?.advanced === true) return this.advanced.maxEmailTemplates;
|
|
255
|
+
if (licenseData.featurePremium === true || licenseData.features?.premium === true) return this.premium.maxEmailTemplates;
|
|
256
|
+
|
|
257
|
+
return this.free.maxEmailTemplates;
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "collectionType",
|
|
3
|
+
"collectionName": "magic_mail_accounts",
|
|
4
|
+
"info": {
|
|
5
|
+
"singularName": "email-account",
|
|
6
|
+
"pluralName": "email-accounts",
|
|
7
|
+
"displayName": "Email Account",
|
|
8
|
+
"description": "Email provider accounts for multi-account email sending"
|
|
9
|
+
},
|
|
10
|
+
"options": {
|
|
11
|
+
"draftAndPublish": false
|
|
12
|
+
},
|
|
13
|
+
"pluginOptions": {
|
|
14
|
+
"content-manager": {
|
|
15
|
+
"visible": false
|
|
16
|
+
},
|
|
17
|
+
"content-type-builder": {
|
|
18
|
+
"visible": false
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"attributes": {
|
|
22
|
+
"name": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"required": true,
|
|
25
|
+
"unique": true
|
|
26
|
+
},
|
|
27
|
+
"description": {
|
|
28
|
+
"type": "text"
|
|
29
|
+
},
|
|
30
|
+
"provider": {
|
|
31
|
+
"type": "enumeration",
|
|
32
|
+
"enum": ["smtp", "gmail-oauth", "microsoft-oauth", "yahoo-oauth", "sendgrid", "mailgun", "ses", "nodemailer"],
|
|
33
|
+
"required": true,
|
|
34
|
+
"default": "smtp"
|
|
35
|
+
},
|
|
36
|
+
"config": {
|
|
37
|
+
"type": "json",
|
|
38
|
+
"required": true
|
|
39
|
+
},
|
|
40
|
+
"oauth": {
|
|
41
|
+
"type": "json"
|
|
42
|
+
},
|
|
43
|
+
"fromEmail": {
|
|
44
|
+
"type": "email",
|
|
45
|
+
"required": true
|
|
46
|
+
},
|
|
47
|
+
"fromName": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"replyTo": {
|
|
51
|
+
"type": "email"
|
|
52
|
+
},
|
|
53
|
+
"isActive": {
|
|
54
|
+
"type": "boolean",
|
|
55
|
+
"default": true,
|
|
56
|
+
"required": true
|
|
57
|
+
},
|
|
58
|
+
"isPrimary": {
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"default": false
|
|
61
|
+
},
|
|
62
|
+
"priority": {
|
|
63
|
+
"type": "integer",
|
|
64
|
+
"default": 1,
|
|
65
|
+
"min": 1,
|
|
66
|
+
"max": 10
|
|
67
|
+
},
|
|
68
|
+
"dailyLimit": {
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"default": 0
|
|
71
|
+
},
|
|
72
|
+
"hourlyLimit": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"default": 0
|
|
75
|
+
},
|
|
76
|
+
"emailsSentToday": {
|
|
77
|
+
"type": "integer",
|
|
78
|
+
"default": 0
|
|
79
|
+
},
|
|
80
|
+
"emailsSentThisHour": {
|
|
81
|
+
"type": "integer",
|
|
82
|
+
"default": 0
|
|
83
|
+
},
|
|
84
|
+
"totalEmailsSent": {
|
|
85
|
+
"type": "integer",
|
|
86
|
+
"default": 0
|
|
87
|
+
},
|
|
88
|
+
"lastUsed": {
|
|
89
|
+
"type": "datetime"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|