@strapi/strapi 4.0.0-next.9 → 4.0.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 +12 -12
- package/bin/strapi.js +32 -5
- package/lib/Strapi.js +140 -72
- package/lib/commands/build.js +16 -6
- package/lib/commands/console.js +1 -1
- package/lib/commands/content-types/list.js +22 -0
- package/lib/commands/develop.js +14 -15
- package/lib/commands/generate-template.js +4 -5
- package/lib/commands/hooks/list.js +22 -0
- package/lib/commands/middlewares/list.js +22 -0
- package/lib/commands/new.js +3 -1
- package/lib/commands/policies/list.js +22 -0
- package/lib/commands/routes/list.js +28 -0
- package/lib/commands/services/list.js +22 -0
- package/lib/commands/watchAdmin.js +18 -9
- package/lib/core/app-configuration/index.js +3 -19
- package/lib/core/bootstrap.js +3 -34
- package/lib/core/domain/content-type/index.js +3 -7
- package/lib/core/domain/module/index.js +8 -6
- package/lib/core/domain/module/validation.js +1 -4
- package/lib/core/loaders/admin.js +2 -2
- package/lib/core/loaders/apis.js +7 -7
- package/lib/core/loaders/components.js +3 -5
- package/lib/core/loaders/index.js +1 -0
- package/lib/core/loaders/middlewares.js +23 -123
- package/lib/core/loaders/plugins/get-enabled-plugins.js +48 -14
- package/lib/core/loaders/plugins/index.js +30 -14
- package/lib/core/loaders/policies.js +1 -1
- package/lib/core/loaders/src-index.js +39 -0
- package/lib/core/registries/apis.js +2 -16
- package/lib/core/registries/content-types.js +50 -6
- package/lib/core/registries/controllers.d.ts +7 -0
- package/lib/core/registries/controllers.js +74 -3
- package/lib/core/registries/hooks.d.ts +20 -0
- package/lib/core/registries/hooks.js +87 -0
- package/lib/core/registries/middlewares.d.ts +5 -0
- package/lib/core/registries/middlewares.js +61 -2
- package/lib/core/registries/modules.js +3 -3
- package/lib/core/registries/plugins.js +2 -2
- package/lib/core/registries/policies.d.ts +9 -0
- package/lib/core/registries/policies.js +57 -6
- package/lib/core/registries/services.d.ts +7 -0
- package/lib/core/registries/services.js +71 -15
- package/lib/core-api/controller/collection-type.js +38 -11
- package/lib/core-api/controller/index.d.ts +25 -0
- package/lib/core-api/controller/index.js +30 -11
- package/lib/core-api/controller/single-type.js +26 -7
- package/lib/core-api/controller/transform.js +28 -3
- package/lib/core-api/routes/index.js +71 -0
- package/lib/core-api/service/collection-type.js +22 -27
- package/lib/core-api/service/index.d.ts +21 -0
- package/lib/core-api/service/index.js +9 -19
- package/lib/core-api/service/pagination.js +7 -2
- package/lib/core-api/service/single-type.js +17 -20
- package/lib/factories.d.ts +48 -0
- package/lib/factories.js +84 -0
- package/lib/index.d.ts +10 -31
- package/lib/index.js +5 -1
- package/lib/middlewares/body.js +33 -0
- package/lib/middlewares/compression.js +8 -0
- package/lib/middlewares/cors.js +58 -0
- package/lib/middlewares/errors.js +40 -0
- package/lib/middlewares/favicon.js +19 -0
- package/lib/middlewares/index.d.ts +5 -0
- package/lib/middlewares/index.js +30 -116
- package/lib/middlewares/ip.js +8 -0
- package/lib/middlewares/logger.js +27 -0
- package/lib/middlewares/powered-by.js +20 -0
- package/lib/middlewares/public/index.js +98 -73
- package/lib/middlewares/query.js +46 -0
- package/lib/middlewares/response-time.js +15 -0
- package/lib/middlewares/responses.js +19 -0
- package/lib/middlewares/security.js +51 -0
- package/lib/middlewares/session/index.js +6 -6
- package/lib/migrations/draft-publish.js +57 -0
- package/lib/services/auth/index.js +87 -0
- package/lib/services/core-store.js +64 -49
- package/lib/services/cron.js +54 -0
- package/lib/services/entity-service/attributes/index.js +31 -0
- package/lib/services/entity-service/attributes/transforms.js +20 -0
- package/lib/services/entity-service/components.js +39 -15
- package/lib/services/entity-service/index.d.ts +91 -0
- package/lib/services/entity-service/index.js +118 -60
- package/lib/services/entity-service/params.js +48 -81
- package/lib/services/entity-validator/index.js +76 -43
- package/lib/services/entity-validator/validators.js +129 -43
- package/lib/services/errors.js +77 -0
- package/lib/services/fs.js +1 -1
- package/lib/services/metrics/index.js +38 -36
- package/lib/services/server/admin-api.js +14 -0
- package/lib/services/server/api.js +36 -0
- package/lib/services/server/compose-endpoint.js +141 -0
- package/lib/services/server/content-api.js +16 -0
- package/lib/{server.js → services/server/http-server.js} +0 -0
- package/lib/services/server/index.js +127 -0
- package/lib/services/server/koa.js +64 -0
- package/lib/services/server/middleware.js +122 -0
- package/lib/services/server/policy.js +32 -0
- package/lib/services/server/register-middlewares.js +110 -0
- package/lib/services/server/register-routes.js +106 -0
- package/lib/services/server/routing.js +120 -0
- package/lib/services/webhook-runner.js +1 -1
- package/lib/utils/ee.js +3 -3
- package/lib/utils/get-dirs.js +17 -0
- package/lib/utils/index.js +2 -0
- package/lib/utils/signals.js +24 -0
- package/lib/utils/update-notifier/index.js +2 -1
- package/package.json +93 -93
- package/lib/core/app-configuration/load-functions.js +0 -28
- package/lib/core-api/index.js +0 -39
- package/lib/middlewares/boom/defaults.json +0 -5
- package/lib/middlewares/boom/index.js +0 -147
- package/lib/middlewares/cors/index.js +0 -66
- package/lib/middlewares/cron/defaults.json +0 -5
- package/lib/middlewares/cron/index.js +0 -43
- package/lib/middlewares/favicon/defaults.json +0 -7
- package/lib/middlewares/favicon/index.js +0 -32
- package/lib/middlewares/gzip/defaults.json +0 -6
- package/lib/middlewares/gzip/index.js +0 -19
- package/lib/middlewares/helmet/defaults.json +0 -18
- package/lib/middlewares/helmet/index.js +0 -9
- package/lib/middlewares/ip/defaults.json +0 -7
- package/lib/middlewares/ip/index.js +0 -25
- package/lib/middlewares/language/defaults.json +0 -9
- package/lib/middlewares/language/index.js +0 -40
- package/lib/middlewares/logger/defaults.json +0 -5
- package/lib/middlewares/logger/index.js +0 -37
- package/lib/middlewares/parser/defaults.json +0 -11
- package/lib/middlewares/parser/index.js +0 -72
- package/lib/middlewares/poweredBy/defaults.json +0 -5
- package/lib/middlewares/poweredBy/index.js +0 -16
- package/lib/middlewares/public/defaults.json +0 -8
- package/lib/middlewares/responseTime/defaults.json +0 -5
- package/lib/middlewares/responseTime/index.js +0 -25
- package/lib/middlewares/responses/defaults.json +0 -5
- package/lib/middlewares/responses/index.js +0 -18
- package/lib/middlewares/router/defaults.json +0 -7
- package/lib/middlewares/router/index.js +0 -72
- package/lib/middlewares/router/utils/compose-endpoint.js +0 -169
- package/lib/utils/get-prefixed-dependencies.js +0 -7
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
const { has, assoc, prop } = require('lodash/fp');
|
|
7
|
+
const { has, assoc, prop, isObject } = require('lodash/fp');
|
|
8
8
|
const strapiUtils = require('@strapi/utils');
|
|
9
9
|
const validators = require('./validators');
|
|
10
10
|
|
|
11
|
-
const { yup,
|
|
11
|
+
const { yup, validateYupSchema } = strapiUtils;
|
|
12
12
|
const { isMediaAttribute, isScalarAttribute, getWritableAttributes } = strapiUtils.contentTypes;
|
|
13
|
+
const { ValidationError } = strapiUtils.errors;
|
|
13
14
|
|
|
14
|
-
const addMinMax = (
|
|
15
|
-
if (
|
|
15
|
+
const addMinMax = (validator, { attr, updatedAttribute }) => {
|
|
16
|
+
if (
|
|
17
|
+
Number.isInteger(attr.min) &&
|
|
18
|
+
(attr.required || (Array.isArray(updatedAttribute.value) && updatedAttribute.value.length > 0))
|
|
19
|
+
) {
|
|
16
20
|
validator = validator.min(attr.min);
|
|
17
21
|
}
|
|
18
22
|
if (Number.isInteger(attr.max)) {
|
|
@@ -21,7 +25,7 @@ const addMinMax = (attr, validator, data) => {
|
|
|
21
25
|
return validator;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
|
-
const addRequiredValidation = createOrUpdate => (
|
|
28
|
+
const addRequiredValidation = createOrUpdate => (validator, { attr: { required } }) => {
|
|
25
29
|
if (required) {
|
|
26
30
|
if (createOrUpdate === 'creation') {
|
|
27
31
|
validator = validator.notNil();
|
|
@@ -34,7 +38,7 @@ const addRequiredValidation = createOrUpdate => (required, validator) => {
|
|
|
34
38
|
return validator;
|
|
35
39
|
};
|
|
36
40
|
|
|
37
|
-
const addDefault = createOrUpdate => (
|
|
41
|
+
const addDefault = createOrUpdate => (validator, { attr }) => {
|
|
38
42
|
if (createOrUpdate === 'creation') {
|
|
39
43
|
if (
|
|
40
44
|
((attr.type === 'component' && attr.repeatable) || attr.type === 'dynamiczone') &&
|
|
@@ -53,7 +57,7 @@ const addDefault = createOrUpdate => (attr, validator) => {
|
|
|
53
57
|
|
|
54
58
|
const preventCast = validator => validator.transform((val, originalVal) => originalVal);
|
|
55
59
|
|
|
56
|
-
const createComponentValidator = createOrUpdate => (attr,
|
|
60
|
+
const createComponentValidator = createOrUpdate => ({ attr, updatedAttribute }, { isDraft }) => {
|
|
57
61
|
let validator;
|
|
58
62
|
|
|
59
63
|
const model = strapi.getModel(attr.component);
|
|
@@ -65,19 +69,23 @@ const createComponentValidator = createOrUpdate => (attr, data, { isDraft }) =>
|
|
|
65
69
|
validator = yup
|
|
66
70
|
.array()
|
|
67
71
|
.of(
|
|
68
|
-
yup.lazy(item =>
|
|
72
|
+
yup.lazy(item =>
|
|
73
|
+
createModelValidator(createOrUpdate)({ model, data: item }, { isDraft }).notNull()
|
|
74
|
+
)
|
|
69
75
|
);
|
|
70
|
-
validator = addRequiredValidation(createOrUpdate)(
|
|
71
|
-
validator = addMinMax(
|
|
76
|
+
validator = addRequiredValidation(createOrUpdate)(validator, { attr: { required: true } });
|
|
77
|
+
validator = addMinMax(validator, { attr, updatedAttribute });
|
|
72
78
|
} else {
|
|
73
|
-
validator = createModelValidator(createOrUpdate)(model,
|
|
74
|
-
validator = addRequiredValidation(createOrUpdate)(
|
|
79
|
+
validator = createModelValidator(createOrUpdate)({ model, updatedAttribute }, { isDraft });
|
|
80
|
+
validator = addRequiredValidation(createOrUpdate)(validator, {
|
|
81
|
+
attr: { required: !isDraft && attr.required },
|
|
82
|
+
});
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
return validator;
|
|
78
86
|
};
|
|
79
87
|
|
|
80
|
-
const createDzValidator = createOrUpdate => (attr,
|
|
88
|
+
const createDzValidator = createOrUpdate => ({ attr, updatedAttribute }, { isDraft }) => {
|
|
81
89
|
let validator;
|
|
82
90
|
|
|
83
91
|
validator = yup.array().of(
|
|
@@ -94,76 +102,85 @@ const createDzValidator = createOrUpdate => (attr, data, { isDraft }) => {
|
|
|
94
102
|
.notNull();
|
|
95
103
|
|
|
96
104
|
return model
|
|
97
|
-
? schema.concat(createModelValidator(createOrUpdate)(model, item, { isDraft }))
|
|
105
|
+
? schema.concat(createModelValidator(createOrUpdate)({ model, data: item }, { isDraft }))
|
|
98
106
|
: schema;
|
|
99
107
|
})
|
|
100
108
|
);
|
|
101
|
-
validator = addRequiredValidation(createOrUpdate)(
|
|
102
|
-
validator = addMinMax(
|
|
109
|
+
validator = addRequiredValidation(createOrUpdate)(validator, { attr: { required: true } });
|
|
110
|
+
validator = addMinMax(validator, { attr, updatedAttribute });
|
|
103
111
|
|
|
104
112
|
return validator;
|
|
105
113
|
};
|
|
106
114
|
|
|
107
|
-
const createRelationValidator = createOrUpdate => (attr,
|
|
115
|
+
const createRelationValidator = createOrUpdate => ({ attr, updatedAttribute }, { isDraft }) => {
|
|
108
116
|
let validator;
|
|
109
117
|
|
|
110
|
-
if (Array.isArray(
|
|
118
|
+
if (Array.isArray(updatedAttribute.value)) {
|
|
111
119
|
validator = yup.array().of(yup.mixed());
|
|
112
120
|
} else {
|
|
113
121
|
validator = yup.mixed();
|
|
114
122
|
}
|
|
115
|
-
|
|
123
|
+
|
|
124
|
+
validator = addRequiredValidation(createOrUpdate)(validator, {
|
|
125
|
+
attr: { required: !isDraft && attr.required },
|
|
126
|
+
});
|
|
116
127
|
|
|
117
128
|
return validator;
|
|
118
129
|
};
|
|
119
130
|
|
|
120
|
-
const createScalarAttributeValidator = createOrUpdate => (
|
|
131
|
+
const createScalarAttributeValidator = createOrUpdate => (metas, options) => {
|
|
121
132
|
let validator;
|
|
122
133
|
|
|
123
|
-
if (has(attr.type, validators)) {
|
|
124
|
-
validator = validators[attr.type](
|
|
134
|
+
if (has(metas.attr.type, validators)) {
|
|
135
|
+
validator = validators[metas.attr.type](metas, options);
|
|
125
136
|
} else {
|
|
126
137
|
// No validators specified - fall back to mixed
|
|
127
138
|
validator = yup.mixed();
|
|
128
139
|
}
|
|
129
140
|
|
|
130
|
-
validator = addRequiredValidation(createOrUpdate)(
|
|
141
|
+
validator = addRequiredValidation(createOrUpdate)(validator, {
|
|
142
|
+
attr: { required: !options.isDraft && metas.attr.required },
|
|
143
|
+
});
|
|
131
144
|
|
|
132
145
|
return validator;
|
|
133
146
|
};
|
|
134
147
|
|
|
135
|
-
const createAttributeValidator = createOrUpdate => (
|
|
148
|
+
const createAttributeValidator = createOrUpdate => (metas, options) => {
|
|
136
149
|
let validator;
|
|
137
150
|
|
|
138
|
-
if (isMediaAttribute(attr)) {
|
|
151
|
+
if (isMediaAttribute(metas.attr)) {
|
|
139
152
|
validator = yup.mixed();
|
|
140
|
-
} else if (isScalarAttribute(attr)) {
|
|
141
|
-
validator = createScalarAttributeValidator(createOrUpdate)(
|
|
153
|
+
} else if (isScalarAttribute(metas.attr)) {
|
|
154
|
+
validator = createScalarAttributeValidator(createOrUpdate)(metas, options);
|
|
142
155
|
} else {
|
|
143
|
-
if (attr.type === 'component') {
|
|
144
|
-
validator = createComponentValidator(createOrUpdate)(
|
|
145
|
-
} else if (attr.type === 'dynamiczone') {
|
|
146
|
-
validator = createDzValidator(createOrUpdate)(
|
|
156
|
+
if (metas.attr.type === 'component') {
|
|
157
|
+
validator = createComponentValidator(createOrUpdate)(metas, options);
|
|
158
|
+
} else if (metas.attr.type === 'dynamiczone') {
|
|
159
|
+
validator = createDzValidator(createOrUpdate)(metas, options);
|
|
147
160
|
} else {
|
|
148
|
-
validator = createRelationValidator(createOrUpdate)(
|
|
161
|
+
validator = createRelationValidator(createOrUpdate)(metas, options);
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
validator = preventCast(validator);
|
|
152
165
|
}
|
|
153
166
|
|
|
154
|
-
validator = addDefault(createOrUpdate)(
|
|
167
|
+
validator = addDefault(createOrUpdate)(validator, metas);
|
|
155
168
|
|
|
156
169
|
return validator;
|
|
157
170
|
};
|
|
158
171
|
|
|
159
|
-
const createModelValidator = createOrUpdate => (model, data,
|
|
172
|
+
const createModelValidator = createOrUpdate => ({ model, data, entity }, options) => {
|
|
160
173
|
const writableAttributes = model ? getWritableAttributes(model) : [];
|
|
161
174
|
|
|
162
175
|
const schema = writableAttributes.reduce((validators, attributeName) => {
|
|
163
176
|
const validator = createAttributeValidator(createOrUpdate)(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
177
|
+
{
|
|
178
|
+
attr: model.attributes[attributeName],
|
|
179
|
+
updatedAttribute: { name: attributeName, value: prop(attributeName, data) },
|
|
180
|
+
model,
|
|
181
|
+
entity,
|
|
182
|
+
},
|
|
183
|
+
options
|
|
167
184
|
);
|
|
168
185
|
|
|
169
186
|
return assoc(attributeName, validator)(validators);
|
|
@@ -172,13 +189,29 @@ const createModelValidator = createOrUpdate => (model, data, { isDraft }) => {
|
|
|
172
189
|
return yup.object().shape(schema);
|
|
173
190
|
};
|
|
174
191
|
|
|
175
|
-
const createValidateEntity = createOrUpdate => async (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
192
|
+
const createValidateEntity = createOrUpdate => async (
|
|
193
|
+
model,
|
|
194
|
+
data,
|
|
195
|
+
{ isDraft = false } = {},
|
|
196
|
+
entity = null
|
|
197
|
+
) => {
|
|
198
|
+
if (!isObject(data)) {
|
|
199
|
+
const { displayName } = model.info;
|
|
200
|
+
|
|
201
|
+
throw new ValidationError(
|
|
202
|
+
`Invalid payload submitted for the ${createOrUpdate} of an entity of type ${displayName}. Expected an object, but got ${typeof data}`
|
|
203
|
+
);
|
|
181
204
|
}
|
|
205
|
+
|
|
206
|
+
const validator = createModelValidator(createOrUpdate)(
|
|
207
|
+
{
|
|
208
|
+
model,
|
|
209
|
+
data,
|
|
210
|
+
entity,
|
|
211
|
+
},
|
|
212
|
+
{ isDraft }
|
|
213
|
+
).required();
|
|
214
|
+
return validateYupSchema(validator, { strict: false, abortEarly: false })(data);
|
|
182
215
|
};
|
|
183
216
|
|
|
184
217
|
module.exports = {
|
|
@@ -4,72 +4,155 @@ const _ = require('lodash');
|
|
|
4
4
|
|
|
5
5
|
const { yup } = require('@strapi/utils');
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @type {import('yup').StringSchema} StringSchema
|
|
9
|
+
* @type {import('yup').NumberSchema} NumberSchema
|
|
10
|
+
* @type {import('yup').AnySchema} AnySchema
|
|
11
|
+
*/
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Utility function to compose validators
|
|
9
15
|
*/
|
|
10
|
-
const composeValidators = (...fns) => (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
const composeValidators = (...fns) => (...args) => {
|
|
17
|
+
let validator = yup.mixed();
|
|
18
|
+
|
|
19
|
+
// if we receive a schema then use it as base schema for nested composition
|
|
20
|
+
if (yup.isSchema(args[0])) {
|
|
21
|
+
validator = args[0];
|
|
22
|
+
args = args.slice(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return fns.reduce((validator, fn) => fn(validator, ...args), validator);
|
|
14
26
|
};
|
|
15
27
|
|
|
16
28
|
/* Validator utils */
|
|
17
29
|
|
|
18
30
|
/**
|
|
19
31
|
* Adds minLength validator
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {Object}
|
|
32
|
+
* @param {StringSchema} validator yup validator
|
|
33
|
+
* @param {Object} metas
|
|
34
|
+
* @param {{ minLength: Number }} metas.attr model attribute
|
|
35
|
+
* @param {Object} options
|
|
36
|
+
* @param {boolean} options.isDraft
|
|
37
|
+
*
|
|
38
|
+
* @returns {StringSchema}
|
|
22
39
|
*/
|
|
23
|
-
const addMinLengthValidator = ({
|
|
24
|
-
_.isInteger(minLength) && !isDraft ? validator.min(minLength) : validator;
|
|
40
|
+
const addMinLengthValidator = (validator, { attr }, { isDraft }) =>
|
|
41
|
+
_.isInteger(attr.minLength) && !isDraft ? validator.min(attr.minLength) : validator;
|
|
25
42
|
|
|
26
43
|
/**
|
|
27
44
|
* Adds maxLength validator
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {Object}
|
|
45
|
+
* @param {StringSchema} validator yup validator
|
|
46
|
+
* @param {Object} metas
|
|
47
|
+
* @param {{ maxLength: Number }} metas.attr model attribute
|
|
48
|
+
*
|
|
49
|
+
* @returns {StringSchema}
|
|
30
50
|
*/
|
|
31
|
-
const addMaxLengthValidator = ({
|
|
32
|
-
_.isInteger(maxLength) ? validator.max(maxLength) : validator;
|
|
51
|
+
const addMaxLengthValidator = (validator, { attr }) =>
|
|
52
|
+
_.isInteger(attr.maxLength) ? validator.max(attr.maxLength) : validator;
|
|
33
53
|
|
|
34
54
|
/**
|
|
35
55
|
* Adds min integer validator
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {Object}
|
|
56
|
+
* @param {NumberSchema} validator yup validator
|
|
57
|
+
* @param {Object} metas
|
|
58
|
+
* @param {{ min: Number }} metas.attr model attribute
|
|
59
|
+
*
|
|
60
|
+
* @returns {NumberSchema}
|
|
38
61
|
*/
|
|
39
|
-
const addMinIntegerValidator = ({
|
|
40
|
-
_.isNumber(min) ? validator.min(_.toInteger(min)) : validator;
|
|
62
|
+
const addMinIntegerValidator = (validator, { attr }) =>
|
|
63
|
+
_.isNumber(attr.min) ? validator.min(_.toInteger(attr.min)) : validator;
|
|
41
64
|
|
|
42
65
|
/**
|
|
43
66
|
* Adds max integer validator
|
|
44
|
-
* @param {
|
|
45
|
-
* @param {Object}
|
|
67
|
+
* @param {NumberSchema} validator yup validator
|
|
68
|
+
* @param {Object} metas
|
|
69
|
+
* @param {{ max: Number }} metas.attr model attribute
|
|
70
|
+
*
|
|
71
|
+
* @returns {NumberSchema}
|
|
46
72
|
*/
|
|
47
|
-
const addMaxIntegerValidator = ({
|
|
48
|
-
_.isNumber(max) ? validator.max(_.toInteger(max)) : validator;
|
|
73
|
+
const addMaxIntegerValidator = (validator, { attr }) =>
|
|
74
|
+
_.isNumber(attr.max) ? validator.max(_.toInteger(attr.max)) : validator;
|
|
49
75
|
|
|
50
76
|
/**
|
|
51
77
|
* Adds min float/decimal validator
|
|
52
|
-
* @param {
|
|
53
|
-
* @param {Object}
|
|
78
|
+
* @param {NumberSchema} validator yup validator
|
|
79
|
+
* @param {Object} metas
|
|
80
|
+
* @param {{ min: Number }} metas.attr model attribute
|
|
81
|
+
*
|
|
82
|
+
* @returns {NumberSchema}
|
|
54
83
|
*/
|
|
55
|
-
const addMinFloatValidator = ({
|
|
56
|
-
_.isNumber(min) ? validator.min(min) : validator;
|
|
84
|
+
const addMinFloatValidator = (validator, { attr }) =>
|
|
85
|
+
_.isNumber(attr.min) ? validator.min(attr.min) : validator;
|
|
57
86
|
|
|
58
87
|
/**
|
|
59
88
|
* Adds max float/decimal validator
|
|
60
|
-
* @param {
|
|
61
|
-
* @param {Object}
|
|
89
|
+
* @param {NumberSchema} validator yup validator
|
|
90
|
+
* @param {Object} metas model attribute
|
|
91
|
+
* @param {{ max: Number }} metas.attr
|
|
92
|
+
*
|
|
93
|
+
* @returns {NumberSchema}
|
|
62
94
|
*/
|
|
63
|
-
const addMaxFloatValidator = ({
|
|
64
|
-
_.isNumber(max) ? validator.max(max) : validator;
|
|
95
|
+
const addMaxFloatValidator = (validator, { attr }) =>
|
|
96
|
+
_.isNumber(attr.max) ? validator.max(attr.max) : validator;
|
|
65
97
|
|
|
66
98
|
/**
|
|
67
99
|
* Adds regex validator
|
|
68
|
-
* @param {
|
|
69
|
-
* @param {Object}
|
|
100
|
+
* @param {StringSchema} validator yup validator
|
|
101
|
+
* @param {Object} metas model attribute
|
|
102
|
+
* @param {{ regex: RegExp }} metas.attr
|
|
103
|
+
*
|
|
104
|
+
* @returns {StringSchema}
|
|
70
105
|
*/
|
|
71
|
-
const addStringRegexValidator = ({
|
|
72
|
-
_.isUndefined(regex) ? validator : validator.matches(new RegExp(regex));
|
|
106
|
+
const addStringRegexValidator = (validator, { attr }) =>
|
|
107
|
+
_.isUndefined(attr.regex) ? validator : validator.matches(new RegExp(attr.regex));
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
*
|
|
111
|
+
* @param {AnySchema} validator
|
|
112
|
+
* @param {Object} metas
|
|
113
|
+
* @param {{ unique: Boolean, type: String }} metas.attr
|
|
114
|
+
* @param {{ uid: String }} metas.model
|
|
115
|
+
* @param {{ name: String, value: any }} metas.updatedAttribute
|
|
116
|
+
* @param {Object} metas.entity
|
|
117
|
+
*
|
|
118
|
+
* @returns {AnySchema}
|
|
119
|
+
*/
|
|
120
|
+
const addUniqueValidator = (validator, { attr, model, updatedAttribute, entity }) => {
|
|
121
|
+
if (!attr.unique && attr.type !== 'uid') {
|
|
122
|
+
return validator;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return validator.test('unique', 'This attribute must be unique', async value => {
|
|
126
|
+
/**
|
|
127
|
+
* If the attribute value is `null` we want to skip the unique validation.
|
|
128
|
+
* Otherwise it'll only accept a single `null` entry in the database.
|
|
129
|
+
*/
|
|
130
|
+
if (_.isNil(updatedAttribute.value)) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* If the attribute is unchanged we skip the unique verification. This will
|
|
136
|
+
* prevent the validator to be triggered in case the user activated the
|
|
137
|
+
* unique constraint after already creating multiple entries with
|
|
138
|
+
* the same attribute value for that field.
|
|
139
|
+
*/
|
|
140
|
+
if (entity && updatedAttribute.value === entity[updatedAttribute.name]) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let whereParams = entity
|
|
145
|
+
? { $and: [{ [updatedAttribute.name]: value }, { $not: { id: entity.id } }] }
|
|
146
|
+
: { [updatedAttribute.name]: value };
|
|
147
|
+
|
|
148
|
+
const record = await strapi.db.query(model.uid).findOne({
|
|
149
|
+
select: ['id'],
|
|
150
|
+
where: whereParams,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return !record;
|
|
154
|
+
});
|
|
155
|
+
};
|
|
73
156
|
|
|
74
157
|
/* Type validators */
|
|
75
158
|
|
|
@@ -77,29 +160,32 @@ const stringValidator = composeValidators(
|
|
|
77
160
|
() => yup.string().transform((val, originalVal) => originalVal),
|
|
78
161
|
addMinLengthValidator,
|
|
79
162
|
addMaxLengthValidator,
|
|
80
|
-
addStringRegexValidator
|
|
163
|
+
addStringRegexValidator,
|
|
164
|
+
addUniqueValidator
|
|
81
165
|
);
|
|
82
166
|
|
|
83
|
-
const emailValidator = composeValidators(stringValidator,
|
|
167
|
+
const emailValidator = composeValidators(stringValidator, validator => validator.email());
|
|
84
168
|
|
|
85
|
-
const uidValidator = composeValidators(stringValidator,
|
|
169
|
+
const uidValidator = composeValidators(stringValidator, validator =>
|
|
86
170
|
validator.matches(new RegExp('^[A-Za-z0-9-_.~]*$'))
|
|
87
171
|
);
|
|
88
172
|
|
|
89
|
-
const enumerationValidator = attr => {
|
|
173
|
+
const enumerationValidator = ({ attr }) => {
|
|
90
174
|
return yup.string().oneOf((Array.isArray(attr.enum) ? attr.enum : [attr.enum]).concat(null));
|
|
91
175
|
};
|
|
92
176
|
|
|
93
177
|
const integerValidator = composeValidators(
|
|
94
178
|
() => yup.number().integer(),
|
|
95
179
|
addMinIntegerValidator,
|
|
96
|
-
addMaxIntegerValidator
|
|
180
|
+
addMaxIntegerValidator,
|
|
181
|
+
addUniqueValidator
|
|
97
182
|
);
|
|
98
183
|
|
|
99
184
|
const floatValidator = composeValidators(
|
|
100
185
|
() => yup.number(),
|
|
101
186
|
addMinFloatValidator,
|
|
102
|
-
addMaxFloatValidator
|
|
187
|
+
addMaxFloatValidator,
|
|
188
|
+
addUniqueValidator
|
|
103
189
|
);
|
|
104
190
|
|
|
105
191
|
module.exports = {
|
|
@@ -113,11 +199,11 @@ module.exports = {
|
|
|
113
199
|
uid: uidValidator,
|
|
114
200
|
json: () => yup.mixed(),
|
|
115
201
|
integer: integerValidator,
|
|
116
|
-
biginteger: ()
|
|
202
|
+
biginteger: composeValidators(addUniqueValidator),
|
|
117
203
|
float: floatValidator,
|
|
118
204
|
decimal: floatValidator,
|
|
119
|
-
date: ()
|
|
120
|
-
time: ()
|
|
121
|
-
datetime: ()
|
|
122
|
-
timestamp: ()
|
|
205
|
+
date: composeValidators(addUniqueValidator),
|
|
206
|
+
time: composeValidators(addUniqueValidator),
|
|
207
|
+
datetime: composeValidators(addUniqueValidator),
|
|
208
|
+
timestamp: composeValidators(addUniqueValidator),
|
|
123
209
|
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const createError = require('http-errors');
|
|
4
|
+
const {
|
|
5
|
+
NotFoundError,
|
|
6
|
+
UnauthorizedError,
|
|
7
|
+
ForbiddenError,
|
|
8
|
+
PayloadTooLargeError,
|
|
9
|
+
} = require('@strapi/utils').errors;
|
|
10
|
+
|
|
11
|
+
const mapErrorsAndStatus = [
|
|
12
|
+
{
|
|
13
|
+
classError: UnauthorizedError,
|
|
14
|
+
status: 401,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
classError: ForbiddenError,
|
|
18
|
+
status: 403,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
classError: NotFoundError,
|
|
22
|
+
status: 404,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
classError: PayloadTooLargeError,
|
|
26
|
+
status: 413,
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const formatApplicationError = error => {
|
|
31
|
+
const errorAndStatus = mapErrorsAndStatus.find(pair => error instanceof pair.classError);
|
|
32
|
+
const status = errorAndStatus ? errorAndStatus.status : 400;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
status,
|
|
36
|
+
body: {
|
|
37
|
+
data: null,
|
|
38
|
+
error: {
|
|
39
|
+
status,
|
|
40
|
+
name: error.name,
|
|
41
|
+
message: error.message,
|
|
42
|
+
details: error.details,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const formatHttpError = error => {
|
|
49
|
+
return {
|
|
50
|
+
status: error.status,
|
|
51
|
+
body: {
|
|
52
|
+
data: null,
|
|
53
|
+
error: {
|
|
54
|
+
status: error.status,
|
|
55
|
+
name: error.name,
|
|
56
|
+
message: error.message,
|
|
57
|
+
details: error.details,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const formatInternalError = error => {
|
|
64
|
+
const httpError = createError(error);
|
|
65
|
+
|
|
66
|
+
if (httpError.expose) {
|
|
67
|
+
return formatHttpError(httpError);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return formatHttpError(createError(httpError.status || 500));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
formatApplicationError,
|
|
75
|
+
formatHttpError,
|
|
76
|
+
formatInternalError,
|
|
77
|
+
};
|
package/lib/services/fs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
3
|
* Strapi telemetry package.
|
|
4
|
-
* You can learn more at https://strapi.io/
|
|
4
|
+
* You can learn more at https://docs.strapi.io/developer-docs/latest/getting-started/usage-information.html
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
@@ -30,42 +30,44 @@ const createTelemetryInstance = strapi => {
|
|
|
30
30
|
const sender = createSender(strapi);
|
|
31
31
|
const sendEvent = wrapWithRateLimit(sender, { limitedEvents: LIMITED_EVENTS });
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
33
|
+
return {
|
|
34
|
+
register() {
|
|
35
|
+
if (!isDisabled) {
|
|
36
|
+
const pingCron = scheduleJob('0 0 12 * * *', () => sendEvent('ping'));
|
|
37
|
+
crons.push(pingCron);
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
strapi.server.use(createMiddleware({ sendEvent }));
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
bootstrap() {
|
|
43
|
+
if (strapi.EE === true && ee.isEE === true) {
|
|
44
|
+
const pingDisabled =
|
|
45
|
+
isTruthy(process.env.STRAPI_LICENSE_PING_DISABLED) && ee.licenseInfo.type === 'gold';
|
|
46
|
+
|
|
47
|
+
const sendLicenseCheck = () => {
|
|
48
|
+
return sendEvent(
|
|
49
|
+
'didCheckLicense',
|
|
50
|
+
{
|
|
51
|
+
licenseInfo: {
|
|
52
|
+
...ee.licenseInfo,
|
|
53
|
+
projectHash: hashProject(strapi),
|
|
54
|
+
dependencyHash: hashDep(strapi),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
headers: { 'x-strapi-project': 'enterprise' },
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (!pingDisabled) {
|
|
64
|
+
const licenseCron = scheduleJob('0 0 0 * * 7', () => sendLicenseCheck());
|
|
65
|
+
crons.push(licenseCron);
|
|
66
|
+
|
|
67
|
+
sendLicenseCheck();
|
|
56
68
|
}
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!pingDisabled) {
|
|
61
|
-
const licenseCron = scheduleJob('0 0 0 * * 7', () => sendLicenseCheck());
|
|
62
|
-
crons.push(licenseCron);
|
|
63
|
-
|
|
64
|
-
sendLicenseCheck();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
+
}
|
|
70
|
+
},
|
|
69
71
|
destroy() {
|
|
70
72
|
// clear open handles
|
|
71
73
|
crons.forEach(cron => cron.cancel());
|
|
@@ -88,7 +90,7 @@ const hashProject = strapi =>
|
|
|
88
90
|
|
|
89
91
|
const hashDep = strapi => {
|
|
90
92
|
const depStr = JSON.stringify(strapi.config.info.dependencies);
|
|
91
|
-
const readmePath = path.join(strapi.
|
|
93
|
+
const readmePath = path.join(strapi.dirs.root, 'README.md');
|
|
92
94
|
|
|
93
95
|
try {
|
|
94
96
|
if (fs.existsSync(readmePath)) {
|