@strapi/strapi 4.6.0-beta.2 → 4.6.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/README.md +6 -6
- package/bin/strapi.js +87 -55
- package/ee/ee-store.js +18 -0
- package/ee/index.js +173 -0
- package/ee/license.js +102 -0
- package/{lib/utils → ee}/resources/key.pub +0 -0
- package/lib/Strapi.js +14 -4
- package/lib/commands/builders/admin.js +1 -1
- package/lib/commands/transfer/export.js +12 -8
- package/lib/commands/transfer/import.js +12 -22
- package/lib/commands/transfer/transfer.js +4 -2
- package/lib/commands/transfer/utils.js +59 -10
- package/lib/commands/utils/commander.js +1 -1
- package/lib/core/loaders/plugins/index.js +9 -1
- package/lib/services/entity-service/components.js +44 -24
- package/lib/services/entity-service/index.js +1 -0
- package/lib/services/metrics/index.js +3 -54
- package/lib/services/metrics/sender.js +13 -3
- package/lib/types/core/attributes/common.d.ts +4 -1
- package/lib/utils/cron.js +56 -0
- package/lib/utils/ee.js +1 -121
- package/lib/utils/startup-logger.js +2 -4
- package/package.json +19 -18
package/README.md
CHANGED
|
@@ -95,12 +95,12 @@ Strapi only supports maintenance and LTS versions of Node.js. Please refer to th
|
|
|
95
95
|
|
|
96
96
|
**Database:**
|
|
97
97
|
|
|
98
|
-
| Database |
|
|
99
|
-
| ---------- |
|
|
100
|
-
| MySQL | 5.7.8 |
|
|
101
|
-
| MariaDB | 10.
|
|
102
|
-
| PostgreSQL |
|
|
103
|
-
| SQLite | 3
|
|
98
|
+
| Database | Recommended | Minimum |
|
|
99
|
+
| ---------- | ----------- | ------- |
|
|
100
|
+
| MySQL | 8.0 | 5.7.8 |
|
|
101
|
+
| MariaDB | 10.6 | 10.3 |
|
|
102
|
+
| PostgreSQL | 14.0 | 11.0 |
|
|
103
|
+
| SQLite | 3 | 3 |
|
|
104
104
|
|
|
105
105
|
**We recommend always using the latest version of Strapi stable to start your new projects**.
|
|
106
106
|
|
package/bin/strapi.js
CHANGED
|
@@ -17,16 +17,19 @@ const packageJSON = require('../package.json');
|
|
|
17
17
|
const {
|
|
18
18
|
promptEncryptionKey,
|
|
19
19
|
confirmMessage,
|
|
20
|
-
parseURL,
|
|
21
20
|
forceOption,
|
|
22
21
|
} = require('../lib/commands/utils/commander');
|
|
23
|
-
const {
|
|
22
|
+
const { exitWith } = require('../lib/commands/utils/helpers');
|
|
24
23
|
const {
|
|
25
24
|
excludeOption,
|
|
26
25
|
onlyOption,
|
|
27
26
|
validateExcludeOnly,
|
|
28
27
|
} = require('../lib/commands/transfer/utils');
|
|
29
28
|
|
|
29
|
+
process.on('SIGINT', () => {
|
|
30
|
+
process.exit();
|
|
31
|
+
});
|
|
32
|
+
|
|
30
33
|
const checkCwdIsStrapiApp = (name) => {
|
|
31
34
|
const logErrorAndExit = () => {
|
|
32
35
|
console.log(
|
|
@@ -269,59 +272,59 @@ program
|
|
|
269
272
|
.option('-s, --silent', `Run the generation silently, without any output`, false)
|
|
270
273
|
.action(getLocalScript('ts/generate-types'));
|
|
271
274
|
|
|
272
|
-
if (process.env.STRAPI_EXPERIMENTAL === 'true') {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
275
|
+
// if (process.env.STRAPI_EXPERIMENTAL === 'true') {
|
|
276
|
+
// // `$ strapi transfer`
|
|
277
|
+
// program
|
|
278
|
+
// .command('transfer')
|
|
279
|
+
// .description('Transfer data from one source to another')
|
|
280
|
+
// .allowExcessArguments(false)
|
|
281
|
+
// .addOption(
|
|
282
|
+
// new Option(
|
|
283
|
+
// '--from <sourceURL>',
|
|
284
|
+
// `URL of the remote Strapi instance to get data from`
|
|
285
|
+
// ).argParser(parseURL)
|
|
286
|
+
// )
|
|
287
|
+
// .addOption(
|
|
288
|
+
// new Option(
|
|
289
|
+
// '--to <destinationURL>',
|
|
290
|
+
// `URL of the remote Strapi instance to send data to`
|
|
291
|
+
// ).argParser(parseURL)
|
|
292
|
+
// )
|
|
293
|
+
// .addOption(forceOption)
|
|
294
|
+
// // Validate URLs
|
|
295
|
+
// .hook(
|
|
296
|
+
// 'preAction',
|
|
297
|
+
// ifOptions(
|
|
298
|
+
// (opts) => opts.from,
|
|
299
|
+
// (thisCommand) => assertUrlHasProtocol(thisCommand.opts().from, ['https:', 'http:'])
|
|
300
|
+
// )
|
|
301
|
+
// )
|
|
302
|
+
// .hook(
|
|
303
|
+
// 'preAction',
|
|
304
|
+
// ifOptions(
|
|
305
|
+
// (opts) => opts.to,
|
|
306
|
+
// (thisCommand) => assertUrlHasProtocol(thisCommand.opts().to, ['https:', 'http:'])
|
|
307
|
+
// )
|
|
308
|
+
// )
|
|
309
|
+
// .hook(
|
|
310
|
+
// 'preAction',
|
|
311
|
+
// ifOptions(
|
|
312
|
+
// (opts) => !opts.from && !opts.to,
|
|
313
|
+
// () => exitWith(1, 'At least one source (from) or destination (to) option must be provided')
|
|
314
|
+
// )
|
|
315
|
+
// )
|
|
316
|
+
// .addOption(forceOption)
|
|
317
|
+
// .addOption(excludeOption)
|
|
318
|
+
// .addOption(onlyOption)
|
|
319
|
+
// .hook('preAction', validateExcludeOnly)
|
|
320
|
+
// .hook(
|
|
321
|
+
// 'preAction',
|
|
322
|
+
// confirmMessage(
|
|
323
|
+
// 'The import will delete all data in the remote database. Are you sure you want to proceed?'
|
|
324
|
+
// )
|
|
325
|
+
// )
|
|
326
|
+
// .action(getLocalScript('transfer/transfer'));
|
|
327
|
+
// }
|
|
325
328
|
|
|
326
329
|
// `$ strapi export`
|
|
327
330
|
program
|
|
@@ -385,6 +388,35 @@ program
|
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
})
|
|
391
|
+
// set decrypt and decompress options based on filename
|
|
392
|
+
.hook('preAction', (thisCommand) => {
|
|
393
|
+
const opts = thisCommand.opts();
|
|
394
|
+
|
|
395
|
+
const { extname, parse } = path;
|
|
396
|
+
|
|
397
|
+
let file = opts.file;
|
|
398
|
+
|
|
399
|
+
if (extname(file) === '.enc') {
|
|
400
|
+
file = parse(file).name; // trim the .enc extension
|
|
401
|
+
thisCommand.opts().decrypt = true;
|
|
402
|
+
} else {
|
|
403
|
+
thisCommand.opts().decrypt = false;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (extname(file) === '.gz') {
|
|
407
|
+
file = parse(file).name; // trim the .gz extension
|
|
408
|
+
thisCommand.opts().decompress = true;
|
|
409
|
+
} else {
|
|
410
|
+
thisCommand.opts().decompress = false;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (extname(file) !== '.tar') {
|
|
414
|
+
exitWith(
|
|
415
|
+
1,
|
|
416
|
+
`The file '${opts.file}' does not appear to be a valid Strapi data file. It must have an extension ending in .tar[.gz][.enc]`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
})
|
|
388
420
|
.hook(
|
|
389
421
|
'preAction',
|
|
390
422
|
confirmMessage(
|
package/ee/ee-store.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const eeStoreModel = {
|
|
4
|
+
uid: 'strapi::ee-store',
|
|
5
|
+
collectionName: 'strapi_ee_store_settings',
|
|
6
|
+
attributes: {
|
|
7
|
+
key: {
|
|
8
|
+
type: 'string',
|
|
9
|
+
},
|
|
10
|
+
value: {
|
|
11
|
+
type: 'text',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
eeStoreModel,
|
|
18
|
+
};
|
package/ee/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { pick } = require('lodash/fp');
|
|
4
|
+
|
|
5
|
+
const { readLicense, verifyLicense, fetchLicense, LicenseCheckError } = require('./license');
|
|
6
|
+
const { eeStoreModel } = require('./ee-store');
|
|
7
|
+
const { shiftCronExpression } = require('../lib/utils/cron');
|
|
8
|
+
|
|
9
|
+
const ONE_MINUTE = 1000 * 60;
|
|
10
|
+
|
|
11
|
+
const ee = {
|
|
12
|
+
enabled: false,
|
|
13
|
+
licenseInfo: {},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const disable = (message) => {
|
|
17
|
+
ee.logger?.warn(`${message} Switching to CE.`);
|
|
18
|
+
// Only keep the license key for potential re-enabling during a later check
|
|
19
|
+
ee.licenseInfo = pick('licenseKey', ee.licenseInfo);
|
|
20
|
+
ee.enabled = false;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let initialized = false;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optimistically enable EE if the format of the license is valid, only run once.
|
|
27
|
+
*/
|
|
28
|
+
const init = (licenseDir, logger) => {
|
|
29
|
+
if (initialized) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
initialized = true;
|
|
34
|
+
ee.logger = logger;
|
|
35
|
+
|
|
36
|
+
if (process.env.STRAPI_DISABLE_EE?.toLowerCase() === 'true') {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const license = process.env.STRAPI_LICENSE || readLicense(licenseDir);
|
|
42
|
+
|
|
43
|
+
if (license) {
|
|
44
|
+
ee.licenseInfo = verifyLicense(license);
|
|
45
|
+
ee.enabled = true;
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
disable(error.message);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Contact the license registry to update the license to its latest state.
|
|
54
|
+
*
|
|
55
|
+
* Store the result in database to avoid unecessary requests, and will fallback to that in case of a network failure.
|
|
56
|
+
*/
|
|
57
|
+
const onlineUpdate = async ({ strapi }) => {
|
|
58
|
+
const { get, commit, rollback } = await strapi.db.transaction();
|
|
59
|
+
const transaction = get();
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const storedInfo = await strapi.db
|
|
63
|
+
.queryBuilder(eeStoreModel.uid)
|
|
64
|
+
.where({ key: 'ee_information' })
|
|
65
|
+
.select('value')
|
|
66
|
+
.first()
|
|
67
|
+
.transacting(transaction)
|
|
68
|
+
.forUpdate()
|
|
69
|
+
.execute()
|
|
70
|
+
.then((result) => (result ? JSON.parse(result.value) : result));
|
|
71
|
+
|
|
72
|
+
const shouldContactRegistry = (storedInfo?.lastCheckAt ?? 0) < Date.now() - ONE_MINUTE;
|
|
73
|
+
const result = { lastCheckAt: Date.now() };
|
|
74
|
+
|
|
75
|
+
const fallback = (error) => {
|
|
76
|
+
if (error instanceof LicenseCheckError && error.shouldFallback && storedInfo?.license) {
|
|
77
|
+
ee.logger?.warn(
|
|
78
|
+
`${error.message} The last stored one will be used as a potential fallback.`
|
|
79
|
+
);
|
|
80
|
+
return storedInfo.license;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
result.error = error.message;
|
|
84
|
+
disable(error.message);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const license = shouldContactRegistry
|
|
88
|
+
? await fetchLicense(ee.licenseInfo.licenseKey, strapi.config.get('uuid')).catch(fallback)
|
|
89
|
+
: storedInfo.license;
|
|
90
|
+
|
|
91
|
+
if (license) {
|
|
92
|
+
try {
|
|
93
|
+
ee.licenseInfo = verifyLicense(license);
|
|
94
|
+
validateInfo();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
disable(error.message);
|
|
97
|
+
}
|
|
98
|
+
} else if (!shouldContactRegistry) {
|
|
99
|
+
disable(storedInfo.error);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (shouldContactRegistry) {
|
|
103
|
+
result.license = license ?? null;
|
|
104
|
+
const query = strapi.db.queryBuilder(eeStoreModel.uid).transacting(transaction);
|
|
105
|
+
|
|
106
|
+
if (!storedInfo) {
|
|
107
|
+
query.insert({ key: 'ee_information', value: JSON.stringify(result) });
|
|
108
|
+
} else {
|
|
109
|
+
query.update({ value: JSON.stringify(result) }).where({ key: 'ee_information' });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await query.execute();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await commit();
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// Example of errors: SQLite does not support FOR UPDATE
|
|
118
|
+
await rollback();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const validateInfo = () => {
|
|
123
|
+
const expirationTime = new Date(ee.licenseInfo.expireAt).getTime();
|
|
124
|
+
|
|
125
|
+
if (expirationTime < new Date().getTime()) {
|
|
126
|
+
return disable('License expired.');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
ee.enabled = true;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const checkLicense = async ({ strapi }) => {
|
|
133
|
+
const shouldStayOffline =
|
|
134
|
+
ee.licenseInfo.type === 'gold' &&
|
|
135
|
+
// This env variable support is temporarily used to ease the migration between online vs offline
|
|
136
|
+
process.env.STRAPI_DISABLE_LICENSE_PING?.toLowerCase() === 'true';
|
|
137
|
+
|
|
138
|
+
if (!shouldStayOffline) {
|
|
139
|
+
await onlineUpdate({ strapi });
|
|
140
|
+
strapi.cron.add({ [shiftCronExpression('0 0 */12 * * *')]: onlineUpdate });
|
|
141
|
+
} else {
|
|
142
|
+
if (!ee.licenseInfo.expireAt) {
|
|
143
|
+
return disable('Your license does not have offline support.');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
validateInfo();
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const list = () => {
|
|
151
|
+
return (
|
|
152
|
+
ee.licenseInfo.features?.map((feature) =>
|
|
153
|
+
typeof feature === 'object' ? feature : { name: feature }
|
|
154
|
+
) || []
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const get = (featureName) => list().find((feature) => feature.name === featureName);
|
|
159
|
+
|
|
160
|
+
module.exports = Object.freeze({
|
|
161
|
+
init,
|
|
162
|
+
checkLicense,
|
|
163
|
+
|
|
164
|
+
get isEE() {
|
|
165
|
+
return ee.enabled;
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
features: Object.freeze({
|
|
169
|
+
list,
|
|
170
|
+
get,
|
|
171
|
+
isEnabled: (featureName) => get(featureName) !== undefined,
|
|
172
|
+
}),
|
|
173
|
+
});
|
package/ee/license.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { join } = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const fetch = require('node-fetch');
|
|
7
|
+
|
|
8
|
+
const machineId = require('../lib/utils/machine-id');
|
|
9
|
+
|
|
10
|
+
const DEFAULT_FEATURES = {
|
|
11
|
+
bronze: [],
|
|
12
|
+
silver: [],
|
|
13
|
+
gold: ['sso', { name: 'audit-logs', options: { retentionDays: 90 } }],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const publicKey = fs.readFileSync(join(__dirname, 'resources/key.pub'));
|
|
17
|
+
|
|
18
|
+
class LicenseCheckError extends Error {
|
|
19
|
+
constructor(message, shouldFallback = false) {
|
|
20
|
+
super(message);
|
|
21
|
+
|
|
22
|
+
this.shouldFallback = shouldFallback;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const readLicense = (directory) => {
|
|
27
|
+
try {
|
|
28
|
+
const path = join(directory, 'license.txt');
|
|
29
|
+
return fs.readFileSync(path).toString();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error.code !== 'ENOENT') {
|
|
32
|
+
throw Error('License file not readable, review its format and access rules.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const verifyLicense = (license) => {
|
|
38
|
+
const [signature, base64Content] = Buffer.from(license, 'base64').toString().split('\n');
|
|
39
|
+
|
|
40
|
+
if (!signature || !base64Content) {
|
|
41
|
+
throw new Error('Invalid license.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const stringifiedContent = Buffer.from(base64Content, 'base64').toString();
|
|
45
|
+
|
|
46
|
+
const verify = crypto.createVerify('RSA-SHA256');
|
|
47
|
+
verify.update(stringifiedContent);
|
|
48
|
+
verify.end();
|
|
49
|
+
|
|
50
|
+
const verified = verify.verify(publicKey, signature, 'base64');
|
|
51
|
+
|
|
52
|
+
if (!verified) {
|
|
53
|
+
throw new Error('Invalid license.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const licenseInfo = JSON.parse(stringifiedContent);
|
|
57
|
+
|
|
58
|
+
if (!licenseInfo.features) {
|
|
59
|
+
licenseInfo.features = DEFAULT_FEATURES[licenseInfo.type];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Object.freeze(licenseInfo.features);
|
|
63
|
+
return licenseInfo;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const throwError = () => {
|
|
67
|
+
throw new LicenseCheckError('Could not proceed to the online validation of your license.', true);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const fetchLicense = async (key, projectId) => {
|
|
71
|
+
const response = await fetch(`https://license.strapi.io/api/licenses/validate`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: { 'Content-Type': 'application/json' },
|
|
74
|
+
body: JSON.stringify({ key, projectId, deviceId: machineId() }),
|
|
75
|
+
}).catch(throwError);
|
|
76
|
+
|
|
77
|
+
const contentType = response.headers.get('Content-Type');
|
|
78
|
+
|
|
79
|
+
if (contentType.includes('application/json')) {
|
|
80
|
+
const { data, error } = await response.json();
|
|
81
|
+
|
|
82
|
+
switch (response.status) {
|
|
83
|
+
case 200:
|
|
84
|
+
return data.license;
|
|
85
|
+
case 400:
|
|
86
|
+
throw new LicenseCheckError(error.message);
|
|
87
|
+
case 404:
|
|
88
|
+
throw new LicenseCheckError('The license used does not exists.');
|
|
89
|
+
default:
|
|
90
|
+
throwError();
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
throwError();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
module.exports = Object.freeze({
|
|
98
|
+
readLicense,
|
|
99
|
+
verifyLicense,
|
|
100
|
+
fetchLicense,
|
|
101
|
+
LicenseCheckError,
|
|
102
|
+
});
|
|
File without changes
|
package/lib/Strapi.js
CHANGED
|
@@ -17,6 +17,7 @@ const { createServer } = require('./services/server');
|
|
|
17
17
|
const createWebhookRunner = require('./services/webhook-runner');
|
|
18
18
|
const { webhookModel, createWebhookStore } = require('./services/webhook-store');
|
|
19
19
|
const { createCoreStore, coreStoreModel } = require('./services/core-store');
|
|
20
|
+
const { eeStoreModel } = require('../ee/ee-store');
|
|
20
21
|
const createEntityService = require('./services/entity-service');
|
|
21
22
|
const createCronService = require('./services/cron');
|
|
22
23
|
const entityValidator = require('./services/entity-validator');
|
|
@@ -120,16 +121,20 @@ class Strapi {
|
|
|
120
121
|
this.customFields = createCustomFields(this);
|
|
121
122
|
|
|
122
123
|
createUpdateNotifier(this).notify();
|
|
124
|
+
|
|
125
|
+
Object.defineProperty(this, 'EE', {
|
|
126
|
+
get: () => {
|
|
127
|
+
ee.init(this.dirs.app.root, this.log);
|
|
128
|
+
return ee.isEE;
|
|
129
|
+
},
|
|
130
|
+
configurable: false,
|
|
131
|
+
});
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
get config() {
|
|
126
135
|
return this.container.get('config');
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
get EE() {
|
|
130
|
-
return ee({ dir: this.dirs.app.root, logger: this.log });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
138
|
get services() {
|
|
134
139
|
return this.container.get('services').getAll();
|
|
135
140
|
}
|
|
@@ -404,6 +409,7 @@ class Strapi {
|
|
|
404
409
|
const contentTypes = [
|
|
405
410
|
coreStoreModel,
|
|
406
411
|
webhookModel,
|
|
412
|
+
eeStoreModel,
|
|
407
413
|
...Object.values(strapi.contentTypes),
|
|
408
414
|
...Object.values(strapi.components),
|
|
409
415
|
];
|
|
@@ -447,6 +453,10 @@ class Strapi {
|
|
|
447
453
|
|
|
448
454
|
await this.db.schema.sync();
|
|
449
455
|
|
|
456
|
+
if (this.EE) {
|
|
457
|
+
await ee.checkLicense({ strapi: this });
|
|
458
|
+
}
|
|
459
|
+
|
|
450
460
|
await this.hook('strapi::content-types.afterSync').call({
|
|
451
461
|
oldContentTypes,
|
|
452
462
|
contentTypes: strapi.contentTypes,
|
|
@@ -30,7 +30,7 @@ module.exports = async ({ buildDestDir, forceBuild = true, optimization, srcDir
|
|
|
30
30
|
// Always remove the .cache and build folders
|
|
31
31
|
await strapiAdmin.clean({ appDir: srcDir, buildDestDir });
|
|
32
32
|
|
|
33
|
-
ee(
|
|
33
|
+
ee.init(srcDir);
|
|
34
34
|
|
|
35
35
|
return strapiAdmin
|
|
36
36
|
.build({
|
|
@@ -11,15 +11,17 @@ const { isObject, isString, isFinite, toNumber } = require('lodash/fp');
|
|
|
11
11
|
const fs = require('fs-extra');
|
|
12
12
|
const chalk = require('chalk');
|
|
13
13
|
|
|
14
|
+
const { TransferEngineTransferError } = require('@strapi/data-transfer/lib/engine/errors');
|
|
14
15
|
const {
|
|
15
16
|
getDefaultExportName,
|
|
16
17
|
buildTransferTable,
|
|
17
18
|
DEFAULT_IGNORED_CONTENT_TYPES,
|
|
18
19
|
createStrapiInstance,
|
|
20
|
+
formatDiagnostic,
|
|
19
21
|
} = require('./utils');
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
|
-
* @typedef
|
|
24
|
+
* @typedef ExportCommandOptions Options given to the CLI import command
|
|
23
25
|
*
|
|
24
26
|
* @property {string} [file] The file path to import
|
|
25
27
|
* @property {boolean} [encrypt] Used to encrypt the final archive
|
|
@@ -32,11 +34,11 @@ const logger = console;
|
|
|
32
34
|
const BYTES_IN_MB = 1024 * 1024;
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
37
|
+
* Export command.
|
|
36
38
|
*
|
|
37
|
-
* It transfers data from a local
|
|
39
|
+
* It transfers data from a local Strapi instance to a file
|
|
38
40
|
*
|
|
39
|
-
* @param {
|
|
41
|
+
* @param {ExportCommandOptions} opts
|
|
40
42
|
*/
|
|
41
43
|
module.exports = async (opts) => {
|
|
42
44
|
// Validate inputs from Commander
|
|
@@ -76,6 +78,8 @@ module.exports = async (opts) => {
|
|
|
76
78
|
},
|
|
77
79
|
});
|
|
78
80
|
|
|
81
|
+
engine.diagnostics.onDiagnostic(formatDiagnostic('export'));
|
|
82
|
+
|
|
79
83
|
const progress = engine.progress.stream;
|
|
80
84
|
|
|
81
85
|
const getTelemetryPayload = (/* payload */) => {
|
|
@@ -101,14 +105,14 @@ module.exports = async (opts) => {
|
|
|
101
105
|
|
|
102
106
|
const outFileExists = await fs.pathExists(outFile);
|
|
103
107
|
if (!outFileExists) {
|
|
104
|
-
throw new
|
|
108
|
+
throw new TransferEngineTransferError(`Export file not created "${outFile}"`);
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
logger.log(`${chalk.bold('Export process has been completed successfully!')}`);
|
|
108
112
|
logger.log(`Export archive is in ${chalk.green(outFile)}`);
|
|
109
|
-
} catch
|
|
113
|
+
} catch {
|
|
110
114
|
await strapi.telemetry.send('didDEITSProcessFail', getTelemetryPayload());
|
|
111
|
-
logger.error('Export process failed
|
|
115
|
+
logger.error('Export process failed.');
|
|
112
116
|
process.exit(1);
|
|
113
117
|
}
|
|
114
118
|
|
|
@@ -131,7 +135,7 @@ const createSourceProvider = (strapi) => {
|
|
|
131
135
|
/**
|
|
132
136
|
* It creates a local file destination provider based on the given options
|
|
133
137
|
*
|
|
134
|
-
* @param {
|
|
138
|
+
* @param {ExportCommandOptions} opts
|
|
135
139
|
*/
|
|
136
140
|
const createDestinationProvider = (opts) => {
|
|
137
141
|
const { file, compress, encrypt, key, maxSizeJsonl } = opts;
|
|
@@ -13,10 +13,13 @@ const {
|
|
|
13
13
|
} = require('@strapi/data-transfer/lib/engine');
|
|
14
14
|
|
|
15
15
|
const { isObject } = require('lodash/fp');
|
|
16
|
-
const path = require('path');
|
|
17
16
|
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
const {
|
|
18
|
+
buildTransferTable,
|
|
19
|
+
DEFAULT_IGNORED_CONTENT_TYPES,
|
|
20
|
+
createStrapiInstance,
|
|
21
|
+
formatDiagnostic,
|
|
22
|
+
} = require('./utils');
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* @typedef {import('@strapi/data-transfer').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
|
|
@@ -41,7 +44,7 @@ module.exports = async (opts) => {
|
|
|
41
44
|
/**
|
|
42
45
|
* To local Strapi instance
|
|
43
46
|
*/
|
|
44
|
-
const strapiInstance = await
|
|
47
|
+
const strapiInstance = await createStrapiInstance();
|
|
45
48
|
|
|
46
49
|
const destinationOptions = {
|
|
47
50
|
async getStrapi() {
|
|
@@ -84,6 +87,8 @@ module.exports = async (opts) => {
|
|
|
84
87
|
|
|
85
88
|
const engine = createTransferEngine(source, destination, engineOptions);
|
|
86
89
|
|
|
90
|
+
engine.diagnostics.onDiagnostic(formatDiagnostic('import'));
|
|
91
|
+
|
|
87
92
|
const progress = engine.progress.stream;
|
|
88
93
|
const getTelemetryPayload = () => {
|
|
89
94
|
return {
|
|
@@ -107,8 +112,7 @@ module.exports = async (opts) => {
|
|
|
107
112
|
logger.info('Import process has been completed successfully!');
|
|
108
113
|
} catch (e) {
|
|
109
114
|
await strapiInstance.telemetry.send('didDEITSProcessFail', getTelemetryPayload());
|
|
110
|
-
logger.error('Import process failed
|
|
111
|
-
logger.error(e);
|
|
115
|
+
logger.error('Import process failed.');
|
|
112
116
|
process.exit(1);
|
|
113
117
|
}
|
|
114
118
|
|
|
@@ -132,23 +136,9 @@ const getLocalFileSourceOptions = (opts) => {
|
|
|
132
136
|
*/
|
|
133
137
|
const options = {
|
|
134
138
|
file: { path: opts.file },
|
|
135
|
-
compression: { enabled:
|
|
136
|
-
encryption: { enabled:
|
|
139
|
+
compression: { enabled: !!opts.decompress },
|
|
140
|
+
encryption: { enabled: !!opts.decrypt, key: opts.key },
|
|
137
141
|
};
|
|
138
142
|
|
|
139
|
-
const { extname, parse } = path;
|
|
140
|
-
|
|
141
|
-
let file = options.file.path;
|
|
142
|
-
|
|
143
|
-
if (extname(file) === '.enc') {
|
|
144
|
-
file = parse(file).name;
|
|
145
|
-
options.encryption = { enabled: true, key: opts.key };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (extname(file) === '.gz') {
|
|
149
|
-
file = parse(file).name;
|
|
150
|
-
options.compression = { enabled: true };
|
|
151
|
-
}
|
|
152
|
-
|
|
153
143
|
return options;
|
|
154
144
|
};
|