strapi-plugin-magic-mail 2.2.0 → 2.2.3
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 +2 -0
- package/dist/server/index.js +431 -353
- package/dist/server/index.mjs +431 -353
- package/package.json +1 -1
- package/server/src/bootstrap.js +36 -32
- package/server/src/config/index.js +4 -1
- package/server/src/destroy.js +8 -4
- package/server/src/services/license-guard.js +44 -39
- package/server/src/utils/logger.js +84 -0
package/package.json
CHANGED
package/server/src/bootstrap.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createLogger } = require('./utils/logger');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Bootstrap: Initialize MagicMail plugin
|
|
5
7
|
* Sets up email account counter resets and health checks
|
|
@@ -7,7 +9,9 @@
|
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
module.exports = async ({ strapi }) => {
|
|
10
|
-
|
|
12
|
+
const log = createLogger(strapi);
|
|
13
|
+
|
|
14
|
+
log.info('[BOOTSTRAP] Starting...');
|
|
11
15
|
|
|
12
16
|
try {
|
|
13
17
|
// Initialize License Guard
|
|
@@ -18,17 +22,17 @@ module.exports = async ({ strapi }) => {
|
|
|
18
22
|
const licenseStatus = await licenseGuardService.initialize();
|
|
19
23
|
|
|
20
24
|
if (!licenseStatus.valid && licenseStatus.demo) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
log.error('╔════════════════════════════════════════════════════════════════╗');
|
|
26
|
+
log.error('║ [ERROR] MAGICMAIL - NO VALID LICENSE ║');
|
|
27
|
+
log.error('║ ║');
|
|
28
|
+
log.error('║ This plugin requires a valid license to operate. ║');
|
|
29
|
+
log.error('║ Please activate your license via Admin UI: ║');
|
|
30
|
+
log.error('║ Go to MagicMail → License tab ║');
|
|
31
|
+
log.error('║ ║');
|
|
32
|
+
log.error('║ Click "Generate Free License" to get started! ║');
|
|
33
|
+
log.error('╚════════════════════════════════════════════════════════════════╝');
|
|
30
34
|
} else if (licenseStatus.gracePeriod) {
|
|
31
|
-
|
|
35
|
+
log.warn('[WARNING] Running on grace period (license server unreachable)');
|
|
32
36
|
}
|
|
33
37
|
// No additional log here, as initialize() already outputs the license box
|
|
34
38
|
}, 2000);
|
|
@@ -49,8 +53,8 @@ module.exports = async ({ strapi }) => {
|
|
|
49
53
|
|
|
50
54
|
// Override the send method
|
|
51
55
|
originalEmailService.send = async (emailData) => {
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
log.info('[EMAIL] Intercepted from native Strapi service');
|
|
57
|
+
log.debug('Email data:', {
|
|
54
58
|
to: emailData.to,
|
|
55
59
|
subject: emailData.subject,
|
|
56
60
|
templateId: emailData.templateId,
|
|
@@ -67,22 +71,22 @@ module.exports = async ({ strapi }) => {
|
|
|
67
71
|
// Route through MagicMail
|
|
68
72
|
const result = await emailRouter.send(emailData);
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
log.info('[SUCCESS] Email routed successfully through MagicMail');
|
|
71
75
|
return result;
|
|
72
76
|
} catch (magicMailError) {
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
log.warn('[WARNING] MagicMail routing failed, falling back to original service');
|
|
78
|
+
log.error('Error:', magicMailError.message);
|
|
75
79
|
|
|
76
80
|
// Fallback to original Strapi email service
|
|
77
81
|
return await originalSend(emailData);
|
|
78
82
|
}
|
|
79
83
|
};
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
log.info('[SUCCESS] Native email service overridden!');
|
|
86
|
+
log.info('[INFO] All strapi.plugins.email.services.email.send() calls will route through MagicMail');
|
|
83
87
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
log.warn('[WARNING] Native email service not found - MagicMail will work standalone');
|
|
89
|
+
log.warn('[INFO] Make sure @strapi/plugin-email is installed');
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
// ============================================================
|
|
@@ -93,14 +97,14 @@ module.exports = async ({ strapi }) => {
|
|
|
93
97
|
const hourlyResetInterval = setInterval(async () => {
|
|
94
98
|
try {
|
|
95
99
|
if (!strapi || !strapi.plugin) {
|
|
96
|
-
console.warn('
|
|
100
|
+
console.warn('Strapi not available for hourly reset');
|
|
97
101
|
return;
|
|
98
102
|
}
|
|
99
103
|
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
100
104
|
await accountMgr.resetCounters('hourly');
|
|
101
|
-
|
|
105
|
+
log.info('[RESET] Hourly counters reset');
|
|
102
106
|
} catch (err) {
|
|
103
|
-
console.error('
|
|
107
|
+
console.error('Hourly reset error:', err.message);
|
|
104
108
|
}
|
|
105
109
|
}, 60 * 60 * 1000); // Every hour
|
|
106
110
|
|
|
@@ -116,38 +120,38 @@ module.exports = async ({ strapi }) => {
|
|
|
116
120
|
setTimeout(async () => {
|
|
117
121
|
try {
|
|
118
122
|
if (!strapi || !strapi.plugin) {
|
|
119
|
-
console.warn('
|
|
123
|
+
console.warn('Strapi not available for daily reset');
|
|
120
124
|
return;
|
|
121
125
|
}
|
|
122
126
|
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
123
127
|
await accountMgr.resetCounters('daily');
|
|
124
|
-
|
|
128
|
+
log.info('[RESET] Daily counters reset');
|
|
125
129
|
|
|
126
130
|
// Then set daily interval
|
|
127
131
|
const dailyResetInterval = setInterval(async () => {
|
|
128
132
|
try {
|
|
129
133
|
if (!strapi || !strapi.plugin) {
|
|
130
|
-
console.warn('
|
|
134
|
+
console.warn('Strapi not available for daily reset');
|
|
131
135
|
return;
|
|
132
136
|
}
|
|
133
137
|
const accountMgr = strapi.plugin('magic-mail').service('account-manager');
|
|
134
138
|
await accountMgr.resetCounters('daily');
|
|
135
|
-
|
|
139
|
+
log.info('[RESET] Daily counters reset');
|
|
136
140
|
} catch (err) {
|
|
137
|
-
console.error('
|
|
141
|
+
console.error('Daily reset error:', err.message);
|
|
138
142
|
}
|
|
139
143
|
}, 24 * 60 * 60 * 1000); // Every 24 hours
|
|
140
144
|
|
|
141
145
|
// Store interval for cleanup
|
|
142
146
|
global.magicMailIntervals.daily = dailyResetInterval;
|
|
143
147
|
} catch (err) {
|
|
144
|
-
console.error('
|
|
148
|
+
console.error('Initial daily reset error:', err.message);
|
|
145
149
|
}
|
|
146
150
|
}, msUntilMidnight);
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
log.info('[SUCCESS] Counter reset schedules initialized');
|
|
153
|
+
log.info('[SUCCESS] Bootstrap complete');
|
|
150
154
|
} catch (err) {
|
|
151
|
-
|
|
155
|
+
log.error('[ERROR] Bootstrap error:', err);
|
|
152
156
|
}
|
|
153
157
|
};
|
package/server/src/destroy.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createLogger } = require('./utils/logger');
|
|
4
|
+
|
|
3
5
|
module.exports = ({ strapi }) => {
|
|
6
|
+
const log = createLogger(strapi);
|
|
7
|
+
|
|
4
8
|
// Cleanup intervals
|
|
5
9
|
if (global.magicMailIntervals) {
|
|
6
10
|
if (global.magicMailIntervals.hourly) {
|
|
7
11
|
clearInterval(global.magicMailIntervals.hourly);
|
|
8
|
-
|
|
12
|
+
log.info('Cleared hourly reset interval');
|
|
9
13
|
}
|
|
10
14
|
if (global.magicMailIntervals.daily) {
|
|
11
15
|
clearInterval(global.magicMailIntervals.daily);
|
|
12
|
-
|
|
16
|
+
log.info('Cleared daily reset interval');
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// Cleanup license guard ping interval
|
|
17
21
|
if (strapi.licenseGuardMagicMail && strapi.licenseGuardMagicMail.pingInterval) {
|
|
18
22
|
clearInterval(strapi.licenseGuardMagicMail.pingInterval);
|
|
19
|
-
|
|
23
|
+
log.info('Cleared license ping interval');
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
log.info('👋 Plugin destroyed gracefully');
|
|
23
27
|
};
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const os = require('os');
|
|
8
8
|
const pluginPkg = require('../../../package.json');
|
|
9
|
+
const { createLogger } = require('../utils/logger');
|
|
9
10
|
|
|
10
11
|
// FIXED LICENSE SERVER URL
|
|
11
12
|
const LICENSE_SERVER_URL = 'https://magicapi.fitlex.me';
|
|
12
13
|
|
|
13
|
-
module.exports = ({ strapi }) =>
|
|
14
|
+
module.exports = ({ strapi }) => {
|
|
15
|
+
const log = createLogger(strapi);
|
|
16
|
+
|
|
17
|
+
return {
|
|
14
18
|
/**
|
|
15
19
|
* Get license server URL
|
|
16
20
|
*/
|
|
@@ -101,14 +105,14 @@ module.exports = ({ strapi }) => ({
|
|
|
101
105
|
const data = await response.json();
|
|
102
106
|
|
|
103
107
|
if (data.success) {
|
|
104
|
-
|
|
108
|
+
log.info('[SUCCESS] License created:', data.data.licenseKey);
|
|
105
109
|
return data.data;
|
|
106
110
|
} else {
|
|
107
|
-
|
|
111
|
+
log.error('[ERROR] License creation failed:', data);
|
|
108
112
|
return null;
|
|
109
113
|
}
|
|
110
114
|
} catch (error) {
|
|
111
|
-
|
|
115
|
+
log.error('[ERROR] Error creating license:', error);
|
|
112
116
|
return null;
|
|
113
117
|
}
|
|
114
118
|
},
|
|
@@ -140,10 +144,10 @@ module.exports = ({ strapi }) => ({
|
|
|
140
144
|
}
|
|
141
145
|
} catch (error) {
|
|
142
146
|
if (allowGracePeriod) {
|
|
143
|
-
|
|
147
|
+
log.warn('[WARNING] License verification timeout - grace period active');
|
|
144
148
|
return { valid: true, data: null, gracePeriod: true };
|
|
145
149
|
}
|
|
146
|
-
|
|
150
|
+
log.error('[ERROR] License verification error:', error.message);
|
|
147
151
|
return { valid: false, data: null };
|
|
148
152
|
}
|
|
149
153
|
},
|
|
@@ -166,7 +170,7 @@ module.exports = ({ strapi }) => ({
|
|
|
166
170
|
|
|
167
171
|
return null;
|
|
168
172
|
} catch (error) {
|
|
169
|
-
|
|
173
|
+
log.error('Error fetching license by key:', error);
|
|
170
174
|
return null;
|
|
171
175
|
}
|
|
172
176
|
},
|
|
@@ -206,7 +210,7 @@ module.exports = ({ strapi }) => ({
|
|
|
206
210
|
name: 'magic-mail'
|
|
207
211
|
});
|
|
208
212
|
await pluginStore.set({ key: 'licenseKey', value: licenseKey });
|
|
209
|
-
|
|
213
|
+
log.info(`[SUCCESS] License key stored: ${licenseKey.substring(0, 8)}...`);
|
|
210
214
|
},
|
|
211
215
|
|
|
212
216
|
startPinging(licenseKey, intervalMinutes = 15) {
|
|
@@ -217,7 +221,7 @@ module.exports = ({ strapi }) => ({
|
|
|
217
221
|
try {
|
|
218
222
|
await this.pingLicense(licenseKey);
|
|
219
223
|
} catch (error) {
|
|
220
|
-
console.error('
|
|
224
|
+
console.error('Ping error:', error);
|
|
221
225
|
}
|
|
222
226
|
}, intervalMinutes * 60 * 1000);
|
|
223
227
|
|
|
@@ -242,7 +246,7 @@ module.exports = ({ strapi }) => ({
|
|
|
242
246
|
const license = await this.getLicenseByKey(licenseKey);
|
|
243
247
|
return license;
|
|
244
248
|
} catch (error) {
|
|
245
|
-
|
|
249
|
+
log.error(`[ERROR] Error loading license:`, error);
|
|
246
250
|
return null;
|
|
247
251
|
}
|
|
248
252
|
},
|
|
@@ -298,7 +302,7 @@ module.exports = ({ strapi }) => ({
|
|
|
298
302
|
*/
|
|
299
303
|
async initialize() {
|
|
300
304
|
try {
|
|
301
|
-
|
|
305
|
+
log.info('[INIT] Initializing License Guard...');
|
|
302
306
|
|
|
303
307
|
// Check if license key exists in plugin store
|
|
304
308
|
const pluginStore = strapi.store({
|
|
@@ -319,26 +323,26 @@ module.exports = ({ strapi }) => ({
|
|
|
319
323
|
withinGracePeriod = hoursSinceValidation < gracePeriodHours;
|
|
320
324
|
}
|
|
321
325
|
|
|
322
|
-
|
|
323
|
-
|
|
326
|
+
log.info('──────────────────────────────────────────────────────────');
|
|
327
|
+
log.info(`📦 Plugin Store Check:`);
|
|
324
328
|
if (licenseKey) {
|
|
325
|
-
|
|
326
|
-
|
|
329
|
+
log.info(` [SUCCESS] License Key found: ${licenseKey}`);
|
|
330
|
+
log.info(` [LICENSE] Key (short): ${licenseKey.substring(0, 10)}...`);
|
|
327
331
|
if (lastValidated) {
|
|
328
332
|
const lastValidatedDate = new Date(lastValidated);
|
|
329
333
|
const hoursAgo = Math.floor((now.getTime() - lastValidatedDate.getTime()) / (1000 * 60 * 60));
|
|
330
|
-
|
|
334
|
+
log.info(` [TIME] Last validated: ${hoursAgo}h ago (Grace: ${withinGracePeriod ? 'ACTIVE' : 'EXPIRED'})`);
|
|
331
335
|
} else {
|
|
332
|
-
|
|
336
|
+
log.info(` [TIME] Last validated: Never (Grace: ACTIVE for first ${gracePeriodHours}h)`);
|
|
333
337
|
}
|
|
334
338
|
} else {
|
|
335
|
-
|
|
339
|
+
log.info(` [ERROR] No license key stored`);
|
|
336
340
|
}
|
|
337
|
-
|
|
341
|
+
log.info('──────────────────────────────────────────────────────────');
|
|
338
342
|
|
|
339
343
|
if (!licenseKey) {
|
|
340
|
-
|
|
341
|
-
|
|
344
|
+
log.info('[DEMO] No license found - Running in demo mode');
|
|
345
|
+
log.info('[INFO] Create a license in the admin panel to activate full features');
|
|
342
346
|
return {
|
|
343
347
|
valid: false,
|
|
344
348
|
demo: true,
|
|
@@ -346,7 +350,7 @@ module.exports = ({ strapi }) => ({
|
|
|
346
350
|
};
|
|
347
351
|
}
|
|
348
352
|
|
|
349
|
-
|
|
353
|
+
log.info('[VERIFY] Verifying stored license key...');
|
|
350
354
|
|
|
351
355
|
// Verify license (allow grace period if we have a last validation)
|
|
352
356
|
const verification = await this.verifyLicense(licenseKey, withinGracePeriod);
|
|
@@ -355,7 +359,7 @@ module.exports = ({ strapi }) => ({
|
|
|
355
359
|
// Get license details for display
|
|
356
360
|
const license = await this.getLicenseByKey(licenseKey);
|
|
357
361
|
|
|
358
|
-
|
|
362
|
+
log.info(`[SUCCESS] License verified online: ACTIVE (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
359
363
|
|
|
360
364
|
// Update last validated timestamp
|
|
361
365
|
await pluginStore.set({
|
|
@@ -363,11 +367,11 @@ module.exports = ({ strapi }) => ({
|
|
|
363
367
|
value: now.toISOString()
|
|
364
368
|
});
|
|
365
369
|
|
|
366
|
-
|
|
370
|
+
log.info('[SUCCESS] License is valid and active');
|
|
367
371
|
|
|
368
372
|
// Start automatic pinging
|
|
369
373
|
const pingInterval = this.startPinging(licenseKey, 15);
|
|
370
|
-
|
|
374
|
+
log.info('[PING] Started pinging license every 15 minutes');
|
|
371
375
|
|
|
372
376
|
// Store interval globally so we can clean it up
|
|
373
377
|
strapi.licenseGuardMagicMail = {
|
|
@@ -377,15 +381,15 @@ module.exports = ({ strapi }) => ({
|
|
|
377
381
|
};
|
|
378
382
|
|
|
379
383
|
// Display license info box
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
384
|
+
log.info('╔════════════════════════════════════════════════════════════════╗');
|
|
385
|
+
log.info('║ [SUCCESS] MAGIC MAIL PLUGIN LICENSE ACTIVE ║');
|
|
386
|
+
log.info('║ ║');
|
|
387
|
+
log.info(`║ License: ${licenseKey.padEnd(38, ' ')}║`);
|
|
388
|
+
log.info(`║ User: ${(license?.firstName + ' ' + license?.lastName).padEnd(41, ' ')}║`);
|
|
389
|
+
log.info(`║ Email: ${(license?.email || 'N/A').padEnd(40, ' ')}║`);
|
|
390
|
+
log.info('║ ║');
|
|
391
|
+
log.info('║ [AUTO] Pinging every 15 minutes ║');
|
|
392
|
+
log.info('╚════════════════════════════════════════════════════════════════╝');
|
|
389
393
|
|
|
390
394
|
return {
|
|
391
395
|
valid: true,
|
|
@@ -394,9 +398,9 @@ module.exports = ({ strapi }) => ({
|
|
|
394
398
|
gracePeriod: verification.gracePeriod || false,
|
|
395
399
|
};
|
|
396
400
|
} else {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
401
|
+
log.error(`[ERROR] License validation failed (Key: ${licenseKey.substring(0, 10)}...)`);
|
|
402
|
+
log.info('──────────────────────────────────────────────────────────');
|
|
403
|
+
log.info('[WARNING] Running in demo mode with limited features');
|
|
400
404
|
return {
|
|
401
405
|
valid: false,
|
|
402
406
|
demo: true,
|
|
@@ -405,7 +409,7 @@ module.exports = ({ strapi }) => ({
|
|
|
405
409
|
};
|
|
406
410
|
}
|
|
407
411
|
} catch (error) {
|
|
408
|
-
|
|
412
|
+
log.error('[ERROR] Error initializing License Guard:', error);
|
|
409
413
|
return {
|
|
410
414
|
valid: false,
|
|
411
415
|
demo: true,
|
|
@@ -414,5 +418,6 @@ module.exports = ({ strapi }) => ({
|
|
|
414
418
|
};
|
|
415
419
|
}
|
|
416
420
|
},
|
|
417
|
-
}
|
|
421
|
+
};
|
|
422
|
+
};
|
|
418
423
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Debug Logger Utility for magic-mail
|
|
5
|
+
* Only logs messages when debug: true in plugin config
|
|
6
|
+
* ALL logs (including errors/warnings) are hidden unless debug mode is enabled
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const PLUGIN_NAME = 'magic-mail';
|
|
10
|
+
const PREFIX = '[magic-mail]';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Format message with prefix - returns a formatted string
|
|
14
|
+
*/
|
|
15
|
+
function formatMessage(prefix, args) {
|
|
16
|
+
if (args.length === 0) return prefix;
|
|
17
|
+
const parts = args.map(arg =>
|
|
18
|
+
typeof arg === 'string' ? arg : JSON.stringify(arg)
|
|
19
|
+
);
|
|
20
|
+
return `${prefix} ${parts.join(' ')}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a logger instance that respects debug config
|
|
25
|
+
* @param {object} strapi - Strapi instance
|
|
26
|
+
* @returns {object} Logger with info, debug, warn, error methods
|
|
27
|
+
*/
|
|
28
|
+
function createLogger(strapi) {
|
|
29
|
+
const getDebugMode = () => {
|
|
30
|
+
try {
|
|
31
|
+
const config = strapi.config.get(`plugin::${PLUGIN_NAME}`) || {};
|
|
32
|
+
return config.debug === true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
/**
|
|
40
|
+
* Log info - only when debug: true
|
|
41
|
+
*/
|
|
42
|
+
info: (...args) => {
|
|
43
|
+
if (getDebugMode()) {
|
|
44
|
+
strapi.log.info(formatMessage(PREFIX, args));
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Log debug - only when debug: true
|
|
50
|
+
*/
|
|
51
|
+
debug: (...args) => {
|
|
52
|
+
if (getDebugMode()) {
|
|
53
|
+
strapi.log.debug(formatMessage(PREFIX, args));
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Log warning - only when debug: true
|
|
59
|
+
*/
|
|
60
|
+
warn: (...args) => {
|
|
61
|
+
if (getDebugMode()) {
|
|
62
|
+
strapi.log.warn(formatMessage(PREFIX, args));
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Log error - only when debug: true
|
|
68
|
+
*/
|
|
69
|
+
error: (...args) => {
|
|
70
|
+
if (getDebugMode()) {
|
|
71
|
+
strapi.log.error(formatMessage(PREFIX, args));
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Force log - always logged (for critical errors only)
|
|
77
|
+
*/
|
|
78
|
+
forceError: (...args) => {
|
|
79
|
+
strapi.log.error(formatMessage(PREFIX, args));
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { createLogger };
|