@strapi/strapi 4.6.0-beta.1 → 4.6.0
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 +14 -10
- package/bin/strapi.js +101 -5
- 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 +15 -5
- package/lib/commands/builders/admin.js +1 -1
- package/lib/commands/transfer/export.js +37 -36
- package/lib/commands/transfer/import.js +42 -50
- package/lib/commands/transfer/transfer.js +129 -0
- package/lib/commands/transfer/utils.js +93 -6
- package/lib/commands/utils/commander.js +76 -13
- package/lib/commands/utils/helpers.js +108 -0
- package/lib/core/loaders/plugins/index.js +9 -1
- package/lib/core-api/service/index.d.ts +1 -1
- package/lib/core-api/service/single-type.js +14 -1
- package/lib/services/entity-service/components.js +39 -36
- package/lib/services/entity-service/index.js +13 -2
- package/lib/services/errors.js +5 -1
- package/lib/services/event-hub.js +70 -8
- package/lib/services/metrics/index.js +3 -54
- package/lib/services/metrics/sender.js +1 -3
- package/lib/types/core/strapi/index.d.ts +5 -0
- package/lib/types/factories.d.ts +1 -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 +18 -17
- package/lib/commands/utils/index.js +0 -20
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper functions for the Strapi CLI
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const { isString, isArray } = require('lodash/fp');
|
|
9
|
+
|
|
10
|
+
const bytesPerKb = 1024;
|
|
11
|
+
const sizes = ['B ', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert bytes to a human readable formatted string, for example "1024" becomes "1KB"
|
|
15
|
+
*
|
|
16
|
+
* @param {number} bytes The bytes to be converted
|
|
17
|
+
* @param {number} decimals How many decimals to include in the final number
|
|
18
|
+
* @param {number} padStart Pad the string with space at the beginning so that it has at least this many characters
|
|
19
|
+
*/
|
|
20
|
+
const readableBytes = (bytes, decimals = 1, padStart = 0) => {
|
|
21
|
+
if (!bytes) {
|
|
22
|
+
return '0';
|
|
23
|
+
}
|
|
24
|
+
const i = Math.floor(Math.log(bytes) / Math.log(bytesPerKb));
|
|
25
|
+
const result = `${parseFloat((bytes / bytesPerKb ** i).toFixed(decimals))} ${sizes[i].padStart(
|
|
26
|
+
2
|
|
27
|
+
)}`;
|
|
28
|
+
|
|
29
|
+
return result.padStart(padStart);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* Display message(s) to console and then call process.exit with code.
|
|
35
|
+
* If code is zero, console.log and green text is used for messages, otherwise console.error and red text.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} code Code to exit process with
|
|
38
|
+
* @param {string | Array} message Message(s) to display before exiting
|
|
39
|
+
*/
|
|
40
|
+
const exitWith = (code, message = undefined) => {
|
|
41
|
+
const logger = (message) => {
|
|
42
|
+
if (code === 0) {
|
|
43
|
+
console.log(chalk.green(message));
|
|
44
|
+
} else {
|
|
45
|
+
console.error(chalk.red(message));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (isString(message)) {
|
|
50
|
+
logger(message);
|
|
51
|
+
} else if (isArray(message)) {
|
|
52
|
+
message.forEach((msg) => logger(msg));
|
|
53
|
+
}
|
|
54
|
+
process.exit(code);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* assert that a URL object has a protocol value
|
|
59
|
+
*
|
|
60
|
+
* @param {URL} url
|
|
61
|
+
* @param {string[]|string|undefined} [protocol]
|
|
62
|
+
*/
|
|
63
|
+
const assertUrlHasProtocol = (url, protocol = undefined) => {
|
|
64
|
+
if (!url.protocol) {
|
|
65
|
+
exitWith(1, `${url.toString()} does not have a protocol`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// if just checking for the existence of a protocol, return
|
|
69
|
+
if (!protocol) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isString(protocol)) {
|
|
74
|
+
if (protocol !== url.protocol) {
|
|
75
|
+
exitWith(1, `${url.toString()} must have the protocol ${protocol}`);
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// assume an array
|
|
81
|
+
if (!protocol.some((protocol) => url.protocol === protocol)) {
|
|
82
|
+
return exitWith(
|
|
83
|
+
1,
|
|
84
|
+
`${url.toString()} must have one of the following protocols: ${protocol.join(',')}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Passes commander options to conditionCallback(). If it returns true, call isMetCallback otherwise call isNotMetCallback
|
|
91
|
+
*/
|
|
92
|
+
const ifOptions = (conditionCallback, isMetCallback = () => {}, isNotMetCallback = () => {}) => {
|
|
93
|
+
return async (command) => {
|
|
94
|
+
const opts = command.opts();
|
|
95
|
+
if (await conditionCallback(opts)) {
|
|
96
|
+
await isMetCallback(command);
|
|
97
|
+
} else {
|
|
98
|
+
await isNotMetCallback(command);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
exitWith,
|
|
105
|
+
assertUrlHasProtocol,
|
|
106
|
+
ifOptions,
|
|
107
|
+
readableBytes,
|
|
108
|
+
};
|
|
@@ -85,7 +85,15 @@ const loadPlugins = async (strapi) => {
|
|
|
85
85
|
for (const pluginName of Object.keys(enabledPlugins)) {
|
|
86
86
|
const enabledPlugin = enabledPlugins[pluginName];
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
let serverEntrypointPath;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
serverEntrypointPath = join(enabledPlugin.pathToPlugin, 'strapi-server.js');
|
|
92
|
+
} catch (e) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Error loading the plugin ${pluginName} because ${pluginName} is not installed. Please either install the plugin or remove it's configuration.`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
89
97
|
|
|
90
98
|
// only load plugins with a server entrypoint
|
|
91
99
|
if (!(await fse.pathExists(serverEntrypointPath))) {
|
|
@@ -21,5 +21,5 @@ export interface CollectionTypeService extends BaseService {
|
|
|
21
21
|
export type Service = SingleTypeService | CollectionTypeService;
|
|
22
22
|
|
|
23
23
|
export type GenericService = Partial<Service> & {
|
|
24
|
-
[method: string | number | symbol]:
|
|
24
|
+
[method: string | number | symbol]: (...args: any) => any;
|
|
25
25
|
};
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { propOr } = require('lodash/fp');
|
|
4
|
+
|
|
3
5
|
const { ValidationError } = require('@strapi/utils').errors;
|
|
6
|
+
const {
|
|
7
|
+
hasDraftAndPublish,
|
|
8
|
+
constants: { PUBLISHED_AT_ATTRIBUTE },
|
|
9
|
+
} = require('@strapi/utils').contentTypes;
|
|
10
|
+
|
|
11
|
+
const setPublishedAt = (data) => {
|
|
12
|
+
data[PUBLISHED_AT_ATTRIBUTE] = propOr(new Date(), PUBLISHED_AT_ATTRIBUTE, data);
|
|
13
|
+
};
|
|
4
14
|
|
|
5
15
|
/**
|
|
6
16
|
* Returns a single type service to handle default core-api actions
|
|
@@ -27,7 +37,7 @@ const createSingleTypeService = ({ contentType }) => {
|
|
|
27
37
|
* @return {Promise}
|
|
28
38
|
*/
|
|
29
39
|
async createOrUpdate({ data, ...params } = {}) {
|
|
30
|
-
const entity = await this.find(params);
|
|
40
|
+
const entity = await this.find({ ...params, publicationState: 'preview' });
|
|
31
41
|
|
|
32
42
|
if (!entity) {
|
|
33
43
|
const count = await strapi.query(uid).count();
|
|
@@ -35,6 +45,9 @@ const createSingleTypeService = ({ contentType }) => {
|
|
|
35
45
|
throw new ValidationError('singleType.alreadyExists');
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
if (hasDraftAndPublish(contentType)) {
|
|
49
|
+
setPublishedAt(data);
|
|
50
|
+
}
|
|
38
51
|
return strapi.entityService.create(uid, { ...params, data });
|
|
39
52
|
}
|
|
40
53
|
|
|
@@ -43,9 +43,10 @@ const createComponents = async (uid, data) => {
|
|
|
43
43
|
throw new Error('Expected an array to create repeatable component');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const components =
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const components = [];
|
|
47
|
+
for (const value of componentValue) {
|
|
48
|
+
components.push(await createComponent(componentUID, value));
|
|
49
|
+
}
|
|
49
50
|
|
|
50
51
|
componentBody[attributeName] = components.map(({ id }) => {
|
|
51
52
|
return {
|
|
@@ -77,18 +78,19 @@ const createComponents = async (uid, data) => {
|
|
|
77
78
|
throw new Error('Expected an array to create repeatable component');
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
const dynamicZoneData = [];
|
|
82
|
+
for (const value of dynamiczoneValues) {
|
|
83
|
+
const { id } = await createComponent(value.__component, value);
|
|
84
|
+
dynamicZoneData.push({
|
|
85
|
+
id,
|
|
86
|
+
__component: value.__component,
|
|
87
|
+
__pivot: {
|
|
88
|
+
field: attributeName,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
componentBody[attributeName] = dynamicZoneData;
|
|
92
94
|
|
|
93
95
|
continue;
|
|
94
96
|
}
|
|
@@ -137,9 +139,10 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
137
139
|
throw new Error('Expected an array to create repeatable component');
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
const components =
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
const components = [];
|
|
143
|
+
for (const value of componentValue) {
|
|
144
|
+
components.push(await updateOrCreateComponent(componentUID, value));
|
|
145
|
+
}
|
|
143
146
|
|
|
144
147
|
componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }) => {
|
|
145
148
|
return {
|
|
@@ -173,19 +176,19 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
173
176
|
throw new Error('Expected an array to create repeatable component');
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
const dynamicZoneData = [];
|
|
180
|
+
for (const value of dynamiczoneValues) {
|
|
181
|
+
const { id } = await updateOrCreateComponent(value.__component, value);
|
|
182
|
+
dynamicZoneData.push({
|
|
183
|
+
id,
|
|
184
|
+
__component: value.__component,
|
|
185
|
+
__pivot: {
|
|
186
|
+
field: attributeName,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
179
190
|
|
|
180
|
-
|
|
181
|
-
id,
|
|
182
|
-
__component: value.__component,
|
|
183
|
-
__pivot: {
|
|
184
|
-
field: attributeName,
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
})
|
|
188
|
-
);
|
|
191
|
+
componentBody[attributeName] = dynamicZoneData;
|
|
189
192
|
|
|
190
193
|
continue;
|
|
191
194
|
}
|
|
@@ -287,14 +290,14 @@ const deleteComponents = async (uid, entityToDelete, { loadComponents = true } =
|
|
|
287
290
|
|
|
288
291
|
if (attribute.type === 'component') {
|
|
289
292
|
const { component: componentUID } = attribute;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
for (const subValue of _.castArray(value)) {
|
|
294
|
+
await deleteComponent(componentUID, subValue);
|
|
295
|
+
}
|
|
293
296
|
} else {
|
|
294
297
|
// delete dynamic zone components
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
+
for (const subValue of _.castArray(value)) {
|
|
299
|
+
await deleteComponent(subValue.__component, subValue);
|
|
300
|
+
}
|
|
298
301
|
}
|
|
299
302
|
|
|
300
303
|
continue;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const delegate = require('delegates');
|
|
5
|
-
const { InvalidTimeError, InvalidDateError, InvalidDateTimeError } =
|
|
5
|
+
const { InvalidTimeError, InvalidDateError, InvalidDateTimeError, InvalidRelationError } =
|
|
6
6
|
require('@strapi/database').errors;
|
|
7
7
|
const {
|
|
8
8
|
webhook: webhookUtils,
|
|
@@ -34,7 +34,12 @@ const transformLoadParamsToQuery = (uid, field, params = {}, pagination = {}) =>
|
|
|
34
34
|
// TODO: those should be strapi events used by the webhooks not the other way arround
|
|
35
35
|
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
|
|
36
36
|
|
|
37
|
-
const databaseErrorsToTransform = [
|
|
37
|
+
const databaseErrorsToTransform = [
|
|
38
|
+
InvalidTimeError,
|
|
39
|
+
InvalidDateTimeError,
|
|
40
|
+
InvalidDateError,
|
|
41
|
+
InvalidRelationError,
|
|
42
|
+
];
|
|
38
43
|
|
|
39
44
|
const creationPipeline = (data, context) => {
|
|
40
45
|
return applyTransforms(data, context);
|
|
@@ -55,11 +60,17 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
55
60
|
},
|
|
56
61
|
|
|
57
62
|
async emitEvent(uid, event, entity) {
|
|
63
|
+
// Ignore audit log events to prevent infinite loops
|
|
64
|
+
if (uid === 'admin::audit-log') {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
const model = strapi.getModel(uid);
|
|
59
69
|
const sanitizedEntity = await sanitize.sanitizers.defaultSanitizeOutput(model, entity);
|
|
60
70
|
|
|
61
71
|
eventHub.emit(event, {
|
|
62
72
|
model: model.modelName,
|
|
73
|
+
uid: model.uid,
|
|
63
74
|
entry: sanitizedEntity,
|
|
64
75
|
});
|
|
65
76
|
},
|
package/lib/services/errors.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const createError = require('http-errors');
|
|
4
|
-
const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError } =
|
|
4
|
+
const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError, RateLimitError } =
|
|
5
5
|
require('@strapi/utils').errors;
|
|
6
6
|
|
|
7
7
|
const mapErrorsAndStatus = [
|
|
@@ -21,6 +21,10 @@ const mapErrorsAndStatus = [
|
|
|
21
21
|
classError: PayloadTooLargeError,
|
|
22
22
|
status: 413,
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
classError: RateLimitError,
|
|
26
|
+
status: 429,
|
|
27
|
+
},
|
|
24
28
|
];
|
|
25
29
|
|
|
26
30
|
const formatApplicationError = (error) => {
|
|
@@ -1,16 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The event hub is Strapi's event control center.
|
|
3
5
|
*/
|
|
6
|
+
module.exports = function createEventHub() {
|
|
7
|
+
const listeners = new Map();
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
// Default subscriber to easily add listeners with the on() method
|
|
10
|
+
const defaultSubscriber = async (eventName, ...args) => {
|
|
11
|
+
if (listeners.has(eventName)) {
|
|
12
|
+
for (const listener of listeners.get(eventName)) {
|
|
13
|
+
await listener(...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
// Store of subscribers that will be called when an event is emitted
|
|
19
|
+
const subscribers = [defaultSubscriber];
|
|
8
20
|
|
|
9
|
-
|
|
21
|
+
const eventHub = {
|
|
22
|
+
async emit(eventName, ...args) {
|
|
23
|
+
for (const subscriber of subscribers) {
|
|
24
|
+
await subscriber(eventName, ...args);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
subscribe(subscriber) {
|
|
29
|
+
subscribers.push(subscriber);
|
|
30
|
+
|
|
31
|
+
// Return a function to remove the subscriber
|
|
32
|
+
return () => {
|
|
33
|
+
eventHub.unsubscribe(subscriber);
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
unsubscribe(subscriber) {
|
|
38
|
+
subscribers.splice(subscribers.indexOf(subscriber), 1);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
on(eventName, listener) {
|
|
42
|
+
if (!listeners.has(eventName)) {
|
|
43
|
+
listeners.set(eventName, [listener]);
|
|
44
|
+
} else {
|
|
45
|
+
listeners.get(eventName).push(listener);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Return a function to remove the listener
|
|
49
|
+
return () => {
|
|
50
|
+
eventHub.off(eventName, listener);
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
off(eventName, listener) {
|
|
55
|
+
listeners.get(eventName).splice(listeners.get(eventName).indexOf(listener), 1);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
once(eventName, listener) {
|
|
59
|
+
return eventHub.on(eventName, async (...args) => {
|
|
60
|
+
eventHub.off(eventName, listener);
|
|
61
|
+
return listener(...args);
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
destroy() {
|
|
66
|
+
listeners.clear();
|
|
67
|
+
subscribers.length = 0;
|
|
68
|
+
return this;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...eventHub,
|
|
74
|
+
removeListener: eventHub.off,
|
|
75
|
+
removeAllListeners: eventHub.destroy,
|
|
76
|
+
addListener: eventHub.on,
|
|
77
|
+
};
|
|
16
78
|
};
|
|
@@ -5,12 +5,8 @@
|
|
|
5
5
|
* You can learn more at https://docs.strapi.io/developer-docs/latest/getting-started/usage-information.html
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const crypto = require('crypto');
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
8
|
const { scheduleJob } = require('node-schedule');
|
|
12
9
|
|
|
13
|
-
const ee = require('../../utils/ee');
|
|
14
10
|
const wrapWithRateLimit = require('./rate-limiter');
|
|
15
11
|
const createSender = require('./sender');
|
|
16
12
|
const createMiddleware = require('./middleware');
|
|
@@ -46,41 +42,14 @@ const createTelemetryInstance = (strapi) => {
|
|
|
46
42
|
strapi.server.use(createMiddleware({ sendEvent }));
|
|
47
43
|
}
|
|
48
44
|
},
|
|
49
|
-
bootstrap() {
|
|
50
|
-
if (strapi.EE === true && ee.isEE === true) {
|
|
51
|
-
const pingDisabled =
|
|
52
|
-
isTruthy(process.env.STRAPI_LICENSE_PING_DISABLED) && ee.licenseInfo.type === 'gold';
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
return sendEvent(
|
|
56
|
-
'didCheckLicense',
|
|
57
|
-
{
|
|
58
|
-
groupProperties: {
|
|
59
|
-
licenseInfo: {
|
|
60
|
-
...ee.licenseInfo,
|
|
61
|
-
projectHash: hashProject(strapi),
|
|
62
|
-
dependencyHash: hashDep(strapi),
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
headers: { 'x-strapi-project': 'enterprise' },
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
};
|
|
46
|
+
bootstrap() {},
|
|
71
47
|
|
|
72
|
-
if (!pingDisabled) {
|
|
73
|
-
const licenseCron = scheduleJob('0 0 0 * * 7', () => sendLicenseCheck());
|
|
74
|
-
crons.push(licenseCron);
|
|
75
|
-
|
|
76
|
-
sendLicenseCheck();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
48
|
destroy() {
|
|
81
|
-
//
|
|
49
|
+
// Clear open handles
|
|
82
50
|
crons.forEach((cron) => cron.cancel());
|
|
83
51
|
},
|
|
52
|
+
|
|
84
53
|
async send(event, payload) {
|
|
85
54
|
if (isDisabled) return true;
|
|
86
55
|
return sendEvent(event, payload);
|
|
@@ -88,24 +57,4 @@ const createTelemetryInstance = (strapi) => {
|
|
|
88
57
|
};
|
|
89
58
|
};
|
|
90
59
|
|
|
91
|
-
const hash = (str) => crypto.createHash('sha256').update(str).digest('hex');
|
|
92
|
-
|
|
93
|
-
const hashProject = (strapi) =>
|
|
94
|
-
hash(`${strapi.config.get('info.name')}${strapi.config.get('info.description')}`);
|
|
95
|
-
|
|
96
|
-
const hashDep = (strapi) => {
|
|
97
|
-
const depStr = JSON.stringify(strapi.config.info.dependencies);
|
|
98
|
-
const readmePath = path.join(strapi.dirs.app.root, 'README.md');
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
if (fs.existsSync(readmePath)) {
|
|
102
|
-
return hash(`${depStr}${fs.readFileSync(readmePath)}`);
|
|
103
|
-
}
|
|
104
|
-
} catch (err) {
|
|
105
|
-
return hash(`${depStr}`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return hash(`${depStr}`);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
60
|
module.exports = createTelemetryInstance;
|
|
@@ -8,7 +8,6 @@ const fetch = require('node-fetch');
|
|
|
8
8
|
const ciEnv = require('ci-info');
|
|
9
9
|
const { isUsingTypeScriptSync } = require('@strapi/typescript-utils');
|
|
10
10
|
const { env } = require('@strapi/utils');
|
|
11
|
-
const ee = require('../../utils/ee');
|
|
12
11
|
const machineID = require('../../utils/machine-id');
|
|
13
12
|
const { generateAdminUserHash } = require('./admin-user-hash');
|
|
14
13
|
|
|
@@ -37,7 +36,6 @@ const addPackageJsonStrapiMetadata = (metadata, strapi) => {
|
|
|
37
36
|
module.exports = (strapi) => {
|
|
38
37
|
const { uuid } = strapi.config;
|
|
39
38
|
const deviceId = machineID();
|
|
40
|
-
const isEE = strapi.EE === true && ee.isEE === true;
|
|
41
39
|
|
|
42
40
|
const serverRootPath = strapi.dirs.app.root;
|
|
43
41
|
const adminRootPath = path.join(strapi.dirs.app.root, 'src', 'admin');
|
|
@@ -55,7 +53,6 @@ module.exports = (strapi) => {
|
|
|
55
53
|
docker: process.env.DOCKER || isDocker(),
|
|
56
54
|
isCI: ciEnv.isCI,
|
|
57
55
|
version: strapi.config.get('info.strapi'),
|
|
58
|
-
projectType: isEE ? 'Enterprise' : 'Community',
|
|
59
56
|
useTypescriptOnServer: isUsingTypeScriptSync(serverRootPath),
|
|
60
57
|
useTypescriptOnAdmin: isUsingTypeScriptSync(adminRootPath),
|
|
61
58
|
projectId: uuid,
|
|
@@ -77,6 +74,7 @@ module.exports = (strapi) => {
|
|
|
77
74
|
userProperties: userId ? { ...anonymousUserProperties, ...payload.userProperties } : {},
|
|
78
75
|
groupProperties: {
|
|
79
76
|
...anonymousGroupProperties,
|
|
77
|
+
projectType: strapi.EE ? 'Enterprise' : 'Community',
|
|
80
78
|
...payload.groupProperties,
|
|
81
79
|
},
|
|
82
80
|
}),
|
package/lib/types/factories.d.ts
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isEmpty, negate } = require('lodash/fp');
|
|
4
|
+
|
|
5
|
+
const INTEGER_REGEX = /^\d+$/;
|
|
6
|
+
const STEP_REGEX = /^\*\/\d+$/;
|
|
7
|
+
const COMPONENTS = [
|
|
8
|
+
{ limit: 60, zeroBasedIndices: true, functionName: 'getSeconds' },
|
|
9
|
+
{ limit: 60, zeroBasedIndices: true, functionName: 'getMinutes' },
|
|
10
|
+
{ limit: 24, zeroBasedIndices: true, functionName: 'getHours' },
|
|
11
|
+
{ limit: 31, zeroBasedIndices: false, functionName: 'getDate' },
|
|
12
|
+
{ limit: 12, zeroBasedIndices: false, functionName: 'getMonth' },
|
|
13
|
+
{ limit: 7, zeroBasedIndices: true, functionName: 'getDay' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const shift = (component, index, date) => {
|
|
17
|
+
if (component === '*') {
|
|
18
|
+
return '*';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { limit, zeroBasedIndices, functionName } = COMPONENTS[index];
|
|
22
|
+
const offset = +!zeroBasedIndices;
|
|
23
|
+
const currentValue = date[functionName]();
|
|
24
|
+
|
|
25
|
+
if (INTEGER_REGEX.test(component)) {
|
|
26
|
+
return ((Number.parseInt(component, 10) + currentValue) % limit) + offset;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (STEP_REGEX.test(component)) {
|
|
30
|
+
const [, step] = component.split('/');
|
|
31
|
+
const frequency = Math.floor(limit / step);
|
|
32
|
+
const list = Array.from({ length: frequency }, (_, index) => index * step);
|
|
33
|
+
return list.map((value) => ((value + currentValue) % limit) + offset).sort((a, b) => a - b);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Unsupported syntax
|
|
37
|
+
return component;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Simulate an interval by shifting a cron expression using the specified date.
|
|
42
|
+
* @param {string} rule A cron expression you want to shift.
|
|
43
|
+
* @param {Date} date The date that's gonna be used as the start of the "interval", it defaults to now.
|
|
44
|
+
* @returns The shifted cron expression.
|
|
45
|
+
*/
|
|
46
|
+
const shiftCronExpression = (rule, date = new Date()) => {
|
|
47
|
+
const components = rule.trim().split(' ').filter(negate(isEmpty));
|
|
48
|
+
const secondsIncluded = components.length === 6;
|
|
49
|
+
return components
|
|
50
|
+
.map((component, index) => shift(component, secondsIncluded ? index : index + 1, date))
|
|
51
|
+
.join(' ');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
shiftCronExpression,
|
|
56
|
+
};
|