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.
Files changed (91) hide show
  1. package/COPYRIGHT_NOTICE.txt +13 -0
  2. package/LICENSE +22 -0
  3. package/README.md +1420 -0
  4. package/admin/jsconfig.json +10 -0
  5. package/admin/src/components/AddAccountModal.jsx +1943 -0
  6. package/admin/src/components/Initializer.jsx +14 -0
  7. package/admin/src/components/LicenseGuard.jsx +475 -0
  8. package/admin/src/components/PluginIcon.jsx +5 -0
  9. package/admin/src/hooks/useAuthRefresh.js +44 -0
  10. package/admin/src/hooks/useLicense.js +158 -0
  11. package/admin/src/index.js +86 -0
  12. package/admin/src/pages/Analytics.jsx +762 -0
  13. package/admin/src/pages/App.jsx +111 -0
  14. package/admin/src/pages/EmailDesigner/EditorPage.jsx +1405 -0
  15. package/admin/src/pages/EmailDesigner/TemplateList.jsx +1807 -0
  16. package/admin/src/pages/HomePage.jsx +1233 -0
  17. package/admin/src/pages/LicensePage.jsx +424 -0
  18. package/admin/src/pages/RoutingRules.jsx +1141 -0
  19. package/admin/src/pages/Settings.jsx +603 -0
  20. package/admin/src/pluginId.js +3 -0
  21. package/admin/src/translations/de.json +71 -0
  22. package/admin/src/translations/en.json +70 -0
  23. package/admin/src/translations/es.json +71 -0
  24. package/admin/src/translations/fr.json +71 -0
  25. package/admin/src/translations/pt.json +71 -0
  26. package/admin/src/utils/fetchWithRetry.js +123 -0
  27. package/admin/src/utils/getTranslation.js +5 -0
  28. package/dist/_chunks/App-B-Gp4Vbr.js +7568 -0
  29. package/dist/_chunks/App-BymMjoGM.mjs +7543 -0
  30. package/dist/_chunks/LicensePage-Bl02myMx.mjs +342 -0
  31. package/dist/_chunks/LicensePage-CJXwPnEe.js +344 -0
  32. package/dist/_chunks/Settings-C_TmKwcz.mjs +400 -0
  33. package/dist/_chunks/Settings-zuFQ3pnn.js +402 -0
  34. package/dist/_chunks/de-CN-G9j1S.js +64 -0
  35. package/dist/_chunks/de-DS04rP54.mjs +64 -0
  36. package/dist/_chunks/en-BDc7Jk8u.js +64 -0
  37. package/dist/_chunks/en-BEFQJXvR.mjs +64 -0
  38. package/dist/_chunks/es-BpV1MIdm.js +64 -0
  39. package/dist/_chunks/es-DQHwzPpP.mjs +64 -0
  40. package/dist/_chunks/fr-BG1WfEVm.mjs +64 -0
  41. package/dist/_chunks/fr-vpziIpRp.js +64 -0
  42. package/dist/_chunks/pt-CMoGrOib.mjs +64 -0
  43. package/dist/_chunks/pt-ODpAhDNa.js +64 -0
  44. package/dist/admin/index.js +89 -0
  45. package/dist/admin/index.mjs +90 -0
  46. package/dist/server/index.js +6214 -0
  47. package/dist/server/index.mjs +6208 -0
  48. package/package.json +113 -0
  49. package/server/jsconfig.json +10 -0
  50. package/server/src/bootstrap.js +153 -0
  51. package/server/src/config/features.js +260 -0
  52. package/server/src/config/index.js +6 -0
  53. package/server/src/content-types/email-account/schema.json +93 -0
  54. package/server/src/content-types/email-event/index.js +8 -0
  55. package/server/src/content-types/email-event/schema.json +57 -0
  56. package/server/src/content-types/email-link/index.js +8 -0
  57. package/server/src/content-types/email-link/schema.json +49 -0
  58. package/server/src/content-types/email-log/index.js +8 -0
  59. package/server/src/content-types/email-log/schema.json +106 -0
  60. package/server/src/content-types/email-template/schema.json +74 -0
  61. package/server/src/content-types/email-template-version/schema.json +60 -0
  62. package/server/src/content-types/index.js +33 -0
  63. package/server/src/content-types/routing-rule/schema.json +59 -0
  64. package/server/src/controllers/accounts.js +220 -0
  65. package/server/src/controllers/analytics.js +347 -0
  66. package/server/src/controllers/controller.js +26 -0
  67. package/server/src/controllers/email-designer.js +474 -0
  68. package/server/src/controllers/index.js +21 -0
  69. package/server/src/controllers/license.js +267 -0
  70. package/server/src/controllers/oauth.js +474 -0
  71. package/server/src/controllers/routing-rules.js +122 -0
  72. package/server/src/controllers/test.js +383 -0
  73. package/server/src/destroy.js +23 -0
  74. package/server/src/index.js +25 -0
  75. package/server/src/middlewares/index.js +3 -0
  76. package/server/src/policies/index.js +3 -0
  77. package/server/src/register.js +5 -0
  78. package/server/src/routes/admin.js +469 -0
  79. package/server/src/routes/content-api.js +37 -0
  80. package/server/src/routes/index.js +9 -0
  81. package/server/src/services/account-manager.js +277 -0
  82. package/server/src/services/analytics.js +496 -0
  83. package/server/src/services/email-designer.js +870 -0
  84. package/server/src/services/email-router.js +1420 -0
  85. package/server/src/services/index.js +17 -0
  86. package/server/src/services/license-guard.js +418 -0
  87. package/server/src/services/oauth.js +515 -0
  88. package/server/src/services/service.js +7 -0
  89. package/server/src/utils/encryption.js +81 -0
  90. package/strapi-admin.js +4 -0
  91. package/strapi-server.js +4 -0
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Test Controller für Template-Version Relations
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ module.exports = {
8
+ /**
9
+ * Test Template-Version Relations
10
+ *
11
+ * Tests beide Richtungen:
12
+ * 1. Version → Template beim Erstellen
13
+ * 2. Template → Version nachträglich via connect
14
+ */
15
+ async testRelations(ctx) {
16
+ try {
17
+ console.log('\n' + '='.repeat(60));
18
+ console.log('🧪 TEST: Template ↔ Version Relations');
19
+ console.log('='.repeat(60));
20
+
21
+ // Initialize all test result variables
22
+ let test1Success = false;
23
+ let test1ReverseSuccess = false;
24
+ let test2Success = false;
25
+ let test2ReverseSuccess = false;
26
+ let test3a_versionCreated = false;
27
+ let test3a_hasTemplate = false;
28
+ let test3b_twoVersions = false;
29
+ let test3b_allHaveTemplate = false;
30
+
31
+ // ============================================================
32
+ // TEST 1: Version mit Template verbinden (manyToOne Direction)
33
+ // ============================================================
34
+ console.log('\n📝 TEST 1: Version → Template Verbindung\n');
35
+
36
+ // Erstelle ein Test-Template
37
+ const testTemplate = await strapi.entityService.create(
38
+ 'plugin::magic-mail.email-template',
39
+ {
40
+ data: {
41
+ templateReferenceId: Math.floor(Math.random() * 1000000),
42
+ name: 'Test Template Relations',
43
+ subject: 'Test Subject',
44
+ bodyHtml: '<p>Test HTML</p>',
45
+ bodyText: 'Test Text',
46
+ category: 'custom',
47
+ isActive: true
48
+ }
49
+ }
50
+ );
51
+
52
+ console.log(`✅ Template erstellt: ID ${testTemplate.id}`);
53
+
54
+ // Erstelle eine Version und verbinde sie direkt mit dem Template
55
+ const version1 = await strapi.entityService.create(
56
+ 'plugin::magic-mail.email-template-version',
57
+ {
58
+ data: {
59
+ template: testTemplate.id, // 👈 Direkte Verbindung beim Erstellen
60
+ versionNumber: 1,
61
+ name: 'Version 1 von Test',
62
+ subject: 'Test Subject V1',
63
+ bodyHtml: '<p>Version 1 HTML</p>',
64
+ bodyText: 'Version 1 Text'
65
+ }
66
+ }
67
+ );
68
+
69
+ console.log(`✅ Version erstellt: ID ${version1.id}, versionNumber: ${version1.versionNumber}`);
70
+
71
+ // Prüfe die Verbindung von Version → Template
72
+ const versionCheck = await strapi.entityService.findOne(
73
+ 'plugin::magic-mail.email-template-version',
74
+ version1.id,
75
+ {
76
+ populate: ['template']
77
+ }
78
+ );
79
+
80
+ console.log('\n🔍 Prüfung Version → Template:');
81
+ test1Success = !!versionCheck.template;
82
+ if (test1Success) {
83
+ console.log(` ✅ SUCCESS: Version ${versionCheck.id} → Template ${versionCheck.template.id}`);
84
+ console.log(` 📋 Template: "${versionCheck.template.name}"`);
85
+ } else {
86
+ console.log(` ❌ FEHLER: Version ${versionCheck.id} hat KEINE Template-Verbindung!`);
87
+ }
88
+
89
+ // Prüfe die Verbindung von Template → Version
90
+ const templateCheck1 = await strapi.entityService.findOne(
91
+ 'plugin::magic-mail.email-template',
92
+ testTemplate.id,
93
+ {
94
+ populate: ['versions']
95
+ }
96
+ );
97
+
98
+ console.log('\n🔍 Prüfung Template → Versions:');
99
+ test1ReverseSuccess = templateCheck1.versions && templateCheck1.versions.length > 0;
100
+ if (test1ReverseSuccess) {
101
+ console.log(` ✅ SUCCESS: Template ${templateCheck1.id} hat ${templateCheck1.versions.length} Version(en)`);
102
+ templateCheck1.versions.forEach(v => {
103
+ console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}`);
104
+ });
105
+ } else {
106
+ console.log(` ❌ FEHLER: Template ${templateCheck1.id} hat KEINE Versionen!`);
107
+ }
108
+
109
+ // ============================================================
110
+ // TEST 2: Nachträglich Version mit Template verbinden
111
+ // ============================================================
112
+ console.log('\n\n📝 TEST 2: Nachträgliche Verbindung (Template Update)\n');
113
+
114
+ // Erstelle Version OHNE Template
115
+ const version2 = await strapi.entityService.create(
116
+ 'plugin::magic-mail.email-template-version',
117
+ {
118
+ data: {
119
+ versionNumber: 2,
120
+ name: 'Version 2 ohne Template',
121
+ subject: 'Test Subject V2',
122
+ bodyHtml: '<p>Version 2 HTML</p>',
123
+ bodyText: 'Version 2 Text'
124
+ }
125
+ }
126
+ );
127
+
128
+ console.log(`✅ Version 2 erstellt: ID ${version2.id} (ohne Template)`);
129
+
130
+ // Verbinde Version nachträglich mit Template über Template-Update
131
+ await strapi.entityService.update(
132
+ 'plugin::magic-mail.email-template',
133
+ testTemplate.id,
134
+ {
135
+ data: {
136
+ versions: {
137
+ connect: [version2.id] // 👈 Nachträgliche Verbindung
138
+ }
139
+ }
140
+ }
141
+ );
142
+
143
+ console.log(`✅ Template updated: Version ${version2.id} verbunden`);
144
+
145
+ // Prüfe ob die Verbindung jetzt existiert
146
+ const templateCheck2 = await strapi.entityService.findOne(
147
+ 'plugin::magic-mail.email-template',
148
+ testTemplate.id,
149
+ {
150
+ populate: ['versions']
151
+ }
152
+ );
153
+
154
+ console.log('\n🔍 Prüfung nach Template Update:');
155
+ test2Success = templateCheck2.versions && templateCheck2.versions.length >= 2;
156
+ if (test2Success) {
157
+ console.log(` ✅ SUCCESS: Template hat jetzt ${templateCheck2.versions.length} Versionen`);
158
+ templateCheck2.versions.forEach(v => {
159
+ console.log(` 📋 Version ${v.id}: versionNumber ${v.versionNumber}, "${v.name}"`);
160
+ });
161
+ } else {
162
+ console.log(` ❌ FEHLER: Template hat nur ${templateCheck2.versions?.length || 0} Version(en)!`);
163
+ }
164
+
165
+ // Prüfe auch von Version 2 aus
166
+ const version2Check = await strapi.entityService.findOne(
167
+ 'plugin::magic-mail.email-template-version',
168
+ version2.id,
169
+ {
170
+ populate: ['template']
171
+ }
172
+ );
173
+
174
+ console.log('\n🔍 Prüfung Version 2 → Template:');
175
+ test2ReverseSuccess = !!version2Check.template;
176
+ if (test2ReverseSuccess) {
177
+ console.log(` ✅ SUCCESS: Version ${version2Check.id} → Template ${version2Check.template.id}`);
178
+ console.log(` 📋 Template: "${version2Check.template.name}"`);
179
+ } else {
180
+ console.log(` ❌ FEHLER: Version ${version2Check.id} hat KEINE Template-Verbindung!`);
181
+ }
182
+
183
+ // ============================================================
184
+ // TEST 3: ECHTER USE CASE - Template Update mit Auto-Versionierung
185
+ // ============================================================
186
+ console.log('\n\n📝 TEST 3: Template Update (Auto-Versionierung)\n');
187
+
188
+ // Erstelle ein neues Template für diesen Test
189
+ const autoTemplate = await strapi.entityService.create(
190
+ 'plugin::magic-mail.email-template',
191
+ {
192
+ data: {
193
+ templateReferenceId: Math.floor(Math.random() * 1000000),
194
+ name: 'Auto Version Test',
195
+ subject: 'Original Subject',
196
+ bodyHtml: '<p>Original HTML</p>',
197
+ bodyText: 'Original Text',
198
+ category: 'custom',
199
+ isActive: true
200
+ }
201
+ }
202
+ );
203
+
204
+ console.log(`✅ Template erstellt: ID ${autoTemplate.id}, name: "${autoTemplate.name}"`);
205
+
206
+ // Prüfe: Sollte noch KEINE Versionen haben
207
+ const beforeUpdate = await strapi.entityService.findOne(
208
+ 'plugin::magic-mail.email-template',
209
+ autoTemplate.id,
210
+ { populate: ['versions'] }
211
+ );
212
+
213
+ console.log(` 📊 Versionen vor Update: ${beforeUpdate.versions?.length || 0}`);
214
+
215
+ // Jetzt UPDATE das Template (sollte automatisch Version erstellen)
216
+ console.log('\n🔄 Führe Template-Update aus...');
217
+
218
+ const emailDesignerService = strapi.plugin('magic-mail').service('email-designer');
219
+ await emailDesignerService.update(autoTemplate.id, {
220
+ subject: 'Updated Subject V1',
221
+ bodyHtml: '<p>Updated HTML V1</p>',
222
+ bodyText: 'Updated Text V1'
223
+ });
224
+
225
+ console.log('✅ Template updated');
226
+
227
+ // Prüfe ob automatisch eine Version erstellt wurde
228
+ const afterFirstUpdate = await strapi.entityService.findOne(
229
+ 'plugin::magic-mail.email-template',
230
+ autoTemplate.id,
231
+ { populate: ['versions'] }
232
+ );
233
+
234
+ console.log('\n🔍 Prüfung nach 1. Update:');
235
+ test3a_versionCreated = afterFirstUpdate.versions && afterFirstUpdate.versions.length === 1;
236
+
237
+ if (test3a_versionCreated) {
238
+ console.log(` ✅ SUCCESS: Automatisch 1 Version erstellt`);
239
+
240
+ // Prüfe ob die Version eine Template-Verbindung hat
241
+ const autoVersion1 = await strapi.entityService.findOne(
242
+ 'plugin::magic-mail.email-template-version',
243
+ afterFirstUpdate.versions[0].id,
244
+ { populate: ['template'] }
245
+ );
246
+
247
+ test3a_hasTemplate = !!autoVersion1.template;
248
+ if (test3a_hasTemplate) {
249
+ console.log(` ✅ SUCCESS: Version ${autoVersion1.id} hat Template-Verbindung (Template ID: ${autoVersion1.template.id})`);
250
+ console.log(` 📋 Version: versionNumber ${autoVersion1.versionNumber}, subject: "${autoVersion1.subject}"`);
251
+ } else {
252
+ console.log(` ❌ FEHLER: Version ${autoVersion1.id} hat KEINE Template-Verbindung!`);
253
+ }
254
+ } else {
255
+ console.log(` ❌ FEHLER: Keine Version erstellt! Versionen: ${afterFirstUpdate.versions?.length || 0}`);
256
+ }
257
+
258
+ // Zweites Update - sollte eine zweite Version erstellen
259
+ console.log('\n🔄 Führe 2. Template-Update aus...');
260
+
261
+ await emailDesignerService.update(autoTemplate.id, {
262
+ subject: 'Updated Subject V2',
263
+ bodyHtml: '<p>Updated HTML V2</p>',
264
+ bodyText: 'Updated Text V2'
265
+ });
266
+
267
+ console.log('✅ Template updated (2. Mal)');
268
+
269
+ // Prüfe ob jetzt 2 Versionen existieren
270
+ const afterSecondUpdate = await strapi.entityService.findOne(
271
+ 'plugin::magic-mail.email-template',
272
+ autoTemplate.id,
273
+ { populate: ['versions'] }
274
+ );
275
+
276
+ console.log('\n🔍 Prüfung nach 2. Update:');
277
+ test3b_twoVersions = afterSecondUpdate.versions && afterSecondUpdate.versions.length === 2;
278
+
279
+ if (test3b_twoVersions) {
280
+ console.log(` ✅ SUCCESS: Jetzt 2 Versionen vorhanden`);
281
+
282
+ // Prüfe beide Versionen
283
+ let allVersionsHaveTemplate = true;
284
+ for (const version of afterSecondUpdate.versions) {
285
+ const fullVersion = await strapi.entityService.findOne(
286
+ 'plugin::magic-mail.email-template-version',
287
+ version.id,
288
+ { populate: ['template'] }
289
+ );
290
+
291
+ if (fullVersion.template) {
292
+ console.log(` ✅ Version ${fullVersion.id} (v${fullVersion.versionNumber}): Template-Verbindung OK`);
293
+ } else {
294
+ console.log(` ❌ Version ${fullVersion.id} (v${fullVersion.versionNumber}): KEINE Template-Verbindung!`);
295
+ allVersionsHaveTemplate = false;
296
+ }
297
+ }
298
+
299
+ test3b_allHaveTemplate = allVersionsHaveTemplate;
300
+ if (allVersionsHaveTemplate) {
301
+ console.log(` ✅ SUCCESS: Alle Versionen haben Template-Verbindung!`);
302
+ }
303
+ } else {
304
+ console.log(` ❌ FEHLER: Falsche Anzahl Versionen! Erwartet: 2, Gefunden: ${afterSecondUpdate.versions?.length || 0}`);
305
+ }
306
+
307
+ // Cleanup für Test 3
308
+ console.log('\n🧹 Cleanup Test 3...');
309
+ if (afterSecondUpdate.versions) {
310
+ for (const version of afterSecondUpdate.versions) {
311
+ await strapi.entityService.delete('plugin::magic-mail.email-template-version', version.id);
312
+ }
313
+ }
314
+ await strapi.entityService.delete('plugin::magic-mail.email-template', autoTemplate.id);
315
+ console.log(' ✅ Test 3 Daten gelöscht');
316
+
317
+ // ============================================================
318
+ // Zusammenfassung
319
+ // ============================================================
320
+ console.log('\n\n' + '='.repeat(60));
321
+ console.log('📊 ZUSAMMENFASSUNG');
322
+ console.log('='.repeat(60));
323
+
324
+ const finalTemplate = await strapi.entityService.findOne(
325
+ 'plugin::magic-mail.email-template',
326
+ testTemplate.id,
327
+ {
328
+ populate: ['versions']
329
+ }
330
+ );
331
+
332
+ console.log(`\n📋 Template: "${finalTemplate.name}" (ID: ${finalTemplate.id})`);
333
+ console.log(` Anzahl Versionen im Template: ${finalTemplate.versions?.length || 0}`);
334
+
335
+ if (finalTemplate.versions && finalTemplate.versions.length > 0) {
336
+ finalTemplate.versions.forEach(v => {
337
+ console.log(` - Version ${v.id}: versionNumber ${v.versionNumber}`);
338
+ });
339
+ }
340
+
341
+ // Cleanup
342
+ console.log('\n🧹 Aufräumen...');
343
+ await strapi.entityService.delete('plugin::magic-mail.email-template-version', version1.id);
344
+ console.log(` ✅ Version ${version1.id} gelöscht`);
345
+ await strapi.entityService.delete('plugin::magic-mail.email-template-version', version2.id);
346
+ console.log(` ✅ Version ${version2.id} gelöscht`);
347
+ await strapi.entityService.delete('plugin::magic-mail.email-template', testTemplate.id);
348
+ console.log(` ✅ Template ${testTemplate.id} gelöscht`);
349
+
350
+ console.log('\n✅ Test abgeschlossen!\n');
351
+
352
+ // Return result
353
+ const allSuccess = test1Success && test1ReverseSuccess && test2Success && test2ReverseSuccess &&
354
+ test3a_versionCreated && test3a_hasTemplate && test3b_twoVersions && test3b_allHaveTemplate;
355
+
356
+ ctx.body = {
357
+ success: allSuccess,
358
+ message: allSuccess ? 'Alle Tests erfolgreich! ✅' : 'Einige Tests fehlgeschlagen ❌',
359
+ tests: {
360
+ test1_version_to_template: test1Success,
361
+ test1_template_to_version: test1ReverseSuccess,
362
+ test2_template_connect: test2Success,
363
+ test2_version_to_template: test2ReverseSuccess,
364
+ test3_auto_version_created: test3a_versionCreated,
365
+ test3_auto_version_has_template: test3a_hasTemplate,
366
+ test3_two_auto_versions: test3b_twoVersions,
367
+ test3_all_auto_versions_have_template: test3b_allHaveTemplate,
368
+ },
369
+ template: {
370
+ id: testTemplate.id,
371
+ name: testTemplate.name,
372
+ versionsCount: finalTemplate.versions?.length || 0
373
+ }
374
+ };
375
+
376
+ } catch (error) {
377
+ console.error('\n❌ FEHLER:', error.message);
378
+ console.error(error.stack);
379
+ ctx.throw(500, error);
380
+ }
381
+ }
382
+ };
383
+
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ module.exports = ({ strapi }) => {
4
+ // Cleanup intervals
5
+ if (global.magicMailIntervals) {
6
+ if (global.magicMailIntervals.hourly) {
7
+ clearInterval(global.magicMailIntervals.hourly);
8
+ strapi.log.info('[magic-mail] Cleared hourly reset interval');
9
+ }
10
+ if (global.magicMailIntervals.daily) {
11
+ clearInterval(global.magicMailIntervals.daily);
12
+ strapi.log.info('[magic-mail] Cleared daily reset interval');
13
+ }
14
+ }
15
+
16
+ // Cleanup license guard ping interval
17
+ if (strapi.licenseGuardMagicMail && strapi.licenseGuardMagicMail.pingInterval) {
18
+ clearInterval(strapi.licenseGuardMagicMail.pingInterval);
19
+ strapi.log.info('[magic-mail] Cleared license ping interval');
20
+ }
21
+
22
+ strapi.log.info('[magic-mail] 👋 Plugin destroyed gracefully');
23
+ };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const register = require('./register');
4
+ const bootstrap = require('./bootstrap');
5
+ const destroy = require('./destroy');
6
+ const config = require('./config');
7
+ const contentTypes = require('./content-types');
8
+ const controllers = require('./controllers');
9
+ const routes = require('./routes');
10
+ const middlewares = require('./middlewares');
11
+ const policies = require('./policies');
12
+ const services = require('./services');
13
+
14
+ module.exports = {
15
+ register,
16
+ bootstrap,
17
+ destroy,
18
+ config,
19
+ contentTypes,
20
+ controllers,
21
+ routes,
22
+ middlewares,
23
+ policies,
24
+ services,
25
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = {};
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = {};
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ module.exports = ({ strapi }) => {
4
+ // register phase
5
+ };